tipi 0.41 → 0.42

Sign up to get free protection for your applications and to get access to all the features.
@@ -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!') }
@@ -31,23 +31,25 @@ f = spin do
31
31
  break unless headers
32
32
  trace headers
33
33
 
34
- body = parser.read_body(headers)
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
- o << "GET /b HTTP/1.1\r\n\r\n"
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
- # o << "post /?q=time&blah=blah HTTP/1\r\nHost: dev.realiteq.net\r\n\r\n"
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
- # data = " " * 4000000
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
- # o << "get /?q=time HTTP/1.1\r\nCookie: foo\r\nCookie: bar\r\n\r\n"
53
+ o.close
50
54
 
51
- # o.close
52
-
53
55
  f.await
@@ -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.current.fiber_scheduling_stats }
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
@@ -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
- $CFLAGS << " -Wno-pointer-arith"
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'
@@ -1,36 +1,66 @@
1
1
  #include "ruby.h"
2
2
  #include "http1_parser.h"
3
3
 
4
- #define str_downcase(str) (rb_funcall((str), ID_downcase, 0))
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
- const int MAX_METHOD_LENGTH = 16;
7
- const int MAX_PATH_LENGTH = 1024;
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 MAX_READ_LENGTH;
21
- VALUE BUFFER_END;
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
- int pos;
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 READ_CONCAT(io, buffer, len) \
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 INC_BUFFER_POS_NO_READ(state) BUFFER_POS(state)++;
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 BUFFER_TRIM_MIN_LEN 4096
166
- #define BUFFER_TRIM_MIN_POS 2048
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
- loop:
196
- switch (BUFFER_CUR(state)) {
197
- case ' ':
198
- if (len < 1 || len > MAX_METHOD_LENGTH) goto bad_request;
199
- INC_BUFFER_POS(state);
200
- goto done;
201
- case '\r':
202
- case '\n':
203
- goto bad_request;
204
- default:
205
- INC_BUFFER_POS_UTF8(state, len);
206
- if (len >= MAX_METHOD_LENGTH) goto bad_request;
207
- goto loop;
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 parse_path(struct parser_state *state, VALUE headers) {
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
- loop:
223
- switch (BUFFER_CUR(state)) {
224
- case ' ':
225
- if (len < 1 || len > MAX_PATH_LENGTH) goto bad_request;
226
- INC_BUFFER_POS(state);
227
- goto done;
228
- case '\r':
229
- case '\n':
230
- goto bad_request;
231
- default:
232
- INC_BUFFER_POS_UTF8(state, len);
233
- if (len >= MAX_PATH_LENGTH) goto bad_request;
234
- goto loop;
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 path");
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
- loop:
261
- switch (BUFFER_CUR(state)) {
262
- case '\r':
263
- INC_BUFFER_POS(state);
264
- goto eol;
265
- case '\n':
266
- INC_BUFFER_POS(state);
267
- goto done;
268
- case '.':
269
- case '1':
270
- len++;
271
- if (len > 8) goto bad_request;
272
- INC_BUFFER_POS(state);
273
- goto loop;
274
- default:
275
- goto bad_request;
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 (!parse_path(state, headers)) goto eof;
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
- loop:
305
- switch (BUFFER_CUR(state)) {
306
- case ':':
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
- INC_BUFFER_POS(state);
310
- goto done;
311
- case '\r':
312
- if (BUFFER_POS(state) > pos) goto bad_request;
313
-
314
- INC_BUFFER_POS(state);
315
- goto eol;
316
- case '\n':
317
- if (BUFFER_POS(state) > pos) goto bad_request;
318
-
319
- INC_BUFFER_POS_NO_READ(state);
320
- goto done;
321
- default:
322
- INC_BUFFER_POS_UTF8(state, len);
323
- if (len >= MAX_HEADER_KEY_LENGTH) goto bad_request;
324
- goto loop;
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
- loop:
346
- switch (BUFFER_CUR(state)) {
347
- case '\r':
348
- INC_BUFFER_POS(state);
349
- goto eol;
350
- case '\n':
351
- goto done;
352
- default:
353
- INC_BUFFER_POS_UTF8(state, len);
354
- if (len >= MAX_HEADER_VALUE_LENGTH) goto bad_request;
355
- goto loop;
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
- VALUE headers = rb_hash_new();
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
- loop:
411
- switch (parse_header(&state, headers)) {
412
- case -1: goto done; // empty header => end of headers
413
- case 0: goto eof;
414
- default: goto loop;
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
- return Qnil;
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
- const int READ_MAX_LEN = 1 << 20;
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
- static inline int parse_content_length(VALUE value) {
431
- VALUE to_i = rb_funcall(value, ID_to_i, 0);
432
- return NUM2INT(to_i);
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
- VALUE read_body_with_content_length(VALUE self, int content_length) {
436
- if (content_length < 0) return Qnil;
550
+ return int_value;
551
+ }
437
552
 
438
- Parser_t *parser;
439
- GetParser(self, parser);
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 > content_length) available = content_length;
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
- left -= available;
453
- len = available;
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 (left) {
461
- int maxlen = left <= READ_MAX_LEN ? left : READ_MAX_LEN;
462
-
463
- if (body != Qnil) {
464
- VALUE tmp_buf = rb_funcall(
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
- RB_GC_GUARD(tmp_buf);
469
- }
470
- else {
471
- body = rb_funcall(
472
- mPolyphony, ID_backend_read, 5, parser->io, Qnil, INT2NUM(maxlen), Qfalse, INT2NUM(0)
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
- int new_len = RSTRING_LEN(body);
476
- int read_bytes = new_len - len;
477
- if (!read_bytes) RAISE_BAD_REQUEST("Incomplete request body");
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
- len = new_len;
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
- VALUE Parser_read_body(VALUE self, VALUE headers) {
488
- VALUE content_length = rb_hash_aref(headers, STR_content_length);
489
- if (content_length != Qnil)
490
- return read_body_with_content_length(self, parse_content_length(content_length));
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
- // VALUE transfer_encoding = rb_hash_aref(headers, STR_transfer_encoding);
493
- // if (chunked_encoding_p(transfer_encoding)) {
494
- // return read_body_with_chunked_encoding(self);
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
- #define GLOBAL_STR(v, s) v = rb_str_new_literal(s); rb_global_variable(&v)
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, 1);
518
-
519
- ID_backend_read = rb_intern("backend_read");
520
- ID_downcase = rb_intern("downcase");
521
- ID_read = rb_intern("read");
522
- ID_readpartial = rb_intern("readpartial");
523
- ID_to_i = rb_intern("to_i");
524
-
525
- MAX_READ_LENGTH = INT2NUM(4096);
526
- BUFFER_END = INT2NUM(-1);
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
  }