raptor 0.22.3 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/Dockerfile +3 -1
  4. data/README.md +38 -31
  5. data/docs/raptor-vs-puma.md +44 -38
  6. data/docs/talk.md +1295 -0
  7. data/ext/raptor_bpf/reuseport_select.bpf.c +18 -9
  8. data/ext/raptor_http/extconf.rb +2 -0
  9. data/ext/raptor_http/raptor_http.c +91 -119
  10. data/ext/raptor_http2/raptor_http2.c +272 -223
  11. data/ext/raptor_native/raptor_native.c +38 -20
  12. data/lib/rackup/handler/raptor.rb +12 -11
  13. data/lib/raptor/binder.rb +12 -12
  14. data/lib/raptor/cli.rb +18 -18
  15. data/lib/raptor/cluster.rb +44 -44
  16. data/lib/raptor/control_server.rb +3 -2
  17. data/lib/raptor/detached_body.rb +6 -2
  18. data/lib/raptor/http.rb +7 -5
  19. data/lib/raptor/http1.rb +232 -207
  20. data/lib/raptor/http2.rb +312 -265
  21. data/lib/raptor/reactor.rb +138 -148
  22. data/lib/raptor/reuseport_bpf.rb +62 -38
  23. data/lib/raptor/server.rb +45 -34
  24. data/lib/raptor/stats.rb +15 -3
  25. data/lib/raptor/thread_locals.rb +4 -3
  26. data/lib/raptor/version.rb +1 -1
  27. data/sig/generated/rackup/handler/raptor.rbs +2 -1
  28. data/sig/generated/raptor/binder.rbs +9 -9
  29. data/sig/generated/raptor/cli.rbs +6 -6
  30. data/sig/generated/raptor/cluster.rbs +9 -9
  31. data/sig/generated/raptor/control_server.rbs +2 -1
  32. data/sig/generated/raptor/detached_body.rbs +5 -1
  33. data/sig/generated/raptor/http.rbs +3 -3
  34. data/sig/generated/raptor/http1.rbs +100 -91
  35. data/sig/generated/raptor/http2.rbs +200 -189
  36. data/sig/generated/raptor/reactor.rbs +107 -118
  37. data/sig/generated/raptor/reuseport_bpf.rbs +54 -17
  38. data/sig/generated/raptor/server.rbs +26 -26
  39. data/sig/generated/raptor/stats.rbs +3 -2
  40. data/sig/generated/raptor/thread_locals.rbs +4 -3
  41. metadata +2 -2
  42. data/docs/brisrails-talk.md +0 -2398
@@ -2,13 +2,16 @@
2
2
  #include <bpf/bpf_helpers.h>
3
3
 
4
4
  #define MAX_WORKERS 64
5
+ #define MAX_LISTENERS 16
6
+ #define UNAVAILABLE_LOAD 0xffffffff
5
7
 
6
- // Per-worker listening sockets, keyed by worker index.
8
+ // Per-worker listening sockets, keyed by listener index * MAX_WORKERS +
9
+ // worker index so each bound address has its own row of worker slots.
7
10
  struct {
8
11
  __uint(type, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY);
9
12
  __type(key, __u32);
10
13
  __type(value, __u64);
11
- __uint(max_entries, MAX_WORKERS);
14
+ __uint(max_entries, MAX_WORKERS * MAX_LISTENERS);
12
15
  } socks SEC(".maps");
13
16
 
14
17
  // Worker count at slot 0, per-worker backlog at slots 1..N.
@@ -19,10 +22,8 @@ struct {
19
22
  __uint(max_entries, MAX_WORKERS + 1);
20
23
  } loads SEC(".maps");
21
24
 
22
- // Uses the power-of-two-choices strategy and reserves the selected worker's
23
- // slot before returning. Sampling two hash-selected workers avoids
24
- // concentrating variable-cost requests, while the reservation prevents a
25
- // burst from repeatedly selecting the same reported load.
25
+ // Account for routed connections before the worker reports its new backlog,
26
+ // so bursts do not keep selecting against stale load.
26
27
  SEC("sk_reuseport")
27
28
  int select_less_loaded(struct sk_reuseport_md *ctx) {
28
29
  __u32 count_key = 0;
@@ -48,11 +49,19 @@ int select_less_loaded(struct sk_reuseport_md *ctx) {
48
49
  }
49
50
  }
