raptor 0.22.2 → 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 +35 -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 +233 -208
  20. data/lib/raptor/http2.rb +314 -267
  21. data/lib/raptor/reactor.rb +139 -149
  22. data/lib/raptor/reuseport_bpf.rb +62 -38
  23. data/lib/raptor/server.rb +53 -38
  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
@@ -1,12 +1,15 @@
1
1
  #include "ruby.h"
2
- #include <string.h>
3
2
  #include <stdint.h>
3
+ #include <string.h>
4
4
 
5
- #define HTTP2_FRAME_HEADER_SIZE 9
6
- #define HTTP2_DEFAULT_HEADER_TABLE_SIZE 4096
7
- #define HTTP2_ENTRY_OVERHEAD 32
8
- #define HTTP2_CONNECTION_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
9
- #define HTTP2_CONNECTION_PREFACE_LEN 24
5
+ #define FRAME_HEADER_SIZE 9
6
+ #define DEFAULT_HEADER_TABLE_SIZE 4096
7
+ #define ENTRY_OVERHEAD 32
8
+ // Largest decoded request header list, matching the HTTP/1.1 header section
9
+ // limit. Fields are sized as in RFC 7541 section 4.1, so fields indexed from
10
+ // the dynamic table count at their full decoded size.
11
+ #define MAX_HEADER_LIST_SIZE (112 * 1024)
12
+ #define CONNECTION_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
10
13
 
11
14
  #define FRAME_DATA 0x0
12
15
  #define FRAME_HEADERS 0x1
@@ -19,12 +22,6 @@
19
22
  #define FRAME_WINDOW_UPDATE 0x8
20
23
  #define FRAME_CONTINUATION 0x9
21
24
 
22
- #define FLAG_END_STREAM 0x1
23
- #define FLAG_END_HEADERS 0x4
24
- #define FLAG_PADDED 0x8
25
- #define FLAG_PRIORITY 0x20
26
- #define FLAG_ACK 0x1
27
-
28
25
  #define SETTINGS_HEADER_TABLE_SIZE 0x1
29
26
  #define SETTINGS_ENABLE_PUSH 0x2
30
27
  #define SETTINGS_MAX_CONCURRENT_STREAMS 0x3
@@ -32,21 +29,41 @@
32
29
  #define SETTINGS_MAX_FRAME_SIZE 0x5
33
30
  #define SETTINGS_MAX_HEADER_LIST_SIZE 0x6
34
31
 
35
- #define STATIC_TABLE_SIZE 61
36
-
37
32
  #define HUFFMAN_ACCEPTED 1
38
33
  #define HUFFMAN_SYM (1 << 1)
39
34
  #define HUFFMAN_FAIL_STATE 0x100
40
35
 
41
- static VALUE cHttp2Parser;
42
36
  static VALUE eHttp2ParserError;
37
+ static ID id_type;
38
+ static ID id_length;
39
+ static ID id_flags;
40
+ static ID id_stream_id;
41
+ static ID id_payload;
42
+ static ID id_data;
43
+ static ID id_headers;
44
+ static ID id_priority;
45
+ static ID id_rst_stream;
46
+ static ID id_settings;
47
+ static ID id_push_promise;
48
+ static ID id_ping;
49
+ static ID id_goaway;
50
+ static ID id_window_update;
51
+ static ID id_continuation;
52
+ static ID id_unknown;
53
+ static ID id_header_table_size;
54
+ static ID id_enable_push;
55
+ static ID id_max_concurrent_streams;
56
+ static ID id_initial_window_size;
57
+ static ID id_max_frame_size;
58
+ static ID id_max_header_list_size;
43
59
 
44
60
  typedef struct {
45
61
  const char *name;
46
62
  const char *value;
47
63
  } hpack_static_entry;
48
64
 
