tipi 0.41 → 0.42
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/.github/workflows/test.yml +4 -0
- data/.gitignore +3 -1
- data/CHANGELOG.md +11 -0
- data/Gemfile +5 -1
- data/Gemfile.lock +26 -12
- data/benchmarks/bm_http1_parser.rb +61 -0
- data/bin/benchmark +37 -0
- data/bin/h1pd +6 -0
- data/bin/tipi +3 -21
- data/df/server.rb +1 -1
- data/df/server_utils.rb +49 -44
- data/examples/full_service.rb +13 -0
- data/examples/http1_parser.rb +10 -8
- data/examples/http_server.rb +4 -1
- data/examples/https_server.rb +3 -0
- data/examples/servername_cb.rb +37 -0
- data/ext/tipi/extconf.rb +3 -2
- data/ext/tipi/http1_parser.c +478 -189
- data/lib/tipi.rb +84 -3
- data/lib/tipi/acme.rb +308 -0
- data/lib/tipi/cli.rb +30 -0
- data/lib/tipi/digital_fabric/agent.rb +2 -2
- data/lib/tipi/digital_fabric/agent_proxy.rb +4 -3
- data/lib/tipi/digital_fabric/executive.rb +6 -2
- data/lib/tipi/digital_fabric/protocol.rb +2 -2
- data/lib/tipi/digital_fabric/request_adapter.rb +0 -4
- data/lib/tipi/digital_fabric/service.rb +5 -10
- data/lib/tipi/http1_adapter.rb +55 -100
- data/lib/tipi/http2_adapter.rb +19 -6
- data/lib/tipi/http2_stream.rb +39 -43
- data/lib/tipi/version.rb +1 -1
- data/security/http1.rb +12 -0
- data/test/helper.rb +60 -11
- data/test/test_http1_parser.rb +586 -0
- data/test/test_http_server.rb +0 -27
- data/test/test_request.rb +1 -28
- data/tipi.gemspec +6 -5
- metadata +48 -27
- data/examples/automatic_certificate.rb +0 -193
- data/lib/tipi/http1_adapter_new.rb +0 -293
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'tipi'
|
5
|
+
|
6
|
+
::Exception.__disable_sanitized_backtrace__ = true
|
7
|
+
|
8
|
+
certificate_db_path = File.expand_path('certificate_store.db', __dir__)
|
9
|
+
certificate_store = Tipi::ACME::SQLiteCertificateStore.new(certificate_db_path)
|
10
|
+
|
11
|
+
Tipi.full_service(
|
12
|
+
certificate_store: certificate_store
|
13
|
+
) { |req| req.respond('Hello, world!') }
|
data/examples/http1_parser.rb
CHANGED
@@ -31,23 +31,25 @@ f = spin do
|
|
31
31
|
break unless headers
|
32
32
|
trace headers
|
33
33
|
|
34
|
-
body = parser.read_body
|
34
|
+
body = parser.read_body
|
35
35
|
trace "body: #{body ? body.bytesize : 0} bytes"
|
36
|
+
trace body if body && body.bytesize < 80
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
39
40
|
o << "GET /a HTTP/1.1\r\n\r\n"
|
40
|
-
|
41
|
+
|
42
|
+
# o << "GET /a HTTP/1.1\r\nContent-Length: 0\r\n\r\n"
|
41
43
|
|
42
44
|
# o << "GET / HTTP/1.1\r\nHost: localhost:10080\r\nUser-Agent: curl/7.74.0\r\nAccept: */*\r\n\r\n"
|
43
45
|
|
44
|
-
|
46
|
+
o << "post /?q=time&blah=blah HTTP/1\r\nTransfer-Encoding: chunked\r\n\r\na\r\nabcdefghij\r\n0\r\n\r\n"
|
47
|
+
|
48
|
+
data = " " * 4000000
|
49
|
+
o << "get /?q=time HTTP/1.1\r\nContent-Length: #{data.bytesize}\r\n\r\n#{data}"
|
45
50
|
|
46
|
-
|
47
|
-
# o << "get /?q=time HTTP/1.1\r\nContent-Length: #{data.bytesize}\r\n\r\n#{data}"
|
51
|
+
o << "get /?q=time HTTP/1.1\r\nCookie: foo\r\nCookie: bar\r\n\r\n"
|
48
52
|
|
49
|
-
|
53
|
+
o.close
|
50
54
|
|
51
|
-
# o.close
|
52
|
-
|
53
55
|
f.await
|
data/examples/http_server.rb
CHANGED
@@ -14,7 +14,7 @@ puts 'Listening on port 10080...'
|
|
14
14
|
# GC.disable
|
15
15
|
# Thread.current.backend.idle_gc_period = 60
|
16
16
|
|
17
|
-
spin_loop(interval: 10) { p Thread.
|
17
|
+
spin_loop(interval: 10) { p Thread.backend.stats }
|
18
18
|
|
19
19
|
spin_loop(interval: 10) do
|
20
20
|
GC.compact
|
@@ -29,6 +29,9 @@ spin do
|
|
29
29
|
sleep 1
|
30
30
|
req.send_chunk("bar\n")
|
31
31
|
req.finish
|
32
|
+
elsif req.path == '/upload'
|
33
|
+
body = req.read
|
34
|
+
req.respond("Body: #{body.inspect} (#{body.bytesize} bytes)")
|
32
35
|
else
|
33
36
|
req.respond("Hello world!\n")
|
34
37
|
end
|
data/examples/https_server.rb
CHANGED
@@ -23,6 +23,9 @@ Tipi.serve('0.0.0.0', 1234, opts) do |req|
|
|
23
23
|
req.send_chunk("foo\n")
|
24
24
|
sleep 0.5
|
25
25
|
req.send_chunk("bar\n", done: true)
|
26
|
+
elsif req.path == '/upload'
|
27
|
+
body = req.read
|
28
|
+
req.respond("Body: #{body.inspect} (#{body.bytesize} bytes)")
|
26
29
|
else
|
27
30
|
req.respond("Hello world!\n")
|
28
31
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
require 'fiber'
|
5
|
+
|
6
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
7
|
+
|
8
|
+
f = Fiber.new { |peer| loop { p peer: peer; _name, peer = peer.transfer nil } }
|
9
|
+
ctx.servername_cb = proc { |_socket, name|
|
10
|
+
p servername_cb: name
|
11
|
+
f.transfer([name, Fiber.current]).tap { |r| p result: r }
|
12
|
+
}
|
13
|
+
|
14
|
+
socket = Socket.new(:INET, :STREAM).tap do |s|
|
15
|
+
s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
|
16
|
+
s.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEPORT, 1)
|
17
|
+
s.bind(Socket.sockaddr_in(12345, '0.0.0.0'))
|
18
|
+
s.listen(Socket::SOMAXCONN)
|
19
|
+
end
|
20
|
+
server = OpenSSL::SSL::SSLServer.new(socket, ctx)
|
21
|
+
|
22
|
+
Thread.new do
|
23
|
+
sleep 0.5
|
24
|
+
socket = TCPSocket.new('127.0.0.1', 12345)
|
25
|
+
client = OpenSSL::SSL::SSLSocket.new(socket)
|
26
|
+
client.hostname = 'example.com'
|
27
|
+
p client: client
|
28
|
+
client.connect
|
29
|
+
rescue => e
|
30
|
+
p client_error: e
|
31
|
+
end
|
32
|
+
|
33
|
+
while true
|
34
|
+
conn = server.accept
|
35
|
+
p accepted: conn
|
36
|
+
break
|
37
|
+
end
|
data/ext/tipi/extconf.rb
CHANGED
@@ -3,10 +3,11 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'mkmf'
|
5
5
|
|
6
|
-
|
6
|
+
require_relative '../../security/http1'
|
7
7
|
|
8
|
+
$CFLAGS << " -Wno-format-security"
|
8
9
|
CONFIG['optflags'] << ' -fno-strict-aliasing' unless RUBY_PLATFORM =~ /mswin/
|
9
|
-
|
10
|
+
Tipi::HTTP1_LIMITS.each { |k, v| $defs << "-D#{k.upcase}=#{v}" }
|
10
11
|
|
11
12
|
dir_config 'tipi_ext'
|
12
13
|
create_makefile 'tipi_ext'
|
data/ext/tipi/http1_parser.c
CHANGED
@@ -1,36 +1,66 @@
|
|
1
1
|
#include "ruby.h"
|
2
2
|
#include "http1_parser.h"
|
3
3
|
|
4
|
-
|
4
|
+
// Security-related limits are defined in security/http1.rb and injected as
|
5
|
+
// defines in extconf.rb
|
6
|
+
|
7
|
+
#define INITIAL_BUFFER_SIZE 4096
|
8
|
+
#define BUFFER_TRIM_MIN_LEN 4096
|
9
|
+
#define BUFFER_TRIM_MIN_POS 2048
|
10
|
+
#define MAX_HEADERS_READ_LENGTH 4096
|
11
|
+
#define MAX_BODY_READ_LENGTH (1 << 20) // 1MB
|
5
12
|
|
6
|
-
|
7
|
-
|
8
|
-
const int MAX_HEADER_KEY_LENGTH = 128;
|
9
|
-
const int MAX_HEADER_VALUE_LENGTH = 2048;
|
13
|
+
#define BODY_READ_MODE_UNKNOWN -2
|
14
|
+
#define BODY_READ_MODE_CHUNKED -1
|
10
15
|
|
16
|
+
ID ID_arity;
|
11
17
|
ID ID_backend_read;
|
18
|
+
ID ID_backend_recv;
|
19
|
+
ID ID_call;
|
12
20
|
ID ID_downcase;
|
21
|
+
ID ID_eq;
|
22
|
+
ID ID_parser_read_method;
|
13
23
|
ID ID_read;
|
14
24
|
ID ID_readpartial;
|
15
25
|
ID ID_to_i;
|
16
26
|
|
17
|
-
VALUE mPolyphony;
|
18
|
-
VALUE cError;
|
27
|
+
static VALUE mPolyphony = Qnil;
|
28
|
+
static VALUE cError;
|
19
29
|
|
20
|
-
VALUE
|
21
|
-
VALUE
|
30
|
+
VALUE NUM_max_headers_read_length;
|
31
|
+
VALUE NUM_buffer_start;
|
32
|
+
VALUE NUM_buffer_end;
|
22
33
|
|
23
34
|
VALUE STR_pseudo_method;
|
24
35
|
VALUE STR_pseudo_path;
|
25
36
|
VALUE STR_pseudo_protocol;
|
37
|
+
VALUE STR_pseudo_rx;
|
26
38
|
|
39
|
+
VALUE STR_chunked;
|
27
40
|
VALUE STR_content_length;
|
28
41
|
VALUE STR_transfer_encoding;
|
29
42
|
|
43
|
+
VALUE SYM_backend_read;
|
44
|
+
VALUE SYM_backend_recv;
|
45
|
+
|
46
|
+
enum read_method {
|
47
|
+
method_readpartial, // receiver.readpartial (Polyphony-specific)
|
48
|
+
method_backend_read, // Polyphony.backend_read (Polyphony-specific)
|
49
|
+
method_backend_recv, // Polyphony.backend_recv (Polyphony-specific)
|
50
|
+
method_call // receiver.call(len) (Universal)
|
51
|
+
};
|
52
|
+
|
30
53
|
typedef struct parser {
|
31
54
|
VALUE io;
|
32
55
|
VALUE buffer;
|
33
|
-
|
56
|
+
VALUE headers;
|
57
|
+
int pos;
|
58
|
+
int current_request_rx;
|
59
|
+
|
60
|
+
enum read_method read_method;
|
61
|
+
int body_read_mode;
|
62
|
+
int body_left;
|
63
|
+
int request_completed;
|
34
64
|
} Parser_t;
|
35
65
|
|
36
66
|
VALUE cParser = Qnil;
|
@@ -39,10 +69,10 @@ static void Parser_mark(void *ptr) {
|
|
39
69
|
Parser_t *parser = ptr;
|
40
70
|
rb_gc_mark(parser->io);
|
41
71
|
rb_gc_mark(parser->buffer);
|
72
|
+
rb_gc_mark(parser->headers);
|
42
73
|
}
|
43
74
|
|
44
75
|
static void Parser_free(void *ptr) {
|
45
|
-
Parser_t *parser = ptr;
|
46
76
|
xfree(ptr);
|
47
77
|
}
|
48
78
|
|
@@ -66,40 +96,43 @@ static VALUE Parser_allocate(VALUE klass) {
|
|
66
96
|
#define GetParser(obj, parser) \
|
67
97
|
TypedData_Get_Struct((obj), Parser_t, &Parser_type, (parser))
|
68
98
|
|
99
|
+
enum read_method detect_read_method(VALUE io) {
|
100
|
+
if (rb_respond_to(io, ID_parser_read_method)) {
|
101
|
+
if (mPolyphony == Qnil)
|
102
|
+
mPolyphony = rb_const_get(rb_cObject, rb_intern("Polyphony"));
|
103
|
+
VALUE method = rb_funcall(io, ID_parser_read_method, 0);
|
104
|
+
if (method == SYM_backend_read) return method_backend_read;
|
105
|
+
if (method == SYM_backend_recv) return method_backend_recv;
|
106
|
+
return method_readpartial;
|
107
|
+
}
|
108
|
+
else if (rb_respond_to(io, ID_call)) {
|
109
|
+
return method_call;
|
110
|
+
}
|
111
|
+
else
|
112
|
+
rb_raise(rb_eRuntimeError, "Provided reader should be a callable or respond to #__parser_read_method__");
|
113
|
+
}
|
114
|
+
|
69
115
|
VALUE Parser_initialize(VALUE self, VALUE io) {
|
70
116
|
Parser_t *parser;
|
71
117
|
GetParser(self, parser);
|
72
118
|
|
73
119
|
parser->io = io;
|
74
120
|
parser->buffer = rb_str_new_literal("");
|
121
|
+
parser->headers = Qnil;
|
75
122
|
parser->pos = 0;
|
76
123
|
|
124
|
+
// pre-allocate the buffer
|
125
|
+
rb_str_modify_expand(parser->buffer, INITIAL_BUFFER_SIZE);
|
126
|
+
|
127
|
+
parser->read_method = detect_read_method(io);
|
128
|
+
parser->body_read_mode = BODY_READ_MODE_UNKNOWN;
|
129
|
+
parser->body_left = 0;
|
77
130
|
return self;
|
78
131
|
}
|
79
132
|
|
80
133
|
////////////////////////////////////////////////////////////////////////////////
|
81
|
-
////////////////////////////////////////////////////////////////////////////////
|
82
|
-
////////////////////////////////////////////////////////////////////////////////
|
83
|
-
|
84
|
-
struct parser_state {
|
85
|
-
struct parser *parser;
|
86
|
-
char *ptr;
|
87
|
-
int len;
|
88
|
-
};
|
89
134
|
|
90
|
-
#define
|
91
|
-
rb_funcall(io, ID_readpartial, 4, len, buffer, BUFFER_END, Qfalse)
|
92
|
-
|
93
|
-
static inline int fill_buffer(struct parser_state *state) {
|
94
|
-
READ_CONCAT(state->parser->io, state->parser->buffer, MAX_READ_LENGTH);
|
95
|
-
int len = RSTRING_LEN(state->parser->buffer);
|
96
|
-
int read_bytes = len - state->len;
|
97
|
-
if (!read_bytes) return 0;
|
98
|
-
|
99
|
-
state->ptr = RSTRING_PTR(state->parser->buffer);
|
100
|
-
state->len = len;
|
101
|
-
return read_bytes;
|
102
|
-
}
|
135
|
+
#define str_downcase(str) (rb_funcall((str), ID_downcase, 0))
|
103
136
|
|
104
137
|
#define FILL_BUFFER_OR_GOTO_EOF(state) { if (!fill_buffer(state)) goto eof; }
|
105
138
|
|
@@ -107,6 +140,7 @@ static inline int fill_buffer(struct parser_state *state) {
|
|
107
140
|
#define BUFFER_LEN(state) ((state)->len)
|
108
141
|
#define BUFFER_CUR(state) ((state)->ptr[(state)->parser->pos])
|
109
142
|
#define BUFFER_AT(state, pos) ((state)->ptr[pos])
|
143
|
+
#define BUFFER_PTR(state, pos) ((state)->ptr + pos)
|
110
144
|
#define BUFFER_STR(state, pos, len) (rb_utf8_str_new((state)->ptr + pos, len))
|
111
145
|
|
112
146
|
#define INC_BUFFER_POS(state) { \
|
@@ -114,7 +148,7 @@ static inline int fill_buffer(struct parser_state *state) {
|
|
114
148
|
if (BUFFER_POS(state) == BUFFER_LEN(state)) FILL_BUFFER_OR_GOTO_EOF(state); \
|
115
149
|
}
|
116
150
|
|
117
|
-
#define
|
151
|
+
#define INC_BUFFER_POS_NO_FILL(state) BUFFER_POS(state)++;
|
118
152
|
|
119
153
|
#define INC_BUFFER_POS_UTF8(state, len) { \
|
120
154
|
unsigned char c = BUFFER_CUR(state); \
|
@@ -162,8 +196,66 @@ static inline int fill_buffer(struct parser_state *state) {
|
|
162
196
|
RB_GC_GUARD(value); \
|
163
197
|
}
|
164
198
|
|
165
|
-
#define
|
166
|
-
|
199
|
+
#define CONSUME_CRLF(state) { \
|
200
|
+
INC_BUFFER_POS(state); \
|
201
|
+
if (BUFFER_CUR(state) != '\n') goto bad_request; \
|
202
|
+
INC_BUFFER_POS(state); \
|
203
|
+
}
|
204
|
+
|
205
|
+
#define CONSUME_CRLF_NO_FILL(state) { \
|
206
|
+
INC_BUFFER_POS(state); \
|
207
|
+
if (BUFFER_CUR(state) != '\n') goto bad_request; \
|
208
|
+
INC_BUFFER_POS_NO_FILL(state); \
|
209
|
+
}
|
210
|
+
|
211
|
+
#define GLOBAL_STR(v, s) v = rb_str_new_literal(s); rb_global_variable(&v)
|
212
|
+
|
213
|
+
struct parser_state {
|
214
|
+
struct parser *parser;
|
215
|
+
char *ptr;
|
216
|
+
int len;
|
217
|
+
};
|
218
|
+
|
219
|
+
////////////////////////////////////////////////////////////////////////////////
|
220
|
+
|
221
|
+
static inline VALUE io_read_call(VALUE io, VALUE maxlen, VALUE buf, VALUE buf_pos) {
|
222
|
+
VALUE result = rb_funcall(io, ID_call, 1, maxlen);
|
223
|
+
if (result == Qnil) return Qnil;
|
224
|
+
|
225
|
+
if (buf_pos == NUM_buffer_start) rb_str_set_len(buf, 0);
|
226
|
+
rb_str_append(buf, result);
|
227
|
+
RB_GC_GUARD(result);
|
228
|
+
return buf;
|
229
|
+
}
|
230
|
+
|
231
|
+
static inline VALUE parser_io_read(Parser_t *parser, VALUE maxlen, VALUE buf, VALUE buf_pos) {
|
232
|
+
switch (parser->read_method) {
|
233
|
+
case method_backend_read:
|
234
|
+
return rb_funcall(mPolyphony, ID_backend_read, 5, parser->io, buf, maxlen, Qfalse, buf_pos);
|
235
|
+
case method_backend_recv:
|
236
|
+
return rb_funcall(mPolyphony, ID_backend_recv, 4, parser->io, buf, maxlen, buf_pos);
|
237
|
+
case method_readpartial:
|
238
|
+
return rb_funcall(parser->io, ID_readpartial, 4, maxlen, buf, buf_pos, Qfalse);
|
239
|
+
case method_call:
|
240
|
+
return io_read_call(parser->io, maxlen, buf, buf_pos);
|
241
|
+
default:
|
242
|
+
return Qnil;
|
243
|
+
}
|
244
|
+
}
|
245
|
+
|
246
|
+
static inline int fill_buffer(struct parser_state *state) {
|
247
|
+
VALUE ret = parser_io_read(state->parser, NUM_max_headers_read_length, state->parser->buffer, NUM_buffer_end);
|
248
|
+
if (ret == Qnil) return 0;
|
249
|
+
|
250
|
+
state->parser->buffer = ret;
|
251
|
+
int len = RSTRING_LEN(state->parser->buffer);
|
252
|
+
int read_bytes = len - state->len;
|
253
|
+
if (!read_bytes) return 0;
|
254
|
+
|
255
|
+
state->ptr = RSTRING_PTR(state->parser->buffer);
|
256
|
+
state->len = len;
|
257
|
+
return read_bytes;
|
258
|
+
}
|
167
259
|
|
168
260
|
static inline void buffer_trim(struct parser_state *state) {
|
169
261
|
int len = RSTRING_LEN(state->parser->buffer);
|
@@ -184,27 +276,34 @@ static inline void buffer_trim(struct parser_state *state) {
|
|
184
276
|
state->parser->pos = 0;
|
185
277
|
}
|
186
278
|
|
187
|
-
|
188
|
-
|
279
|
+
static inline void str_append_from_buffer(VALUE str, char *ptr, int len) {
|
280
|
+
int str_len = RSTRING_LEN(str);
|
281
|
+
rb_str_modify_expand(str, len);
|
282
|
+
memcpy(RSTRING_PTR(str) + str_len, ptr, len);
|
283
|
+
rb_str_set_len(str, str_len + len);
|
284
|
+
}
|
285
|
+
|
189
286
|
////////////////////////////////////////////////////////////////////////////////
|
190
287
|
|
191
288
|
static inline int parse_method(struct parser_state *state, VALUE headers) {
|
192
289
|
int pos = BUFFER_POS(state);
|
193
290
|
int len = 0;
|
194
291
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
292
|
+
while (1) {
|
293
|
+
switch (BUFFER_CUR(state)) {
|
294
|
+
case ' ':
|
295
|
+
if (len < 1 || len > MAX_METHOD_LENGTH) goto bad_request;
|
296
|
+
INC_BUFFER_POS(state);
|
297
|
+
goto done;
|
298
|
+
case '\r':
|
299
|
+
case '\n':
|
300
|
+
goto bad_request;
|
301
|
+
default:
|
302
|
+
INC_BUFFER_POS(state);
|
303
|
+
len++;
|
304
|
+
// INC_BUFFER_POS_UTF8(state, len);
|
305
|
+
if (len > MAX_METHOD_LENGTH) goto bad_request;
|
306
|
+
}
|
208
307
|
}
|
209
308
|
done:
|
210
309
|
SET_HEADER_DOWNCASE_VALUE_FROM_BUFFER(state, headers, STR_pseudo_method, pos, len);
|
@@ -215,29 +314,31 @@ eof:
|
|
215
314
|
return 0;
|
216
315
|
}
|
217
316
|
|
218
|
-
static int
|
317
|
+
static int parse_request_target(struct parser_state *state, VALUE headers) {
|
219
318
|
while (BUFFER_CUR(state) == ' ') INC_BUFFER_POS(state);
|
220
319
|
int pos = BUFFER_POS(state);
|
221
320
|
int len = 0;
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
321
|
+
while (1) {
|
322
|
+
switch (BUFFER_CUR(state)) {
|
323
|
+
case ' ':
|
324
|
+
if (len < 1 || len > MAX_PATH_LENGTH) goto bad_request;
|
325
|
+
INC_BUFFER_POS(state);
|
326
|
+
goto done;
|
327
|
+
case '\r':
|
328
|
+
case '\n':
|
329
|
+
goto bad_request;
|
330
|
+
default:
|
331
|
+
INC_BUFFER_POS(state);
|
332
|
+
len++;
|
333
|
+
// INC_BUFFER_POS_UTF8(state, len);
|
334
|
+
if (len > MAX_PATH_LENGTH) goto bad_request;
|
335
|
+
}
|
235
336
|
}
|
236
337
|
done:
|
237
338
|
SET_HEADER_VALUE_FROM_BUFFER(state, headers, STR_pseudo_path, pos, len);
|
238
339
|
return 1;
|
239
340
|
bad_request:
|
240
|
-
RAISE_BAD_REQUEST("Invalid
|
341
|
+
RAISE_BAD_REQUEST("Invalid request target");
|
241
342
|
eof:
|
242
343
|
return 0;
|
243
344
|
}
|
@@ -257,26 +358,27 @@ static int parse_protocol(struct parser_state *state, VALUE headers) {
|
|
257
358
|
if (BUFFER_CUR(state) == '/') INC_BUFFER_POS(state) else goto bad_request;
|
258
359
|
if (BUFFER_CUR(state) == '1') INC_BUFFER_POS(state) else goto bad_request;
|
259
360
|
len = 6;
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
361
|
+
while (1) {
|
362
|
+
switch (BUFFER_CUR(state)) {
|
363
|
+
case '\r':
|
364
|
+
CONSUME_CRLF(state);
|
365
|
+
goto done;
|
366
|
+
case '\n':
|
367
|
+
INC_BUFFER_POS(state);
|
368
|
+
goto done;
|
369
|
+
case '.':
|
370
|
+
INC_BUFFER_POS(state);
|
371
|
+
char c = BUFFER_CUR(state);
|
372
|
+
if (c == '0' || c == '1') {
|
373
|
+
INC_BUFFER_POS(state);
|
374
|
+
len += 2;
|
375
|
+
continue;
|
376
|
+
}
|
377
|
+
goto bad_request;
|
378
|
+
default:
|
379
|
+
goto bad_request;
|
380
|
+
}
|
276
381
|
}
|
277
|
-
eol:
|
278
|
-
if (BUFFER_CUR(state) != '\n') goto bad_request;
|
279
|
-
INC_BUFFER_POS(state);
|
280
382
|
done:
|
281
383
|
if (len < 6 || len > 8) goto bad_request;
|
282
384
|
SET_HEADER_DOWNCASE_VALUE_FROM_BUFFER(state, headers, STR_pseudo_protocol, pos, len);
|
@@ -289,7 +391,7 @@ eof:
|
|
289
391
|
|
290
392
|
int parse_request_line(struct parser_state *state, VALUE headers) {
|
291
393
|
if (!parse_method(state, headers)) goto eof;
|
292
|
-
if (!
|
394
|
+
if (!parse_request_target(state, headers)) goto eof;
|
293
395
|
if (!parse_protocol(state, headers)) goto eof;
|
294
396
|
|
295
397
|
return 1;
|
@@ -301,31 +403,31 @@ static inline int parse_header_key(struct parser_state *state, VALUE *key) {
|
|
301
403
|
int pos = BUFFER_POS(state);
|
302
404
|
int len = 0;
|
303
405
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
if (len < 1 || len > MAX_HEADER_KEY_LENGTH)
|
406
|
+
while (1) {
|
407
|
+
switch (BUFFER_CUR(state)) {
|
408
|
+
case ' ':
|
308
409
|
goto bad_request;
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
410
|
+
case ':':
|
411
|
+
if (len < 1 || len > MAX_HEADER_KEY_LENGTH)
|
412
|
+
goto bad_request;
|
413
|
+
INC_BUFFER_POS(state);
|
414
|
+
goto done;
|
415
|
+
case '\r':
|
416
|
+
if (BUFFER_POS(state) > pos) goto bad_request;
|
417
|
+
CONSUME_CRLF_NO_FILL(state);
|
418
|
+
goto done;
|
419
|
+
case '\n':
|
420
|
+
if (BUFFER_POS(state) > pos) goto bad_request;
|
421
|
+
|
422
|
+
INC_BUFFER_POS_NO_FILL(state);
|
423
|
+
goto done;
|
424
|
+
default:
|
425
|
+
INC_BUFFER_POS(state);
|
426
|
+
len++;
|
427
|
+
// INC_BUFFER_POS_UTF8(state, len);
|
428
|
+
if (len > MAX_HEADER_KEY_LENGTH) goto bad_request;
|
429
|
+
}
|
325
430
|
}
|
326
|
-
eol:
|
327
|
-
if (BUFFER_CUR(state) != '\n') goto bad_request;
|
328
|
-
INC_BUFFER_POS_NO_READ(state);
|
329
431
|
done:
|
330
432
|
if (len == 0) return -1;
|
331
433
|
(*key) = str_downcase(BUFFER_STR(state, pos, len));
|
@@ -342,21 +444,19 @@ static inline int parse_header_value(struct parser_state *state, VALUE *value) {
|
|
342
444
|
int pos = BUFFER_POS(state);
|
343
445
|
int len = 0;
|
344
446
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
447
|
+
while (1) {
|
448
|
+
switch (BUFFER_CUR(state)) {
|
449
|
+
case '\r':
|
450
|
+
CONSUME_CRLF(state);
|
451
|
+
goto done;
|
452
|
+
case '\n':
|
453
|
+
INC_BUFFER_POS(state);
|
454
|
+
goto done;
|
455
|
+
default:
|
456
|
+
INC_BUFFER_POS_UTF8(state, len);
|
457
|
+
if (len > MAX_HEADER_VALUE_LENGTH) goto bad_request;
|
458
|
+
}
|
356
459
|
}
|
357
|
-
eol:
|
358
|
-
if (BUFFER_CUR(state) != '\n') goto bad_request;
|
359
|
-
INC_BUFFER_POS(state);
|
360
460
|
done:
|
361
461
|
if (len < 1 || len > MAX_HEADER_VALUE_LENGTH) goto bad_request;
|
362
462
|
(*value) = BUFFER_STR(state, pos, len);
|
@@ -400,111 +500,285 @@ eof:
|
|
400
500
|
VALUE Parser_parse_headers(VALUE self) {
|
401
501
|
struct parser_state state;
|
402
502
|
GetParser(self, state.parser);
|
403
|
-
|
503
|
+
state.parser->headers = rb_hash_new();
|
404
504
|
|
405
505
|
buffer_trim(&state);
|
506
|
+
int initial_pos = state.parser->pos;
|
406
507
|
INIT_PARSER_STATE(&state);
|
508
|
+
state.parser->current_request_rx = 0;
|
407
509
|
|
408
|
-
if (!parse_request_line(&state, headers)) goto eof;
|
510
|
+
if (!parse_request_line(&state, state.parser->headers)) goto eof;
|
409
511
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
512
|
+
int header_count = 0;
|
513
|
+
while (1) {
|
514
|
+
if (header_count > MAX_HEADER_COUNT) RAISE_BAD_REQUEST("Too many headers");
|
515
|
+
switch (parse_header(&state, state.parser->headers)) {
|
516
|
+
case -1: goto done; // empty header => end of headers
|
517
|
+
case 0: goto eof;
|
518
|
+
}
|
519
|
+
header_count++;
|
415
520
|
}
|
416
|
-
|
417
|
-
done:
|
418
|
-
RB_GC_GUARD(headers);
|
419
|
-
return headers;
|
420
521
|
eof:
|
421
|
-
|
522
|
+
state.parser->headers = Qnil;
|
523
|
+
done:
|
524
|
+
state.parser->body_read_mode = BODY_READ_MODE_UNKNOWN;
|
525
|
+
int read_bytes = BUFFER_POS(&state) - initial_pos;
|
526
|
+
|
527
|
+
state.parser->current_request_rx += read_bytes;
|
528
|
+
if (state.parser->headers != Qnil)
|
529
|
+
rb_hash_aset(state.parser->headers, STR_pseudo_rx, INT2NUM(read_bytes));
|
530
|
+
return state.parser->headers;
|
422
531
|
}
|
423
532
|
|
424
|
-
////////////////////////////////////////////////////////////////////////////////
|
425
|
-
////////////////////////////////////////////////////////////////////////////////
|
426
533
|
////////////////////////////////////////////////////////////////////////////////
|
427
534
|
|
428
|
-
|
535
|
+
static inline int str_to_int(VALUE value, const char *error_msg) {
|
536
|
+
char *ptr = RSTRING_PTR(value);
|
537
|
+
int len = RSTRING_LEN(value);
|
538
|
+
int int_value = 0;
|
429
539
|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
540
|
+
while (len) {
|
541
|
+
char c = *ptr;
|
542
|
+
if ((c >= '0') && (c <= '9'))
|
543
|
+
int_value = int_value * 10 + (c - '0');
|
544
|
+
else
|
545
|
+
RAISE_BAD_REQUEST(error_msg);
|
546
|
+
len--;
|
547
|
+
ptr++;
|
548
|
+
}
|
434
549
|
|
435
|
-
|
436
|
-
|
550
|
+
return int_value;
|
551
|
+
}
|
437
552
|
|
438
|
-
|
439
|
-
|
553
|
+
VALUE read_body_with_content_length(Parser_t *parser, int read_entire_body, int buffered_only) {
|
554
|
+
if (parser->body_left <= 0) return Qnil;
|
440
555
|
|
441
|
-
VALUE body;
|
556
|
+
VALUE body = Qnil;
|
442
557
|
|
443
558
|
int len = RSTRING_LEN(parser->buffer);
|
444
559
|
int pos = parser->pos;
|
445
|
-
int left = content_length;
|
446
560
|
|
447
561
|
if (pos < len) {
|
448
562
|
int available = len - pos;
|
449
|
-
if (available >
|
563
|
+
if (available > parser->body_left) available = parser->body_left;
|
450
564
|
body = rb_str_new(RSTRING_PTR(parser->buffer) + pos, available);
|
451
565
|
parser->pos += available;
|
452
|
-
|
453
|
-
|
566
|
+
parser->current_request_rx += available;
|
567
|
+
parser->body_left -= available;
|
568
|
+
if (!parser->body_left) parser->request_completed = 1;
|
454
569
|
}
|
455
570
|
else {
|
456
571
|
body = Qnil;
|
457
572
|
len = 0;
|
458
573
|
}
|
574
|
+
if (buffered_only) return body;
|
459
575
|
|
460
|
-
while (
|
461
|
-
int maxlen =
|
462
|
-
|
463
|
-
if (
|
464
|
-
|
465
|
-
mPolyphony, ID_backend_read, 5, parser->io, Qnil, INT2NUM(maxlen), Qfalse, INT2NUM(0)
|
466
|
-
);
|
576
|
+
while (parser->body_left) {
|
577
|
+
int maxlen = parser->body_left <= MAX_BODY_READ_LENGTH ? parser->body_left : MAX_BODY_READ_LENGTH;
|
578
|
+
VALUE tmp_buf = parser_io_read(parser, INT2NUM(maxlen), Qnil, NUM_buffer_start);
|
579
|
+
if (tmp_buf == Qnil) goto eof;
|
580
|
+
if (body != Qnil)
|
467
581
|
rb_str_append(body, tmp_buf);
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
582
|
+
else
|
583
|
+
body = tmp_buf;
|
584
|
+
int read_bytes = RSTRING_LEN(tmp_buf);
|
585
|
+
parser->current_request_rx += read_bytes;
|
586
|
+
parser->body_left -= read_bytes;
|
587
|
+
if (!parser->body_left) parser->request_completed = 1;
|
588
|
+
RB_GC_GUARD(tmp_buf);
|
589
|
+
if (!read_entire_body) goto done;
|
590
|
+
}
|
591
|
+
done:
|
592
|
+
rb_hash_aset(parser->headers, STR_pseudo_rx, INT2NUM(parser->current_request_rx));
|
593
|
+
RB_GC_GUARD(body);
|
594
|
+
return body;
|
595
|
+
eof:
|
596
|
+
RAISE_BAD_REQUEST("Incomplete body");
|
597
|
+
}
|
598
|
+
|
599
|
+
int chunked_encoding_p(VALUE transfer_encoding) {
|
600
|
+
if (transfer_encoding == Qnil) return 0;
|
601
|
+
return rb_funcall(str_downcase(transfer_encoding), ID_eq, 1, STR_chunked) == Qtrue;
|
602
|
+
}
|
603
|
+
|
604
|
+
int parse_chunk_size(struct parser_state *state, int *chunk_size) {
|
605
|
+
int len = 0;
|
606
|
+
int value = 0;
|
607
|
+
int initial_pos = BUFFER_POS(state);
|
608
|
+
|
609
|
+
while (1) {
|
610
|
+
char c = BUFFER_CUR(state);
|
611
|
+
if ((c >= '0') && (c <= '9')) value = (value << 4) + (c - '0');
|
612
|
+
else if ((c >= 'a') && (c <= 'f')) value = (value << 4) + (c - 'a' + 10);
|
613
|
+
else if ((c >= 'A') && (c <= 'F')) value = (value << 4) + (c - 'A' + 10);
|
614
|
+
else switch (c) {
|
615
|
+
case '\r':
|
616
|
+
CONSUME_CRLF_NO_FILL(state);
|
617
|
+
goto done;
|
618
|
+
case '\n':
|
619
|
+
INC_BUFFER_POS_NO_FILL(state);
|
620
|
+
goto done;
|
621
|
+
default:
|
622
|
+
goto bad_request;
|
474
623
|
}
|
475
|
-
|
476
|
-
|
477
|
-
if (
|
624
|
+
INC_BUFFER_POS(state);
|
625
|
+
len++;
|
626
|
+
if (len >= MAX_CHUNKED_ENCODING_CHUNK_SIZE_LENGTH) goto bad_request;
|
627
|
+
}
|
628
|
+
done:
|
629
|
+
if (len == 0) goto bad_request;
|
630
|
+
(*chunk_size) = value;
|
631
|
+
state->parser->current_request_rx += BUFFER_POS(state) - initial_pos;
|
632
|
+
return 1;
|
633
|
+
bad_request:
|
634
|
+
RAISE_BAD_REQUEST("Invalid chunk size");
|
635
|
+
eof:
|
636
|
+
return 0;
|
637
|
+
}
|
478
638
|
|
479
|
-
|
639
|
+
int read_body_chunk_with_chunked_encoding(struct parser_state *state, VALUE *body, int chunk_size, int buffered_only) {
|
640
|
+
int len = RSTRING_LEN(state->parser->buffer);
|
641
|
+
int pos = state->parser->pos;
|
642
|
+
int left = chunk_size;
|
643
|
+
|
644
|
+
if (pos < len) {
|
645
|
+
int available = len - pos;
|
646
|
+
if (available > left) available = left;
|
647
|
+
if (*body != Qnil)
|
648
|
+
str_append_from_buffer(*body, RSTRING_PTR(state->parser->buffer) + pos, available);
|
649
|
+
else
|
650
|
+
*body = rb_str_new(RSTRING_PTR(state->parser->buffer) + pos, available);
|
651
|
+
state->parser->pos += available;
|
652
|
+
state->parser->current_request_rx += available;
|
653
|
+
left -= available;
|
654
|
+
}
|
655
|
+
if (buffered_only) return 1;
|
656
|
+
|
657
|
+
while (left) {
|
658
|
+
int maxlen = left <= MAX_BODY_READ_LENGTH ? left : MAX_BODY_READ_LENGTH;
|
659
|
+
|
660
|
+
VALUE tmp_buf = parser_io_read(state->parser, INT2NUM(maxlen), Qnil, NUM_buffer_start);
|
661
|
+
if (tmp_buf == Qnil) goto eof;
|
662
|
+
if (*body != Qnil)
|
663
|
+
rb_str_append(*body, tmp_buf);
|
664
|
+
else
|
665
|
+
*body = tmp_buf;
|
666
|
+
int read_bytes = RSTRING_LEN(tmp_buf);
|
667
|
+
state->parser->current_request_rx += read_bytes;
|
480
668
|
left -= read_bytes;
|
669
|
+
RB_GC_GUARD(tmp_buf);
|
481
670
|
}
|
671
|
+
return 1;
|
672
|
+
eof:
|
673
|
+
return 0;
|
674
|
+
}
|
675
|
+
|
676
|
+
static inline int parse_chunk_postfix(struct parser_state *state) {
|
677
|
+
int initial_pos = BUFFER_POS(state);
|
678
|
+
if (initial_pos == BUFFER_LEN(state)) FILL_BUFFER_OR_GOTO_EOF(state);
|
679
|
+
switch (BUFFER_CUR(state)) {
|
680
|
+
case '\r':
|
681
|
+
CONSUME_CRLF_NO_FILL(state);
|
682
|
+
goto done;
|
683
|
+
case '\n':
|
684
|
+
INC_BUFFER_POS_NO_FILL(state);
|
685
|
+
goto done;
|
686
|
+
default:
|
687
|
+
goto bad_request;
|
688
|
+
}
|
689
|
+
done:
|
690
|
+
state->parser->current_request_rx += BUFFER_POS(state) - initial_pos;
|
691
|
+
return 1;
|
692
|
+
bad_request:
|
693
|
+
RAISE_BAD_REQUEST("Invalid protocol");
|
694
|
+
eof:
|
695
|
+
return 0;
|
696
|
+
}
|
482
697
|
|
698
|
+
VALUE read_body_with_chunked_encoding(Parser_t *parser, int read_entire_body, int buffered_only) {
|
699
|
+
struct parser_state state;
|
700
|
+
state.parser = parser;
|
701
|
+
buffer_trim(&state);
|
702
|
+
INIT_PARSER_STATE(&state);
|
703
|
+
VALUE body = Qnil;
|
704
|
+
|
705
|
+
while (1) {
|
706
|
+
int chunk_size = 0;
|
707
|
+
if (BUFFER_POS(&state) == BUFFER_LEN(&state)) FILL_BUFFER_OR_GOTO_EOF(&state);
|
708
|
+
if (!parse_chunk_size(&state, &chunk_size)) goto bad_request;
|
709
|
+
|
710
|
+
if (chunk_size) {
|
711
|
+
if (!read_body_chunk_with_chunked_encoding(&state, &body, chunk_size, buffered_only)) goto bad_request;
|
712
|
+
}
|
713
|
+
else parser->request_completed = 1;
|
714
|
+
|
715
|
+
if (!parse_chunk_postfix(&state)) goto bad_request;
|
716
|
+
if (!chunk_size || !read_entire_body) goto done;
|
717
|
+
}
|
718
|
+
bad_request:
|
719
|
+
RAISE_BAD_REQUEST("Malformed request body");
|
720
|
+
eof:
|
721
|
+
RAISE_BAD_REQUEST("Incomplete request body");
|
722
|
+
done:
|
723
|
+
rb_hash_aset(parser->headers, STR_pseudo_rx, INT2NUM(state.parser->current_request_rx));
|
483
724
|
RB_GC_GUARD(body);
|
484
725
|
return body;
|
485
726
|
}
|
486
727
|
|
487
|
-
|
488
|
-
VALUE content_length = rb_hash_aref(headers, STR_content_length);
|
489
|
-
if (content_length != Qnil)
|
490
|
-
|
728
|
+
static inline void detect_body_read_mode(Parser_t *parser) {
|
729
|
+
VALUE content_length = rb_hash_aref(parser->headers, STR_content_length);
|
730
|
+
if (content_length != Qnil) {
|
731
|
+
int int_content_length = str_to_int(content_length, "Invalid content length");
|
732
|
+
if (int_content_length < 0) RAISE_BAD_REQUEST("Invalid body content length");
|
733
|
+
parser->body_read_mode = parser->body_left = int_content_length;
|
734
|
+
parser->request_completed = 0;
|
735
|
+
return;
|
736
|
+
}
|
491
737
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
738
|
+
VALUE transfer_encoding = rb_hash_aref(parser->headers, STR_transfer_encoding);
|
739
|
+
if (chunked_encoding_p(transfer_encoding)) {
|
740
|
+
parser->body_read_mode = BODY_READ_MODE_CHUNKED;
|
741
|
+
parser->request_completed = 0;
|
742
|
+
return;
|
743
|
+
}
|
744
|
+
parser->request_completed = 1;
|
496
745
|
|
497
|
-
return Qnil;
|
498
746
|
}
|
499
747
|
|
500
|
-
|
748
|
+
static inline VALUE read_body(VALUE self, int read_entire_body, int buffered_only) {
|
749
|
+
Parser_t *parser;
|
750
|
+
GetParser(self, parser);
|
751
|
+
|
752
|
+
if (parser->body_read_mode == BODY_READ_MODE_UNKNOWN)
|
753
|
+
detect_body_read_mode(parser);
|
754
|
+
|
755
|
+
if (parser->body_read_mode == BODY_READ_MODE_CHUNKED)
|
756
|
+
return read_body_with_chunked_encoding(parser, read_entire_body, buffered_only);
|
757
|
+
return read_body_with_content_length(parser, read_entire_body, buffered_only);
|
758
|
+
}
|
759
|
+
|
760
|
+
VALUE Parser_read_body(VALUE self) {
|
761
|
+
return read_body(self, 1, 0);
|
762
|
+
}
|
763
|
+
|
764
|
+
VALUE Parser_read_body_chunk(VALUE self, VALUE buffered_only) {
|
765
|
+
return read_body(self, 0, buffered_only == Qtrue);
|
766
|
+
}
|
767
|
+
|
768
|
+
VALUE Parser_complete_p(VALUE self) {
|
769
|
+
Parser_t *parser;
|
770
|
+
GetParser(self, parser);
|
771
|
+
|
772
|
+
if (parser->body_read_mode == BODY_READ_MODE_UNKNOWN)
|
773
|
+
detect_body_read_mode(parser);
|
774
|
+
|
775
|
+
return parser->request_completed ? Qtrue : Qfalse;
|
776
|
+
}
|
501
777
|
|
502
778
|
void Init_HTTP1_Parser() {
|
503
779
|
VALUE mTipi;
|
504
780
|
VALUE cHTTP1Parser;
|
505
781
|
|
506
|
-
mPolyphony = rb_const_get(rb_cObject, rb_intern("Polyphony"));
|
507
|
-
|
508
782
|
mTipi = rb_define_module("Tipi");
|
509
783
|
cHTTP1Parser = rb_define_class_under(mTipi, "HTTP1Parser", rb_cObject);
|
510
784
|
rb_define_alloc_func(cHTTP1Parser, Parser_allocate);
|
@@ -514,21 +788,36 @@ void Init_HTTP1_Parser() {
|
|
514
788
|
// backend methods
|
515
789
|
rb_define_method(cHTTP1Parser, "initialize", Parser_initialize, 1);
|
516
790
|
rb_define_method(cHTTP1Parser, "parse_headers", Parser_parse_headers, 0);
|
517
|
-
rb_define_method(cHTTP1Parser, "read_body", Parser_read_body,
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
791
|
+
rb_define_method(cHTTP1Parser, "read_body", Parser_read_body, 0);
|
792
|
+
rb_define_method(cHTTP1Parser, "read_body_chunk", Parser_read_body_chunk, 1);
|
793
|
+
rb_define_method(cHTTP1Parser, "complete?", Parser_complete_p, 0);
|
794
|
+
|
795
|
+
ID_arity = rb_intern("arity");
|
796
|
+
ID_backend_read = rb_intern("backend_read");
|
797
|
+
ID_backend_recv = rb_intern("backend_recv");
|
798
|
+
ID_call = rb_intern("call");
|
799
|
+
ID_downcase = rb_intern("downcase");
|
800
|
+
ID_eq = rb_intern("==");
|
801
|
+
ID_parser_read_method = rb_intern("__parser_read_method__");
|
802
|
+
ID_read = rb_intern("read");
|
803
|
+
ID_readpartial = rb_intern("readpartial");
|
804
|
+
ID_to_i = rb_intern("to_i");
|
805
|
+
|
806
|
+
NUM_max_headers_read_length = INT2NUM(MAX_HEADERS_READ_LENGTH);
|
807
|
+
NUM_buffer_start = INT2NUM(0);
|
808
|
+
NUM_buffer_end = INT2NUM(-1);
|
527
809
|
|
528
810
|
GLOBAL_STR(STR_pseudo_method, ":method");
|
529
811
|
GLOBAL_STR(STR_pseudo_path, ":path");
|
530
812
|
GLOBAL_STR(STR_pseudo_protocol, ":protocol");
|
813
|
+
GLOBAL_STR(STR_pseudo_rx, ":rx");
|
531
814
|
|
815
|
+
GLOBAL_STR(STR_chunked, "chunked");
|
532
816
|
GLOBAL_STR(STR_content_length, "content-length");
|
533
817
|
GLOBAL_STR(STR_transfer_encoding, "transfer-encoding");
|
818
|
+
|
819
|
+
SYM_backend_read = ID2SYM(ID_backend_read);
|
820
|
+
SYM_backend_recv = ID2SYM(ID_backend_recv);
|
821
|
+
|
822
|
+
rb_global_variable(&mTipi);
|
534
823
|
}
|