50
51
 
51
- if (chosen_load) {
52
- __sync_fetch_and_add(chosen_load, 1);
52
+ // Every bound address shares this program, and the kernel only accepts a
53
+ // socket from the reuseport group the connection arrived on, so try the
54
+ // chosen worker's socket for each address until one belongs to the group.
55
+ for (__u32 listener = 0; listener < MAX_LISTENERS; listener++) {
56
+ __u32 sock_key = listener * MAX_WORKERS + chosen_idx;
57
+ if (bpf_sk_select_reuseport(ctx, &socks, &sock_key, 0) == 0) {
58
+ if (chosen_load && *chosen_load != UNAVAILABLE_LOAD) {
59
+ __sync_fetch_and_add(chosen_load, 1);
60
+ }
61
+ break;
62
+ }
53
63
  }
54
64
 
55
- bpf_sk_select_reuseport(ctx, &socks, &chosen_idx, 0);
56
65
  return SK_PASS;
57
66
  }
58
67
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "mkmf"
4
4
 
5
+ # The Ragel-generated parser compares `char` values against 0, which GCC flags
6
+ # as always true where `char` is unsigned (e.g. aarch64 Linux).
5
7
  append_cflags(["-fvisibility=hidden", "-Wno-type-limits"])
6
8
 
7
9
  create_makefile("raptor/raptor_http")
@@ -6,15 +6,24 @@
6
6
  #include "ruby.h"
7
7
  #include "ruby/encoding.h"
8
8
  #include <assert.h>
9
- #include <string.h>
10
9
  #include <ctype.h>
10
+ #include <stdint.h>
11
+ #include <string.h>
11
12
 
12
13
  #define MAX_HEADER_LENGTH (112 * 1024)
13
14
  #define MAX_URI_LENGTH (12 * 1024)
14
15
  #define MAX_FIELD_NAME 256
15
16
  #define MAX_FIELD_VALUE (80 * 1024)
16
17
 
