From 8dd284da05223debdfd814d94325579ecc49ae9c Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 3 Jun 2026 20:43:53 +0900 Subject: [PATCH 1/2] Use Data_Make_Struct for parser allocation Allocate the HTTP 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. --- ext/thin_parser/thin.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ext/thin_parser/thin.c b/ext/thin_parser/thin.c index 082d7bd5..470819c1 100644 --- a/ext/thin_parser/thin.c +++ b/ext/thin_parser/thin.c @@ -221,7 +221,7 @@ void Thin_HttpParser_free(void *data) { TRACE(); if(data) { - free(data); + xfree(data); } } @@ -229,8 +229,9 @@ void Thin_HttpParser_free(void *data) { VALUE Thin_HttpParser_alloc(VALUE klass) { VALUE obj; - http_parser *hp = ALLOC_N(http_parser, 1); + http_parser *hp; TRACE(); + obj = Data_Make_Struct(klass, http_parser, NULL, Thin_HttpParser_free, hp); hp->http_field = http_field; hp->request_method = request_method; hp->request_uri = request_uri; @@ -241,8 +242,6 @@ VALUE Thin_HttpParser_alloc(VALUE klass) hp->header_done = header_done; thin_http_parser_init(hp); - obj = Data_Wrap_Struct(klass, NULL, Thin_HttpParser_free, hp); - return obj; } From 5db56f3bfc5e924458d06cd22a3b0c22419d6544 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Wed, 3 Jun 2026 22:10:37 +0900 Subject: [PATCH 2/2] Use typed data for HTTP parser Use TypedData_Make_Struct and TypedData_Get_Struct when available so Thin::HttpParser does not depend on the old Data_* accessors removed by newer Ruby. --- ext/thin_parser/ext_help.h | 2 +- ext/thin_parser/thin.c | 26 ++++++++++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/ext/thin_parser/ext_help.h b/ext/thin_parser/ext_help.h index 8b4d754c..1293fc44 100644 --- a/ext/thin_parser/ext_help.h +++ b/ext/thin_parser/ext_help.h @@ -2,7 +2,7 @@ #define ext_help_h #define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "NULL found for " # T " when shouldn't be."); -#define DATA_GET(from,type,name) Data_Get_Struct(from,type,name); RAISE_NOT_NULL(name); +#define DATA_GET(from,type,name,data_type) TypedData_Get_Struct(from,type,data_type,name); RAISE_NOT_NULL(name); #define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "Wrong argument type for " # V " required " # T); #ifdef DEBUG diff --git a/ext/thin_parser/thin.c b/ext/thin_parser/thin.c index 470819c1..e6dba531 100644 --- a/ext/thin_parser/thin.c +++ b/ext/thin_parser/thin.c @@ -225,13 +225,23 @@ void Thin_HttpParser_free(void *data) { } } +static size_t Thin_HttpParser_memsize(const void *data) { + return data ? sizeof(http_parser) : 0; +} + +static const rb_data_type_t Thin_HttpParser_type = { + "Thin::HttpParser", + {NULL, Thin_HttpParser_free, Thin_HttpParser_memsize,}, + NULL, NULL, 0, +}; + VALUE Thin_HttpParser_alloc(VALUE klass) { VALUE obj; http_parser *hp; TRACE(); - obj = Data_Make_Struct(klass, http_parser, NULL, Thin_HttpParser_free, hp); + obj = TypedData_Make_Struct(klass, http_parser, &Thin_HttpParser_type, hp); hp->http_field = http_field; hp->request_method = request_method; hp->request_uri = request_uri; @@ -255,7 +265,7 @@ VALUE Thin_HttpParser_alloc(VALUE klass) VALUE Thin_HttpParser_init(VALUE self) { http_parser *http = NULL; - DATA_GET(self, http_parser, http); + DATA_GET(self, http_parser, http, &Thin_HttpParser_type); thin_http_parser_init(http); return self; @@ -272,7 +282,7 @@ VALUE Thin_HttpParser_init(VALUE self) VALUE Thin_HttpParser_reset(VALUE self) { http_parser *http = NULL; - DATA_GET(self, http_parser, http); + DATA_GET(self, http_parser, http, &Thin_HttpParser_type); thin_http_parser_init(http); return Qnil; @@ -289,7 +299,7 @@ VALUE Thin_HttpParser_reset(VALUE self) VALUE Thin_HttpParser_finish(VALUE self) { http_parser *http = NULL; - DATA_GET(self, http_parser, http); + DATA_GET(self, http_parser, http, &Thin_HttpParser_type); thin_http_parser_finish(http); return thin_http_parser_is_finished(http) ? Qtrue : Qfalse; @@ -320,7 +330,7 @@ VALUE Thin_HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE star char *dptr = NULL; long dlen = 0; - DATA_GET(self, http_parser, http); + DATA_GET(self, http_parser, http, &Thin_HttpParser_type); from = FIX2INT(start); dptr = RSTRING_PTR(data); @@ -353,7 +363,7 @@ VALUE Thin_HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE star VALUE Thin_HttpParser_has_error(VALUE self) { http_parser *http = NULL; - DATA_GET(self, http_parser, http); + DATA_GET(self, http_parser, http, &Thin_HttpParser_type); return thin_http_parser_has_error(http) ? Qtrue : Qfalse; } @@ -368,7 +378,7 @@ VALUE Thin_HttpParser_has_error(VALUE self) VALUE Thin_HttpParser_is_finished(VALUE self) { http_parser *http = NULL; - DATA_GET(self, http_parser, http); + DATA_GET(self, http_parser, http, &Thin_HttpParser_type); return thin_http_parser_is_finished(http) ? Qtrue : Qfalse; } @@ -384,7 +394,7 @@ VALUE Thin_HttpParser_is_finished(VALUE self) VALUE Thin_HttpParser_nread(VALUE self) { http_parser *http = NULL; - DATA_GET(self, http_parser, http); + DATA_GET(self, http_parser, http, &Thin_HttpParser_type); return INT2FIX(http->nread); }