iodine 0.7.38 → 0.7.43

Sign up to get free protection for your applications and to get access to all the features.
@@ -33,6 +33,7 @@ VALUE IodineBaseModule;
33
33
  VALUE iodine_default_args;
34
34
 
35
35
  ID iodine_call_id;
36
+ ID iodine_to_s_id;
36
37
 
37
38
  static VALUE address_sym;
38
39
  static VALUE app_sym;
@@ -1302,6 +1303,7 @@ void Init_iodine(void) {
1302
1303
  IodineBaseModule = rb_define_module_under(IodineModule, "Base");
1303
1304
  VALUE IodineCLIModule = rb_define_module_under(IodineBaseModule, "CLI");
1304
1305
  iodine_call_id = rb_intern2("call", 4);
1306
+ iodine_to_s_id = rb_intern("to_s");
1305
1307
 
1306
1308
  // register core methods
1307
1309
  rb_define_module_function(IodineModule, "threads", iodine_threads_get, 0);
@@ -53,6 +53,7 @@ extern VALUE IodineModule;
53
53
  extern VALUE IodineBaseModule;
54
54
  extern VALUE iodine_default_args;
55
55
  extern ID iodine_call_id;
56
+ extern ID iodine_to_s_id;
56
57
 
57
58
  #define IODINE_RSTRINFO(rstr) \
58
59
  ((fio_str_info_s){.len = RSTRING_LEN(rstr), .data = RSTRING_PTR(rstr)})
@@ -5,7 +5,7 @@
5
5
 
6
6
  #include <fio.h>
7
7
 
8
- static __thread volatile uint8_t iodine_GVL_state;
8
+ static __thread volatile uint8_t iodine_GVL_state = 1;
9
9
 
10
10
  /* *****************************************************************************
11
11
  Calling protected Ruby methods
@@ -159,7 +159,7 @@ Ruby Connection Methods - write, close open? pending
159
159
  ***************************************************************************** */
160
160
 
161
161
  /**
162
- * Writes data to the connection asynchronously.
162
+ * Writes data to the connection asynchronously. `data` MUST be a String.
163
163
  *
164
164
  * In effect, the `write` call does nothing, it only schedules the data to be
165
165
  * sent and marks the data as pending.
@@ -174,6 +174,16 @@ static VALUE iodine_connection_write(VALUE self, VALUE data) {
174
174
  return Qnil;
175
175
  // rb_raise(rb_eIOError, "Connection closed or invalid.");
176
176
  }
177
+ if (!RB_TYPE_P(data, T_STRING)) {
178
+ VALUE tmp = data;
179
+ data = IodineCaller.call(data, iodine_to_s_id);
180
+ if (!RB_TYPE_P(data, T_STRING))
181
+ Check_Type(tmp, T_STRING);
182
+ rb_backtrace();
183
+ FIO_LOG_WARNING(
184
+ "`Iodine::Connection#write` was called with a non-String object.");
185
+ }
186
+
177
187
  switch (c->info.type) {
178
188
  case IODINE_CONNECTION_WEBSOCKET:
179
189
  /* WebSockets*/
@@ -234,6 +244,14 @@ static VALUE iodine_connection_is_open(VALUE self) {
234
244
  }
235
245
  return Qfalse;
236
246
  }
247
+
248
+ /**
249
+ * Always returns true, since Iodine connections support the pub/sub extension.
250
+ */
251
+ static VALUE iodine_connection_is_pubsub(VALUE self) {
252
+ return INT2NUM(0);
253
+ (void)self;
254
+ }
237
255
  /**
238
256
  * Returns the number of pending `write` operations that need to complete
239
257
  * before the next `on_drained` callback is called.
@@ -675,13 +693,6 @@ The method accepts an optional `engine` argument:
675
693
 
676
694
  publish(to, message, my_pubsub_engine)
677
695
 
678
-
679
- Alternatively, accepts the following named arguments:
680
-
681
- - `:to` - The channel to publish to (required).
682
- - `:message` - The message to be published (required).
683
- - `:engine` - If provided, the engine to use for pub/sub. Otherwise the default engine is used.
684
-
685
696
  */