17
- typedef struct raptor_parser {
18
+ // Upper bound on chunk-size lines, extensions and CRLFs a chunked body may
19
+ // carry beyond its payload before it is rejected as malformed.
20
+ #define MAX_CHUNK_OVERHEAD (16 * 1024)
21
+
22
+ #define FLAG_CHUNKED 0x1
23
+ #define FLAG_HAS_BODY 0x2
24
+ #define FLAG_FINISHED 0x4
25
+
26
+ typedef struct {
18
27
  int cs;
19
28
  size_t mark;
20
29
  size_t field_start;
@@ -22,10 +31,10 @@ typedef struct raptor_parser {
22
31
  size_t query_start;
23
32
  size_t nread;
24
33
  size_t body_start;
25
- off_t content_len;
34
+ int64_t content_len;
26
35
  unsigned int flags;
27
36
  VALUE request;
28
- VALUE body;
37
+ // Scratch space for the env key: "HTTP_" prefix + field name + NUL.
29
38
  char buf[MAX_FIELD_NAME + 6];
30
39
  } raptor_parser;
31
40
 
@@ -33,33 +42,29 @@ typedef struct raptor_parser {
33
42
  #define LEN(AT, P) ((P) - buffer - parser->AT)
34
43
  #define PTR_TO(F) (buffer + parser->F)
35
44
 
36
- #define FLAG_CHUNKED 0x1
37
- #define FLAG_HAS_BODY 0x2
38
- #define FLAG_FINISHED 0x4
39
-
40
- #define MAX_CHUNK_OVERHEAD (16 * 1024)
41
-
42
45
  static VALUE eHttpParserError;
43
46
  static VALUE global_request_method;
44
47
  static VALUE global_request_uri;
45
48
  static VALUE global_query_string;
46
49
  static VALUE global_server_protocol;
47
- static VALUE global_request_path;
50
+ static VALUE global_path_info;
48
51
  static VALUE global_fragment;
49
52
  static ID id_complete;
50
53
  static ID id_incomplete;
51
54
  static ID id_malformed;
52
55
  static ID id_too_large;
53
56
 
54
- struct common_field {
57
+ typedef struct {
55
58
  const char *name;
56
59
  size_t len;
57
60
  VALUE interned;
58
- };
61
+ } common_field;
59
62
 
60
63
  #define FIELD(name) { name, sizeof(name) - 1, Qnil }
61
64
 
62
- static struct common_field common_fields[] = {
65
+ // Env keys for frequently seen headers, interned once at load time so parsing
66
+ // these headers does not need to look up the fstring table.
67
+ static common_field common_fields[] = {
63
68
  FIELD("HTTP_HOST"),
64
69
  FIELD("HTTP_USER_AGENT"),
65
70
  FIELD("HTTP_CONNECTION"),
@@ -111,7 +116,7 @@ static struct common_field common_fields[] = {
111
116
 
112
117
  #define NUM_COMMON_FIELDS (sizeof(common_fields) / sizeof(common_fields[0]))
113
118
 
114
- static VALUE raptor_http_intern_field(const char *buf, size_t len) {
119
+ static VALUE intern_field(const char *buf, size_t len) {
115
120
  for (size_t i = 0; i < NUM_COMMON_FIELDS; i++) {
116
121
  if (common_fields[i].len == len && memcmp(common_fields[i].name, buf, len) == 0) {
117
122
  return common_fields[i].interned;
@@ -142,11 +147,21 @@ static int contains_chunked(const char *value, long len) {
142
147
  return 0;
143
148
  }
144
149
 
150
+ // Ruby checks for non-digit characters when it validates request framing.
151
+ static int64_t parse_content_length(const char *value, long len) {
152
+ int64_t content_len = 0;
153
+ for (long i = 0; i < len && value[i] >= '0' && value[i] <= '9'; i++) {
154
+ int digit = value[i] - '0';
155
+ if (content_len > (INT64_MAX - digit) / 10)
156
+ rb_raise(eHttpParserError, "content length too large");
157
+ content_len = content_len * 10 + digit;
158
+ }
159
+ return content_len;
160
+ }
161
+
145
162
  static const int raptor_parser_start = 1;
146
- static const int raptor_parser_first_final = 46;
147
- static const int raptor_parser_error = 0;
148
163
 
149
- size_t raptor_parser_execute(raptor_parser *parser, const char *buffer, size_t len) {
164
+ static size_t raptor_parser_execute(raptor_parser *parser, const char *buffer, size_t len) {
150
165
  const char *p, *pe;
151
166
  int cs = parser->cs;
152
167
 
@@ -257,7 +272,7 @@ tr33:
257
272
  tr37:
258
273
  {
259
274
  VALUE path = rb_str_new(PTR_TO(mark), LEN(mark,p));
260
- rb_hash_aset(parser->request, global_request_path, path);
275
+ rb_hash_aset(parser->request, global_path_info, path);
261
276
  }
262
277
  {
263
278
  if (LEN(mark, p) > MAX_URI_LENGTH)
@@ -397,7 +412,7 @@ tr26:
397
412
  parser->buf[key_len] = '\0';
398
413
  }
399
414
 
400
- VALUE key = raptor_http_intern_field(parser->buf, key_len);
415
+ VALUE key = intern_field(parser->buf, key_len);
401
416
  VALUE value = rb_str_new(PTR_TO(mark), value_len);
402
417
 
403
418
  char *value_ptr = RSTRING_PTR(value);
@@ -408,7 +423,7 @@ tr26:
408
423
  rb_str_set_len(value, value_real_len);
409
424
 
410
425
  if (!needs_http_prefix && parser->field_len == 14) {
411
- parser->content_len = strtoul(RSTRING_PTR(value), NULL, 10);
426
+ parser->content_len = parse_content_length(value_ptr, value_real_len);
412
427
  if (parser->content_len > 0)
413
428
  parser->flags |= FLAG_HAS_BODY;
414
429
  } else if (needs_http_prefix && parser->field_len == 17 &&
@@ -421,7 +436,7 @@ tr26:
421
436
 
422
437
  VALUE existing = rb_hash_aref(parser->request, key);
423
438
  if (!NIL_P(existing)) {
424
- rb_str_cat2(existing, ", ");
439
+ rb_str_cat(existing, ", ", 2);
425
440
  rb_str_append(existing, value);
426
441
  } else {
427
442
  rb_hash_aset(parser->request, key, value);
@@ -457,7 +472,7 @@ tr29:
457
472
  parser->buf[key_len] = '\0';
458
473
  }
459
474
 
460
- VALUE key = raptor_http_intern_field(parser->buf, key_len);
475
+ VALUE key = intern_field(parser->buf, key_len);
461
476
  VALUE value = rb_str_new(PTR_TO(mark), value_len);
462
477
 
463
478
  char *value_ptr = RSTRING_PTR(value);
@@ -468,7 +483,7 @@ tr29:
468
483
  rb_str_set_len(value, value_real_len);
469
484
 
470
485
  if (!needs_http_prefix && parser->field_len == 14) {
471
- parser->content_len = strtoul(RSTRING_PTR(value), NULL, 10);
486
+ parser->content_len = parse_content_length(value_ptr, value_real_len);
472
487
  if (parser->content_len > 0)
473
488
  parser->flags |= FLAG_HAS_BODY;
474
489
  } else if (needs_http_prefix && parser->field_len == 17 &&
@@ -481,7 +496,7 @@ tr29:
481
496
 
482
497
  VALUE existing = rb_hash_aref(parser->request, key);
483
498
  if (!NIL_P(existing)) {
484
- rb_str_cat2(existing, ", ");
499
+ rb_str_cat(existing, ", ", 2);
485
500
  rb_str_append(existing, value);
486
501
  } else {
487
502
  rb_hash_aset(parser->request, key, value);
@@ -629,7 +644,7 @@ tr9:
629
644
  tr38:
630
645
  {
631
646
  VALUE path = rb_str_new(PTR_TO(mark), LEN(mark,p));
632
- rb_hash_aset(parser->request, global_request_path, path);
647
+ rb_hash_aset(parser->request, global_path_info, path);
633
648
  }
634
649
  {
635
650
  if (LEN(mark, p) > MAX_URI_LENGTH)
@@ -761,7 +776,7 @@ case 24:
761
776
  tr39:
762
777
  {
763
778
  VALUE path = rb_str_new(PTR_TO(mark), LEN(mark,p));
764
- rb_hash_aset(parser->request, global_request_path, path);
779
+ rb_hash_aset(parser->request, global_path_info, path);
765
780
  }
766
781
  goto st25;
767
782
  st25:
@@ -1193,31 +1208,7 @@ done:
1193
1208
  return parser->nread;
1194
1209
  }
1195
1210
 
1196
- int raptor_parser_finished(raptor_parser *parser) {
1197
- return (parser->flags & FLAG_FINISHED) != 0;
1198
- }
1199
-
1200
- int raptor_parser_has_body(raptor_parser *parser) {
1201
- return (parser->flags & FLAG_HAS_BODY) != 0;
1202
- }
1203
-
1204
- size_t raptor_parser_content_length(raptor_parser *parser) {
1205
- return parser->content_len;
1206
- }
1207
-
1208
- int raptor_parser_is_chunked(raptor_parser *parser) {
1209
- return (parser->flags & FLAG_CHUNKED) != 0;
1210
- }
1211
-
1212
- int raptor_parser_has_error(raptor_parser *parser) {
1213
- return parser->cs == raptor_parser_error;
1214
- }
1215
-
1216
- int raptor_parser_is_finished(raptor_parser *parser) {
1217
- return parser->cs >= raptor_parser_first_final;
1218
- }
1219
-
1220
- void raptor_parser_init(raptor_parser *parser) {
1211
+ static void raptor_parser_init(raptor_parser *parser) {
1221
1212
  parser->cs = raptor_parser_start;
1222
1213
  parser->mark = 0;
1223
1214
  parser->field_start = 0;
@@ -1228,19 +1219,11 @@ void raptor_parser_init(raptor_parser *parser) {
1228
1219
  parser->content_len = 0;
1229
1220
  parser->flags = 0;
1230
1221
  parser->request = Qnil;
1231
- parser->body = Qnil;
1232
1222
  }
1233
1223
 
1234
- static VALUE cHttpParser;
1235
-
1236
1224
  static void parser_mark(void *ptr) {
1237
1225
  raptor_parser *parser = ptr;
1238
1226
  rb_gc_mark(parser->request);
1239
- rb_gc_mark(parser->body);
1240
- }
1241
-
1242
- static void parser_free(void *ptr) {
1243
- xfree(ptr);
1244
1227
  }
1245
1228
 
1246
1229
  static size_t parser_memsize(const void *ptr) {
@@ -1248,9 +1231,13 @@ static size_t parser_memsize(const void *ptr) {
1248
1231
  }
1249
1232
 
1250
1233
  static const rb_data_type_t parser_type = {
1251
- "raptor_http_parser",
1252
- { parser_mark, parser_free, parser_memsize },
1253
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
1234
+ .wrap_struct_name = "raptor_http_parser",
1235
+ .function = {
1236
+ .dmark = parser_mark,
1237
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
1238
+ .dsize = parser_memsize,
1239
+ },
1240
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
1254
1241
  };
1255
1242
 
1256
1243
  static VALUE parser_alloc(VALUE klass) {
@@ -1282,31 +1269,25 @@ static VALUE parser_execute(VALUE self, VALUE req_hash, VALUE buffer, VALUE star
1282
1269
  static VALUE parser_finished_p(VALUE self) {
1283
1270
  raptor_parser *parser;
1284
1271
  TypedData_Get_Struct(self, raptor_parser, &parser_type, parser);
1285
- return raptor_parser_finished(parser) ? Qtrue : Qfalse;
1272
+ return (parser->flags & FLAG_FINISHED) ? Qtrue : Qfalse;
1286
1273
  }
1287
1274
 
1288
1275
  static VALUE parser_has_body_p(VALUE self) {
1289
1276
  raptor_parser *parser;
1290
1277
  TypedData_Get_Struct(self, raptor_parser, &parser_type, parser);
1291
- return raptor_parser_has_body(parser) ? Qtrue : Qfalse;
1278
+ return (parser->flags & FLAG_HAS_BODY) ? Qtrue : Qfalse;
1292
1279
  }
1293
1280
 
1294
1281
  static VALUE parser_chunked_p(VALUE self) {
1295
1282
  raptor_parser *parser;
1296
1283
  TypedData_Get_Struct(self, raptor_parser, &parser_type, parser);
1297
- return raptor_parser_is_chunked(parser) ? Qtrue : Qfalse;
1284
+ return (parser->flags & FLAG_CHUNKED) ? Qtrue : Qfalse;
1298
1285
  }
1299
1286
 
1300
1287
  static VALUE parser_content_length(VALUE self) {
1301
1288
  raptor_parser *parser;
1302
1289
  TypedData_Get_Struct(self, raptor_parser, &parser_type, parser);
1303
- return SIZET2NUM(raptor_parser_content_length(parser));
1304
- }
1305
-
1306
- static VALUE parser_nread(VALUE self) {
1307
- raptor_parser *parser;
1308
- TypedData_Get_Struct(self, raptor_parser, &parser_type, parser);
1309
- return SIZET2NUM(parser->nread);
1290
+ return LL2NUM(parser->content_len);
1310
1291
  }
1311
1292
 
1312
1293
  static VALUE parser_reset(VALUE self) {
@@ -1316,12 +1297,6 @@ static VALUE parser_reset(VALUE self) {
1316
1297
  return Qnil;
1317
1298
  }
1318
1299
 
1319
- static VALUE parser_body(VALUE self) {
1320
- raptor_parser *parser;
1321
- TypedData_Get_Struct(self, raptor_parser, &parser_type, parser);
1322
- return parser->body;
1323
- }
1324
-
1325
1300
  static const char *find_crlf(const char *buf, size_t len) {
1326
1301
  if (len < 2) return NULL;
1327
1302
  const char *end = buf + len - 1;
@@ -1331,8 +1306,10 @@ static const char *find_crlf(const char *buf, size_t len) {
1331
1306
  return NULL;
1332
1307
  }
1333
1308
 
1309
+ // Decodes a chunked body into [decoded, state], appending the number of bytes
1310
+ // the body consumed when state is :complete so the caller can find any
1311
+ // pipelined request that follows it.
1334
1312
  static VALUE parser_decode_chunked(int argc, VALUE *argv, VALUE self) {
1335
- (void)self;
1336
1313
  VALUE buffer, max_size;
1337
1314
  rb_scan_args(argc, argv, "11", &buffer, &max_size);
1338
1315
  Check_Type(buffer, T_STRING);
@@ -1378,7 +1355,7 @@ static VALUE parser_decode_chunked(int argc, VALUE *argv, VALUE self) {
1378
1355
  if (trailer_offset >= len) return rb_ary_new_from_args(2, decoded, ID2SYM(id_incomplete));
1379
1356
  const char *tcrlf = find_crlf(buf + trailer_offset, len - trailer_offset);
1380
1357
  if (!tcrlf) return rb_ary_new_from_args(2, decoded, ID2SYM(id_incomplete));
1381
- if (tcrlf == buf + trailer_offset) return rb_ary_new_from_args(2, decoded, ID2SYM(id_complete));
1358
+ if (tcrlf == buf + trailer_offset) return rb_ary_new_from_args(3, decoded, ID2SYM(id_complete), SIZET2NUM(trailer_offset + 2));
1382
1359
  trailer_offset = (size_t)(tcrlf - buf) + 2;
1383
1360
  }
1384
1361
  }
@@ -1404,6 +1381,8 @@ static VALUE parser_decode_chunked(int argc, VALUE *argv, VALUE self) {
1404
1381
  return rb_ary_new_from_args(2, decoded, ID2SYM(id_incomplete));
1405
1382
  }
1406
1383
 
1384
+ // One bit per byte value. Header names reject C0 controls, space, DEL and
1385
+ // separators; header values reject C0 controls other than horizontal tab.
1407
1386
  static uint8_t illegal_header_key_bitmap[32];
1408
1387
  static uint8_t illegal_header_value_bitmap[32];
1409
1388
 
@@ -1411,6 +1390,16 @@ static void bitmap_set(uint8_t *bitmap, unsigned char byte) {
1411
1390
  bitmap[byte >> 3] |= (uint8_t)(1u << (byte & 7));
1412
1391
  }
1413
1392
 
1393
+ static void init_illegal_header_bitmaps(void) {
1394
+ for (int byte = 0x00; byte <= 0x20; byte++) bitmap_set(illegal_header_key_bitmap, (unsigned char)byte);
1395
+ const char *key_specials = "()<>@,;:\\\"/[]?={}";
1396
+ for (const char *p = key_specials; *p; p++) bitmap_set(illegal_header_key_bitmap, (unsigned char)*p);
1397
+ bitmap_set(illegal_header_key_bitmap, 0x7F);
1398
+
1399
+ for (int byte = 0x00; byte <= 0x08; byte++) bitmap_set(illegal_header_value_bitmap, (unsigned char)byte);
1400
+ for (int byte = 0x0A; byte <= 0x1F; byte++) bitmap_set(illegal_header_value_bitmap, (unsigned char)byte);
1401
+ }
1402
+
1414
1403
  static int contains_illegal_byte(const uint8_t *bitmap, const char *str, size_t len) {
1415
1404
  for (size_t i = 0; i < len; i++) {
1416
1405
  unsigned char byte = (unsigned char)str[i];
@@ -1420,13 +1409,11 @@ static int contains_illegal_byte(const uint8_t *bitmap, const char *str, size_t
1420
1409
  }
1421
1410
 
1422
1411
  static VALUE parser_illegal_header_key_p(VALUE self, VALUE key) {
1423
- (void)self;
1424
1412
  Check_Type(key, T_STRING);
1425
1413
  return contains_illegal_byte(illegal_header_key_bitmap, RSTRING_PTR(key), (size_t)RSTRING_LEN(key)) ? Qtrue : Qfalse;
1426
1414
  }
1427
1415
 
1428
1416
  static VALUE parser_illegal_header_value_p(VALUE self, VALUE value) {
1429
- (void)self;
1430
1417
  Check_Type(value, T_STRING);
1431
1418
  return contains_illegal_byte(illegal_header_value_bitmap, RSTRING_PTR(value), (size_t)RSTRING_LEN(value)) ? Qtrue : Qfalse;
1432
1419
  }
@@ -1442,7 +1429,7 @@ static void emit_header_line(VALUE buffer, const char *name, long name_len,
1442
1429
  }
1443
1430
 
1444
1431
  static void emit_header(VALUE buffer, const char *name, long name_len, VALUE value) {
1445
- VALUE str = RB_TYPE_P(value, T_STRING) ? value : rb_funcall(value, rb_intern("to_s"), 0);
1432
+ VALUE str = rb_obj_as_string(value);
1446
1433
  const char *ptr = RSTRING_PTR(str);
1447
1434
  long len = RSTRING_LEN(str);
1448
1435
  if (len == 0) return;
@@ -1459,13 +1446,10 @@ static void emit_header(VALUE buffer, const char *name, long name_len, VALUE val
1459
1446
  line_start = i + 1;
1460
1447
  }
1461
1448
  }
1462
- if (line_start < len) {
1463
- emit_header_line(buffer, name, name_len, ptr + line_start, len - line_start);
1464
- }
1449
+ if (line_start < len) emit_header_line(buffer, name, name_len, ptr + line_start, len - line_start);
1465
1450
  }
1466
1451
 
1467
- static int format_header_iter(VALUE key, VALUE value, VALUE data) {
1468
- VALUE buffer = (VALUE)data;
1452
+ static int format_header_iter(VALUE key, VALUE value, VALUE buffer) {
1469
1453
  if (!RB_TYPE_P(key, T_STRING)) return ST_CONTINUE;
1470
1454
  const char *name = RSTRING_PTR(key);
1471
1455
  long name_len = RSTRING_LEN(key);
@@ -1483,7 +1467,6 @@ static int format_header_iter(VALUE key, VALUE value, VALUE data) {
1483
1467
  }
1484
1468
 
1485
1469
  static VALUE parser_format_headers(VALUE self, VALUE buffer, VALUE headers) {
1486
- (void)self;
1487
1470
  Check_Type(buffer, T_STRING);
1488
1471
  Check_Type(headers, T_HASH);
1489
1472
  rb_hash_foreach(headers, format_header_iter, buffer);
@@ -1491,7 +1474,6 @@ static VALUE parser_format_headers(VALUE self, VALUE buffer, VALUE headers) {
1491
1474
  }
1492
1475
 
1493
1476
  static VALUE parser_chunked_encode(VALUE self, VALUE buffer, VALUE chunk) {
1494
- (void)self;
1495
1477
  Check_Type(buffer, T_STRING);
1496
1478
  Check_Type(chunk, T_STRING);
1497
1479
  long chunk_len = RSTRING_LEN(chunk);
@@ -1506,36 +1488,27 @@ static VALUE parser_chunked_encode(VALUE self, VALUE buffer, VALUE chunk) {
1506
1488
  return buffer;
1507
1489
  }
1508
1490
 
1509
- static void init_illegal_header_bitmaps(void) {
1510
- for (int byte = 0x00; byte <= 0x20; byte++) bitmap_set(illegal_header_key_bitmap, (unsigned char)byte);
1511
- const char *key_specials = "()<>@,;:\\\"/[]?={}";
1512
- for (const char *p = key_specials; *p; p++) bitmap_set(illegal_header_key_bitmap, (unsigned char)*p);
1513
- bitmap_set(illegal_header_key_bitmap, 0x7F);
1514
-
1515
- for (int byte = 0x00; byte <= 0x08; byte++) bitmap_set(illegal_header_value_bitmap, (unsigned char)byte);
1516
- for (int byte = 0x0A; byte <= 0x1F; byte++) bitmap_set(illegal_header_value_bitmap, (unsigned char)byte);
1517
- }
1518
-
1519
1491
  RUBY_FUNC_EXPORTED void Init_raptor_http(void) {
1520
1492
  rb_ext_ractor_safe(true);
1521
1493
 
1522
- VALUE mRaptor = rb_define_module("Raptor");
1523
- cHttpParser = rb_define_class_under(mRaptor, "HttpParser", rb_cObject);
1524
- eHttpParserError = rb_define_class_under(mRaptor, "HttpParserError", rb_eStandardError);
1525
-
1526
1494
  rb_global_variable(&global_request_method);
1527
1495
  rb_global_variable(&global_request_uri);
1528
1496
  rb_global_variable(&global_query_string);
1529
1497
  rb_global_variable(&global_server_protocol);
1530
- rb_global_variable(&global_request_path);
1498
+ rb_global_variable(&global_path_info);
1531
1499
  rb_global_variable(&global_fragment);
1532
1500
 
1533
- global_request_method = rb_str_new2("REQUEST_METHOD");
1534
- global_request_uri = rb_str_new2("REQUEST_URI");
1535
- global_query_string = rb_str_new2("QUERY_STRING");
1536
- global_server_protocol = rb_str_new2("SERVER_PROTOCOL");
1537
- global_request_path = rb_str_new2("PATH_INFO");
1538
- global_fragment = rb_str_new2("FRAGMENT");
1501
+ global_request_method = rb_str_new_lit("REQUEST_METHOD");
1502
+ global_request_uri = rb_str_new_lit("REQUEST_URI");
1503
+ global_query_string = rb_str_new_lit("QUERY_STRING");
1504
+ global_server_protocol = rb_str_new_lit("SERVER_PROTOCOL");
1505
+ global_path_info = rb_str_new_lit("PATH_INFO");
1506
+ global_fragment = rb_str_new_lit("FRAGMENT");
1507
+
1508
+ for (size_t i = 0; i < NUM_COMMON_FIELDS; i++) {
1509
+ rb_global_variable(&common_fields[i].interned);
1510
+ common_fields[i].interned = rb_enc_interned_str(common_fields[i].name, common_fields[i].len, rb_utf8_encoding());
1511
+ }
1539
1512
 
1540
1513
  id_complete = rb_intern("complete");
1541
1514
  id_incomplete = rb_intern("incomplete");
@@ -1544,10 +1517,11 @@ RUBY_FUNC_EXPORTED void Init_raptor_http(void) {
1544
1517
 
1545
1518
  init_illegal_header_bitmaps();
1546
1519
 
1547
- for (size_t i = 0; i < NUM_COMMON_FIELDS; i++) {
1548
- common_fields[i].interned = rb_enc_interned_str(common_fields[i].name, common_fields[i].len, rb_utf8_encoding());
1549
- rb_global_variable(&common_fields[i].interned);
1550
- }
1520
+ VALUE mRaptor = rb_define_module("Raptor");
1521
+ VALUE cHttpParser = rb_define_class_under(mRaptor, "HttpParser", rb_cObject);
1522
+ eHttpParserError = rb_define_class_under(mRaptor, "HttpParserError", rb_eStandardError);
1523
+
1524
+ rb_define_const(cHttpParser, "MAX_CHUNK_OVERHEAD", INT2FIX(MAX_CHUNK_OVERHEAD));
1551
1525
 
1552
1526
  rb_define_alloc_func(cHttpParser, parser_alloc);
1553
1527
  rb_define_method(cHttpParser, "execute", parser_execute, 3);
@@ -1555,13 +1529,11 @@ RUBY_FUNC_EXPORTED void Init_raptor_http(void) {
1555
1529
  rb_define_method(cHttpParser, "has_body?", parser_has_body_p, 0);
1556
1530
  rb_define_method(cHttpParser, "chunked?", parser_chunked_p, 0);
1557
1531
  rb_define_method(cHttpParser, "content_length", parser_content_length, 0);
1558
- rb_define_method(cHttpParser, "nread", parser_nread, 0);
1559
1532
  rb_define_method(cHttpParser, "reset", parser_reset, 0);
1560
- rb_define_method(cHttpParser, "body", parser_body, 0);
1533
+
1561
1534
  rb_define_singleton_method(cHttpParser, "decode_chunked", parser_decode_chunked, -1);
1562
1535
  rb_define_singleton_method(cHttpParser, "illegal_header_key?", parser_illegal_header_key_p, 1);
1563
1536
  rb_define_singleton_method(cHttpParser, "illegal_header_value?", parser_illegal_header_value_p, 1);
1564
1537
  rb_define_singleton_method(cHttpParser, "format_headers", parser_format_headers, 2);
1565
1538
  rb_define_singleton_method(cHttpParser, "chunked_encode", parser_chunked_encode, 2);
1566
- rb_define_const(cHttpParser, "MAX_CHUNK_OVERHEAD", INT2FIX(MAX_CHUNK_OVERHEAD));
1567
1539
  }