iodine 0.7.37 → 0.7.42

Sign up to get free protection for your applications and to get access to all the features.
@@ -181,6 +181,7 @@ static void iodine_defer_performe_once(void *block, void *ignr) {
181
181
  }
182
182
 
183
183
  static void iodine_defer_run_timer(void *block) {
184
+ /* TODO, update return value to allow timer cancellation */
184
185
  IodineCaller.call((VALUE)block, call_id);
185
186
  }
186
187
 
@@ -28,6 +28,8 @@ typedef struct {
28
28
  } iodine_http_settings_s;
29
29
 
30
30
  /* these three are used also by iodin_rack_io.c */
31
+ VALUE IODINE_R_INPUT_DEFAULT;
32
+ VALUE IODINE_R_INPUT;
31
33
  VALUE IODINE_R_HIJACK;
32
34
  VALUE IODINE_R_HIJACK_IO;
33
35
  VALUE IODINE_R_HIJACK_CB;
@@ -846,6 +848,7 @@ static void initialize_env_template(void) {
846
848
  }
847
849
  add_str_to_env(env_template_no_upgrade, "SCRIPT_NAME", sn);
848
850
  }
851
+ rb_hash_aset(env_template_no_upgrade, IODINE_R_INPUT, IODINE_R_INPUT_DEFAULT);
849
852
  add_value_to_env(env_template_no_upgrade, "rack.errors", rb_stderr);
850
853
  add_value_to_env(env_template_no_upgrade, "rack.hijack?", Qtrue);
851
854
  add_value_to_env(env_template_no_upgrade, "rack.multiprocess", Qtrue);
