From 15b09f8f62fc357c97ac97d4d5ccf11d49a7d722 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 1 Jun 2026 20:43:40 +0900 Subject: [PATCH 1/3] Use Data_Make_Struct for parser allocation Allocate the parser with Data_Make_Struct so Ruby owns the memory as soon as the object is created. This avoids leaking the malloc buffer if Data_Wrap_Struct raises while allocating the Ruby object. --- mri/ext/llhttp/llhttp_ext.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mri/ext/llhttp/llhttp_ext.c b/mri/ext/llhttp/llhttp_ext.c index f6bee8c..a694790 100644 --- a/mri/ext/llhttp/llhttp_ext.c +++ b/mri/ext/llhttp/llhttp_ext.c @@ -42,13 +42,14 @@ static void rb_llhttp_free(llhttp_t *parser) { } VALUE rb_llhttp_allocate(VALUE klass) { - llhttp_t *parser = (llhttp_t *)malloc(sizeof(llhttp_t)); + llhttp_t *parser; + VALUE self = Data_Make_Struct(klass, llhttp_t, 0, rb_llhttp_free, parser); // Set data to false so we know when the parser has been initialized. // parser->data = 0; - return Data_Wrap_Struct(klass, 0, rb_llhttp_free, parser); + return self; } VALUE rb_llhttp_callback_call(VALUE delegate, ID method) { From 169d7ba00c66ea315796bd6d4533755beae96fb9 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 1 Jun 2026 21:35:56 +0900 Subject: [PATCH 2/3] Use typed data for LLHttp parser Use TypedData_Make_Struct and TypedData_Get_Struct when available so the parser wrapper does not depend on the old Data_* accessors removed by newer Ruby. --- mri/ext/llhttp/llhttp_ext.c | 51 +++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/mri/ext/llhttp/llhttp_ext.c b/mri/ext/llhttp/llhttp_ext.c index a694790..1ff03f7 100644 --- a/mri/ext/llhttp/llhttp_ext.c +++ b/mri/ext/llhttp/llhttp_ext.c @@ -28,7 +28,8 @@ typedef struct { ID on_header_value_complete; } rb_llhttp_parser_data; -static void rb_llhttp_free(llhttp_t *parser) { +static void rb_llhttp_free(void *data) { + llhttp_t *parser = data; rb_llhttp_parser_data *parserData = (rb_llhttp_parser_data*) parser->data; // If `parserData` is not `0`, it (and settings) were initialized. @@ -41,9 +42,37 @@ static void rb_llhttp_free(llhttp_t *parser) { free(parser); } +#ifdef TypedData_Make_Struct +static size_t rb_llhttp_memsize(const void *data) { + return data ? sizeof(llhttp_t) : 0; +} + +static const rb_data_type_t rb_llhttp_type = { + "LLHttp::Parser", + {0, rb_llhttp_free, rb_llhttp_memsize,}, + 0, 0, 0, +}; +#endif + +static llhttp_t *rb_llhttp_get(VALUE self) { + llhttp_t *parser; + +#ifdef TypedData_Make_Struct + TypedData_Get_Struct(self, llhttp_t, &rb_llhttp_type, parser); +#else + Data_Get_Struct(self, llhttp_t, parser); +#endif + + return parser; +} + VALUE rb_llhttp_allocate(VALUE klass) { llhttp_t *parser; +#ifdef TypedData_Make_Struct + VALUE self = TypedData_Make_Struct(klass, llhttp_t, &rb_llhttp_type, parser); +#else VALUE self = Data_Make_Struct(klass, llhttp_t, 0, rb_llhttp_free, parser); +#endif // Set data to false so we know when the parser has been initialized. // @@ -167,7 +196,7 @@ int rb_llhttp_on_header_value_complete(llhttp_t *parser) { VALUE rb_llhttp_parse(VALUE self, VALUE data) { llhttp_t *parser; - Data_Get_Struct(self, llhttp_t, parser); + parser = rb_llhttp_get(self); enum llhttp_errno err = llhttp_execute(parser, RSTRING_PTR(data), RSTRING_LEN(data)); @@ -181,7 +210,7 @@ VALUE rb_llhttp_parse(VALUE self, VALUE data) { VALUE rb_llhttp_finish(VALUE self) { llhttp_t *parser; - Data_Get_Struct(self, llhttp_t, parser); + parser = rb_llhttp_get(self); enum llhttp_errno err = llhttp_finish(parser); @@ -195,7 +224,7 @@ VALUE rb_llhttp_finish(VALUE self) { VALUE rb_llhttp_reset(VALUE self) { llhttp_t *parser; - Data_Get_Struct(self, llhttp_t, parser); + parser = rb_llhttp_get(self); llhttp_reset(parser); @@ -205,7 +234,7 @@ VALUE rb_llhttp_reset(VALUE self) { VALUE rb_llhttp_content_length(VALUE self) { llhttp_t *parser; - Data_Get_Struct(self, llhttp_t, parser); + parser = rb_llhttp_get(self); return ULL2NUM(parser->content_length); } @@ -213,7 +242,7 @@ VALUE rb_llhttp_content_length(VALUE self) { VALUE rb_llhttp_method_name(VALUE self) { llhttp_t *parser; - Data_Get_Struct(self, llhttp_t, parser); + parser = rb_llhttp_get(self); return rb_str_new_cstr(llhttp_method_name(parser->method)); } @@ -221,7 +250,7 @@ VALUE rb_llhttp_method_name(VALUE self) { VALUE rb_llhttp_status_code(VALUE self) { llhttp_t *parser; - Data_Get_Struct(self, llhttp_t, parser); + parser = rb_llhttp_get(self); return UINT2NUM(parser->status_code); } @@ -229,7 +258,7 @@ VALUE rb_llhttp_status_code(VALUE self) { VALUE rb_llhttp_http_major(VALUE self) { llhttp_t *parser; - Data_Get_Struct(self, llhttp_t, parser); + parser = rb_llhttp_get(self); return UINT2NUM(parser->http_major); } @@ -237,7 +266,7 @@ VALUE rb_llhttp_http_major(VALUE self) { VALUE rb_llhttp_http_minor(VALUE self) { llhttp_t *parser; - Data_Get_Struct(self, llhttp_t, parser); + parser = rb_llhttp_get(self); return UINT2NUM(parser->http_minor); } @@ -245,7 +274,7 @@ VALUE rb_llhttp_http_minor(VALUE self) { VALUE rb_llhttp_keep_alive(VALUE self) { llhttp_t *parser; - Data_Get_Struct(self, llhttp_t, parser); + parser = rb_llhttp_get(self); int ret = llhttp_should_keep_alive(parser); @@ -255,7 +284,7 @@ VALUE rb_llhttp_keep_alive(VALUE self) { static VALUE rb_llhttp_init(VALUE self, VALUE type) { llhttp_t *parser; - Data_Get_Struct(self, llhttp_t, parser); + parser = rb_llhttp_get(self); llhttp_settings_t *settings = (llhttp_settings_t *)malloc(sizeof(llhttp_settings_t)); llhttp_settings_init(settings); From 11448f79b1a0a43d98c2c71207ac0d004ad79741 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 1 Jun 2026 21:37:41 +0900 Subject: [PATCH 3/3] Match llhttp_data_cb signatures Use const char pointers for data callbacks so their signatures match llhttp_data_cb and the assignments no longer need function pointer casts. --- mri/ext/llhttp/llhttp_ext.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/mri/ext/llhttp/llhttp_ext.c b/mri/ext/llhttp/llhttp_ext.c index 1ff03f7..92552fb 100644 --- a/mri/ext/llhttp/llhttp_ext.c +++ b/mri/ext/llhttp/llhttp_ext.c @@ -85,7 +85,7 @@ VALUE rb_llhttp_callback_call(VALUE delegate, ID method) { return rb_funcall(delegate, method, 0); } -void rb_llhttp_data_callback_call(VALUE delegate, ID method, char *data, size_t length) { +void rb_llhttp_data_callback_call(VALUE delegate, ID method, const char *data, size_t length) { rb_funcall(delegate, method, 1, rb_str_new(data, length)); } @@ -113,7 +113,7 @@ int rb_llhttp_on_chunk_header(llhttp_t *parser) { return NUM2INT(rb_llhttp_callback_call(parserData->delegate, parserData->on_chunk_header)); } -int rb_llhttp_on_url(llhttp_t *parser, char *data, size_t length) { +int rb_llhttp_on_url(llhttp_t *parser, const char *data, size_t length) { rb_llhttp_parser_data *parserData = (rb_llhttp_parser_data*) parser->data; rb_llhttp_data_callback_call(parserData->delegate, parserData->on_url, data, length); @@ -121,7 +121,7 @@ int rb_llhttp_on_url(llhttp_t *parser, char *data, size_t length) { return 0; } -int rb_llhttp_on_status(llhttp_t *parser, char *data, size_t length) { +int rb_llhttp_on_status(llhttp_t *parser, const char *data, size_t length) { rb_llhttp_parser_data *parserData = (rb_llhttp_parser_data*) parser->data; rb_llhttp_data_callback_call(parserData->delegate, parserData->on_status, data, length); @@ -129,7 +129,7 @@ int rb_llhttp_on_status(llhttp_t *parser, char *data, size_t length) { return 0; } -int rb_llhttp_on_header_field(llhttp_t *parser, char *data, size_t length) { +int rb_llhttp_on_header_field(llhttp_t *parser, const char *data, size_t length) { rb_llhttp_parser_data *parserData = (rb_llhttp_parser_data*) parser->data; rb_llhttp_data_callback_call(parserData->delegate, parserData->on_header_field, data, length); @@ -137,7 +137,7 @@ int rb_llhttp_on_header_field(llhttp_t *parser, char *data, size_t length) { return 0; } -int rb_llhttp_on_header_value(llhttp_t *parser, char *data, size_t length) { +int rb_llhttp_on_header_value(llhttp_t *parser, const char *data, size_t length) { rb_llhttp_parser_data *parserData = (rb_llhttp_parser_data*) parser->data; rb_llhttp_data_callback_call(parserData->delegate, parserData->on_header_value, data, length); @@ -145,7 +145,7 @@ int rb_llhttp_on_header_value(llhttp_t *parser, char *data, size_t length) { return 0; } -int rb_llhttp_on_body(llhttp_t *parser, char *data, size_t length) { +int rb_llhttp_on_body(llhttp_t *parser, const char *data, size_t length) { rb_llhttp_parser_data *parserData = (rb_llhttp_parser_data*) parser->data; rb_llhttp_data_callback_call(parserData->delegate, parserData->on_body, data, length); @@ -324,23 +324,23 @@ static VALUE rb_llhttp_init(VALUE self, VALUE type) { } if (rb_respond_to(parserData->delegate, parserData->on_url)) { - settings->on_url = (llhttp_data_cb)rb_llhttp_on_url; + settings->on_url = rb_llhttp_on_url; } if (rb_respond_to(parserData->delegate, parserData->on_status)) { - settings->on_status = (llhttp_data_cb)rb_llhttp_on_status; + settings->on_status = rb_llhttp_on_status; } if (rb_respond_to(parserData->delegate, parserData->on_header_field)) { - settings->on_header_field = (llhttp_data_cb)rb_llhttp_on_header_field; + settings->on_header_field = rb_llhttp_on_header_field; } if (rb_respond_to(parserData->delegate, parserData->on_header_value)) { - settings->on_header_value = (llhttp_data_cb)rb_llhttp_on_header_value; + settings->on_header_value = rb_llhttp_on_header_value; } if (rb_respond_to(parserData->delegate, parserData->on_body)) { - settings->on_body = (llhttp_data_cb)rb_llhttp_on_body; + settings->on_body = rb_llhttp_on_body; } if (rb_respond_to(parserData->delegate, parserData->on_chunk_complete)) {