686
697
  static VALUE iodine_pubsub_publish(int argc, VALUE *argv, VALUE self) {
687
698
  // clang-format on
@@ -903,6 +914,7 @@ void iodine_connection_init(void) {
903
914
  0);
904
915
  rb_define_method(ConnectionKlass, "handler=", iodine_connection_handler_set,
905
916
  1);
917
+ rb_define_method(ConnectionKlass, "pubsub?", iodine_connection_is_pubsub, 0);
906
918
  rb_define_method(ConnectionKlass, "subscribe", iodine_pubsub_subscribe, -1);
907
919
  rb_define_method(ConnectionKlass, "unsubscribe", iodine_pubsub_unsubscribe,
908
920
  1);
@@ -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
 
@@ -51,7 +51,6 @@ static VALUE hijack_func_sym;
51
51
  static ID close_method_id;
52
52
  static ID each_method_id;
53
53
  static ID attach_method_id;
54
- static ID iodine_to_s_method_id;
55
54
  static ID iodine_call_proc_id;
56
55
 
57
56
  static VALUE env_template_no_upgrade;
@@ -294,9 +293,9 @@ static int iodine_copy2env_task(FIOBJ o, void *env_) {
294
293
 
295
294
  } else {
296
295
  /* it's an array */
297
- VALUE ary = rb_ary_new();
298
- rb_hash_aset(env, hname, ary);
299
296
  size_t count = fiobj_ary_count(o);
297
+ VALUE ary = rb_ary_new2(count);
298
+ rb_hash_aset(env, hname, ary);
300
299
  for (size_t i = 0; i < count; ++i) {
301
300
  tmp = fiobj_obj2cstr(fiobj_ary_index(o, i));
302
301
  rb_ary_push(ary, rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
@@ -476,11 +475,11 @@ static int for_each_header_data(VALUE key, VALUE val, VALUE h_) {
476
475
  http_s *h = (http_s *)h_;
477
476
  // fprintf(stderr, "For_each - headers\n");
478
477
  if (TYPE(key) != T_STRING)
479
- key = IodineCaller.call(key, iodine_to_s_method_id);
478
+ key = IodineCaller.call(key, iodine_to_s_id);
480
479
  if (TYPE(key) != T_STRING)
481
480
  return ST_CONTINUE;
482
481
  if (TYPE(val) != T_STRING) {
483
- val = IodineCaller.call(val, iodine_to_s_method_id);
482
+ val = IodineCaller.call(val, iodine_to_s_id);
484
483
  if (TYPE(val) != T_STRING)
485
484
  return ST_STOP;
486
485
  }
@@ -1130,7 +1129,6 @@ void iodine_init_http(void) {
1130
1129
  close_method_id = rb_intern("close");
1131
1130
  each_method_id = rb_intern("each");
1132
1131
  attach_method_id = rb_intern("attach_fd");
1133
- iodine_to_s_method_id = rb_intern("to_s");
1134
1132
  iodine_call_proc_id = rb_intern("call");
1135
1133
 
1136
1134
  IodineUTF8Encoding = rb_enc_find("UTF-8");
@@ -8,7 +8,6 @@
8
8
  #include <fio.h>
9
9
 
10
10
  static ID call_func_id;
11
- static ID to_s_func_id;
12
11
  static VALUE filename_id;
13
12
  static VALUE data_id;
14
13
  static VALUE template_id;
@@ -169,7 +168,7 @@ static int mustache_on_arg(mustache_section_s *section, const char *name,
169
168
  if (rb_respond_to(o, call_func_id))
170
169
  o = IodineCaller.call(o, call_func_id);
171
170
  if (!RB_TYPE_P(o, T_STRING))
172
- o = IodineCaller.call(o, to_s_func_id);
171
+ o = IodineCaller.call(o, iodine_to_s_id);
173
172
  }
174
173
  if (!RB_TYPE_P(o, T_STRING) || !RSTRING_LEN(o))
175
174
  return 0;
@@ -220,7 +219,7 @@ static int32_t mustache_on_section_test(mustache_section_s *section,
220
219
  }
221
220
  o = IodineCaller.call2(o, call_func_id, 1, &str);
222
221
  if (!RB_TYPE_P(o, T_STRING))
223
- o = rb_funcall2(o, to_s_func_id, 0, NULL);
222
+ o = rb_funcall2(o, iodine_to_s_id, 0, NULL);
224
223
  if (RB_TYPE_P(o, T_STRING) && RSTRING_LEN(o))
225
224
  mustache_write_text(section, RSTRING_PTR(o), RSTRING_LEN(o), 0);
226
225
  return 0;
@@ -553,7 +552,6 @@ Initialize Iodine::Mustache
553
552
 
554
553
  void iodine_init_mustache(void) {
555
554
  call_func_id = rb_intern2("call", 4);
556
- to_s_func_id = rb_intern2("to_s", 4);
557
555
  filename_id = rb_id2sym(rb_intern2("filename", 8));
558
556
  data_id = rb_id2sym(rb_intern2("data", 4));
559
557
  template_id = rb_id2sym(rb_intern2("template", 8));
@@ -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) {
@@ -178,7 +178,7 @@ Iodine.on_state(:after_fork) do
178
178
  end
179
179
 
180
180
  ### Parse CLI for default HTTP settings
181
- Iodine::Base::CLI.parse
181
+ Iodine::Base::CLI.parse if defined?(IODINE_PARSE_CLI) && IODINE_PARSE_CLI
182
182
 
183
183
  ### Set default port (if missing)
184
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.38'.freeze
2
+ VERSION = '0.7.43'.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.38
4
+ version: 0.7.43
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-12-09 00:00:00.000000000 Z
11
+ date: 2020-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -142,6 +142,7 @@ files:
142
142
  - bin/poc/config.ru
143
143
  - bin/poc/gemfile
144
144
  - bin/poc/www/index.html
145
+ - examples/async_task.ru
145
146
  - examples/config.ru
146
147
  - examples/echo.ru
147
148
  - examples/hello.ru
@@ -188,7 +189,6 @@ files:
188
189
  - ext/iodine/http.h
189
190
  - ext/iodine/http1.c
190
191
  - ext/iodine/http1.h
191
- - ext/iodine/http1_parser.c
192
192
  - ext/iodine/http1_parser.h
193
193
  - ext/iodine/http_internal.c
194
194
  - ext/iodine/http_internal.h
@@ -244,7 +244,7 @@ licenses:
244
244
  metadata:
245
245
  allowed_push_host: https://rubygems.org
246
246
  post_install_message: |-
247
- Thank you for installing Iodine 0.7.38.
247
+ Thank you for installing Iodine 0.7.43.
248
248
  Remember: if iodine supports your business, it's only fair to give value back (code contributions / donations).
249
249
  rdoc_options: []
250
250
  require_paths:
@@ -266,7 +266,7 @@ requirements:
266
266
  - Ruby >= 2.3.8 (Ruby EOL).
267
267
  - Ruby >= 2.5.0 recommended.
268
268
  - TLS requires OpenSSL >= 1.1.0
269
- rubygems_version: 3.0.1
269
+ rubygems_version: 3.1.2
270
270
  signing_key:
271
271
  specification_version: 4
272
272
  summary: iodine - a fast HTTP / Websocket Server with Pub/Sub support, optimized for
@@ -1,616 +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
- #ifndef HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
26
- #define HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING 1
27
- #endif
28
-
29
- #if FIO_MEMCHAR
30
-
31
- /**
32
- * This seems to be faster on some systems, especially for smaller distances.
33
- *
34
- * On newer systems, `memchr` should be faster.
35
- */
36
- static int seek2ch(uint8_t **buffer, register uint8_t *const limit,
37
- const uint8_t c) {
38
- if (*buffer >= limit)
39
- return 0;
40
-
41
- if (**buffer == c) {
42
- #if HTTP1_PARSER_CONVERT_EOL2NUL
43
- **buffer = 0;
44
- #endif
45
- return 1;
46
- }
47
-
48
- #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
49
- /* too short for this mess */
50
- if ((uintptr_t)limit <= 16 + ((uintptr_t)*buffer & (~(uintptr_t)7)))
51
- goto finish;
52
-
53
- /* align memory */
54
- {
55
- const uint8_t *alignment =
56
- (uint8_t *)(((uintptr_t)(*buffer) & (~(uintptr_t)7)) + 8);
57
- if (limit >= alignment) {
58
- while (*buffer < alignment) {
59
- if (**buffer == c) {
60
- #if HTTP1_PARSER_CONVERT_EOL2NUL
61
- **buffer = 0;
62
- #endif
63
- return 1;
64
- }
65
- *buffer += 1;
66
- }
67
- }
68
- }
69
- const uint8_t *limit64 = (uint8_t *)((uintptr_t)limit & (~(uintptr_t)7));
70
- #else
71
- const uint8_t *limit64 = (uint8_t *)limit - 7;
72
- #endif
73
- uint64_t wanted1 = 0x0101010101010101ULL * c;
74
- for (; *buffer < limit64; *buffer += 8) {
75
- const uint64_t eq1 = ~((*((uint64_t *)*buffer)) ^ wanted1);
76
- const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
77
- const uint64_t t1 = (eq1 & 0x8080808080808080llu);
78
- if ((t0 & t1)) {
79
- break;
80
- }
81
- }
82
- #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
83
- finish:
84
- #endif
85
- while (*buffer < limit) {
86
- if (**buffer == c) {
87
- #if HTTP1_PARSER_CONVERT_EOL2NUL
88
- **buffer = 0;
89
- #endif
90
- return 1;
91
- }
92
- (*buffer)++;
93
- }
94
- return 0;
95
- }
96
-
97
- #else
98
-
99
- /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
100
- inline static uint8_t seek2ch(uint8_t **pos, uint8_t *const limit, uint8_t ch) {
101
- /* This is library based alternative that is sometimes slower */
102
- if (*pos >= limit)
103
- return 0;
104
- if (**pos == ch) {
105
- return 1;
106
- }
107
- uint8_t *tmp = memchr(*pos, ch, limit - (*pos));
108
- if (tmp) {
109
- *pos = tmp;
110
- #if HTTP1_PARSER_CONVERT_EOL2NUL
111
- *tmp = 0;
112
- #endif
113
- return 1;
114
- }
115
- *pos = limit;
116
- return 0;
117
- }
118
-
119
- #endif
120
-
121
- /* a helper that seeks the EOL, converts it to NUL and returns it's length */
122
- inline static uint8_t seek2eol(uint8_t **pos, uint8_t *const limit) {
123
- /* single char lookup using memchr might be better when target is far... */
124
- if (!seek2ch(pos, limit, '\n'))
125
- return 0;
126
- if ((*pos)[-1] == '\r') {
127
- #if HTTP1_PARSER_CONVERT_EOL2NUL
128
- (*pos)[-1] = 0;
129
- #endif
130
- return 2;
131
- }
132
- return 1;
133
- }
134
-
135
- /* *****************************************************************************
136
- String to Number
137
- ***************************************************************************** */
138
-
139
- /** Converts a String to a number using base 10 */
140
- static long long http1_atol(const uint8_t *buf, const uint8_t **end) {
141
- register unsigned long long i = 0;
142
- uint8_t inv = 0;
143
- while (*buf == ' ' || *buf == '\t' || *buf == '\f')
144
- ++buf;
145
- while (*buf == '-' || *buf == '+')
146
- inv ^= (*(buf++) == '-');
147
- while (i <= ((((~0ULL) >> 1) / 10)) && *buf >= '0' && *buf <= '9') {
148
- i = i * 10;
149
- i += *buf - '0';
150
- ++buf;
151
- }
152
- /* test for overflow */
153
- if (i >= (~((~0ULL) >> 1)) || (*buf >= '0' && *buf <= '9'))
154
- i = (~0ULL >> 1);
155
- if (inv)
156
- i = 0ULL - i;
157
- if (end)
158
- *end = buf;
159
- return i;
160
- }
161
-
162
- /** Converts a String to a number using base 16, overflow limited to 113bytes */
163
- static long long http1_atol16(const uint8_t *buf, const uint8_t **end) {
164
- register unsigned long long i = 0;
165
- uint8_t inv = 0;
166
- for (int limit_ = 0;
167
- (*buf == ' ' || *buf == '\t' || *buf == '\f') && limit_ < 32; ++limit_)
168
- ++buf;
169
- for (int limit_ = 0; (*buf == '-' || *buf == '+') && limit_ < 32; ++limit_)
170
- inv ^= (*(buf++) == '-');
171
- if (*buf == '0')
172
- ++buf;
173
- if ((*buf | 32) == 'x')
174
- ++buf;
175
- for (int limit_ = 0; (*buf == '0') && limit_ < 32; ++limit_)
176
- ++buf;
177
- while (!(i & (~((~(0ULL)) >> 4)))) {
178
- if (*buf >= '0' && *buf <= '9') {
179
- i <<= 4;
180
- i |= *buf - '0';
181
- } else if ((*buf | 32) >= 'a' && (*buf | 32) <= 'f') {
182
- i <<= 4;
183
- i |= (*buf | 32) - ('a' - 10);
184
- } else
185
- break;
186
- ++buf;
187
- }
188
- if (inv)
189
- i = 0ULL - i;
190
- if (end)
191
- *end = buf;
192
- return i;
193
- }
194
-
195
- /* *****************************************************************************
196
- HTTP/1.1 parsre stages
197
- ***************************************************************************** */
198
-
199
- inline static int consume_response_line(struct http1_fio_parser_args_s *args,
200
- uint8_t *start, uint8_t *end) {
201
- args->parser->state.reserved |= 128;
202
- uint8_t *tmp = start;
203
- if (!seek2ch(&tmp, end, ' '))
204
- return -1;
205
- if (args->on_http_version(args->parser, (char *)start, tmp - start))
206
- return -1;
207
- tmp = start = tmp + 1;
208
- if (!seek2ch(&tmp, end, ' '))
209
- return -1;
210
- if (args->on_status(args->parser, atol((char *)start), (char *)(tmp + 1),
211
- end - tmp))
212
- return -1;
213
- return 0;
214
- }
215
-
216
- inline static int consume_request_line(struct http1_fio_parser_args_s *args,
217
- uint8_t *start, uint8_t *end) {
218
- uint8_t *tmp = start;
219
- uint8_t *host_start = NULL;
220
- uint8_t *host_end = NULL;
221
- if (!seek2ch(&tmp, end, ' '))
222
- return -1;
223
- if (args->on_method(args->parser, (char *)start, tmp - start))
224
- return -1;
225
- tmp = start = tmp + 1;
226
- if (start[0] == 'h' && start[1] == 't' && start[2] == 't' &&
227
- start[3] == 'p') {
228
- if (start[4] == ':' && start[5] == '/' && start[6] == '/') {
229
- /* Request URI is in long form... emulate Host header instead. */
230
- tmp = host_end = host_start = (start += 7);
231
- } else if (start[4] == 's' && start[5] == ':' && start[6] == '/' &&
232
- start[7] == '/') {
233
- /* Secure request is in long form... emulate Host header instead. */
234
- tmp = host_end = host_start = (start += 8);
235
- } else
236
- goto review_path;
237
- if (!seek2ch(&tmp, end, ' '))
238
- return -1;
239
- *tmp = ' ';
240
- if (!seek2ch(&host_end, tmp, '/')) {
241
- if (args->on_path(args->parser, (char *)"/", 1))
242
- return -1;
243
- goto start_version;
244
- }
245
- host_end[0] = '/';
246
- start = host_end;
247
- }
248
- review_path:
249
- tmp = start;
250
- if (seek2ch(&tmp, end, '?')) {
251
- if (args->on_path(args->parser, (char *)start, tmp - start))
252
- return -1;
253
- tmp = start = tmp + 1;
254
- if (!seek2ch(&tmp, end, ' '))
255
- return -1;
256
- if (tmp - start > 0 &&
257
- args->on_query(args->parser, (char *)start, tmp - start))
258
- return -1;
259
- } else {
260
- tmp = start;
261
- if (!seek2ch(&tmp, end, ' '))
262
- return -1;
263
- if (args->on_path(args->parser, (char *)start, tmp - start))
264
- return -1;
265
- }
266
- start_version:
267
- start = tmp + 1;
268
- if (start + 5 >= end) /* require "HTTP/" */
269
- return -1;
270
- if (args->on_http_version(args->parser, (char *)start, end - start))
271
- return -1;
272
- /* */
273
- if (host_start && args->on_header(args->parser, (char *)"host", 4,
274
- (char *)host_start, host_end - host_start))
275
- return -1;
276
- return 0;
277
- }
278
-
279
- inline static int consume_header(struct http1_fio_parser_args_s *args,
280
- uint8_t *start, uint8_t *end) {
281
- uint8_t *end_name = start;
282
- /* divide header name from data */
283
- if (!seek2ch(&end_name, end, ':'))
284
- return -1;
285
- #if HTTP_HEADERS_LOWERCASE
286
- for (uint8_t *t = start; t < end_name; t++) {
287
- *t = tolower(*t);
288
- }
289
- #endif
290
- uint8_t *start_value = end_name + 1;
291
- if (start_value[0] == ' ') {
292
- start_value++;
293
- };
294
-
295
- if ((end_name - start) == 14 &&
296
- #if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED && HTTP_HEADERS_LOWERCASE
297
- *((uint64_t *)start) == *((uint64_t *)"content-") &&
298
- *((uint64_t *)(start + 6)) == *((uint64_t *)"t-length")
299
- #else
300
- HEADER_NAME_IS_EQ((char *)start, "content-length", 14)
301
- #endif
302
- ) {
303
- /* handle the special `content-length` header */
304
- args->parser->state.content_length = http1_atol(start_value, NULL);
305
- } else if ((end_name - start) == 17 && (end - start_value) >= 7 &&
306
- #if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED && HTTP_HEADERS_LOWERCASE
307
- *((uint64_t *)start) == *((uint64_t *)"transfer") &&
308
- *((uint64_t *)(start + 8)) == *((uint64_t *)"-encodin")
309
- #else
310
- HEADER_NAME_IS_EQ((char *)start, "transfer-encoding", 17)
311
- #endif
312
- ) {
313
- /* handle the special `transfer-encoding: chunked` header? */
314
- /* this removes the `chunked` marker and "unchunks" the data */
315
- if (
316
- #if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED
317
- (((uint32_t *)(start_value))[0] | 0x20202020) ==
318
- ((uint32_t *)"chun")[0] &&
319
- (((uint32_t *)(start_value + 3))[0] | 0x20202020) ==
320
- ((uint32_t *)"nked")[0]
321
- #else
322
- ((start_value[0] | 32) == 'c' && (start_value[1] | 32) == 'h' &&
323
- (start_value[2] | 32) == 'u' && (start_value[3] | 32) == 'n' &&
324
- (start_value[4] | 32) == 'k' && (start_value[5] | 32) == 'e' &&
325
- (start_value[6] | 32) == 'd')
326
- #endif
327
- ) {
328
- /* simple case,`chunked` at the beginning */
329
- args->parser->state.reserved |= 64;
330
- start_value += 7;
331
- while (start_value < end && (*start_value == ',' || *start_value == ' '))
332
- ++start_value;
333
- if (!(end - start_value))
334
- return 0;
335
- } else if ((end - start_value) > 7 &&
336
- ((end[(-7 + 0)] | 32) == 'c' && (end[(-7 + 1)] | 32) == 'h' &&
337
- (end[(-7 + 2)] | 32) == 'u' && (end[(-7 + 3)] | 32) == 'n' &&
338
- (end[(-7 + 4)] | 32) == 'k' && (end[(-7 + 5)] | 32) == 'e' &&
339
- (end[(-7 + 6)] | 32) == 'd')) {
340
- /* simple case,`chunked` at the end of list */
341
- args->parser->state.reserved |= 64;
342
- end -= 7;
343
- while (start_value < end && (end[-1] == ',' || end[-1] == ' '))
344
- --end;
345
- if (!(end - start_value))
346
- return 0;
347
- } else if ((end - start_value) > 7 && (end - start_value) < 256) {
348
- /* complex case, `the, chunked, marker, is in the middle of list */
349
- uint8_t val[256];
350
- size_t val_len = 0;
351
- while (start_value < end) {
352
- if (
353
- #if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED
354
- (((uint32_t *)(start_value))[0] | 0x20202020) ==
355
- ((uint32_t *)"chun")[0] &&
356
- (((uint32_t *)(start_value + 3))[0] | 0x20202020) ==
357
- ((uint32_t *)"nked")[0]
358
- #else
359
- ((start_value[0] | 32) == 'c' && (start_value[1] | 32) == 'h' &&
360
- (start_value[2] | 32) == 'u' && (start_value[3] | 32) == 'n' &&
361
- (start_value[4] | 32) == 'k' && (start_value[5] | 32) == 'e' &&
362
- (start_value[6] | 32) == 'd')
363
- #endif
364
-
365
- ) {
366
- args->parser->state.reserved |= 64;
367
- start_value += 7;
368
- /* skip comma / white space */
369
- while (start_value < end &&
370
- (*start_value == ',' || *start_value == ' '))
371
- ++start_value;
372
- break;
373
- }
374
- val[val_len++] = *start_value;
375
- ++start_value;
376
- }
377
- while (start_value < end) {
378
- val[val_len++] = *start_value;
379
- ++start_value;
380
- }
381
- /* perform callback with `val` */
382
- val[val_len] = 0;
383
- if (val_len && args->on_header(args->parser, (char *)start,
384
- (end_name - start), (char *)val, val_len))
385
- return -1;
386
- return 0;
387
- }
388
- } else if ((end_name - start) == 7 &&
389
- #if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED && HTTP_HEADERS_LOWERCASE
390
- *((uint64_t *)start) == *((uint64_t *)"trailer")
391
- #else
392
- HEADER_NAME_IS_EQ((char *)start, "trailer", 7)
393
- #endif
394
- ) {
395
- /* chunked data with trailer... */
396
- args->parser->state.reserved |= 64;
397
- args->parser->state.reserved |= 32;
398
- // return 0; /* hide Trailer header, since we process the headers? */
399
- }
400
-
401
- /* perform callback */
402
- if (args->on_header(args->parser, (char *)start, (end_name - start),
403
- (char *)start_value, end - start_value))
404
- return -1;
405
- return 0;
406
- }
407
-
408
- /* *****************************************************************************
409
- HTTP/1.1 Body handling
410
- ***************************************************************************** */
411
-
412
- inline static int consume_body_streamed(struct http1_fio_parser_args_s *args,
413
- uint8_t **start) {
414
- uint8_t *end =
415
- *start + args->parser->state.content_length - args->parser->state.read;
416
- uint8_t *const stop = ((uint8_t *)args->buffer) + args->length;
417
- if (end > stop)
418
- end = stop;
419
- if (end > *start &&
420
- args->on_body_chunk(args->parser, (char *)(*start), end - *start))
421
- return -1;
422
- args->parser->state.read += (end - *start);
423
- *start = end;
424
- if (args->parser->state.content_length <= args->parser->state.read)
425
- args->parser->state.reserved |= 4;
426
- return 0;
427
- }
428
-
429
- inline static int consume_body_chunked(struct http1_fio_parser_args_s *args,
430
- uint8_t **start) {
431
- uint8_t *const stop = ((uint8_t *)args->buffer) + args->length;
432
- uint8_t *end = *start;
433
- while (*start < stop) {
434
- if (args->parser->state.content_length == 0) {
435
- if (end + 2 >= stop)
436
- return 0;
437
- if ((end[0] == '\r' && end[1] == '\n')) {
438
- /* remove tailing EOL that wasn't processed and retest */
439
- end += 2;
440
- *start = end;
441
- if (end + 2 >= stop)
442
- return 0;
443
- }
444
- long long chunk_len = http1_atol16(end, (const uint8_t **)&end);
445
- if (end + 2 > stop) /* overflowed? */
446
- return 0;
447
- if ((end[0] != '\r' || end[1] != '\n'))
448
- return -1; /* required EOL after content length */
449
- end += 2;
450
-
451
- args->parser->state.content_length = 0 - chunk_len;
452
- *start = end;
453
- if (args->parser->state.content_length == 0) {
454
- /* all chunked data was parsed - update content-length */
455
- args->parser->state.content_length = args->parser->state.read;
456
- /* consume trailing EOL */
457
- if (*start + 2 <= stop)
458
- *start += 2;
459
- #ifdef HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
460
- { /* add virtual header ... ? */
461
- char buf[512];
462
- size_t buf_len = 512;
463
- size_t tmp_len = args->parser->state.read;
464
- buf[--buf_len] = 0;
465
- while (tmp_len) {
466
- size_t mod = tmp_len / 10;
467
- buf[--buf_len] = '0' + (tmp_len - (mod * 10));
468
- tmp_len = mod;
469
- }
470
- if (args->on_header(args->parser, "content-length", 14,
471
- (char *)buf + buf_len, 511 - buf_len))
472
- return -1;
473
- }
474
- #endif
475
- if (args->parser->state.reserved & 32) {
476
- /* remove the "headers complete" and "trailer" flags */
477
- args->parser->state.reserved &= 0xDD; /* 0xDD == ~2 & ~32 & 0xFF */
478
- return -2;
479
- }
480
- /* the parsing complete flag */
481
- args->parser->state.reserved |= 4;
482
- return 0;
483
- }
484
- }
485
- end = *start + (0 - args->parser->state.content_length);
486
- if (end > stop)
487
- end = stop;
488
- if (end > *start &&
489
- args->on_body_chunk(args->parser, (char *)(*start), end - *start)) {
490
- return -1;
491
- }
492
- args->parser->state.read += (end - *start);
493
- args->parser->state.content_length += (end - *start);
494
- *start = end;
495
- }
496
- return 0;
497
- }
498
-
499
- inline static int consume_body(struct http1_fio_parser_args_s *args,
500
- uint8_t **start) {
501
- if (args->parser->state.content_length > 0 &&
502
- args->parser->state.content_length > args->parser->state.read) {
503
- /* normal, streamed data */
504
- return consume_body_streamed(args, start);
505
- } else if (args->parser->state.content_length <= 0 &&
506
- (args->parser->state.reserved & 64)) {
507
- /* chuncked encoding */
508
- return consume_body_chunked(args, start);
509
- } else {
510
- /* nothing to do - parsing complete */
511
- args->parser->state.reserved |= 4;
512
- }
513
- return 0;
514
- }
515
-
516
- /* *****************************************************************************
517
- HTTP/1.1 parsre function
518
- ***************************************************************************** */
519
- #if DEBUG
520
- #include <assert.h>
521
- #else
522
- #define DEBUG 0
523
- #define assert(...)
524
- #endif
525
-
526
- size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args) {
527
- assert(args->parser && args->buffer);
528
- args->parser->state.next = NULL;
529
- uint8_t *start = args->buffer;
530
- uint8_t *end = start;
531
- uint8_t *const stop = start + args->length;
532
- uint8_t eol_len = 0;
533
- #define CONSUMED ((size_t)((uintptr_t)start - (uintptr_t)args->buffer))
534
- // fprintf(stderr, "** resuming with at %p with %.*s...(%lu)\n", args->buffer,
535
- // 4,
536
- // start, args->length);
537
- re_eval:
538
- switch ((args->parser->state.reserved & 15)) {
539
-
540
- /* request / response line */
541
- case 0:
542
- /* clear out any leadinng white space */
543
- while ((start < stop) &&
544
- (*start == '\r' || *start == '\n' || *start == ' ' || *start == 0)) {
545
- ++start;
546
- }
547
- end = start;
548
- /* make sure the whole line is available*/
549
- if (!(eol_len = seek2eol(&end, stop)))
550
- return CONSUMED;
551
-
552
- if (start[0] == 'H' && start[1] == 'T' && start[2] == 'T' &&
553
- start[3] == 'P') {
554
- /* HTTP response */
555
- if (consume_response_line(args, start, end - eol_len + 1))
556
- goto error;
557
- } else if (tolower(start[0]) >= 'a' && tolower(start[0]) <= 'z') {
558
- /* HTTP request */
559
- if (consume_request_line(args, start, end - eol_len + 1))
560
- goto error;
561
- }
562
- end = start = end + 1;
563
- args->parser->state.reserved |= 1;
564
-
565
- /* fallthrough */
566
- /* headers */
567
- case 1:
568
- do {
569
- if (start >= stop)
570
- return CONSUMED; /* buffer ended on header line */
571
- if (*start == '\r' || *start == '\n') {
572
- goto finished_headers; /* empty line, end of headers */
573
- }
574
- if (!(eol_len = seek2eol(&end, stop)))
575
- return CONSUMED;
576
- if (consume_header(args, start, end - eol_len + 1))
577
- goto error;
578
- end = start = end + 1;
579
- } while ((args->parser->state.reserved & 2) == 0);
580
- finished_headers:
581
- ++start;
582
- if (*start == '\n')
583
- ++start;
584
- end = start;
585
- args->parser->state.reserved |= 2;
586
- /* fallthrough */
587
- /* request body */
588
- case 3: { /* 2 | 1 == 3 */
589
- int t3 = consume_body(args, &start);
590
- switch (t3) {
591
- case -1:
592
- goto error;
593
- case -2:
594
- goto re_eval;
595
- }
596
- break;
597
- }
598
- }
599
- /* are we done ? */
600
- if (args->parser->state.reserved & 4) {
601
- args->parser->state.next = start;
602
- if (((args->parser->state.reserved & 128) ? args->on_response
603
- : args->on_request)(args->parser))
604
- goto error;
605
- args->parser->state =
606
- (struct http1_parser_protected_read_only_state_s){0, 0, 0};
607
- }
608
- return CONSUMED;
609
- error:
610
- args->on_error(args->parser);
611
- args->parser->state =
612
- (struct http1_parser_protected_read_only_state_s){0, 0, 0};
613
- return args->length;
614
- }
615
-
616
- #undef CONSUMED