@@ -1111,6 +1114,7 @@ void iodine_init_http(void) {
1111
1114
  rack_set(XSENDFILE_TYPE_HEADER, "HTTP_X_SENDFILE_TYPE");
1112
1115
  rack_set(CONTENT_LENGTH_HEADER, "Content-Length");
1113
1116
 
1117
+ rack_set(IODINE_R_INPUT, "rack.input");
1114
1118
  rack_set(IODINE_R_HIJACK_IO, "rack.hijack_io");
1115
1119
  rack_set(IODINE_R_HIJACK, "rack.hijack");
1116
1120
  rack_set(IODINE_R_HIJACK_CB, "iodine.hijack_cb");
@@ -1132,5 +1136,12 @@ void iodine_init_http(void) {
1132
1136
  IodineUTF8Encoding = rb_enc_find("UTF-8");
1133
1137
  IodineBinaryEncoding = rb_enc_find("binary");
1134
1138
 
1139
+ {
1140
+ VALUE STRIO_CLASS = rb_const_get(rb_cObject, rb_intern("StringIO"));
1141
+ IODINE_R_INPUT_DEFAULT = rb_str_new_static("", 0);
1142
+ IODINE_R_INPUT_DEFAULT =
1143
+ rb_funcallv(STRIO_CLASS, rb_intern("new"), 1, &IODINE_R_INPUT_DEFAULT);
1144
+ rb_global_variable(&IODINE_R_INPUT_DEFAULT);
1145
+ }
1135
1146
  initialize_env_template();
1136
1147
  }
@@ -9,6 +9,8 @@ Feel free to copy, use and enjoy according to the license provided.
9
9
  #include "iodine.h"
10
10
 
11
11
  /* these three are used also by rb-rack-io.c */
12
+ extern VALUE IODINE_R_INPUT;
13
+ extern VALUE IODINE_R_INPUT_DEFAULT;
12
14
  extern VALUE IODINE_R_HIJACK;
13
15
  extern VALUE IODINE_R_HIJACK_IO;
14
16
  extern VALUE IODINE_R_HIJACK_CB;
@@ -216,7 +216,7 @@ static VALUE new_rack_io(http_s *h, VALUE env) {
216
216
  rb_ivar_set(rack_io, io_id, ULL2NUM(h->body));
217
217
  set_handle(rack_io, h);
218
218
  rb_ivar_set(rack_io, env_id, env);
219
- rb_hash_aset(env, R_INPUT, rack_io);
219
+ rb_hash_aset(env, IODINE_R_INPUT, rack_io);
220
220
  rb_hash_aset(env, IODINE_R_HIJACK, rb_obj_method(rack_io, hijack_func_sym));
221
221
  return rack_io;
222
222
  }
@@ -240,10 +240,6 @@ static void init_rack_io(void) {
240
240
  iodine_new_func_id = rb_intern("new");
241
241
  hijack_func_sym = ID2SYM(rb_intern("_hijack"));
242
242
 
243
- R_INPUT = rb_enc_str_new("rack.input", 10, rb_ascii8bit_encoding());
244
- rb_obj_freeze(R_INPUT);
245
- IodineStore.add(R_INPUT);
246
-
247
243
  TCPSOCKET_CLASS = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
248
244
  // IO methods
249
245
 
@@ -208,22 +208,8 @@ static VALUE iodine_tls_alpn(VALUE self, VALUE protocol_name) {
208
208
  }
209
209
 
210
210
  /**
211
- Loads the mustache template found in `:filename`. If `:template` is provided
212
- it will be used instead of reading the file's content.
213
-
214
- Iodine::Mustache.new(filename, template = nil)
215
-
216
- When template data is provided, filename (if any) will only be used for
217
- partial template path resolution and the template data will be used for the
218
- template's content. This allows, for example, for front matter to be extracted
219
- before parsing the template.
220
-
221
- Once a template was loaded, it could be rendered using {render}.
222
-
223
- Accepts named arguments as well:
224
-
225
- Iodine::Mustache.new(filename: "foo.mustache", template: "{{ bar }}")
226
-
211
+ Creates a new {Iodine::TLS} object and calles the {#use_certificate} method with
212
+ the supplied arguments.
227
213
  */
228
214
  static VALUE iodine_tls_new(int argc, VALUE *argv, VALUE self) {
229
215
  if (argc) {
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
33
33
 
34
34
  spec.requirements << 'A Unix based system: Linux / macOS / BSD.'
35
35
  spec.requirements << 'An updated C compiler.'
36
- spec.requirements << 'Ruby >= 2.2.2 required for Rack 2.'
36
+ spec.requirements << 'Ruby >= 2.3.8 (Ruby EOL).'
37
37
  spec.requirements << 'Ruby >= 2.5.0 recommended.'
38
38
  spec.requirements << 'TLS requires OpenSSL >= 1.1.0'
39
39
 
@@ -1,3 +1,4 @@
1
+ require 'stringio' # Used internally as a default RackIO
1
2
  require 'socket' # TCPSocket is used internally for Hijack support
2
3
  # require 'openssl' # For SSL/TLS support using OpenSSL
3
4
 
@@ -177,7 +178,7 @@ Iodine.on_state(:after_fork) do
177
178
  end
178
179
 
179
180
  ### Parse CLI for default HTTP settings
180
- Iodine::Base::CLI.parse
181
+ Iodine::Base::CLI.parse if defined?(IODINE_PARSE_CLI) && IODINE_PARSE_CLI
181
182
 
182
183
  ### Set default port (if missing)
183
184
  Iodine::DEFAULT_SETTINGS[:port] ||= (ENV["PORT"] || "3000")
@@ -17,28 +17,40 @@ module Iodine
17
17
  # Instances of this class are passed to the callback objects. i.e.:
18
18
  #
19
19
  # module MyConnectionCallbacks
20
+ #
20
21
  # # called when the callback object is linked with a new client
21
22
  # def on_open client
22
23
  # client.is_a?(Iodine::Connection) # => true
23
24
  # end
25
+ #
24
26
  # # called when data is available
25
27
  # def on_message client, data
26
28
  # client.is_a?(Iodine::Connection) # => true
27
29
  # end
30
+ #
28
31
  # # called when the server is shutting down, before closing the client
29
32
  # # (it's still possible to send messages to the client)
30
33
  # def on_shutdown client
31
34
  # client.is_a?(Iodine::Connection) # => true
32
35
  # end
36
+ #
33
37
  # # called when the client is closed (no longer available)
34
38
  # def on_close client
35
39
  # client.is_a?(Iodine::Connection) # => true
36
40
  # end
41
+ #
37
42
  # # called when all the previous calls to `client.write` have completed
38
43
  # # (the local buffer was drained and is now empty)
39
44
  # def on_drained client
40
45
  # client.is_a?(Iodine::Connection) # => true
41
46
  # end
47
+ #
48
+ # # called when timeout was reached, llowing a `ping` to be sent
49
+ # def ping client
50
+ # client.is_a?(Iodine::Connection) # => true
51
+ # clint.close() # close connection on timeout is the default
52
+ # end
53
+ #
42
54
  # # Allows the module to be used as a static callback object (avoiding object allocation)
43
55
  # extend self
44
56
  # end
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.7.37'.freeze
2
+ VERSION = '0.7.42'.freeze
3
3
  end
@@ -14,6 +14,12 @@ module Iodine
14
14
 
15
15
  true
16
16
  end
17
+
18
+ # patches an assumption by Rack, issue #98 code donated by @Shelvak (Néstor Coppi)
19
+ def self.shutdown
20
+ Iodine.stop
21
+ end
22
+
17
23
  IODINE_RACK_LOADED = true
18
24
  end
19
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.37
4
+ version: 0.7.42
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-13 00:00:00.000000000 Z
11
+ date: 2020-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -121,6 +121,7 @@ extensions:
121
121
  - ext/iodine/extconf.rb
122
122
  extra_rdoc_files: []
123
123
  files:
124
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
124
125
  - ".gitignore"
125
126
  - ".rspec"
126
127
  - ".travis.yml"
@@ -187,7 +188,6 @@ files:
187
188
  - ext/iodine/http.h
188
189
  - ext/iodine/http1.c
189
190
  - ext/iodine/http1.h
190
- - ext/iodine/http1_parser.c
191
191
  - ext/iodine/http1_parser.h
192
192
  - ext/iodine/http_internal.c
193
193
  - ext/iodine/http_internal.h
@@ -243,7 +243,7 @@ licenses:
243
243
  metadata:
244
244
  allowed_push_host: https://rubygems.org
245
245
  post_install_message: |-
246
- Thank you for installing Iodine 0.7.37.
246
+ Thank you for installing Iodine 0.7.42.
247
247
  Remember: if iodine supports your business, it's only fair to give value back (code contributions / donations).
248
248
  rdoc_options: []
249
249
  require_paths:
@@ -262,10 +262,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
262
262
  requirements:
263
263
  - 'A Unix based system: Linux / macOS / BSD.'
264
264
  - An updated C compiler.
265
- - Ruby >= 2.2.2 required for Rack 2.
265
+ - Ruby >= 2.3.8 (Ruby EOL).
266
266
  - Ruby >= 2.5.0 recommended.
267
267
  - TLS requires OpenSSL >= 1.1.0
268
- rubygems_version: 3.0.1
268
+ rubygems_version: 3.1.2
269
269
  signing_key:
270
270
  specification_version: 4
271
271
  summary: iodine - a fast HTTP / Websocket Server with Pub/Sub support, optimized for
@@ -1,460 +0,0 @@
1
- /*
2
- Copyright: Boaz Segev, 2017-2019
3
- License: MIT
4
-
5
- Feel free to copy, use and enjoy according to the license provided.
6
- */
7
- #ifndef __GNU_SOURCE
8
- #define __GNU_SOURCE
9
- #endif
10
-
11
- #include <http1_parser.h>
12
-
13
- #include <ctype.h>
14
- #include <stdio.h>
15
- #include <string.h>
16
-
17
- /* *****************************************************************************
18
- Seeking for characters in a string
19
- ***************************************************************************** */
20
-
21
- #ifndef ALLOW_UNALIGNED_MEMORY_ACCESS
22
- #define ALLOW_UNALIGNED_MEMORY_ACCESS 0
23
- #endif
24
-
25
- #if FIO_MEMCHAR
26
-
27
- /**
28
- * This seems to be faster on some systems, especially for smaller distances.
29
- *
30
- * On newer systems, `memchr` should be faster.
31
- */
32
- static int seek2ch(uint8_t **buffer, register uint8_t *const limit,
33
- const uint8_t c) {
34
- if (**buffer == c) {
35
- #if HTTP1_PARSER_CONVERT_EOL2NUL
36
- **buffer = 0;
37
- #endif
38
- return 1;
39
- }
40
-
41
- #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
42
- /* too short for this mess */
43
- if ((uintptr_t)limit <= 16 + ((uintptr_t)*buffer & (~(uintptr_t)7)))
44
- goto finish;
45
-
46
- /* align memory */
47
- {
48
- const uint8_t *alignment =
49
- (uint8_t *)(((uintptr_t)(*buffer) & (~(uintptr_t)7)) + 8);
50
- if (*buffer < alignment)
51
- *buffer += 1; /* already tested this char */
52
- if (limit >= alignment) {
53
- while (*buffer < alignment) {
54
- if (**buffer == c) {
55
- #if HTTP1_PARSER_CONVERT_EOL2NUL
56
- **buffer = 0;
57
- #endif
58
- return 1;
59
- }
60
- *buffer += 1;
61
- }
62
- }
63
- }
64
- const uint8_t *limit64 = (uint8_t *)((uintptr_t)limit & (~(uintptr_t)7));
65
- #else
66
- const uint8_t *limit64 = (uint8_t *)limit - 7;
67
- #endif
68
- uint64_t wanted1 = 0x0101010101010101ULL * c;
69
- for (; *buffer < limit64; *buffer += 8) {
70
- const uint64_t eq1 = ~((*((uint64_t *)*buffer)) ^ wanted1);
71
- const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
72
- const uint64_t t1 = (eq1 & 0x8080808080808080llu);
73
- if ((t0 & t1)) {
74
- break;
75
- }
76
- }
77
- #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
78
- finish:
79
- #endif
80
- while (*buffer < limit) {
81
- if (**buffer == c) {
82
- #if HTTP1_PARSER_CONVERT_EOL2NUL
83
- **buffer = 0;
84
- #endif
85
- return 1;
86
- }
87
- (*buffer)++;
88
- }
89
- return 0;
90
- }
91
-
92
- #else
93
-
94
- /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
95
- inline static uint8_t seek2ch(uint8_t **pos, uint8_t *const limit, uint8_t ch) {
96
- /* This is library based alternative that is sometimes slower */
97
- if (*pos >= limit || **pos == ch) {
98
- return 1;
99
- }
100
- uint8_t *tmp = memchr(*pos, ch, limit - (*pos));
101
- if (tmp) {
102
- *pos = tmp;
103
- #if HTTP1_PARSER_CONVERT_EOL2NUL
104
- *tmp = 0;
105
- #endif
106
- return 1;
107
- }
108
- *pos = limit;
109
- return 0;
110
- }
111
-
112
- #endif
113
-
114
- /* a helper that seeks the EOL, converts it to NUL and returns it's length */
115
- inline static uint8_t seek2eol(uint8_t **pos, uint8_t *const limit) {
116
- /* single char lookup using memchr might be better when target is far... */
117
- if (!seek2ch(pos, limit, '\n'))
118
- return 0;
119
- if ((*pos)[-1] == '\r') {
120
- #if HTTP1_PARSER_CONVERT_EOL2NUL
121
- (*pos)[-1] = 0;
122
- #endif
123
- return 2;
124
- }
125
- return 1;
126
- }
127
-
128
- /* *****************************************************************************
129
- HTTP/1.1 parsre stages
130
- ***************************************************************************** */
131
-
132
- inline static int consume_response_line(struct http1_fio_parser_args_s *args,
133
- uint8_t *start, uint8_t *end) {
134
- args->parser->state.reserved |= 128;
135
- uint8_t *tmp = start;
136
- if (!seek2ch(&tmp, end, ' '))
137
- return -1;
138
- if (args->on_http_version(args->parser, (char *)start, tmp - start))
139
- return -1;
140
- tmp = start = tmp + 1;
141
- if (!seek2ch(&tmp, end, ' '))
142
- return -1;
143
- if (args->on_status(args->parser, atol((char *)start), (char *)(tmp + 1),
144
- end - tmp))
145
- return -1;
146
- return 0;
147
- }
148
-
149
- inline static int consume_request_line(struct http1_fio_parser_args_s *args,
150
- uint8_t *start, uint8_t *end) {
151
- uint8_t *tmp = start;
152
- uint8_t *host_start = NULL;
153
- uint8_t *host_end = NULL;
154
- if (!seek2ch(&tmp, end, ' '))
155
- return -1;
156
- if (args->on_method(args->parser, (char *)start, tmp - start))
157
- return -1;
158
- tmp = start = tmp + 1;
159
- if (start[0] == 'h' && start[1] == 't' && start[2] == 't' &&
160
- start[3] == 'p') {
161
- if (start[4] == ':' && start[5] == '/' && start[6] == '/') {
162
- /* Request URI is in long form... emulate Host header instead. */
163
- tmp = host_end = host_start = (start += 7);
164
- } else if (start[4] == 's' && start[5] == ':' && start[6] == '/' &&
165
- start[7] == '/') {
166
- /* Secure request is in long form... emulate Host header instead. */
167
- tmp = host_end = host_start = (start += 8);
168
- } else
169
- goto review_path;
170
- if (!seek2ch(&tmp, end, ' '))
171
- return -1;
172
- *tmp = ' ';
173
- if (!seek2ch(&host_end, tmp, '/')) {
174
- if (args->on_path(args->parser, (char *)"/", 1))
175
- return -1;
176
- goto start_version;
177
- }
178
- host_end[0] = '/';
179
- start = host_end;
180
- }
181
- review_path:
182
- tmp = start;
183
- if (seek2ch(&tmp, end, '?')) {
184
- if (args->on_path(args->parser, (char *)start, tmp - start))
185
- return -1;
186
- tmp = start = tmp + 1;
187
- if (!seek2ch(&tmp, end, ' '))
188
- return -1;
189
- if (tmp - start > 0 &&
190
- args->on_query(args->parser, (char *)start, tmp - start))
191
- return -1;
192
- } else {
193
- tmp = start;
194
- if (!seek2ch(&tmp, end, ' '))
195
- return -1;
196
- if (args->on_path(args->parser, (char *)start, tmp - start))
197
- return -1;
198
- }
199
- start_version:
200
- start = tmp + 1;
201
- if (start + 5 >= end) /* require "HTTP/" */
202
- return -1;
203
- if (args->on_http_version(args->parser, (char *)start, end - start))
204
- return -1;
205
- /* */
206
- if (host_start && args->on_header(args->parser, (char *)"host", 4,
207
- (char *)host_start, host_end - host_start))
208
- return -1;
209
- return 0;
210
- }
211
-
212
- inline static int consume_header(struct http1_fio_parser_args_s *args,
213
- uint8_t *start, uint8_t *end) {
214
- uint8_t *end_name = start;
215
- /* divide header name from data */
216
- if (!seek2ch(&end_name, end, ':'))
217
- return -1;
218
- #if HTTP_HEADERS_LOWERCASE
219
- for (uint8_t *t = start; t < end_name; t++) {
220
- *t = tolower(*t);
221
- }
222
- #endif
223
- uint8_t *start_value = end_name + 1;
224
- if (start_value[0] == ' ') {
225
- start_value++;
226
- };
227
- #if ALLOW_UNALIGNED_MEMORY_ACCESS && HTTP_HEADERS_LOWERCASE
228
- /* enable this section to test unaligned memory access */
229
- if ((end_name - start) == 14 &&
230
- *((uint64_t *)start) == *((uint64_t *)"content-") &&
231
- *((uint64_t *)(start + 6)) == *((uint64_t *)"t-length")) {
232
- /* handle the special `content-length` header */
233
- args->parser->state.content_length = atol((char *)start_value);
234
- } else if ((end_name - start) == 17 &&
235
- *((uint64_t *)start) == *((uint64_t *)"transfer") &&
236
- *((uint64_t *)(start + 8)) == *((uint64_t *)"-encodin") &&
237
- *((uint32_t *)start_value) == *((uint32_t *)"chun") &&
238
- *((uint32_t *)(start_value + 3)) == *((uint32_t *)"nked")) {
239
- /* handle the special `transfer-encoding: chunked` header */
240
- args->parser->state.reserved |= 64;
241
- } else if ((end_name - start) == 7 &&
242
- *((uint64_t *)start) == *((uint64_t *)"trailer")) {
243
- /* chunked data with trailer... */
244
- args->parser->state.reserved |= 64;
245
- args->parser->state.reserved |= 32;
246
- }
247
- #else
248
- if ((end_name - start) == 14 &&
249
- HEADER_NAME_IS_EQ((char *)start, "content-length", 14)) {
250
- /* handle the special `content-length` header */
251
- args->parser->state.content_length = atol((char *)start_value);
252
- } else if ((end_name - start) == 17 &&
253
- HEADER_NAME_IS_EQ((char *)start, "transfer-encoding", 17) &&
254
- memcmp(start_value, "chunked", 7)) {
255
- /* handle the special `transfer-encoding: chunked` header */
256
- args->parser->state.reserved |= 64;
257
- } else if ((end_name - start) == 7 &&
258
- HEADER_NAME_IS_EQ((char *)start, "trailer", 7)) {
259
- /* chunked data with trailer... */
260
- args->parser->state.reserved |= 64;
261
- args->parser->state.reserved |= 32;
262
- }
263
- #endif
264
- /* perform callback */
265
- if (args->on_header(args->parser, (char *)start, (end_name - start),
266
- (char *)start_value, end - start_value))
267
- return -1;
268
- return 0;
269
- }
270
-
271
- /* *****************************************************************************
272
- HTTP/1.1 Body handling
273
- ***************************************************************************** */
274
-
275
- inline static int consume_body_streamed(struct http1_fio_parser_args_s *args,
276
- uint8_t **start) {
277
- uint8_t *end =
278
- *start + args->parser->state.content_length - args->parser->state.read;
279
- uint8_t *const stop = ((uint8_t *)args->buffer) + args->length;
280
- if (end > stop)
281
- end = stop;
282
- if (end > *start &&
283
- args->on_body_chunk(args->parser, (char *)(*start), end - *start))
284
- return -1;
285
- args->parser->state.read += (end - *start);
286
- *start = end;
287
- if (args->parser->state.content_length <= args->parser->state.read)
288
- args->parser->state.reserved |= 4;
289
- return 0;
290
- }
291
-
292
- inline static int consume_body_chunked(struct http1_fio_parser_args_s *args,
293
- uint8_t **start) {
294
- uint8_t *const stop = ((uint8_t *)args->buffer) + args->length;
295
- uint8_t *end = *start;
296
- while (*start < stop) {
297
- if (args->parser->state.content_length == 0) {
298
- size_t eol_len;
299
- /* consume seperator */
300
- while (*start < stop && (**start == '\n' || **start == '\r'))
301
- ++(*start);
302
- /* collect chunked length */
303
- if (!(eol_len = seek2eol(&end, stop))) {
304
- /* requires length data to continue */
305
- return 0;
306
- }
307
- /* an empty EOL is possible in mid stream processing */
308
- if (*start + eol_len > end && (*start = end) && !seek2eol(&end, stop)) {
309
- return 0;
310
- }
311
- args->parser->state.content_length = 0 - strtol((char *)*start, NULL, 16);
312
- *start = end = end + 1;
313
- if (args->parser->state.content_length == 0) {
314
- /* all chunked data was parsed */
315
- args->parser->state.content_length = args->parser->state.read;
316
- /* consume trailing EOL */
317
- if (seek2eol(start, stop))
318
- (*start)++;
319
- if (args->parser->state.reserved & 32) {
320
- /* remove the "headers complete" and "trailer" flags */
321
- args->parser->state.reserved &= 0xDD; /* 0xDD == ~2 & ~32 & 0xFF */
322
- return -2;
323
- }
324
- /* the parsing complete flag */
325
- args->parser->state.reserved |= 4;
326
- return 0;
327
- }
328
- }
329
- end = *start + (0 - args->parser->state.content_length);
330
- if (end > stop)
331
- end = stop;
332
- if (end > *start &&
333
- args->on_body_chunk(args->parser, (char *)(*start), end - *start)) {
334
- return -1;
335
- }
336
- args->parser->state.read += (end - *start);
337
- args->parser->state.content_length += (end - *start);
338
- *start = end;
339
- }
340
- return 0;
341
- }
342
-
343
- inline static int consume_body(struct http1_fio_parser_args_s *args,
344
- uint8_t **start) {
345
- if (args->parser->state.content_length > 0 &&
346
- args->parser->state.content_length > args->parser->state.read) {
347
- /* normal, streamed data */
348
- return consume_body_streamed(args, start);
349
- } else if (args->parser->state.content_length <= 0 &&
350
- (args->parser->state.reserved & 64)) {
351
- /* chuncked encoding */
352
- return consume_body_chunked(args, start);
353
- } else {
354
- /* nothing to do - parsing complete */
355
- args->parser->state.reserved |= 4;
356
- }
357
- return 0;
358
- }
359
-
360
- /* *****************************************************************************
361
- HTTP/1.1 parsre function
362
- ***************************************************************************** */
363
- #if DEBUG
364
- #include <assert.h>
365
- #else
366
- #define DEBUG 0
367
- #define assert(...)
368
- #endif
369
-
370
- size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args) {
371
- assert(args->parser && args->buffer);
372
- args->parser->state.next = NULL;
373
- uint8_t *start = args->buffer;
374
- uint8_t *end = start;
375
- uint8_t *const stop = start + args->length;
376
- uint8_t eol_len = 0;
377
- #define CONSUMED ((size_t)((uintptr_t)start - (uintptr_t)args->buffer))
378
- // fprintf(stderr, "** resuming with at %p with %.*s...(%lu)\n", args->buffer,
379
- // 4,
380
- // start, args->length);
381
- re_eval:
382
- switch ((args->parser->state.reserved & 15)) {
383
-
384
- /* request / response line */
385
- case 0:
386
- /* clear out any leadinng white space */
387
- while ((start < stop) &&
388
- (*start == '\r' || *start == '\n' || *start == ' ' || *start == 0)) {
389
- ++start;
390
- }
391
- end = start;
392
- /* make sure the whole line is available*/
393
- if (!(eol_len = seek2eol(&end, stop)))
394
- return CONSUMED;
395
-
396
- if (start[0] == 'H' && start[1] == 'T' && start[2] == 'T' &&
397
- start[3] == 'P') {
398
- /* HTTP response */
399
- if (consume_response_line(args, start, end - eol_len + 1))
400
- goto error;
401
- } else if (tolower(start[0]) >= 'a' && tolower(start[0]) <= 'z') {
402
- /* HTTP request */
403
- if (consume_request_line(args, start, end - eol_len + 1))
404
- goto error;
405
- }
406
- end = start = end + 1;
407
- args->parser->state.reserved |= 1;
408
-
409
- /* fallthrough */
410
- /* headers */
411
- case 1:
412
- do {
413
- if (start >= stop)
414
- return CONSUMED; /* buffer ended on header line */
415
- if (*start == '\r' || *start == '\n') {
416
- goto finished_headers; /* empty line, end of headers */
417
- }
418
- if (!(eol_len = seek2eol(&end, stop)))
419
- return CONSUMED;
420
- if (consume_header(args, start, end - eol_len + 1))
421
- goto error;
422
- end = start = end + 1;
423
- } while ((args->parser->state.reserved & 2) == 0);
424
- finished_headers:
425
- ++start;
426
- if (*start == '\n')
427
- ++start;
428
- end = start;
429
- args->parser->state.reserved |= 2;
430
- /* fallthrough */
431
- /* request body */
432
- case 3: { /* 2 | 1 == 3 */
433
- int t3 = consume_body(args, &start);
434
- switch (t3) {
435
- case -1:
436
- goto error;
437
- case -2:
438
- goto re_eval;
439
- }
440
- break;
441
- }
442
- }
443
- /* are we done ? */
444
- if (args->parser->state.reserved & 4) {
445
- args->parser->state.next = start;
446
- if (((args->parser->state.reserved & 128) ? args->on_response
447
- : args->on_request)(args->parser))
448
- goto error;
449
- args->parser->state =
450
- (struct http1_parser_protected_read_only_state_s){0, 0, 0};
451
- }
452
- return CONSUMED;
453
- error:
454
- args->on_error(args->parser);
455
- args->parser->state =
456
- (struct http1_parser_protected_read_only_state_s){0, 0, 0};
457
- return args->length;
458
- }
459
-
460
- #undef CONSUMED