iodine 0.7.36 → 0.7.41

Sign up to get free protection for your applications and to get access to all the features.
@@ -117,6 +117,8 @@ Core API
117
117
  * i.e., -2 == half the number of detected CPU cores.
118
118
  *
119
119
  * Zero values promise nothing (iodine will decide what to do with them).
120
+ *
121
+ * @return [FixNum] Thread Count
120
122
  */
121
123
  static VALUE iodine_threads_get(VALUE self) {
122
124
  VALUE i = rb_ivar_get(self, rb_intern2("@threads", 8));
@@ -133,6 +135,8 @@ static VALUE iodine_threads_get(VALUE self) {
133
135
  * i.e., -2 == half the number of detected CPU cores.
134
136
  *
135
137
  * Zero values promise nothing (iodine will decide what to do with them).
138
+ *
139
+ * @param thread_count [FixNum] The number of worker threads to use
136
140
  */
137
141
  static VALUE iodine_threads_set(VALUE self, VALUE val) {
138
142
  Check_Type(val, T_FIXNUM);
@@ -157,6 +161,8 @@ static VALUE iodine_threads_set(VALUE self, VALUE val) {
157
161
  *
158
162
  * Logging is always performed to the process's STDERR and can be piped away.
159
163
  *
164
+ * @return [FixNum] Logging Level
165
+ *
160
166
  * NOTE: this does NOT effect HTTP logging.
161
167
  */
162
168
  static VALUE iodine_logging_get(VALUE self) {
@@ -178,6 +184,8 @@ static VALUE iodine_logging_get(VALUE self) {
178
184
  *
179
185
  * Logging is always performed to the process's STDERR and can be piped away.
180
186
  *
187
+ * @param log_level [FixNum] Sets the logging level
188
+ *
181
189
  * NOTE: this does NOT effect HTTP logging.
182
190
  */
183
191
  static VALUE iodine_logging_set(VALUE self, VALUE val) {
@@ -196,6 +204,8 @@ static VALUE iodine_logging_set(VALUE self, VALUE val) {
196
204
  * Zero values promise nothing (iodine will decide what to do with them).
197
205
  *
198
206
  * 1 == single process mode, the msater process acts as a worker process.
207
+ *
208
+ * @return [FixNum] Worker Count
199
209
  */
200
210
  static VALUE iodine_workers_get(VALUE self) {
201
211
  VALUE i = rb_ivar_get(self, rb_intern2("@workers", 8));
@@ -214,6 +224,8 @@ static VALUE iodine_workers_get(VALUE self) {
214
224
  * Zero values promise nothing (iodine will decide what to do with them).
215
225
  *
216
226
  * 1 == single process mode, the msater process acts as a worker process.
227
+ *
228
+ * @param worker_count [FixNum] Number of worker processes
217
229
  */
218
230
  static VALUE iodine_workers_set(VALUE self, VALUE val) {
219
231
  Check_Type(val, T_FIXNUM);
@@ -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
 
@@ -45,5 +45,5 @@ Gem::Specification.new do |spec|
45
45
  spec.add_development_dependency 'rake-compiler', '>= 1', '< 2.0'
46
46
 
47
47
  spec.post_install_message = "Thank you for installing Iodine #{Iodine::VERSION}.\n" +
48
- "Remember: if iodine supports your business, it's is only fair to give value back (code contributions / donations)."
48
+ "Remember: if iodine supports your business, it's only fair to give value back (code contributions / donations)."
49
49
  end
@@ -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.36'.freeze
2
+ VERSION = '0.7.41'.freeze
3
3
  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.36
4
+ version: 0.7.41
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-10-24 00:00:00.000000000 Z
11
+ date: 2020-07-23 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,8 +243,8 @@ 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.36.
247
- Remember: if iodine supports your business, it's is only fair to give value back (code contributions / donations).
246
+ Thank you for installing Iodine 0.7.41.
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:
250
250
  - lib
@@ -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,458 +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 (limit >= alignment) {
51
- while (*buffer < alignment) {
52
- if (**buffer == c) {
53
- #if HTTP1_PARSER_CONVERT_EOL2NUL
54
- **buffer = 0;
55
- #endif
56
- return 1;
57
- }
58
- *buffer += 1;
59
- }
60
- }
61
- }
62
- const uint8_t *limit64 = (uint8_t *)((uintptr_t)limit & (~(uintptr_t)7));
63
- #else
64
- const uint8_t *limit64 = (uint8_t *)limit - 7;
65
- #endif
66
- uint64_t wanted1 = 0x0101010101010101ULL * c;
67
- for (; *buffer < limit64; *buffer += 8) {
68
- const uint64_t eq1 = ~((*((uint64_t *)*buffer)) ^ wanted1);
69
- const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
70
- const uint64_t t1 = (eq1 & 0x8080808080808080llu);
71
- if ((t0 & t1)) {
72
- break;
73
- }
74
- }
75
- #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
76
- finish:
77
- #endif
78
- while (*buffer < limit) {
79
- if (**buffer == c) {
80
- #if HTTP1_PARSER_CONVERT_EOL2NUL
81
- **buffer = 0;
82
- #endif
83
- return 1;
84
- }
85
- (*buffer)++;
86
- }
87
- return 0;
88
- }
89
-
90
- #else
91
-
92
- /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
93
- inline static uint8_t seek2ch(uint8_t **pos, uint8_t *const limit, uint8_t ch) {
94
- /* This is library based alternative that is sometimes slower */
95
- if (*pos >= limit || **pos == ch) {
96
- return 0;
97
- }
98
- uint8_t *tmp = memchr(*pos, ch, limit - (*pos));
99
- if (tmp) {
100
- *pos = tmp;
101
- #if HTTP1_PARSER_CONVERT_EOL2NUL
102
- *tmp = 0;
103
- #endif
104
- return 1;
105
- }
106
- *pos = limit;
107
- return 0;
108
- }
109
-
110
- #endif
111
-
112
- /* a helper that seeks the EOL, converts it to NUL and returns it's length */
113
- inline static uint8_t seek2eol(uint8_t **pos, uint8_t *const limit) {
114
- /* single char lookup using memchr might be better when target is far... */
115
- if (!seek2ch(pos, limit, '\n'))
116
- return 0;
117
- if ((*pos)[-1] == '\r') {
118
- #if HTTP1_PARSER_CONVERT_EOL2NUL
119
- (*pos)[-1] = 0;
120
- #endif
121
- return 2;
122
- }
123
- return 1;
124
- }
125
-
126
- /* *****************************************************************************
127
- HTTP/1.1 parsre stages
128
- ***************************************************************************** */
129
-
130
- inline static int consume_response_line(struct http1_fio_parser_args_s *args,
131
- uint8_t *start, uint8_t *end) {
132
- args->parser->state.reserved |= 128;
133
- uint8_t *tmp = start;
134
- if (!seek2ch(&tmp, end, ' '))
135
- return -1;
136
- if (args->on_http_version(args->parser, (char *)start, tmp - start))
137
- return -1;
138
- tmp = start = tmp + 1;
139
- if (!seek2ch(&tmp, end, ' '))
140
- return -1;
141
- if (args->on_status(args->parser, atol((char *)start), (char *)(tmp + 1),
142
- end - tmp))
143
- return -1;
144
- return 0;
145
- }
146
-
147
- inline static int consume_request_line(struct http1_fio_parser_args_s *args,
148
- uint8_t *start, uint8_t *end) {
149
- uint8_t *tmp = start;
150
- uint8_t *host_start = NULL;
151
- uint8_t *host_end = NULL;
152
- if (!seek2ch(&tmp, end, ' '))
153
- return -1;
154
- if (args->on_method(args->parser, (char *)start, tmp - start))
155
- return -1;
156
- tmp = start = tmp + 1;
157
- if (start[0] == 'h' && start[1] == 't' && start[2] == 't' &&
158
- start[3] == 'p') {
159
- if (start[4] == ':' && start[5] == '/' && start[6] == '/') {
160
- /* Request URI is in long form... emulate Host header instead. */
161
- tmp = host_end = host_start = (start += 7);
162
- } else if (start[4] == 's' && start[5] == ':' && start[6] == '/' &&
163
- start[7] == '/') {
164
- /* Secure request is in long form... emulate Host header instead. */
165
- tmp = host_end = host_start = (start += 8);
166
- } else
167
- goto review_path;
168
- if (!seek2ch(&tmp, end, ' '))
169
- return -1;
170
- *tmp = ' ';
171
- if (!seek2ch(&host_end, tmp, '/')) {
172
- if (args->on_path(args->parser, (char *)"/", 1))
173
- return -1;
174
- goto start_version;
175
- }
176
- host_end[0] = '/';
177
- start = host_end;
178
- }
179
- review_path:
180
- tmp = start;
181
- if (seek2ch(&tmp, end, '?')) {
182
- if (args->on_path(args->parser, (char *)start, tmp - start))
183
- return -1;
184
- tmp = start = tmp + 1;
185
- if (!seek2ch(&tmp, end, ' '))
186
- return -1;
187
- if (tmp - start > 0 &&
188
- args->on_query(args->parser, (char *)start, tmp - start))
189
- return -1;
190
- } else {
191
- tmp = start;
192
- if (!seek2ch(&tmp, end, ' '))
193
- return -1;
194
- if (args->on_path(args->parser, (char *)start, tmp - start))
195
- return -1;
196
- }
197
- start_version:
198
- start = tmp + 1;
199
- if (start + 5 >= end) /* require "HTTP/" */
200
- return -1;
201
- if (args->on_http_version(args->parser, (char *)start, end - start))
202
- return -1;
203
- /* */
204
- if (host_start && args->on_header(args->parser, (char *)"host", 4,
205
- (char *)host_start, host_end - host_start))
206
- return -1;
207
- return 0;
208
- }
209
-
210
- inline static int consume_header(struct http1_fio_parser_args_s *args,
211
- uint8_t *start, uint8_t *end) {
212
- uint8_t *end_name = start;
213
- /* divide header name from data */
214
- if (!seek2ch(&end_name, end, ':'))
215
- return -1;
216
- #if HTTP_HEADERS_LOWERCASE
217
- for (uint8_t *t = start; t < end_name; t++) {
218
- *t = tolower(*t);
219
- }
220
- #endif
221
- uint8_t *start_value = end_name + 1;
222
- if (start_value[0] == ' ') {
223
- start_value++;
224
- };
225
- #if ALLOW_UNALIGNED_MEMORY_ACCESS && HTTP_HEADERS_LOWERCASE
226
- /* enable this section to test unaligned memory access */
227
- if ((end_name - start) == 14 &&
228
- *((uint64_t *)start) == *((uint64_t *)"content-") &&
229
- *((uint64_t *)(start + 6)) == *((uint64_t *)"t-length")) {
230
- /* handle the special `content-length` header */
231
- args->parser->state.content_length = atol((char *)start_value);
232
- } else if ((end_name - start) == 17 &&
233
- *((uint64_t *)start) == *((uint64_t *)"transfer") &&
234
- *((uint64_t *)(start + 8)) == *((uint64_t *)"-encodin") &&
235
- *((uint32_t *)start_value) == *((uint32_t *)"chun") &&
236
- *((uint32_t *)(start_value + 3)) == *((uint32_t *)"nked")) {
237
- /* handle the special `transfer-encoding: chunked` header */
238
- args->parser->state.reserved |= 64;
239
- } else if ((end_name - start) == 7 &&
240
- *((uint64_t *)start) == *((uint64_t *)"trailer")) {
241
- /* chunked data with trailer... */
242
- args->parser->state.reserved |= 64;
243
- args->parser->state.reserved |= 32;
244
- }
245
- #else
246
- if ((end_name - start) == 14 &&
247
- HEADER_NAME_IS_EQ((char *)start, "content-length", 14)) {
248
- /* handle the special `content-length` header */
249
- args->parser->state.content_length = atol((char *)start_value);
250
- } else if ((end_name - start) == 17 &&
251
- HEADER_NAME_IS_EQ((char *)start, "transfer-encoding", 17) &&
252
- memcmp(start_value, "chunked", 7)) {
253
- /* handle the special `transfer-encoding: chunked` header */
254
- args->parser->state.reserved |= 64;
255
- } else if ((end_name - start) == 7 &&
256
- HEADER_NAME_IS_EQ((char *)start, "trailer", 7)) {
257
- /* chunked data with trailer... */
258
- args->parser->state.reserved |= 64;
259
- args->parser->state.reserved |= 32;
260
- }
261
- #endif
262
- /* perform callback */
263
- if (args->on_header(args->parser, (char *)start, (end_name - start),
264
- (char *)start_value, end - start_value))
265
- return -1;
266
- return 0;
267
- }
268
-
269
- /* *****************************************************************************
270
- HTTP/1.1 Body handling
271
- ***************************************************************************** */
272
-
273
- inline static int consume_body_streamed(struct http1_fio_parser_args_s *args,
274
- uint8_t **start) {
275
- uint8_t *end =
276
- *start + args->parser->state.content_length - args->parser->state.read;
277
- uint8_t *const stop = ((uint8_t *)args->buffer) + args->length;
278
- if (end > stop)
279
- end = stop;
280
- if (end > *start &&
281
- args->on_body_chunk(args->parser, (char *)(*start), end - *start))
282
- return -1;
283
- args->parser->state.read += (end - *start);
284
- *start = end;
285
- if (args->parser->state.content_length <= args->parser->state.read)
286
- args->parser->state.reserved |= 4;
287
- return 0;
288
- }
289
-
290
- inline static int consume_body_chunked(struct http1_fio_parser_args_s *args,
291
- uint8_t **start) {
292
- uint8_t *const stop = ((uint8_t *)args->buffer) + args->length;
293
- uint8_t *end = *start;
294
- while (*start < stop) {
295
- if (args->parser->state.content_length == 0) {
296
- size_t eol_len;
297
- /* consume seperator */
298
- while (*start < stop && (**start == '\n' || **start == '\r'))
299
- ++(*start);
300
- /* collect chunked length */
301
- if (!(eol_len = seek2eol(&end, stop))) {
302
- /* requires length data to continue */
303
- return 0;
304
- }
305
- /* an empty EOL is possible in mid stream processing */
306
- if (*start + eol_len > end && (*start = end) && !seek2eol(&end, stop)) {
307
- return 0;
308
- }
309
- args->parser->state.content_length = 0 - strtol((char *)*start, NULL, 16);
310
- *start = end = end + 1;
311
- if (args->parser->state.content_length == 0) {
312
- /* all chunked data was parsed */
313
- args->parser->state.content_length = args->parser->state.read;
314
- /* consume trailing EOL */
315
- if (seek2eol(start, stop))
316
- (*start)++;
317
- if (args->parser->state.reserved & 32) {
318
- /* remove the "headers complete" and "trailer" flags */
319
- args->parser->state.reserved &= 0xDD; /* 0xDD == ~2 & ~32 & 0xFF */
320
- return -2;
321
- }
322
- /* the parsing complete flag */
323
- args->parser->state.reserved |= 4;
324
- return 0;
325
- }
326
- }
327
- end = *start + (0 - args->parser->state.content_length);
328
- if (end > stop)
329
- end = stop;
330
- if (end > *start &&
331
- args->on_body_chunk(args->parser, (char *)(*start), end - *start)) {
332
- return -1;
333
- }
334
- args->parser->state.read += (end - *start);
335
- args->parser->state.content_length += (end - *start);
336
- *start = end;
337
- }
338
- return 0;
339
- }
340
-
341
- inline static int consume_body(struct http1_fio_parser_args_s *args,
342
- uint8_t **start) {
343
- if (args->parser->state.content_length > 0 &&
344
- args->parser->state.content_length > args->parser->state.read) {
345
- /* normal, streamed data */
346
- return consume_body_streamed(args, start);
347
- } else if (args->parser->state.content_length <= 0 &&
348
- (args->parser->state.reserved & 64)) {
349
- /* chuncked encoding */
350
- return consume_body_chunked(args, start);
351
- } else {
352
- /* nothing to do - parsing complete */
353
- args->parser->state.reserved |= 4;
354
- }
355
- return 0;
356
- }
357
-
358
- /* *****************************************************************************
359
- HTTP/1.1 parsre function
360
- ***************************************************************************** */
361
- #if DEBUG
362
- #include <assert.h>
363
- #else
364
- #define DEBUG 0
365
- #define assert(...)
366
- #endif
367
-
368
- size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args) {
369
- assert(args->parser && args->buffer);
370
- args->parser->state.next = NULL;
371
- uint8_t *start = args->buffer;
372
- uint8_t *end = start;
373
- uint8_t *const stop = start + args->length;
374
- uint8_t eol_len = 0;
375
- #define CONSUMED ((size_t)((uintptr_t)start - (uintptr_t)args->buffer))
376
- // fprintf(stderr, "** resuming with at %p with %.*s...(%lu)\n", args->buffer,
377
- // 4,
378
- // start, args->length);
379
- re_eval:
380
- switch ((args->parser->state.reserved & 15)) {
381
-
382
- /* request / response line */
383
- case 0:
384
- /* clear out any leadinng white space */
385
- while ((start < stop) &&
386
- (*start == '\r' || *start == '\n' || *start == ' ' || *start == 0)) {
387
- ++start;
388
- }
389
- end = start;
390
- /* make sure the whole line is available*/
391
- if (!(eol_len = seek2eol(&end, stop)))
392
- return CONSUMED;
393
-
394
- if (start[0] == 'H' && start[1] == 'T' && start[2] == 'T' &&
395
- start[3] == 'P') {
396
- /* HTTP response */
397
- if (consume_response_line(args, start, end - eol_len + 1))
398
- goto error;
399
- } else if (tolower(start[0]) >= 'a' && tolower(start[0]) <= 'z') {
400
- /* HTTP request */
401
- if (consume_request_line(args, start, end - eol_len + 1))
402
- goto error;
403
- }
404
- end = start = end + 1;
405
- args->parser->state.reserved |= 1;
406
-
407
- /* fallthrough */
408
- /* headers */
409
- case 1:
410
- do {
411
- if (start >= stop)
412
- return CONSUMED; /* buffer ended on header line */
413
- if (*start == '\r' || *start == '\n') {
414
- goto finished_headers; /* empty line, end of headers */
415
- }
416
- if (!(eol_len = seek2eol(&end, stop)))
417
- return CONSUMED;
418
- if (consume_header(args, start, end - eol_len + 1))
419
- goto error;
420
- end = start = end + 1;
421
- } while ((args->parser->state.reserved & 2) == 0);
422
- finished_headers:
423
- ++start;
424
- if (*start == '\n')
425
- ++start;
426
- end = start;
427
- args->parser->state.reserved |= 2;
428
- /* fallthrough */
429
- /* request body */
430
- case 3: { /* 2 | 1 == 3 */
431
- int t3 = consume_body(args, &start);
432
- switch (t3) {
433
- case -1:
434
- goto error;
435
- case -2:
436
- goto re_eval;
437
- }
438
- break;
439
- }
440
- }
441
- /* are we done ? */
442
- if (args->parser->state.reserved & 4) {
443
- args->parser->state.next = start;
444
- if (((args->parser->state.reserved & 128) ? args->on_response
445
- : args->on_request)(args->parser))
446
- goto error;
447
- args->parser->state =
448
- (struct http1_parser_protected_read_only_state_s){0, 0, 0};
449
- }
450
- return CONSUMED;
451
- error:
452
- args->on_error(args->parser);
453
- args->parser->state =
454
- (struct http1_parser_protected_read_only_state_s){0, 0, 0};
455
- return args->length;
456
- }
457
-
458
- #undef CONSUMED