raptor 0.22.3 → 0.24.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -0
- data/Dockerfile +10 -2
- data/README.md +183 -61
- data/docs/assets/profile_picture.png +0 -0
- data/docs/assets/startup_output.svg +37 -0
- data/docs/raptor-vs-puma.md +104 -424
- data/docs/talk.md +1388 -0
- data/ext/raptor_bpf/reuseport_select.bpf.c +18 -9
- data/ext/raptor_http/extconf.rb +2 -0
- data/ext/raptor_http/raptor_http.c +165 -234
- data/ext/raptor_http2/raptor_http2.c +272 -223
- data/ext/raptor_native/raptor_native.c +38 -20
- data/lib/rackup/handler/raptor.rb +14 -12
- data/lib/raptor/binder.rb +12 -12
- data/lib/raptor/cli.rb +19 -18
- data/lib/raptor/cluster.rb +120 -74
- data/lib/raptor/control_server.rb +5 -2
- data/lib/raptor/detached_body.rb +6 -2
- data/lib/raptor/http.rb +7 -6
- data/lib/raptor/http1.rb +281 -234
- data/lib/raptor/http2.rb +356 -276
- data/lib/raptor/plugin.rb +98 -0
- data/lib/raptor/reactor.rb +138 -148
- data/lib/raptor/reuseport_bpf.rb +62 -38
- data/lib/raptor/server.rb +45 -34
- data/lib/raptor/stats.rb +15 -3
- data/lib/raptor/thread_locals.rb +4 -3
- data/lib/raptor/version.rb +1 -1
- data/sig/generated/rackup/handler/raptor.rbs +2 -1
- data/sig/generated/raptor/binder.rbs +9 -9
- data/sig/generated/raptor/cli.rbs +6 -6
- data/sig/generated/raptor/cluster.rbs +18 -5
- data/sig/generated/raptor/control_server.rbs +2 -1
- data/sig/generated/raptor/detached_body.rbs +5 -1
- data/sig/generated/raptor/http.rbs +3 -5
- data/sig/generated/raptor/http1.rbs +119 -102
- data/sig/generated/raptor/http2.rbs +204 -190
- data/sig/generated/raptor/plugin.rbs +52 -0
- data/sig/generated/raptor/reactor.rbs +107 -118
- data/sig/generated/raptor/reuseport_bpf.rbs +54 -17
- data/sig/generated/raptor/server.rbs +26 -26
- data/sig/generated/raptor/stats.rbs +3 -2
- data/sig/generated/raptor/thread_locals.rbs +4 -3
- metadata +6 -2
- data/docs/brisrails-talk.md +0 -2398
|
@@ -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
|
-
|
|
18
|
+
// Bounds each chunk-size line and the terminal chunk/trailer section, including
|
|
19
|
+
// CRLFs. Also limits cumulative nonterminal overhead beyond the payload size.
|
|
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,11 @@ typedef struct raptor_parser {
|
|
|
22
31
|
size_t query_start;
|
|
23
32
|
size_t nread;
|
|
24
33
|
size_t body_start;
|
|
25
|
-
|
|
34
|
+
int64_t content_len;
|
|
26
35
|
unsigned int flags;
|
|
27
36
|
VALUE request;
|
|
28
|
-
VALUE
|
|
37
|
+
VALUE underscored_headers;
|
|
38
|
+
// Scratch space for the env key: "HTTP_" prefix + field name + NUL.
|
|
29
39
|
char buf[MAX_FIELD_NAME + 6];
|
|
30
40
|
} raptor_parser;
|
|
31
41
|
|
|
@@ -33,33 +43,29 @@ typedef struct raptor_parser {
|
|
|
33
43
|
#define LEN(AT, P) ((P) - buffer - parser->AT)
|
|
34
44
|
#define PTR_TO(F) (buffer + parser->F)
|
|
35
45
|
|
|
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
46
|
static VALUE eHttpParserError;
|
|
43
47
|
static VALUE global_request_method;
|
|
44
48
|
static VALUE global_request_uri;
|
|
45
49
|
static VALUE global_query_string;
|
|
46
50
|
static VALUE global_server_protocol;
|
|
47
|
-
static VALUE
|
|
51
|
+
static VALUE global_path_info;
|
|
48
52
|
static VALUE global_fragment;
|
|
49
53
|
static ID id_complete;
|
|
50
54
|
static ID id_incomplete;
|
|
51
55
|
static ID id_malformed;
|
|
52
56
|
static ID id_too_large;
|
|
53
57
|
|
|
54
|
-
struct
|
|
58
|
+
typedef struct {
|
|
55
59
|
const char *name;
|
|
56
60
|
size_t len;
|
|
57
61
|
VALUE interned;
|
|
58
|
-
};
|
|
62
|
+
} common_field;
|
|
59
63
|
|
|
60
64
|
#define FIELD(name) { name, sizeof(name) - 1, Qnil }
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
// Env keys for frequently seen headers, interned once at load time so parsing
|
|
67
|
+
// these headers does not need to look up the fstring table.
|
|
68
|
+
static common_field common_fields[] = {
|
|
63
69
|
FIELD("HTTP_HOST"),
|
|
64
70
|
FIELD("HTTP_USER_AGENT"),
|
|
65
71
|
FIELD("HTTP_CONNECTION"),
|
|
@@ -111,7 +117,7 @@ static struct common_field common_fields[] = {
|
|
|
111
117
|
|
|
112
118
|
#define NUM_COMMON_FIELDS (sizeof(common_fields) / sizeof(common_fields[0]))
|
|
113
119
|
|
|
114
|
-
static VALUE
|
|
120
|
+
static VALUE intern_field(const char *buf, size_t len) {
|
|
115
121
|
for (size_t i = 0; i < NUM_COMMON_FIELDS; i++) {
|
|
116
122
|
if (common_fields[i].len == len && memcmp(common_fields[i].name, buf, len) == 0) {
|
|
117
123
|
return common_fields[i].interned;
|
|
@@ -120,13 +126,6 @@ static VALUE raptor_http_intern_field(const char *buf, size_t len) {
|
|
|
120
126
|
return rb_enc_interned_str(buf, len, rb_utf8_encoding());
|
|
121
127
|
}
|
|
122
128
|
|
|
123
|
-
static inline void upcase_header_char(char *c) {
|
|
124
|
-
if (*c >= 'a' && *c <= 'z')
|
|
125
|
-
*c &= ~0x20;
|
|
126
|
-
else if (*c == '-')
|
|
127
|
-
*c = '_';
|
|
128
|
-
}
|
|
129
|
-
|
|
130
129
|
static int contains_chunked(const char *value, long len) {
|
|
131
130
|
static const char chunked[] = "chunked";
|
|
132
131
|
static const long chunked_len = sizeof(chunked) - 1;
|
|
@@ -142,11 +141,80 @@ static int contains_chunked(const char *value, long len) {
|
|
|
142
141
|
return 0;
|
|
143
142
|
}
|
|
144
143
|
|
|
144
|
+
// Ruby checks for non-digit characters when it validates request framing.
|
|
145
|
+
static int64_t parse_content_length(const char *value, long len) {
|
|
146
|
+
int64_t content_len = 0;
|
|
147
|
+
for (long i = 0; i < len && value[i] >= '0' && value[i] <= '9'; i++) {
|
|
148
|
+
int digit = value[i] - '0';
|
|
149
|
+
if (content_len > (INT64_MAX - digit) / 10)
|
|
150
|
+
rb_raise(eHttpParserError, "content length too large");
|
|
151
|
+
content_len = content_len * 10 + digit;
|
|
152
|
+
}
|
|
153
|
+
return content_len;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
static void parse_header(raptor_parser *parser, const char *field, const char *value, size_t value_len) {
|
|
157
|
+
size_t field_len = parser->field_len;
|
|
158
|
+
if (field_len == 0 || field_len > MAX_FIELD_NAME)
|
|
159
|
+
rb_raise(eHttpParserError, "invalid field name length");
|
|
160
|
+
if (value_len > MAX_FIELD_VALUE)
|
|
161
|
+
rb_raise(eHttpParserError, "field value too long");
|
|
162
|
+
|
|
163
|
+
int underscored = 0;
|
|
164
|
+
char *normalized = parser->buf + 5;
|
|
165
|
+
for (size_t i = 0; i < field_len; i++) {
|
|
166
|
+
char c = field[i];
|
|
167
|
+
if (c == '_') underscored = 1;
|
|
168
|
+
normalized[i] = c == '-' ? '_' : (c >= 'a' && c <= 'z' ? c & ~0x20 : c);
|
|
169
|
+
}
|
|
170
|
+
int content_length = field_len == 14 && memcmp(normalized, "CONTENT_LENGTH", 14) == 0;
|
|
171
|
+
int content_type = field_len == 12 && memcmp(normalized, "CONTENT_TYPE", 12) == 0;
|
|
172
|
+
int transfer_encoding = field_len == 17 && memcmp(normalized, "TRANSFER_ENCODING", 17) == 0;
|
|
173
|
+
if (underscored && (content_length || transfer_encoding))
|
|
174
|
+
rb_raise(eHttpParserError, "invalid framing header name");
|
|
175
|
+
|
|
176
|
+
size_t key_len = field_len;
|
|
177
|
+
if (content_length || content_type) {
|
|
178
|
+
memmove(parser->buf, normalized, field_len);
|
|
179
|
+
} else {
|
|
180
|
+
memcpy(parser->buf, "HTTP_", 5);
|
|
181
|
+
key_len += 5;
|
|
182
|
+
}
|
|
183
|
+
VALUE key = intern_field(parser->buf, key_len);
|
|
184
|
+
VALUE existing = rb_hash_aref(parser->request, key);
|
|
185
|
+
int existing_underscored = !NIL_P(parser->underscored_headers) &&
|
|
186
|
+
RTEST(rb_hash_aref(parser->underscored_headers, key));
|
|
187
|
+
// Canonical names win in either order; true duplicates still merge.
|
|
188
|
+
if (underscored) {
|
|
189
|
+
if (!NIL_P(existing) && !existing_underscored) return;
|
|
190
|
+
if (NIL_P(parser->underscored_headers)) parser->underscored_headers = rb_hash_new();
|
|
191
|
+
rb_hash_aset(parser->underscored_headers, key, Qtrue);
|
|
192
|
+
} else if (existing_underscored) {
|
|
193
|
+
existing = Qnil;
|
|
194
|
+
rb_hash_delete(parser->underscored_headers, key);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
while (value_len > 0 && (value[value_len - 1] == ' ' || value[value_len - 1] == '\t'))
|
|
198
|
+
value_len--;
|
|
199
|
+
if (content_length) {
|
|
200
|
+
parser->content_len = parse_content_length(value, (long)value_len);
|
|
201
|
+
if (parser->content_len > 0) parser->flags |= FLAG_HAS_BODY;
|
|
202
|
+
} else if (transfer_encoding && contains_chunked(value, (long)value_len)) {
|
|
203
|
+
parser->flags |= FLAG_CHUNKED | FLAG_HAS_BODY;
|
|
204
|
+
parser->content_len = 0;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (!NIL_P(existing)) {
|
|
208
|
+
rb_str_cat(existing, ", ", 2);
|
|
209
|
+
rb_str_cat(existing, value, (long)value_len);
|
|
210
|
+
} else {
|
|
211
|
+
rb_hash_aset(parser->request, key, rb_str_new(value, (long)value_len));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
145
215
|
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
216
|
|
|
149
|
-
size_t raptor_parser_execute(raptor_parser *parser, const char *buffer, size_t len) {
|
|
217
|
+
static size_t raptor_parser_execute(raptor_parser *parser, const char *buffer, size_t len) {
|
|
150
218
|
const char *p, *pe;
|
|
151
219
|
int cs = parser->cs;
|
|
152
220
|
|
|
@@ -257,7 +325,7 @@ tr33:
|
|
|
257
325
|
tr37:
|
|
258
326
|
{
|
|
259
327
|
VALUE path = rb_str_new(PTR_TO(mark), LEN(mark,p));
|
|
260
|
-
rb_hash_aset(parser->request,
|
|
328
|
+
rb_hash_aset(parser->request, global_path_info, path);
|
|
261
329
|
}
|
|
262
330
|
{
|
|
263
331
|
if (LEN(mark, p) > MAX_URI_LENGTH)
|
|
@@ -370,122 +438,12 @@ tr18:
|
|
|
370
438
|
tr26:
|
|
371
439
|
{ MARK(mark, p); }
|
|
372
440
|
{
|
|
373
|
-
|
|
374
|
-
rb_raise(eHttpParserError, "invalid field name length");
|
|
375
|
-
|
|
376
|
-
size_t value_len = LEN(mark, p);
|
|
377
|
-
if (value_len > MAX_FIELD_VALUE)
|
|
378
|
-
rb_raise(eHttpParserError, "field value too long");
|
|
379
|
-
|
|
380
|
-
const char *field_ptr = PTR_TO(field_start);
|
|
381
|
-
|
|
382
|
-
int needs_http_prefix = 1;
|
|
383
|
-
if (parser->field_len == 14 && memcmp(field_ptr, "CONTENT_LENGTH", 14) == 0)
|
|
384
|
-
needs_http_prefix = 0;
|
|
385
|
-
else if (parser->field_len == 12 && memcmp(field_ptr, "CONTENT_TYPE", 12) == 0)
|
|
386
|
-
needs_http_prefix = 0;
|
|
387
|
-
|
|
388
|
-
size_t key_len;
|
|
389
|
-
if (needs_http_prefix) {
|
|
390
|
-
memcpy(parser->buf, "HTTP_", 5);
|
|
391
|
-
memcpy(parser->buf + 5, field_ptr, parser->field_len);
|
|
392
|
-
key_len = 5 + parser->field_len;
|
|
393
|
-
parser->buf[key_len] = '\0';
|
|
394
|
-
} else {
|
|
395
|
-
memcpy(parser->buf, field_ptr, parser->field_len);
|
|
396
|
-
key_len = parser->field_len;
|
|
397
|
-
parser->buf[key_len] = '\0';
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
VALUE key = raptor_http_intern_field(parser->buf, key_len);
|
|
401
|
-
VALUE value = rb_str_new(PTR_TO(mark), value_len);
|
|
402
|
-
|
|
403
|
-
char *value_ptr = RSTRING_PTR(value);
|
|
404
|
-
long value_real_len = RSTRING_LEN(value);
|
|
405
|
-
while (value_real_len > 0 && (value_ptr[value_real_len - 1] == ' ' ||
|
|
406
|
-
value_ptr[value_real_len - 1] == '\t'))
|
|
407
|
-
value_real_len--;
|
|
408
|
-
rb_str_set_len(value, value_real_len);
|
|
409
|
-
|
|
410
|
-
if (!needs_http_prefix && parser->field_len == 14) {
|
|
411
|
-
parser->content_len = strtoul(RSTRING_PTR(value), NULL, 10);
|
|
412
|
-
if (parser->content_len > 0)
|
|
413
|
-
parser->flags |= FLAG_HAS_BODY;
|
|
414
|
-
} else if (needs_http_prefix && parser->field_len == 17 &&
|
|
415
|
-
memcmp(field_ptr, "TRANSFER_ENCODING", 17) == 0) {
|
|
416
|
-
if (contains_chunked(value_ptr, value_real_len)) {
|
|
417
|
-
parser->flags |= FLAG_CHUNKED | FLAG_HAS_BODY;
|
|
418
|
-
parser->content_len = 0;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
VALUE existing = rb_hash_aref(parser->request, key);
|
|
423
|
-
if (!NIL_P(existing)) {
|
|
424
|
-
rb_str_cat2(existing, ", ");
|
|
425
|
-
rb_str_append(existing, value);
|
|
426
|
-
} else {
|
|
427
|
-
rb_hash_aset(parser->request, key, value);
|
|
428
|
-
}
|
|
441
|
+
parse_header(parser, PTR_TO(field_start), PTR_TO(mark), LEN(mark, p));
|
|
429
442
|
}
|
|
430
443
|
goto st14;
|
|
431
444
|
tr29:
|
|
432
445
|
{
|
|
433
|
-
|
|
434
|
-
rb_raise(eHttpParserError, "invalid field name length");
|
|
435
|
-
|
|
436
|
-
size_t value_len = LEN(mark, p);
|
|
437
|
-
if (value_len > MAX_FIELD_VALUE)
|
|
438
|
-
rb_raise(eHttpParserError, "field value too long");
|
|
439
|
-
|
|
440
|
-
const char *field_ptr = PTR_TO(field_start);
|
|
441
|
-
|
|
442
|
-
int needs_http_prefix = 1;
|
|
443
|
-
if (parser->field_len == 14 && memcmp(field_ptr, "CONTENT_LENGTH", 14) == 0)
|
|
444
|
-
needs_http_prefix = 0;
|
|
445
|
-
else if (parser->field_len == 12 && memcmp(field_ptr, "CONTENT_TYPE", 12) == 0)
|
|
446
|
-
needs_http_prefix = 0;
|
|
447
|
-
|
|
448
|
-
size_t key_len;
|
|
449
|
-
if (needs_http_prefix) {
|
|
450
|
-
memcpy(parser->buf, "HTTP_", 5);
|
|
451
|
-
memcpy(parser->buf + 5, field_ptr, parser->field_len);
|
|
452
|
-
key_len = 5 + parser->field_len;
|
|
453
|
-
parser->buf[key_len] = '\0';
|
|
454
|
-
} else {
|
|
455
|
-
memcpy(parser->buf, field_ptr, parser->field_len);
|
|
456
|
-
key_len = parser->field_len;
|
|
457
|
-
parser->buf[key_len] = '\0';
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
VALUE key = raptor_http_intern_field(parser->buf, key_len);
|
|
461
|
-
VALUE value = rb_str_new(PTR_TO(mark), value_len);
|
|
462
|
-
|
|
463
|
-
char *value_ptr = RSTRING_PTR(value);
|
|
464
|
-
long value_real_len = RSTRING_LEN(value);
|
|
465
|
-
while (value_real_len > 0 && (value_ptr[value_real_len - 1] == ' ' ||
|
|
466
|
-
value_ptr[value_real_len - 1] == '\t'))
|
|
467
|
-
value_real_len--;
|
|
468
|
-
rb_str_set_len(value, value_real_len);
|
|
469
|
-
|
|
470
|
-
if (!needs_http_prefix && parser->field_len == 14) {
|
|
471
|
-
parser->content_len = strtoul(RSTRING_PTR(value), NULL, 10);
|
|
472
|
-
if (parser->content_len > 0)
|
|
473
|
-
parser->flags |= FLAG_HAS_BODY;
|
|
474
|
-
} else if (needs_http_prefix && parser->field_len == 17 &&
|
|
475
|
-
memcmp(field_ptr, "TRANSFER_ENCODING", 17) == 0) {
|
|
476
|
-
if (contains_chunked(value_ptr, value_real_len)) {
|
|
477
|
-
parser->flags |= FLAG_CHUNKED | FLAG_HAS_BODY;
|
|
478
|
-
parser->content_len = 0;
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
VALUE existing = rb_hash_aref(parser->request, key);
|
|
483
|
-
if (!NIL_P(existing)) {
|
|
484
|
-
rb_str_cat2(existing, ", ");
|
|
485
|
-
rb_str_append(existing, value);
|
|
486
|
-
} else {
|
|
487
|
-
rb_hash_aset(parser->request, key, value);
|
|
488
|
-
}
|
|
446
|
+
parse_header(parser, PTR_TO(field_start), PTR_TO(mark), LEN(mark, p));
|
|
489
447
|
}
|
|
490
448
|
goto st14;
|
|
491
449
|
st14:
|
|
@@ -545,10 +503,8 @@ case 46:
|
|
|
545
503
|
goto st0;
|
|
546
504
|
tr21:
|
|
547
505
|
{ MARK(field_start, p); }
|
|
548
|
-
{ upcase_header_char((char *)p); }
|
|
549
506
|
goto st17;
|
|
550
507
|
tr23:
|
|
551
|
-
{ upcase_header_char((char *)p); }
|
|
552
508
|
goto st17;
|
|
553
509
|
st17:
|
|
554
510
|
if ( ++p == pe )
|
|
@@ -591,6 +547,7 @@ st18:
|
|
|
591
547
|
goto _test_eof18;
|
|
592
548
|
case 18:
|
|
593
549
|
switch( (*p) ) {
|
|
550
|
+
case 9: goto tr27;
|
|
594
551
|
case 13: goto tr26;
|
|
595
552
|
case 32: goto tr27;
|
|
596
553
|
case 127: goto st0;
|
|
@@ -629,7 +586,7 @@ tr9:
|
|
|
629
586
|
tr38:
|
|
630
587
|
{
|
|
631
588
|
VALUE path = rb_str_new(PTR_TO(mark), LEN(mark,p));
|
|
632
|
-
rb_hash_aset(parser->request,
|
|
589
|
+
rb_hash_aset(parser->request, global_path_info, path);
|
|
633
590
|
}
|
|
634
591
|
{
|
|
635
592
|
if (LEN(mark, p) > MAX_URI_LENGTH)
|
|
@@ -761,7 +718,7 @@ case 24:
|
|
|
761
718
|
tr39:
|
|
762
719
|
{
|
|
763
720
|
VALUE path = rb_str_new(PTR_TO(mark), LEN(mark,p));
|
|
764
|
-
rb_hash_aset(parser->request,
|
|
721
|
+
rb_hash_aset(parser->request, global_path_info, path);
|
|
765
722
|
}
|
|
766
723
|
goto st25;
|
|
767
724
|
st25:
|
|
@@ -1193,31 +1150,7 @@ done:
|
|
|
1193
1150
|
return parser->nread;
|
|
1194
1151
|
}
|
|
1195
1152
|
|
|
1196
|
-
|
|
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) {
|
|
1153
|
+
static void raptor_parser_init(raptor_parser *parser) {
|
|
1221
1154
|
parser->cs = raptor_parser_start;
|
|
1222
1155
|
parser->mark = 0;
|
|
1223
1156
|
parser->field_start = 0;
|
|
@@ -1228,19 +1161,13 @@ void raptor_parser_init(raptor_parser *parser) {
|
|
|
1228
1161
|
parser->content_len = 0;
|
|
1229
1162
|
parser->flags = 0;
|
|
1230
1163
|
parser->request = Qnil;
|
|
1231
|
-
parser->
|
|
1164
|
+
parser->underscored_headers = Qnil;
|
|
1232
1165
|
}
|
|
1233
1166
|
|
|
1234
|
-
static VALUE cHttpParser;
|
|
1235
|
-
|
|
1236
1167
|
static void parser_mark(void *ptr) {
|
|
1237
1168
|
raptor_parser *parser = ptr;
|
|
1238
1169
|
rb_gc_mark(parser->request);
|
|
1239
|
-
rb_gc_mark(parser->
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
static void parser_free(void *ptr) {
|
|
1243
|
-
xfree(ptr);
|
|
1170
|
+
rb_gc_mark(parser->underscored_headers);
|
|
1244
1171
|
}
|
|
1245
1172
|
|
|
1246
1173
|
static size_t parser_memsize(const void *ptr) {
|
|
@@ -1248,9 +1175,13 @@ static size_t parser_memsize(const void *ptr) {
|
|
|
1248
1175
|
}
|
|
1249
1176
|
|
|
1250
1177
|
static const rb_data_type_t parser_type = {
|
|
1251
|
-
"raptor_http_parser",
|
|
1252
|
-
|
|
1253
|
-
|
|
1178
|
+
.wrap_struct_name = "raptor_http_parser",
|
|
1179
|
+
.function = {
|
|
1180
|
+
.dmark = parser_mark,
|
|
1181
|
+
.dfree = RUBY_TYPED_DEFAULT_FREE,
|
|
1182
|
+
.dsize = parser_memsize,
|
|
1183
|
+
},
|
|
1184
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
1254
1185
|
};
|
|
1255
1186
|
|
|
1256
1187
|
static VALUE parser_alloc(VALUE klass) {
|
|
@@ -1282,31 +1213,25 @@ static VALUE parser_execute(VALUE self, VALUE req_hash, VALUE buffer, VALUE star
|
|
|
1282
1213
|
static VALUE parser_finished_p(VALUE self) {
|
|
1283
1214
|
raptor_parser *parser;
|
|
1284
1215
|
TypedData_Get_Struct(self, raptor_parser, &parser_type, parser);
|
|
1285
|
-
return
|
|
1216
|
+
return (parser->flags & FLAG_FINISHED) ? Qtrue : Qfalse;
|
|
1286
1217
|
}
|
|
1287
1218
|
|
|
1288
1219
|
static VALUE parser_has_body_p(VALUE self) {
|
|
1289
1220
|
raptor_parser *parser;
|
|
1290
1221
|
TypedData_Get_Struct(self, raptor_parser, &parser_type, parser);
|
|
1291
|
-
return
|
|
1222
|
+
return (parser->flags & FLAG_HAS_BODY) ? Qtrue : Qfalse;
|
|
1292
1223
|
}
|
|
1293
1224
|
|
|
1294
1225
|
static VALUE parser_chunked_p(VALUE self) {
|
|
1295
1226
|
raptor_parser *parser;
|
|
1296
1227
|
TypedData_Get_Struct(self, raptor_parser, &parser_type, parser);
|
|
1297
|
-
return
|
|
1228
|
+
return (parser->flags & FLAG_CHUNKED) ? Qtrue : Qfalse;
|
|
1298
1229
|
}
|
|
1299
1230
|
|
|
1300
1231
|
static VALUE parser_content_length(VALUE self) {
|
|
1301
1232
|
raptor_parser *parser;
|
|
1302
1233
|
TypedData_Get_Struct(self, raptor_parser, &parser_type, parser);
|
|
1303
|
-
return
|
|
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);
|
|
1234
|
+
return LL2NUM(parser->content_len);
|
|
1310
1235
|
}
|
|
1311
1236
|
|
|
1312
1237
|
static VALUE parser_reset(VALUE self) {
|
|
@@ -1316,12 +1241,6 @@ static VALUE parser_reset(VALUE self) {
|
|
|
1316
1241
|
return Qnil;
|
|
1317
1242
|
}
|
|
1318
1243
|
|
|
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
1244
|
static const char *find_crlf(const char *buf, size_t len) {
|
|
1326
1245
|
if (len < 2) return NULL;
|
|
1327
1246
|
const char *end = buf + len - 1;
|
|
@@ -1331,8 +1250,10 @@ static const char *find_crlf(const char *buf, size_t len) {
|
|
|
1331
1250
|
return NULL;
|
|
1332
1251
|
}
|
|
1333
1252
|
|
|
1253
|
+
// Decodes a chunked body into [decoded, state], appending the number of bytes
|
|
1254
|
+
// the body consumed when state is :complete so the caller can find any
|
|
1255
|
+
// pipelined request that follows it.
|
|
1334
1256
|
static VALUE parser_decode_chunked(int argc, VALUE *argv, VALUE self) {
|
|
1335
|
-
(void)self;
|
|
1336
1257
|
VALUE buffer, max_size;
|
|
1337
1258
|
rb_scan_args(argc, argv, "11", &buffer, &max_size);
|
|
1338
1259
|
Check_Type(buffer, T_STRING);
|
|
@@ -1348,6 +1269,9 @@ static VALUE parser_decode_chunked(int argc, VALUE *argv, VALUE self) {
|
|
|
1348
1269
|
|
|
1349
1270
|
while (offset < len) {
|
|
1350
1271
|
const char *crlf = find_crlf(buf + offset, len - offset);
|
|
1272
|
+
size_t line_bytes = crlf ? (size_t)(crlf - (buf + offset)) + 2 : len - offset;
|
|
1273
|
+
if (line_bytes > MAX_CHUNK_OVERHEAD)
|
|
1274
|
+
return rb_ary_new_from_args(2, decoded, ID2SYM(id_malformed));
|
|
1351
1275
|
if (!crlf) return rb_ary_new_from_args(2, decoded, ID2SYM(id_incomplete));
|
|
1352
1276
|
|
|
1353
1277
|
size_t line_len = (size_t)(crlf - (buf + offset));
|
|
@@ -1377,8 +1301,11 @@ static VALUE parser_decode_chunked(int argc, VALUE *argv, VALUE self) {
|
|
|
1377
1301
|
while (1) {
|
|
1378
1302
|
if (trailer_offset >= len) return rb_ary_new_from_args(2, decoded, ID2SYM(id_incomplete));
|
|
1379
1303
|
const char *tcrlf = find_crlf(buf + trailer_offset, len - trailer_offset);
|
|
1304
|
+
size_t terminal_bytes = tcrlf ? (size_t)(tcrlf - (buf + offset)) + 2 : len - offset;
|
|
1305
|
+
if (terminal_bytes > MAX_CHUNK_OVERHEAD)
|
|
1306
|
+
return rb_ary_new_from_args(2, decoded, ID2SYM(id_malformed));
|
|
1380
1307
|
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(
|
|
1308
|
+
if (tcrlf == buf + trailer_offset) return rb_ary_new_from_args(3, decoded, ID2SYM(id_complete), SIZET2NUM(trailer_offset + 2));
|
|
1382
1309
|
trailer_offset = (size_t)(tcrlf - buf) + 2;
|
|
1383
1310
|
}
|
|
1384
1311
|
}
|
|
@@ -1398,12 +1325,23 @@ static VALUE parser_decode_chunked(int argc, VALUE *argv, VALUE self) {
|
|
|
1398
1325
|
size_t bytes_to_copy = (size_t)chunk_size < available ? (size_t)chunk_size : available;
|
|
1399
1326
|
rb_str_cat(decoded, buf + data_offset, (long)bytes_to_copy);
|
|
1400
1327
|
|
|
1328
|
+
if (available <= (size_t)chunk_size)
|
|
1329
|
+
return rb_ary_new_from_args(2, decoded, ID2SYM(id_incomplete));
|
|
1330
|
+
if (buf[data_offset + (size_t)chunk_size] != '\r')
|
|
1331
|
+
return rb_ary_new_from_args(2, decoded, ID2SYM(id_malformed));
|
|
1332
|
+
if (available == (size_t)chunk_size + 1)
|
|
1333
|
+
return rb_ary_new_from_args(2, decoded, ID2SYM(id_incomplete));
|
|
1334
|
+
if (buf[data_offset + (size_t)chunk_size + 1] != '\n')
|
|
1335
|
+
return rb_ary_new_from_args(2, decoded, ID2SYM(id_malformed));
|
|
1336
|
+
|
|
1401
1337
|
offset = data_offset + (size_t)chunk_size + 2;
|
|
1402
1338
|
}
|
|
1403
1339
|
|
|
1404
1340
|
return rb_ary_new_from_args(2, decoded, ID2SYM(id_incomplete));
|
|
1405
1341
|
}
|
|
1406
1342
|
|
|
1343
|
+
// One bit per byte value. Header names reject C0 controls, space, DEL and
|
|
1344
|
+
// separators; header values reject C0 controls other than horizontal tab.
|
|
1407
1345
|
static uint8_t illegal_header_key_bitmap[32];
|
|
1408
1346
|
static uint8_t illegal_header_value_bitmap[32];
|
|
1409
1347
|
|
|
@@ -1411,6 +1349,16 @@ static void bitmap_set(uint8_t *bitmap, unsigned char byte) {
|
|
|
1411
1349
|
bitmap[byte >> 3] |= (uint8_t)(1u << (byte & 7));
|
|
1412
1350
|
}
|
|
1413
1351
|
|
|
1352
|
+
static void init_illegal_header_bitmaps(void) {
|
|
1353
|
+
for (int byte = 0x00; byte <= 0x20; byte++) bitmap_set(illegal_header_key_bitmap, (unsigned char)byte);
|
|
1354
|
+
const char *key_specials = "()<>@,;:\\\"/[]?={}";
|
|
1355
|
+
for (const char *p = key_specials; *p; p++) bitmap_set(illegal_header_key_bitmap, (unsigned char)*p);
|
|
1356
|
+
bitmap_set(illegal_header_key_bitmap, 0x7F);
|
|
1357
|
+
|
|
1358
|
+
for (int byte = 0x00; byte <= 0x08; byte++) bitmap_set(illegal_header_value_bitmap, (unsigned char)byte);
|
|
1359
|
+
for (int byte = 0x0A; byte <= 0x1F; byte++) bitmap_set(illegal_header_value_bitmap, (unsigned char)byte);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1414
1362
|
static int contains_illegal_byte(const uint8_t *bitmap, const char *str, size_t len) {
|
|
1415
1363
|
for (size_t i = 0; i < len; i++) {
|
|
1416
1364
|
unsigned char byte = (unsigned char)str[i];
|
|
@@ -1420,13 +1368,11 @@ static int contains_illegal_byte(const uint8_t *bitmap, const char *str, size_t
|
|
|
1420
1368
|
}
|
|
1421
1369
|
|
|
1422
1370
|
static VALUE parser_illegal_header_key_p(VALUE self, VALUE key) {
|
|
1423
|
-
(void)self;
|
|
1424
1371
|
Check_Type(key, T_STRING);
|
|
1425
1372
|
return contains_illegal_byte(illegal_header_key_bitmap, RSTRING_PTR(key), (size_t)RSTRING_LEN(key)) ? Qtrue : Qfalse;
|
|
1426
1373
|
}
|
|
1427
1374
|
|
|
1428
1375
|
static VALUE parser_illegal_header_value_p(VALUE self, VALUE value) {
|
|
1429
|
-
(void)self;
|
|
1430
1376
|
Check_Type(value, T_STRING);
|
|
1431
1377
|
return contains_illegal_byte(illegal_header_value_bitmap, RSTRING_PTR(value), (size_t)RSTRING_LEN(value)) ? Qtrue : Qfalse;
|
|
1432
1378
|
}
|
|
@@ -1442,7 +1388,7 @@ static void emit_header_line(VALUE buffer, const char *name, long name_len,
|
|
|
1442
1388
|
}
|
|
1443
1389
|
|
|
1444
1390
|
static void emit_header(VALUE buffer, const char *name, long name_len, VALUE value) {
|
|
1445
|
-
VALUE str =
|
|
1391
|
+
VALUE str = rb_obj_as_string(value);
|
|
1446
1392
|
const char *ptr = RSTRING_PTR(str);
|
|
1447
1393
|
long len = RSTRING_LEN(str);
|
|
1448
1394
|
if (len == 0) return;
|
|
@@ -1459,13 +1405,10 @@ static void emit_header(VALUE buffer, const char *name, long name_len, VALUE val
|
|
|
1459
1405
|
line_start = i + 1;
|
|
1460
1406
|
}
|
|
1461
1407
|
}
|
|
1462
|
-
if (line_start < len)
|
|
1463
|
-
emit_header_line(buffer, name, name_len, ptr + line_start, len - line_start);
|
|
1464
|
-
}
|
|
1408
|
+
if (line_start < len) emit_header_line(buffer, name, name_len, ptr + line_start, len - line_start);
|
|
1465
1409
|
}
|
|
1466
1410
|
|
|
1467
|
-
static int format_header_iter(VALUE key, VALUE value, VALUE
|
|
1468
|
-
VALUE buffer = (VALUE)data;
|
|
1411
|
+
static int format_header_iter(VALUE key, VALUE value, VALUE buffer) {
|
|
1469
1412
|
if (!RB_TYPE_P(key, T_STRING)) return ST_CONTINUE;
|
|
1470
1413
|
const char *name = RSTRING_PTR(key);
|
|
1471
1414
|
long name_len = RSTRING_LEN(key);
|
|
@@ -1483,7 +1426,6 @@ static int format_header_iter(VALUE key, VALUE value, VALUE data) {
|
|
|
1483
1426
|
}
|
|
1484
1427
|
|
|
1485
1428
|
static VALUE parser_format_headers(VALUE self, VALUE buffer, VALUE headers) {
|
|
1486
|
-
(void)self;
|
|
1487
1429
|
Check_Type(buffer, T_STRING);
|
|
1488
1430
|
Check_Type(headers, T_HASH);
|
|
1489
1431
|
rb_hash_foreach(headers, format_header_iter, buffer);
|
|
@@ -1491,7 +1433,6 @@ static VALUE parser_format_headers(VALUE self, VALUE buffer, VALUE headers) {
|
|
|
1491
1433
|
}
|
|
1492
1434
|
|
|
1493
1435
|
static VALUE parser_chunked_encode(VALUE self, VALUE buffer, VALUE chunk) {
|
|
1494
|
-
(void)self;
|
|
1495
1436
|
Check_Type(buffer, T_STRING);
|
|
1496
1437
|
Check_Type(chunk, T_STRING);
|
|
1497
1438
|
long chunk_len = RSTRING_LEN(chunk);
|
|
@@ -1506,36 +1447,27 @@ static VALUE parser_chunked_encode(VALUE self, VALUE buffer, VALUE chunk) {
|
|
|
1506
1447
|
return buffer;
|
|
1507
1448
|
}
|
|
1508
1449
|
|
|
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
1450
|
RUBY_FUNC_EXPORTED void Init_raptor_http(void) {
|
|
1520
1451
|
rb_ext_ractor_safe(true);
|
|
1521
1452
|
|
|
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
1453
|
rb_global_variable(&global_request_method);
|
|
1527
1454
|
rb_global_variable(&global_request_uri);
|
|
1528
1455
|
rb_global_variable(&global_query_string);
|
|
1529
1456
|
rb_global_variable(&global_server_protocol);
|
|
1530
|
-
rb_global_variable(&
|
|
1457
|
+
rb_global_variable(&global_path_info);
|
|
1531
1458
|
rb_global_variable(&global_fragment);
|
|
1532
1459
|
|
|
1533
|
-
global_request_method =
|
|
1534
|
-
global_request_uri =
|
|
1535
|
-
global_query_string =
|
|
1536
|
-
global_server_protocol =
|
|
1537
|
-
|
|
1538
|
-
global_fragment =
|
|
1460
|
+
global_request_method = rb_str_new_lit("REQUEST_METHOD");
|
|
1461
|
+
global_request_uri = rb_str_new_lit("REQUEST_URI");
|
|
1462
|
+
global_query_string = rb_str_new_lit("QUERY_STRING");
|
|
1463
|
+
global_server_protocol = rb_str_new_lit("SERVER_PROTOCOL");
|
|
1464
|
+
global_path_info = rb_str_new_lit("PATH_INFO");
|
|
1465
|
+
global_fragment = rb_str_new_lit("FRAGMENT");
|
|
1466
|
+
|
|
1467
|
+
for (size_t i = 0; i < NUM_COMMON_FIELDS; i++) {
|
|
1468
|
+
rb_global_variable(&common_fields[i].interned);
|
|
1469
|
+
common_fields[i].interned = rb_enc_interned_str(common_fields[i].name, common_fields[i].len, rb_utf8_encoding());
|
|
1470
|
+
}
|
|
1539
1471
|
|
|
1540
1472
|
id_complete = rb_intern("complete");
|
|
1541
1473
|
id_incomplete = rb_intern("incomplete");
|
|
@@ -1544,10 +1476,11 @@ RUBY_FUNC_EXPORTED void Init_raptor_http(void) {
|
|
|
1544
1476
|
|
|
1545
1477
|
init_illegal_header_bitmaps();
|
|
1546
1478
|
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1479
|
+
VALUE mRaptor = rb_define_module("Raptor");
|
|
1480
|
+
VALUE cHttpParser = rb_define_class_under(mRaptor, "HttpParser", rb_cObject);
|
|
1481
|
+
eHttpParserError = rb_define_class_under(mRaptor, "HttpParserError", rb_eStandardError);
|
|
1482
|
+
|
|
1483
|
+
rb_define_const(cHttpParser, "MAX_CHUNK_OVERHEAD", INT2FIX(MAX_CHUNK_OVERHEAD));
|
|
1551
1484
|
|
|
1552
1485
|
rb_define_alloc_func(cHttpParser, parser_alloc);
|
|
1553
1486
|
rb_define_method(cHttpParser, "execute", parser_execute, 3);
|
|
@@ -1555,13 +1488,11 @@ RUBY_FUNC_EXPORTED void Init_raptor_http(void) {
|
|
|
1555
1488
|
rb_define_method(cHttpParser, "has_body?", parser_has_body_p, 0);
|
|
1556
1489
|
rb_define_method(cHttpParser, "chunked?", parser_chunked_p, 0);
|
|
1557
1490
|
rb_define_method(cHttpParser, "content_length", parser_content_length, 0);
|
|
1558
|
-
rb_define_method(cHttpParser, "nread", parser_nread, 0);
|
|
1559
1491
|
rb_define_method(cHttpParser, "reset", parser_reset, 0);
|
|
1560
|
-
|
|
1492
|
+
|
|
1561
1493
|
rb_define_singleton_method(cHttpParser, "decode_chunked", parser_decode_chunked, -1);
|
|
1562
1494
|
rb_define_singleton_method(cHttpParser, "illegal_header_key?", parser_illegal_header_key_p, 1);
|
|
1563
1495
|
rb_define_singleton_method(cHttpParser, "illegal_header_value?", parser_illegal_header_value_p, 1);
|
|
1564
1496
|
rb_define_singleton_method(cHttpParser, "format_headers", parser_format_headers, 2);
|
|
1565
1497
|
rb_define_singleton_method(cHttpParser, "chunked_encode", parser_chunked_encode, 2);
|
|
1566
|
-
rb_define_const(cHttpParser, "MAX_CHUNK_OVERHEAD", INT2FIX(MAX_CHUNK_OVERHEAD));
|
|
1567
1498
|
}
|