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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/Dockerfile +3 -1
- data/README.md +38 -31
- data/docs/raptor-vs-puma.md +44 -38
- data/docs/talk.md +1295 -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 +91 -119
- data/ext/raptor_http2/raptor_http2.c +272 -223
- data/ext/raptor_native/raptor_native.c +38 -20
- data/lib/rackup/handler/raptor.rb +12 -11
- data/lib/raptor/binder.rb +12 -12
- data/lib/raptor/cli.rb +18 -18
- data/lib/raptor/cluster.rb +44 -44
- data/lib/raptor/control_server.rb +3 -2
- data/lib/raptor/detached_body.rb +6 -2
- data/lib/raptor/http.rb +7 -5
- data/lib/raptor/http1.rb +232 -207
- data/lib/raptor/http2.rb +312 -265
- 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 +9 -9
- 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 -3
- data/sig/generated/raptor/http1.rbs +100 -91
- data/sig/generated/raptor/http2.rbs +200 -189
- 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 +2 -2
- 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
|
|
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
|
-
//
|
|
23
|
-
//
|
|
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
|
-
|
|
52
|
-
|
|
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
|
|
data/ext/raptor_http/extconf.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
34
|
+
int64_t content_len;
|
|
26
35
|
unsigned int flags;
|
|
27
36
|
VALUE request;
|
|
28
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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,
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
1253
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
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(&
|
|
1498
|
+
rb_global_variable(&global_path_info);
|
|
1531
1499
|
rb_global_variable(&global_fragment);
|
|
1532
1500
|
|
|
1533
|
-
global_request_method =
|
|
1534
|
-
global_request_uri =
|
|
1535
|
-
global_query_string =
|
|
1536
|
-
global_server_protocol =
|
|
1537
|
-
|
|
1538
|
-
global_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
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
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
|
-
|
|
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
|
}
|