49
- static const hpack_static_entry STATIC_TABLE[] = {
65
+ // RFC 7541 Appendix A. HPACK indexes are 1-based, so slot 0 is a placeholder.
66
+ static const hpack_static_entry static_table[] = {
50
67
  {NULL, NULL},
51
68
  {":authority", ""},
52
69
  {":method", "GET"},
@@ -111,12 +128,15 @@ static const hpack_static_entry STATIC_TABLE[] = {
111
128
  {"www-authenticate", ""}
112
129
  };
113
130
 
131
+ #define STATIC_TABLE_SIZE ((long)(sizeof(static_table) / sizeof(static_table[0])) - 1)
132
+
114
133
  typedef struct {
115
134
  uint32_t code;
116
135
  uint8_t bits;
117
136
  } huffman_entry;
118
137
 
119
- static const huffman_entry HUFFMAN_TABLE[257] = {
138
+ // RFC 7541 Appendix B: one code per byte value plus EOS.
139
+ static const huffman_entry huffman_table[257] = {
120
140
  {0x1ff8, 13}, {0x7fffd8, 23}, {0xfffffe2, 28}, {0xfffffe3, 28},
121
141
  {0xfffffe4, 28}, {0xfffffe5, 28}, {0xfffffe6, 28}, {0xfffffe7, 28},
122
142
  {0xfffffe8, 28}, {0xffffea, 24}, {0x3ffffffc, 30}, {0xfffffe9, 28},
@@ -285,16 +305,15 @@ static int hpack_decode_string(const uint8_t *buf, size_t len, size_t *pos, VALU
285
305
  if (*pos + str_len > len) return -1;
286
306
 
287
307
  if (huffman) {
288
- size_t decoded_cap = str_len * 2 + 256;
289
- uint8_t *decoded = ALLOCA_N(uint8_t, decoded_cap);
308
+ // The shortest Huffman code is five bits, bounding the decoded length.
309
+ size_t decoded_cap = str_len * 8 / 5;
310
+ VALUE decoded = rb_str_new(NULL, (long)decoded_cap);
290
311
  size_t decoded_len;
291
312
 
292
- if (hpack_decode_huffman(buf + *pos, (size_t)str_len, decoded, decoded_cap, &decoded_len) < 0) {
293
- rb_raise(rb_eRuntimeError, "HPACK Huffman decode error");
294
- return -1;
295
- }
313
+ if (hpack_decode_huffman(buf + *pos, (size_t)str_len, (uint8_t *)RSTRING_PTR(decoded), decoded_cap, &decoded_len) < 0) return -1;
296
314
 
297
- *out_str = rb_str_new((const char *)decoded, decoded_len);
315
+ rb_str_set_len(decoded, (long)decoded_len);
316
+ *out_str = decoded;
298
317
  } else {
299
318
  *out_str = rb_str_new((const char *)(buf + *pos), (long)str_len);
300
319
  }
@@ -306,7 +325,7 @@ static int hpack_decode_string(const uint8_t *buf, size_t len, size_t *pos, VALU
306
325
  static size_t hpack_huffman_encode_len(const uint8_t *src, size_t src_len) {
307
326
  size_t bits = 0;
308
327
  for (size_t i = 0; i < src_len; i++)
309
- bits += HUFFMAN_TABLE[src[i]].bits;
328
+ bits += huffman_table[src[i]].bits;
310
329
  return (bits + 7) / 8;
311
330
  }
312
331
 
@@ -316,8 +335,8 @@ static size_t hpack_huffman_encode(const uint8_t *src, size_t src_len, uint8_t *
316
335
  int remaining = 0;
317
336
 
318
337
  for (size_t i = 0; i < src_len; i++) {
319
- uint32_t code = HUFFMAN_TABLE[src[i]].code;
320
- uint8_t bits = HUFFMAN_TABLE[src[i]].bits;
338
+ uint32_t code = huffman_table[src[i]].code;
339
+ uint8_t bits = huffman_table[src[i]].bits;
321
340
 
322
341
  current = (current << bits) | code;
323
342
  remaining += bits;
@@ -336,26 +355,33 @@ static size_t hpack_huffman_encode(const uint8_t *src, size_t src_len, uint8_t *
336
355
  return pos;
337
356
  }
338
357
 
339
- static VALUE dynamic_table_lookup(VALUE dyn_table, long index, VALUE *name, VALUE *value) {
340
- if (index < 1) return Qfalse;
358
+ // Encodes into the Ruby buffer to avoid stack allocations sized by header values.
359
+ static void hpack_append_huffman(VALUE buf, const uint8_t *src, size_t src_len, size_t huff_len) {
360
+ long offset = RSTRING_LEN(buf);
361
+ rb_str_resize(buf, offset + (long)huff_len);
362
+ hpack_huffman_encode(src, src_len, (uint8_t *)RSTRING_PTR(buf) + offset);
363
+ }
364
+
365
+ static int hpack_table_lookup(VALUE dyn_table, long index, VALUE *name, VALUE *value) {
366
+ if (index < 1) return -1;
341
367
 
342
368
  if (index <= STATIC_TABLE_SIZE) {
343
- *name = rb_str_new_cstr(STATIC_TABLE[index].name);
344
- *value = rb_str_new_cstr(STATIC_TABLE[index].value);
345
- return Qtrue;
369
+ *name = rb_str_new_cstr(static_table[index].name);
370
+ *value = rb_str_new_cstr(static_table[index].value);
371
+ return 0;
346
372
  }
347
373
 
348
374
  long dyn_index = index - STATIC_TABLE_SIZE - 1;
349
- if (dyn_index >= RARRAY_LEN(dyn_table)) return Qfalse;
375
+ if (dyn_index >= RARRAY_LEN(dyn_table)) return -1;
350
376
 
351
377
  VALUE entry = rb_ary_entry(dyn_table, dyn_index);
352
378
  *name = rb_ary_entry(entry, 0);
353
379
  *value = rb_ary_entry(entry, 1);
354
- return Qtrue;
380
+ return 0;
355
381
  }
356
382
 
357
- static VALUE dynamic_table_add(VALUE dyn_table, VALUE name, VALUE value, long max_size) {
358
- long entry_size = RSTRING_LEN(name) + RSTRING_LEN(value) + HTTP2_ENTRY_OVERHEAD;
383
+ static VALUE hpack_dynamic_table_add(VALUE dyn_table, VALUE name, VALUE value, long max_size) {
384
+ long entry_size = RSTRING_LEN(name) + RSTRING_LEN(value) + ENTRY_OVERHEAD;
359
385
 
360
386
  VALUE new_table = rb_ary_new();
361
387
  VALUE new_entry = rb_ary_new_from_args(2, rb_str_freeze(rb_str_dup(name)), rb_str_freeze(rb_str_dup(value)));
@@ -366,7 +392,7 @@ static VALUE dynamic_table_add(VALUE dyn_table, VALUE name, VALUE value, long ma
366
392
  VALUE existing = rb_ary_entry(dyn_table, i);
367
393
  VALUE ename = rb_ary_entry(existing, 0);
368
394
  VALUE evalue = rb_ary_entry(existing, 1);
369
- long esize = RSTRING_LEN(ename) + RSTRING_LEN(evalue) + HTTP2_ENTRY_OVERHEAD;
395
+ long esize = RSTRING_LEN(ename) + RSTRING_LEN(evalue) + ENTRY_OVERHEAD;
370
396
 
371
397
  if (current_size + esize > max_size) break;
372
398
 
@@ -377,7 +403,7 @@ static VALUE dynamic_table_add(VALUE dyn_table, VALUE name, VALUE value, long ma
377
403
  return new_table;
378
404
  }
379
405
 
380
- static VALUE dynamic_table_evict(VALUE dyn_table, long max_size) {
406
+ static VALUE hpack_dynamic_table_evict(VALUE dyn_table, long max_size) {
381
407
  long current_size = 0;
382
408
  long keep = 0;
383
409
 
@@ -385,7 +411,7 @@ static VALUE dynamic_table_evict(VALUE dyn_table, long max_size) {
385
411
  VALUE entry = rb_ary_entry(dyn_table, i);
386
412
  VALUE name = rb_ary_entry(entry, 0);
387
413
  VALUE value = rb_ary_entry(entry, 1);
388
- long esize = RSTRING_LEN(name) + RSTRING_LEN(value) + HTTP2_ENTRY_OVERHEAD;
414
+ long esize = RSTRING_LEN(name) + RSTRING_LEN(value) + ENTRY_OVERHEAD;
389
415
 
390
416
  if (current_size + esize > max_size) break;
391
417
 
@@ -405,61 +431,68 @@ static int hpack_decode_header_block(const uint8_t *buf, size_t len,
405
431
  VALUE headers_out, VALUE *dyn_table,
406
432
  long *max_table_size) {
407
433
  size_t pos = 0;
434
+ size_t list_size = 0;
408
435
 
409
436
  while (pos < len) {
410
437
  uint8_t byte = buf[pos];
411
438
 
412
439
  if (byte & 0x80) {
413
- /* indexed (RFC 7541 6.1) */
440
+ // Indexed header field (RFC 7541 6.1).
414
441
  uint64_t index;
415
442
  if (hpack_decode_int(buf, len, &pos, 7, &index) < 0) return -1;
416
443
  if (index == 0) return -1;
417
444
 
418
445
  VALUE name, value;
419
- if (!RTEST(dynamic_table_lookup(*dyn_table, (long)index, &name, &value))) return -1;
446
+ if (hpack_table_lookup(*dyn_table, (long)index, &name, &value) < 0) return -1;
420
447
 
421
448
  rb_ary_push(headers_out, rb_ary_new_from_args(2, name, value));
449
+ list_size += RSTRING_LEN(name) + RSTRING_LEN(value) + ENTRY_OVERHEAD;
450
+ if (list_size > MAX_HEADER_LIST_SIZE) return -1;
422
451
 
423
452
  } else if ((byte & 0xc0) == 0x40) {
424
- /* literal with incremental indexing (RFC 7541 6.2.1) */
453
+ // Literal header field with incremental indexing (RFC 7541 6.2.1).
425
454
  uint64_t index;
426
455
  if (hpack_decode_int(buf, len, &pos, 6, &index) < 0) return -1;
427
456
 
428
457
  VALUE name, value;
429
458
  if (index > 0) {
430
459
  VALUE dummy;
431
- if (!RTEST(dynamic_table_lookup(*dyn_table, (long)index, &name, &dummy))) return -1;
460
+ if (hpack_table_lookup(*dyn_table, (long)index, &name, &dummy) < 0) return -1;
432
461
  } else {
433
462
  if (hpack_decode_string(buf, len, &pos, &name) < 0) return -1;
434
463
  }
435
464
  if (hpack_decode_string(buf, len, &pos, &value) < 0) return -1;
436
465
 
437
466
  rb_ary_push(headers_out, rb_ary_new_from_args(2, name, value));
438
- *dyn_table = dynamic_table_add(*dyn_table, name, value, *max_table_size);
467
+ list_size += RSTRING_LEN(name) + RSTRING_LEN(value) + ENTRY_OVERHEAD;
468
+ if (list_size > MAX_HEADER_LIST_SIZE) return -1;
469
+ *dyn_table = hpack_dynamic_table_add(*dyn_table, name, value, *max_table_size);
439
470
 
440
471
  } else if ((byte & 0xf0) == 0x00 || (byte & 0xf0) == 0x10) {
441
- /* literal without indexing / never indexed (RFC 7541 6.2.2, 6.2.3) */
472
+ // Literal header field without indexing or never indexed (RFC 7541 6.2.2, 6.2.3).
442
473
  uint64_t index;
443
474
  if (hpack_decode_int(buf, len, &pos, 4, &index) < 0) return -1;
444
475
 
445
476
  VALUE name, value;
446
477
  if (index > 0) {
447
478
  VALUE dummy;
448
- if (!RTEST(dynamic_table_lookup(*dyn_table, (long)index, &name, &dummy))) return -1;
479
+ if (hpack_table_lookup(*dyn_table, (long)index, &name, &dummy) < 0) return -1;
449
480
  } else {
450
481
  if (hpack_decode_string(buf, len, &pos, &name) < 0) return -1;
451
482
  }
452
483
  if (hpack_decode_string(buf, len, &pos, &value) < 0) return -1;
453
484
 
454
485
  rb_ary_push(headers_out, rb_ary_new_from_args(2, name, value));
486
+ list_size += RSTRING_LEN(name) + RSTRING_LEN(value) + ENTRY_OVERHEAD;
487
+ if (list_size > MAX_HEADER_LIST_SIZE) return -1;
455
488
 
456
489
  } else if ((byte & 0xe0) == 0x20) {
457
- /* dynamic table size update (RFC 7541 6.3) */
490
+ // Dynamic table size update (RFC 7541 6.3).
458
491
  uint64_t new_size;
459
492
  if (hpack_decode_int(buf, len, &pos, 5, &new_size) < 0) return -1;
460
- if (new_size > HTTP2_DEFAULT_HEADER_TABLE_SIZE) return -1;
493
+ if (new_size > DEFAULT_HEADER_TABLE_SIZE) return -1;
461
494
  *max_table_size = (long)new_size;
462
- *dyn_table = dynamic_table_evict(*dyn_table, *max_table_size);
495
+ *dyn_table = hpack_dynamic_table_evict(*dyn_table, *max_table_size);
463
496
 
464
497
  } else {
465
498
  return -1;
@@ -487,8 +520,8 @@ static VALUE hpack_encode_header_block(VALUE headers) {
487
520
 
488
521
  int found_index = 0;
489
522
  for (int idx = 1; idx <= STATIC_TABLE_SIZE; idx++) {
490
- if (strlen(STATIC_TABLE[idx].name) == name_len &&
491
- memcmp(STATIC_TABLE[idx].name, name_ptr, name_len) == 0) {
523
+ if (strlen(static_table[idx].name) == name_len &&
524
+ memcmp(static_table[idx].name, name_ptr, name_len) == 0) {
492
525
  found_index = idx;
493
526
  break;
494
527
  }
@@ -496,35 +529,31 @@ static VALUE hpack_encode_header_block(VALUE headers) {
496
529
 
497
530
  if (found_index > 0) {
498
531
  int_len = hpack_encode_int(int_buf, found_index, 4, 0x00);
499
- rb_str_buf_cat(buf, (const char *)int_buf, int_len);
532
+ rb_str_cat(buf, (const char *)int_buf, int_len);
500
533
  } else {
501
- rb_str_buf_cat(buf, "\x00", 1);
534
+ rb_str_cat(buf, "\x00", 1);
502
535
 
503
536
  size_t huff_len = hpack_huffman_encode_len(name_ptr, name_len);
504
537
  if (huff_len < name_len) {
505
538
  int_len = hpack_encode_int(int_buf, huff_len, 7, 0x80);
506
- rb_str_buf_cat(buf, (const char *)int_buf, int_len);
507
- uint8_t *huff_buf = ALLOCA_N(uint8_t, huff_len);
508
- hpack_huffman_encode(name_ptr, name_len, huff_buf);
509
- rb_str_buf_cat(buf, (const char *)huff_buf, huff_len);
539
+ rb_str_cat(buf, (const char *)int_buf, int_len);
540
+ hpack_append_huffman(buf, name_ptr, name_len, huff_len);
510
541
  } else {
511
542
  int_len = hpack_encode_int(int_buf, name_len, 7, 0x00);
512
- rb_str_buf_cat(buf, (const char *)int_buf, int_len);
513
- rb_str_buf_cat(buf, (const char *)name_ptr, name_len);
543
+ rb_str_cat(buf, (const char *)int_buf, int_len);
544
+ rb_str_cat(buf, (const char *)name_ptr, name_len);
514
545
  }
515
546
  }
516
547
 
517
548
  size_t huff_len = hpack_huffman_encode_len(value_ptr, value_len);
518
549
  if (huff_len < value_len) {
519
550
  int_len = hpack_encode_int(int_buf, huff_len, 7, 0x80);
520
- rb_str_buf_cat(buf, (const char *)int_buf, int_len);
521
- uint8_t *huff_buf = ALLOCA_N(uint8_t, huff_len);
522
- hpack_huffman_encode(value_ptr, value_len, huff_buf);
523
- rb_str_buf_cat(buf, (const char *)huff_buf, huff_len);
551
+ rb_str_cat(buf, (const char *)int_buf, int_len);
552
+ hpack_append_huffman(buf, value_ptr, value_len, huff_len);
524
553
  } else {
525
554
  int_len = hpack_encode_int(int_buf, value_len, 7, 0x00);
526
- rb_str_buf_cat(buf, (const char *)int_buf, int_len);
527
- rb_str_buf_cat(buf, (const char *)value_ptr, value_len);
555
+ rb_str_cat(buf, (const char *)int_buf, int_len);
556
+ rb_str_cat(buf, (const char *)value_ptr, value_len);
528
557
  }
529
558
  }
530
559
 
@@ -541,73 +570,30 @@ static void parse_frame_header(const uint8_t *buf, frame_header *fh) {
541
570
 
542
571
  static VALUE frame_type_sym(uint8_t type) {
543
572
  switch (type) {
544
- case FRAME_DATA: return ID2SYM(rb_intern("data"));
545
- case FRAME_HEADERS: return ID2SYM(rb_intern("headers"));
546
- case FRAME_PRIORITY: return ID2SYM(rb_intern("priority"));
547
- case FRAME_RST_STREAM: return ID2SYM(rb_intern("rst_stream"));
548
- case FRAME_SETTINGS: return ID2SYM(rb_intern("settings"));
549
- case FRAME_PUSH_PROMISE: return ID2SYM(rb_intern("push_promise"));
550
- case FRAME_PING: return ID2SYM(rb_intern("ping"));
551
- case FRAME_GOAWAY: return ID2SYM(rb_intern("goaway"));
552
- case FRAME_WINDOW_UPDATE: return ID2SYM(rb_intern("window_update"));
553
- case FRAME_CONTINUATION: return ID2SYM(rb_intern("continuation"));
554
- default: return ID2SYM(rb_intern("unknown"));
573
+ case FRAME_DATA: return ID2SYM(id_data);
574
+ case FRAME_HEADERS: return ID2SYM(id_headers);
575
+ case FRAME_PRIORITY: return ID2SYM(id_priority);
576
+ case FRAME_RST_STREAM: return ID2SYM(id_rst_stream);
577
+ case FRAME_SETTINGS: return ID2SYM(id_settings);
578
+ case FRAME_PUSH_PROMISE: return ID2SYM(id_push_promise);
579
+ case FRAME_PING: return ID2SYM(id_ping);
580
+ case FRAME_GOAWAY: return ID2SYM(id_goaway);
581
+ case FRAME_WINDOW_UPDATE: return ID2SYM(id_window_update);
582
+ case FRAME_CONTINUATION: return ID2SYM(id_continuation);
583
+ default: return ID2SYM(id_unknown);
555
584
  }
556
585
  }
557
586
 
558
- static void h2_parser_mark(void *ptr) { (void)ptr; }
559
- static void h2_parser_free(void *ptr) { xfree(ptr); }
560
- static size_t h2_parser_memsize(const void *ptr) { return sizeof(raptor_h2_parser); }
561
-
562
- static const rb_data_type_t h2_parser_type = {
563
- "raptor_http2_parser",
564
- {h2_parser_mark, h2_parser_free, h2_parser_memsize},
565
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
566
- };
567
-
568
- static VALUE h2_parser_alloc(VALUE klass) {
569
- raptor_h2_parser *parser;
570
- VALUE obj = TypedData_Make_Struct(klass, raptor_h2_parser, &h2_parser_type, parser);
571
- parser->max_table_size = HTTP2_DEFAULT_HEADER_TABLE_SIZE;
572
- return obj;
573
- }
574
-
575
- static VALUE h2_parse_headers(VALUE self, VALUE header_block, VALUE dyn_table) {
576
- raptor_h2_parser *parser;
577
- TypedData_Get_Struct(self, raptor_h2_parser, &h2_parser_type, parser);
578
-
579
- Check_Type(header_block, T_STRING);
580
- Check_Type(dyn_table, T_ARRAY);
581
-
582
- const uint8_t *buf = (const uint8_t *)RSTRING_PTR(header_block);
583
- size_t len = RSTRING_LEN(header_block);
584
-
585
- VALUE headers = rb_ary_new();
586
- VALUE table = rb_ary_dup(dyn_table);
587
- long max_size = parser->max_table_size;
588
-
589
- if (hpack_decode_header_block(buf, len, headers, &table, &max_size) < 0)
590
- rb_raise(eHttp2ParserError, "HPACK header block decode error");
591
-
592
- parser->max_table_size = max_size;
593
- return rb_ary_new_from_args(2, headers, table);
594
- }
595
-
596
- static VALUE h2_encode_headers(VALUE self, VALUE headers) {
597
- (void)self;
598
- Check_Type(headers, T_ARRAY);
599
- return hpack_encode_header_block(headers);
600
- }
601
-
602
- static const char *const HOP_BY_HOP_HEADERS[] = {
587
+ static const char *const hop_by_hop_headers[] = {
603
588
  "connection", "transfer-encoding", "keep-alive", "upgrade", "proxy-connection"
604
589
  };
605
- #define NUM_HOP_BY_HOP_HEADERS 5
590
+
591
+ #define NUM_HOP_BY_HOP_HEADERS (sizeof(hop_by_hop_headers) / sizeof(hop_by_hop_headers[0]))
606
592
 
607
593
  static int is_hop_by_hop(const char *name, long name_len) {
608
594
  for (size_t i = 0; i < NUM_HOP_BY_HOP_HEADERS; i++) {
609
- size_t hbh_len = strlen(HOP_BY_HOP_HEADERS[i]);
610
- if ((long)hbh_len == name_len && memcmp(name, HOP_BY_HOP_HEADERS[i], hbh_len) == 0) return 1;
595
+ size_t hbh_len = strlen(hop_by_hop_headers[i]);
596
+ if ((long)hbh_len == name_len && memcmp(name, hop_by_hop_headers[i], hbh_len) == 0) return 1;
611
597
  }
612
598
  return 0;
613
599
  }
@@ -622,8 +608,7 @@ static VALUE ascii_lowercase_str(VALUE str) {
622
608
  return lower;
623
609
  }
624
610
 
625
- static int response_headers_iter(VALUE key, VALUE value, VALUE data) {
626
- VALUE pairs = data;
611
+ static int response_headers_iter(VALUE key, VALUE value, VALUE pairs) {
627
612
  if (!RB_TYPE_P(key, T_STRING)) return ST_CONTINUE;
628
613
 
629
614
  VALUE lowered = ascii_lowercase_str(key);
@@ -638,78 +623,96 @@ static int response_headers_iter(VALUE key, VALUE value, VALUE data) {
638
623
  long entries = RARRAY_LEN(value);
639
624
  for (long i = 0; i < entries; i++) {
640
625
  VALUE entry = RARRAY_AREF(value, i);
641
- VALUE str = RB_TYPE_P(entry, T_STRING) ? entry : rb_obj_as_string(entry);
642
- rb_ary_push(pairs, rb_ary_new_from_args(2, lowered, str));
626
+ rb_ary_push(pairs, rb_ary_new_from_args(2, lowered, rb_obj_as_string(entry)));
643
627
  }
644
628
  } else {
645
- VALUE str = RB_TYPE_P(value, T_STRING) ? value : rb_obj_as_string(value);
646
- rb_ary_push(pairs, rb_ary_new_from_args(2, lowered, str));
629
+ rb_ary_push(pairs, rb_ary_new_from_args(2, lowered, rb_obj_as_string(value)));
647
630
  }
648
631
  return ST_CONTINUE;
649
632
  }
650
633
 
651
- static VALUE h2_encode_response_headers(VALUE self, VALUE status, VALUE headers) {
652
- (void)self;
653
- Check_Type(headers, T_HASH);
654
-
655
- VALUE pairs = rb_ary_new();
656
- rb_ary_push(pairs, rb_ary_new_from_args(2, rb_str_new_lit(":status"), rb_obj_as_string(status)));
657
- rb_hash_foreach(headers, response_headers_iter, pairs);
658
-
659
- return hpack_encode_header_block(pairs);
634
+ static int build_settings_iter(VALUE key, VALUE val, VALUE buf) {
635
+ ID key_id = SYM2ID(key);
636
+
637
+ uint16_t id;
638
+ if (key_id == id_header_table_size) id = SETTINGS_HEADER_TABLE_SIZE;
639
+ else if (key_id == id_enable_push) id = SETTINGS_ENABLE_PUSH;
640
+ else if (key_id == id_max_concurrent_streams) id = SETTINGS_MAX_CONCURRENT_STREAMS;
641
+ else if (key_id == id_initial_window_size) id = SETTINGS_INITIAL_WINDOW_SIZE;
642
+ else if (key_id == id_max_frame_size) id = SETTINGS_MAX_FRAME_SIZE;
643
+ else if (key_id == id_max_header_list_size) id = SETTINGS_MAX_HEADER_LIST_SIZE;
644
+ else return ST_CONTINUE;
645
+
646
+ uint32_t v = NUM2UINT(val);
647
+ uint8_t entry[6];
648
+ entry[0] = (id >> 8) & 0xff;
649
+ entry[1] = id & 0xff;
650
+ entry[2] = (v >> 24) & 0xff;
651
+ entry[3] = (v >> 16) & 0xff;
652
+ entry[4] = (v >> 8) & 0xff;
653
+ entry[5] = v & 0xff;
654
+ rb_str_cat(buf, (const char *)entry, sizeof(entry));
655
+ return ST_CONTINUE;
660
656
  }
661
657
 
662
- static VALUE h2_encode_response_trailers(VALUE self, VALUE trailers) {
663
- (void)self;
664
- Check_Type(trailers, T_HASH);
658
+ static size_t h2_parser_memsize(const void *ptr) {
659
+ return sizeof(raptor_h2_parser);
660
+ }
665
661
 
666
- VALUE pairs = rb_ary_new();
667
- rb_hash_foreach(trailers, response_headers_iter, pairs);
662
+ static const rb_data_type_t h2_parser_type = {
663
+ .wrap_struct_name = "raptor_http2_parser",
664
+ .function = {
665
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
666
+ .dsize = h2_parser_memsize,
667
+ },
668
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
669
+ };
668
670
 
669
- return hpack_encode_header_block(pairs);
671
+ static VALUE h2_parser_alloc(VALUE klass) {
672
+ raptor_h2_parser *parser;
673
+ VALUE obj = TypedData_Make_Struct(klass, raptor_h2_parser, &h2_parser_type, parser);
674
+ parser->max_table_size = DEFAULT_HEADER_TABLE_SIZE;
675
+ return obj;
670
676
  }
671
677
 
672
- static VALUE h2_parse_frame(VALUE self, VALUE buffer) {
673
- (void)self;
678
+ static VALUE h2_parser_parse_frame(VALUE self, VALUE buffer) {
674
679
  Check_Type(buffer, T_STRING);
675
680
 
676
681
  const uint8_t *buf = (const uint8_t *)RSTRING_PTR(buffer);
677
682
  size_t len = RSTRING_LEN(buffer);
678
683
 
679
- if (len < HTTP2_FRAME_HEADER_SIZE) return Qnil;
684
+ if (len < FRAME_HEADER_SIZE) return Qnil;
680
685
 
681
686
  frame_header fh;
682
687
  parse_frame_header(buf, &fh);
683
688
 
684
- size_t total = HTTP2_FRAME_HEADER_SIZE + fh.length;
689
+ size_t total = FRAME_HEADER_SIZE + fh.length;
685
690
  if (len < total) return Qnil;
686
691
 
687
692
  VALUE frame = rb_hash_new();
688
- rb_hash_aset(frame, ID2SYM(rb_intern("type")), frame_type_sym(fh.type));
689
- rb_hash_aset(frame, ID2SYM(rb_intern("length")), UINT2NUM(fh.length));
690
- rb_hash_aset(frame, ID2SYM(rb_intern("flags")), UINT2NUM(fh.flags));
691
- rb_hash_aset(frame, ID2SYM(rb_intern("stream_id")), UINT2NUM(fh.stream_id));
692
- rb_hash_aset(frame, ID2SYM(rb_intern("payload")), rb_str_new((const char *)(buf + HTTP2_FRAME_HEADER_SIZE), fh.length));
693
+ rb_hash_aset(frame, ID2SYM(id_type), frame_type_sym(fh.type));
694
+ rb_hash_aset(frame, ID2SYM(id_length), UINT2NUM(fh.length));
695
+ rb_hash_aset(frame, ID2SYM(id_flags), UINT2NUM(fh.flags));
696
+ rb_hash_aset(frame, ID2SYM(id_stream_id), UINT2NUM(fh.stream_id));
697
+ rb_hash_aset(frame, ID2SYM(id_payload), rb_str_new((const char *)(buf + FRAME_HEADER_SIZE), fh.length));
693
698
 
694
699
  return rb_ary_new_from_args(2, frame, SIZET2NUM(total));
695
700
  }
696
701
 
697
- static VALUE h2_build_frame(VALUE self, VALUE type, VALUE flags, VALUE stream_id, VALUE payload) {
698
- (void)self;
699
-
702
+ static VALUE h2_parser_build_frame(VALUE self, VALUE type, VALUE flags, VALUE stream_id, VALUE payload) {
700
703
  uint8_t frame_type;
701
704
  ID type_id = SYM2ID(type);
702
705
 
703
- if (type_id == rb_intern("data")) frame_type = FRAME_DATA;
704
- else if (type_id == rb_intern("headers")) frame_type = FRAME_HEADERS;
705
- else if (type_id == rb_intern("priority")) frame_type = FRAME_PRIORITY;
706
- else if (type_id == rb_intern("rst_stream")) frame_type = FRAME_RST_STREAM;
707
- else if (type_id == rb_intern("settings")) frame_type = FRAME_SETTINGS;
708
- else if (type_id == rb_intern("push_promise")) frame_type = FRAME_PUSH_PROMISE;
709
- else if (type_id == rb_intern("ping")) frame_type = FRAME_PING;
710
- else if (type_id == rb_intern("goaway")) frame_type = FRAME_GOAWAY;
711
- else if (type_id == rb_intern("window_update")) frame_type = FRAME_WINDOW_UPDATE;
712
- else if (type_id == rb_intern("continuation")) frame_type = FRAME_CONTINUATION;
706
+ if (type_id == id_data) frame_type = FRAME_DATA;
707
+ else if (type_id == id_headers) frame_type = FRAME_HEADERS;
708
+ else if (type_id == id_priority) frame_type = FRAME_PRIORITY;
709
+ else if (type_id == id_rst_stream) frame_type = FRAME_RST_STREAM;
710
+ else if (type_id == id_settings) frame_type = FRAME_SETTINGS;
711
+ else if (type_id == id_push_promise) frame_type = FRAME_PUSH_PROMISE;
712
+ else if (type_id == id_ping) frame_type = FRAME_PING;
713
+ else if (type_id == id_goaway) frame_type = FRAME_GOAWAY;
714
+ else if (type_id == id_window_update) frame_type = FRAME_WINDOW_UPDATE;
715
+ else if (type_id == id_continuation) frame_type = FRAME_CONTINUATION;
713
716
  else rb_raise(rb_eArgError, "unknown frame type");
714
717
 
715
718
  uint8_t frame_flags = (uint8_t)NUM2UINT(flags);
@@ -718,7 +721,7 @@ static VALUE h2_build_frame(VALUE self, VALUE type, VALUE flags, VALUE stream_id
718
721
  const char *payload_ptr = NIL_P(payload) ? "" : RSTRING_PTR(payload);
719
722
  size_t payload_len = NIL_P(payload) ? 0 : RSTRING_LEN(payload);
720
723
 
721
- uint8_t header[HTTP2_FRAME_HEADER_SIZE];
724
+ uint8_t header[FRAME_HEADER_SIZE];
722
725
  header[0] = (payload_len >> 16) & 0xff;
723
726
  header[1] = (payload_len >> 8) & 0xff;
724
727
  header[2] = payload_len & 0xff;
@@ -729,18 +732,57 @@ static VALUE h2_build_frame(VALUE self, VALUE type, VALUE flags, VALUE stream_id
729
732
  header[7] = (sid >> 8) & 0xff;
730
733
  header[8] = sid & 0xff;
731
734
 
732
- VALUE result = rb_str_new((const char *)header, HTTP2_FRAME_HEADER_SIZE);
733
- rb_str_buf_cat(result, payload_ptr, payload_len);
735
+ VALUE result = rb_str_new((const char *)header, FRAME_HEADER_SIZE);
736
+ rb_str_cat(result, payload_ptr, payload_len);
734
737
  return result;
735
738
  }
736
739
 
737
- static VALUE h2_connection_preface(VALUE self) {
738
- (void)self;
739
- return rb_str_new(HTTP2_CONNECTION_PREFACE, HTTP2_CONNECTION_PREFACE_LEN);
740
+ static VALUE h2_parser_parse_headers(VALUE self, VALUE header_block, VALUE dyn_table) {
741
+ raptor_h2_parser *parser;
742
+ TypedData_Get_Struct(self, raptor_h2_parser, &h2_parser_type, parser);
743
+
744
+ Check_Type(header_block, T_STRING);
745
+ Check_Type(dyn_table, T_ARRAY);
746
+
747
+ const uint8_t *buf = (const uint8_t *)RSTRING_PTR(header_block);
748
+ size_t len = RSTRING_LEN(header_block);
749
+
750
+ VALUE headers = rb_ary_new();
751
+ VALUE table = rb_ary_dup(dyn_table);
752
+ long max_size = parser->max_table_size;
753
+
754
+ if (hpack_decode_header_block(buf, len, headers, &table, &max_size) < 0)
755
+ rb_raise(eHttp2ParserError, "HPACK header block decode error");
756
+
757
+ parser->max_table_size = max_size;
758
+ return rb_ary_new_from_args(2, headers, table);
740
759
  }
741
760
 
742
- static VALUE h2_parse_settings(VALUE self, VALUE payload) {
743
- (void)self;
761
+ static VALUE h2_parser_encode_headers(VALUE self, VALUE headers) {
762
+ Check_Type(headers, T_ARRAY);
763
+ return hpack_encode_header_block(headers);
764
+ }
765
+
766
+ static VALUE h2_parser_encode_response_headers(VALUE self, VALUE status, VALUE headers) {
767
+ Check_Type(headers, T_HASH);
768
+
769
+ VALUE pairs = rb_ary_new();
770
+ rb_ary_push(pairs, rb_ary_new_from_args(2, rb_str_new_lit(":status"), rb_obj_as_string(status)));
771
+ rb_hash_foreach(headers, response_headers_iter, pairs);
772
+
773
+ return hpack_encode_header_block(pairs);
774
+ }
775
+
776
+ static VALUE h2_parser_encode_response_trailers(VALUE self, VALUE trailers) {
777
+ Check_Type(trailers, T_HASH);
778
+
779
+ VALUE pairs = rb_ary_new();
780
+ rb_hash_foreach(trailers, response_headers_iter, pairs);
781
+
782
+ return hpack_encode_header_block(pairs);
783
+ }
784
+
785
+ static VALUE h2_parser_parse_settings(VALUE self, VALUE payload) {
744
786
  Check_Type(payload, T_STRING);
745
787
 
746
788
  const uint8_t *buf = (const uint8_t *)RSTRING_PTR(payload);
@@ -758,60 +800,38 @@ static VALUE h2_parse_settings(VALUE self, VALUE payload) {
758
800
 
759
801
  switch (id) {
760
802
  case SETTINGS_HEADER_TABLE_SIZE:
761
- rb_hash_aset(settings, ID2SYM(rb_intern("header_table_size")), UINT2NUM(val)); break;
803
+ rb_hash_aset(settings, ID2SYM(id_header_table_size), UINT2NUM(val));
804
+ break;
762
805
  case SETTINGS_ENABLE_PUSH:
763
- rb_hash_aset(settings, ID2SYM(rb_intern("enable_push")), UINT2NUM(val)); break;
806
+ rb_hash_aset(settings, ID2SYM(id_enable_push), UINT2NUM(val));
807
+ break;
764
808
  case SETTINGS_MAX_CONCURRENT_STREAMS:
765
- rb_hash_aset(settings, ID2SYM(rb_intern("max_concurrent_streams")), UINT2NUM(val)); break;
809
+ rb_hash_aset(settings, ID2SYM(id_max_concurrent_streams), UINT2NUM(val));
810
+ break;
766
811
  case SETTINGS_INITIAL_WINDOW_SIZE:
767
- rb_hash_aset(settings, ID2SYM(rb_intern("initial_window_size")), UINT2NUM(val)); break;
812
+ rb_hash_aset(settings, ID2SYM(id_initial_window_size), UINT2NUM(val));
813
+ break;
768
814
  case SETTINGS_MAX_FRAME_SIZE:
769
- rb_hash_aset(settings, ID2SYM(rb_intern("max_frame_size")), UINT2NUM(val)); break;
815
+ rb_hash_aset(settings, ID2SYM(id_max_frame_size), UINT2NUM(val));
816
+ break;
770
817
  case SETTINGS_MAX_HEADER_LIST_SIZE:
771
- rb_hash_aset(settings, ID2SYM(rb_intern("max_header_list_size")), UINT2NUM(val)); break;
818
+ rb_hash_aset(settings, ID2SYM(id_max_header_list_size), UINT2NUM(val));
819
+ break;
772
820
  }
773
821
  }
774
822
 
775
823
  return settings;
776
824
  }
777
825
 
778
- static VALUE h2_build_settings(VALUE self, VALUE settings) {
779
- (void)self;
826
+ static VALUE h2_parser_build_settings(VALUE self, VALUE settings) {
780
827
  Check_Type(settings, T_HASH);
781
828
 
782
829
  VALUE buf = rb_str_buf_new(36);
783
- VALUE keys = rb_funcall(settings, rb_intern("keys"), 0);
784
-
785
- for (long i = 0; i < RARRAY_LEN(keys); i++) {
786
- VALUE key = rb_ary_entry(keys, i);
787
- VALUE val = rb_hash_aref(settings, key);
788
- ID key_id = SYM2ID(key);
789
-
790
- uint16_t id;
791
- if (key_id == rb_intern("header_table_size")) id = SETTINGS_HEADER_TABLE_SIZE;
792
- else if (key_id == rb_intern("enable_push")) id = SETTINGS_ENABLE_PUSH;
793
- else if (key_id == rb_intern("max_concurrent_streams")) id = SETTINGS_MAX_CONCURRENT_STREAMS;
794
- else if (key_id == rb_intern("initial_window_size")) id = SETTINGS_INITIAL_WINDOW_SIZE;
795
- else if (key_id == rb_intern("max_frame_size")) id = SETTINGS_MAX_FRAME_SIZE;
796
- else if (key_id == rb_intern("max_header_list_size")) id = SETTINGS_MAX_HEADER_LIST_SIZE;
797
- else continue;
798
-
799
- uint32_t v = NUM2UINT(val);
800
- uint8_t entry[6];
801
- entry[0] = (id >> 8) & 0xff;
802
- entry[1] = id & 0xff;
803
- entry[2] = (v >> 24) & 0xff;
804
- entry[3] = (v >> 16) & 0xff;
805
- entry[4] = (v >> 8) & 0xff;
806
- entry[5] = v & 0xff;
807
- rb_str_buf_cat(buf, (const char *)entry, 6);
808
- }
809
-
830
+ rb_hash_foreach(settings, build_settings_iter, buf);
810
831
  return buf;
811
832
  }
812
833
 
813
- static VALUE h2_parse_window_update(VALUE self, VALUE payload) {
814
- (void)self;
834
+ static VALUE h2_parser_parse_window_update(VALUE self, VALUE payload) {
815
835
  Check_Type(payload, T_STRING);
816
836
 
817
837
  if (RSTRING_LEN(payload) != 4)
@@ -824,22 +844,51 @@ static VALUE h2_parse_window_update(VALUE self, VALUE payload) {
824
844
  return UINT2NUM(increment);
825
845
  }
826
846
 
847
+ static VALUE h2_parser_connection_preface(VALUE self) {
848
+ return rb_str_new_lit(CONNECTION_PREFACE);
849
+ }
850
+
827
851
  RUBY_FUNC_EXPORTED void Init_raptor_http2(void) {
828
852
  rb_ext_ractor_safe(true);
829
853
 
830
854
  VALUE mRaptor = rb_define_module("Raptor");
831
- cHttp2Parser = rb_define_class_under(mRaptor, "Http2Parser", rb_cObject);
855
+ VALUE cHttp2Parser = rb_define_class_under(mRaptor, "Http2Parser", rb_cObject);
832
856
  eHttp2ParserError = rb_define_class_under(mRaptor, "Http2ParserError", rb_eStandardError);
857
+ rb_define_const(cHttp2Parser, "MAX_HEADER_LIST_SIZE", INT2NUM(MAX_HEADER_LIST_SIZE));
858
+
859
+ id_type = rb_intern("type");
860
+ id_length = rb_intern("length");
861
+ id_flags = rb_intern("flags");
862
+ id_stream_id = rb_intern("stream_id");
863
+ id_payload = rb_intern("payload");
864
+ id_data = rb_intern("data");
865
+ id_headers = rb_intern("headers");
866
+ id_priority = rb_intern("priority");
867
+ id_rst_stream = rb_intern("rst_stream");
868
+ id_settings = rb_intern("settings");
869
+ id_push_promise = rb_intern("push_promise");
870
+ id_ping = rb_intern("ping");
871
+ id_goaway = rb_intern("goaway");
872
+ id_window_update = rb_intern("window_update");
873
+ id_continuation = rb_intern("continuation");
874
+ id_unknown = rb_intern("unknown");
875
+ id_header_table_size = rb_intern("header_table_size");
876
+ id_enable_push = rb_intern("enable_push");
877
+ id_max_concurrent_streams = rb_intern("max_concurrent_streams");
878
+ id_initial_window_size = rb_intern("initial_window_size");
879
+ id_max_frame_size = rb_intern("max_frame_size");
880
+ id_max_header_list_size = rb_intern("max_header_list_size");
833
881
 
834
882
  rb_define_alloc_func(cHttp2Parser, h2_parser_alloc);
835
- rb_define_method(cHttp2Parser, "parse_frame", h2_parse_frame, 1);
836
- rb_define_method(cHttp2Parser, "parse_headers", h2_parse_headers, 2);
837
- rb_define_method(cHttp2Parser, "encode_headers", h2_encode_headers, 1);
838
- rb_define_method(cHttp2Parser, "encode_response_headers", h2_encode_response_headers, 2);
839
- rb_define_method(cHttp2Parser, "encode_response_trailers", h2_encode_response_trailers, 1);
840
- rb_define_method(cHttp2Parser, "parse_settings", h2_parse_settings, 1);
841
- rb_define_method(cHttp2Parser, "build_settings", h2_build_settings, 1);
842
- rb_define_method(cHttp2Parser, "build_frame", h2_build_frame, 4);
843
- rb_define_method(cHttp2Parser, "parse_window_update", h2_parse_window_update, 1);
844
- rb_define_singleton_method(cHttp2Parser, "connection_preface", h2_connection_preface, 0);
883
+ rb_define_method(cHttp2Parser, "parse_frame", h2_parser_parse_frame, 1);
884
+ rb_define_method(cHttp2Parser, "build_frame", h2_parser_build_frame, 4);
885
+ rb_define_method(cHttp2Parser, "parse_headers", h2_parser_parse_headers, 2);
886
+ rb_define_method(cHttp2Parser, "encode_headers", h2_parser_encode_headers, 1);
887
+ rb_define_method(cHttp2Parser, "encode_response_headers", h2_parser_encode_response_headers, 2);
888
+ rb_define_method(cHttp2Parser, "encode_response_trailers", h2_parser_encode_response_trailers, 1);
889
+ rb_define_method(cHttp2Parser, "parse_settings", h2_parser_parse_settings, 1);
890
+ rb_define_method(cHttp2Parser, "build_settings", h2_parser_build_settings, 1);
891
+ rb_define_method(cHttp2Parser, "parse_window_update", h2_parser_parse_window_update, 1);
892
+
893
+ rb_define_singleton_method(cHttp2Parser, "connection_preface", h2_parser_connection_preface, 0);
845
894
  }