iodine 0.4.19 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/CHANGELOG.md +22 -0
  4. data/LIMITS.md +19 -9
  5. data/README.md +92 -77
  6. data/SPEC-PubSub-Draft.md +113 -0
  7. data/SPEC-Websocket-Draft.md +127 -143
  8. data/bin/http-hello +0 -1
  9. data/bin/raw-rbhttp +1 -1
  10. data/bin/raw_broadcast +8 -10
  11. data/bin/updated api +2 -2
  12. data/bin/ws-broadcast +2 -4
  13. data/bin/ws-echo +2 -2
  14. data/examples/config.ru +13 -13
  15. data/examples/echo.ru +5 -6
  16. data/examples/hello.ru +2 -3
  17. data/examples/info.md +316 -0
  18. data/examples/pubsub_engine.ru +81 -0
  19. data/examples/redis.ru +9 -9
  20. data/examples/shootout.ru +45 -11
  21. data/ext/iodine/defer.c +194 -297
  22. data/ext/iodine/defer.h +61 -53
  23. data/ext/iodine/evio.c +0 -260
  24. data/ext/iodine/evio.h +50 -22
  25. data/ext/iodine/evio_callbacks.c +26 -0
  26. data/ext/iodine/evio_epoll.c +251 -0
  27. data/ext/iodine/evio_kqueue.c +193 -0
  28. data/ext/iodine/extconf.rb +1 -1
  29. data/ext/iodine/facil.c +1420 -542
  30. data/ext/iodine/facil.h +151 -64
  31. data/ext/iodine/fio_ary.h +418 -0
  32. data/ext/iodine/{base64.c → fio_base64.c} +33 -24
  33. data/ext/iodine/{base64.h → fio_base64.h} +6 -7
  34. data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
  35. data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
  36. data/ext/iodine/fio_hashmap.h +759 -0
  37. data/ext/iodine/fio_json_parser.h +651 -0
  38. data/ext/iodine/fio_llist.h +257 -0
  39. data/ext/iodine/fio_mem.c +672 -0
  40. data/ext/iodine/fio_mem.h +140 -0
  41. data/ext/iodine/fio_random.c +248 -0
  42. data/ext/iodine/{random.h → fio_random.h} +11 -14
  43. data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
  44. data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
  45. data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
  46. data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
  47. data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
  48. data/ext/iodine/fio_siphash.h +18 -0
  49. data/ext/iodine/fio_tmpfile.h +38 -0
  50. data/ext/iodine/fiobj.h +24 -7
  51. data/ext/iodine/fiobj4sock.h +23 -0
  52. data/ext/iodine/fiobj_ary.c +143 -226
  53. data/ext/iodine/fiobj_ary.h +17 -16
  54. data/ext/iodine/fiobj_data.c +1160 -0
  55. data/ext/iodine/fiobj_data.h +164 -0
  56. data/ext/iodine/fiobj_hash.c +298 -406
  57. data/ext/iodine/fiobj_hash.h +101 -54
  58. data/ext/iodine/fiobj_json.c +478 -601
  59. data/ext/iodine/fiobj_json.h +34 -9
  60. data/ext/iodine/fiobj_numbers.c +383 -51
  61. data/ext/iodine/fiobj_numbers.h +87 -11
  62. data/ext/iodine/fiobj_str.c +423 -184
  63. data/ext/iodine/fiobj_str.h +81 -32
  64. data/ext/iodine/fiobject.c +273 -522
  65. data/ext/iodine/fiobject.h +477 -112
  66. data/ext/iodine/http.c +2243 -83
  67. data/ext/iodine/http.h +842 -121
  68. data/ext/iodine/http1.c +810 -385
  69. data/ext/iodine/http1.h +16 -39
  70. data/ext/iodine/http1_parser.c +146 -74
  71. data/ext/iodine/http1_parser.h +15 -4
  72. data/ext/iodine/http_internal.c +1258 -0
  73. data/ext/iodine/http_internal.h +226 -0
  74. data/ext/iodine/http_mime_parser.h +341 -0
  75. data/ext/iodine/iodine.c +86 -68
  76. data/ext/iodine/iodine.h +26 -11
  77. data/ext/iodine/iodine_helpers.c +8 -7
  78. data/ext/iodine/iodine_http.c +487 -324
  79. data/ext/iodine/iodine_json.c +304 -0
  80. data/ext/iodine/iodine_json.h +6 -0
  81. data/ext/iodine/iodine_protocol.c +107 -45
  82. data/ext/iodine/iodine_pubsub.c +526 -225
  83. data/ext/iodine/iodine_pubsub.h +10 -0
  84. data/ext/iodine/iodine_websockets.c +268 -510
  85. data/ext/iodine/iodine_websockets.h +2 -4
  86. data/ext/iodine/pubsub.c +726 -432
  87. data/ext/iodine/pubsub.h +85 -103
  88. data/ext/iodine/rb-call.c +4 -4
  89. data/ext/iodine/rb-defer.c +46 -22
  90. data/ext/iodine/rb-fiobj2rb.h +117 -0
  91. data/ext/iodine/rb-rack-io.c +73 -238
  92. data/ext/iodine/rb-rack-io.h +2 -2
  93. data/ext/iodine/rb-registry.c +35 -93
  94. data/ext/iodine/rb-registry.h +1 -0
  95. data/ext/iodine/redis_engine.c +742 -304
  96. data/ext/iodine/redis_engine.h +42 -39
  97. data/ext/iodine/resp_parser.h +311 -0
  98. data/ext/iodine/sock.c +627 -490
  99. data/ext/iodine/sock.h +345 -297
  100. data/ext/iodine/spnlock.inc +15 -4
  101. data/ext/iodine/websocket_parser.h +16 -20
  102. data/ext/iodine/websockets.c +188 -257
  103. data/ext/iodine/websockets.h +24 -133
  104. data/lib/iodine.rb +52 -7
  105. data/lib/iodine/cli.rb +6 -24
  106. data/lib/iodine/json.rb +40 -0
  107. data/lib/iodine/version.rb +1 -1
  108. data/lib/iodine/websocket.rb +5 -3
  109. data/lib/rack/handler/iodine.rb +58 -13
  110. metadata +38 -48
  111. data/bin/ws-shootout +0 -107
  112. data/examples/broadcast.ru +0 -56
  113. data/ext/iodine/bscrypt-common.h +0 -116
  114. data/ext/iodine/bscrypt.h +0 -49
  115. data/ext/iodine/fio2resp.c +0 -60
  116. data/ext/iodine/fio2resp.h +0 -51
  117. data/ext/iodine/fio_dict.c +0 -446
  118. data/ext/iodine/fio_dict.h +0 -99
  119. data/ext/iodine/fio_hash_table.h +0 -370
  120. data/ext/iodine/fio_list.h +0 -111
  121. data/ext/iodine/fiobj_internal.h +0 -280
  122. data/ext/iodine/fiobj_primitives.c +0 -131
  123. data/ext/iodine/fiobj_primitives.h +0 -55
  124. data/ext/iodine/fiobj_sym.c +0 -135
  125. data/ext/iodine/fiobj_sym.h +0 -60
  126. data/ext/iodine/hex.c +0 -124
  127. data/ext/iodine/hex.h +0 -70
  128. data/ext/iodine/http1_request.c +0 -81
  129. data/ext/iodine/http1_request.h +0 -58
  130. data/ext/iodine/http1_response.c +0 -417
  131. data/ext/iodine/http1_response.h +0 -95
  132. data/ext/iodine/http_request.c +0 -111
  133. data/ext/iodine/http_request.h +0 -102
  134. data/ext/iodine/http_response.c +0 -1703
  135. data/ext/iodine/http_response.h +0 -250
  136. data/ext/iodine/misc.c +0 -182
  137. data/ext/iodine/misc.h +0 -74
  138. data/ext/iodine/random.c +0 -208
  139. data/ext/iodine/redis_connection.c +0 -278
  140. data/ext/iodine/redis_connection.h +0 -86
  141. data/ext/iodine/resp.c +0 -842
  142. data/ext/iodine/resp.h +0 -261
  143. data/ext/iodine/siphash.c +0 -154
  144. data/ext/iodine/siphash.h +0 -22
  145. data/ext/iodine/xor-crypt.c +0 -193
  146. data/ext/iodine/xor-crypt.h +0 -107
data/ext/iodine/http1.h CHANGED
@@ -1,52 +1,29 @@
1
1
  /*
2
- copyright: Boaz segev, 2016-2017
3
- license: MIT
4
-
5
- Feel free to copy, use and enjoy according to the license provided.
2
+ Copyright: Boaz Segev, 2017
3
+ License: MIT
6
4
  */
7
5
  #ifndef H_HTTP1_H
8
6
  #define H_HTTP1_H
7
+
9
8
  #include "http.h"
10
9
 
11
- #ifndef HTTP1_MAX_HEADER_SIZE
10
+ #ifndef HTTP1_READ_BUFFER
12
11
  /**
13
- Sets the maximum allowed size of a requests header section
14
- (all cookies, headers and other data that isn't the request's "body").
15
-
16
- This value includes the request line itself.
17
-
18
- Defaults to ~16Kb headers per request.
19
- */
20
- #define HTTP1_MAX_HEADER_SIZE (16 * 1024) /* ~16kb */
21
- #endif
22
-
23
- #ifndef HTTP1_MAX_HEADER_COUNT
24
- /** Sets the maximum allowed headers per request (obligatory).
25
-
26
- Defaults to 64 headers per request */
27
- #define HTTP1_MAX_HEADER_COUNT (64)
12
+ * The size of a single `read` command, it sets the limit for an HTTP/1.1
13
+ * header line.
14
+ */
15
+ #define HTTP1_READ_BUFFER (8 * 1024) /* ~8kb */
28
16
  #endif
29
17
 
30
- #ifndef HTTP1_POOL_SIZE
31
- /** Defines the pre-allocated memory for incoming concurrent connections.
18
+ /** Creates an HTTP1 protocol object and handles any unread data in the buffer
19
+ * (if any). */
20
+ protocol_s *http1_new(uintptr_t uuid, http_settings_s *settings,
21
+ void *unread_data, size_t unread_length);
32
22
 
33
- Any concurrent HTTP1 connections over this amount will be dynamically allocated.
34
- */
35
- #define HTTP1_POOL_SIZE (64) /* should be ~0.5Mb with default values*/
36
- #endif
37
-
38
- /** The HTTP/1.1 protocol identifier. */
39
- extern char *HTTP1_Protocol_String;
40
-
41
- /* *****************************************************************************
42
- HTTP listening helpers
43
- */
44
-
45
- /**
46
- Allocates memory for an upgradable HTTP/1.1 protocol.
23
+ /** Manually destroys the HTTP1 protocol object. */
24
+ void http1_destroy(protocol_s *);
47
25
 
48
- The protocol self destructs when the `on_close` callback is called.
49
- */
50
- protocol_s *http1_on_open(intptr_t fd, http_settings_s *settings);
26
+ /** returns the HTTP/1.1 protocol's VTable. */
27
+ void * http1_vtable(void);
51
28
 
52
29
  #endif
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright: Boaz segev, 2017
2
+ Copyright: Boaz Segev, 2017-2018
3
3
  License: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
@@ -13,58 +13,72 @@ Feel free to copy, use and enjoy according to the license provided.
13
13
  #include <stdio.h>
14
14
  #include <string.h>
15
15
 
16
- #define PREFER_MEMCHAR 0
17
-
18
16
  /* *****************************************************************************
19
17
  Seeking for characters in a string
20
18
  ***************************************************************************** */
21
19
 
22
- #if PREFER_MEMCHAR
23
-
24
- /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
25
- inline static uint8_t seek2ch(uint8_t **pos, uint8_t *const limit, uint8_t ch) {
26
- /* This is library based alternative that is sometimes slower */
27
- if (*pos >= limit || **pos == ch) {
28
- return 0;
29
- }
30
- uint8_t *tmp = memchr(*pos, ch, limit - (*pos));
31
- if (tmp) {
32
- *pos = tmp;
33
- *tmp = 0;
34
- return 1;
35
- }
36
- *pos = limit;
37
- return 0;
38
- }
20
+ #ifndef ALLOW_UNALIGNED_MEMORY_ACCESS
21
+ #define ALLOW_UNALIGNED_MEMORY_ACCESS 0
22
+ #endif
39
23
 
40
- #else
24
+ #if FIO_MEMCHAR
41
25
 
42
- /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
43
- static inline uint8_t seek2ch(uint8_t **buffer, const uint8_t *const limit,
44
- const uint8_t c) {
45
- /* this single char lookup is better when target is closer... */
26
+ /**
27
+ * This seems to be faster on some systems, especially for smaller distances.
28
+ *
29
+ * On newer systems, `memchr` should be faster.
30
+ */
31
+ static inline int seek2ch(uint8_t **buffer, register uint8_t *const limit,
32
+ const uint8_t c) {
46
33
  if (**buffer == c) {
34
+ #if HTTP1_PARSER_CONVERT_EOL2NUL
47
35
  **buffer = 0;
36
+ #endif
48
37
  return 1;
49
38
  }
50
39
 
51
- uint64_t wanted = 0x0101010101010101ULL * c;
52
- uint64_t *lpos = (uint64_t *)*buffer;
53
- uint64_t *llimit = ((uint64_t *)limit) - 1;
40
+ #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
41
+ /* too short for this mess */
42
+ if ((uintptr_t)limit <= 16 + ((uintptr_t)*buffer & (~(uintptr_t)7)))
43
+ goto finish;
54
44
 
55
- for (; lpos < llimit; lpos++) {
56
- const uint64_t eq = ~((*lpos) ^ wanted);
57
- const uint64_t t0 = (eq & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
58
- const uint64_t t1 = (eq & 0x8080808080808080llu);
45
+ /* align memory */
46
+ {
47
+ const uint8_t *alignment =
48
+ (uint8_t *)(((uintptr_t)(*buffer) & (~(uintptr_t)7)) + 8);
49
+ if (limit >= alignment) {
50
+ while (*buffer < alignment) {
51
+ if (**buffer == c) {
52
+ #if HTTP1_PARSER_CONVERT_EOL2NUL
53
+ **buffer = 0;
54
+ #endif
55
+ return 1;
56
+ }
57
+ *buffer += 1;
58
+ }
59
+ }
60
+ }
61
+ const uint8_t *limit64 = (uint8_t *)((uintptr_t)limit & (~(uintptr_t)7));
62
+ #else
63
+ const uint8_t *limit64 = (uint8_t *)limit - 7;
64
+ #endif
65
+ uint64_t wanted1 = 0x0101010101010101ULL * c;
66
+ for (; *buffer < limit64; *buffer += 8) {
67
+ const uint64_t eq1 = ~((*((uint64_t *)*buffer)) ^ wanted1);
68
+ const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
69
+ const uint64_t t1 = (eq1 & 0x8080808080808080llu);
59
70
  if ((t0 & t1)) {
60
71
  break;
61
72
  }
62
73
  }
63
-
64
- *buffer = (uint8_t *)lpos;
74
+ #if !defined(__x86_64__)
75
+ finish:
76
+ #endif
65
77
  while (*buffer < limit) {
66
78
  if (**buffer == c) {
79
+ #if HTTP1_PARSER_CONVERT_EOL2NUL
67
80
  **buffer = 0;
81
+ #endif
68
82
  return 1;
69
83
  }
70
84
  (*buffer)++;
@@ -72,6 +86,26 @@ static inline uint8_t seek2ch(uint8_t **buffer, const uint8_t *const limit,
72
86
  return 0;
73
87
  }
74
88
 
89
+ #else
90
+
91
+ /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
92
+ inline static uint8_t seek2ch(uint8_t **pos, uint8_t *const limit, uint8_t ch) {
93
+ /* This is library based alternative that is sometimes slower */
94
+ if (*pos >= limit || **pos == ch) {
95
+ return 0;
96
+ }
97
+ uint8_t *tmp = memchr(*pos, ch, limit - (*pos));
98
+ if (tmp) {
99
+ *pos = tmp;
100
+ #if HTTP1_PARSER_CONVERT_EOL2NUL
101
+ *tmp = 0;
102
+ #endif
103
+ return 1;
104
+ }
105
+ *pos = limit;
106
+ return 0;
107
+ }
108
+
75
109
  #endif
76
110
 
77
111
  /* a helper that seeks the EOL, converts it to NUL and returns it's length */
@@ -80,7 +114,9 @@ inline static uint8_t seek2eol(uint8_t **pos, uint8_t *const limit) {
80
114
  if (!seek2ch(pos, limit, '\n'))
81
115
  return 0;
82
116
  if ((*pos)[-1] == '\r') {
117
+ #if HTTP1_PARSER_CONVERT_EOL2NUL
83
118
  (*pos)[-1] = 0;
119
+ #endif
84
120
  return 2;
85
121
  }
86
122
  return 1;
@@ -110,11 +146,37 @@ inline static int consume_response_line(struct http1_fio_parser_args_s *args,
110
146
  inline static int consume_request_line(struct http1_fio_parser_args_s *args,
111
147
  uint8_t *start, uint8_t *end) {
112
148
  uint8_t *tmp = start;
149
+ uint8_t *host_start = NULL;
150
+ uint8_t *host_end = NULL;
113
151
  if (!seek2ch(&tmp, end, ' '))
114
152
  return -1;
115
153
  if (args->on_method(args->parser, (char *)start, tmp - start))
116
154
  return -1;
117
155
  tmp = start = tmp + 1;
156
+ if (start[0] == 'h' && start[1] == 't' && start[2] == 't' &&
157
+ start[3] == 'p') {
158
+ if (start[4] == ':' && start[5] == '/' && start[6] == '/') {
159
+ /* Request URI is in long form... emulate Host header instead. */
160
+ tmp = host_end = host_start = (start += 7);
161
+ } else if (start[4] == 's' && start[5] == ':' && start[6] == '/' &&
162
+ start[7] == '/') {
163
+ /* Secure request is in long form... emulate Host header instead. */
164
+ tmp = host_end = host_start = (start += 8);
165
+ } else
166
+ goto review_path;
167
+ if (!seek2ch(&tmp, end, ' '))
168
+ return -1;
169
+ *tmp = ' ';
170
+ if (!seek2ch(&host_end, tmp, '/')) {
171
+ if (args->on_path(args->parser, "/", 1))
172
+ return -1;
173
+ goto start_version;
174
+ }
175
+ host_end[0] = '/';
176
+ start = host_end;
177
+ }
178
+ review_path:
179
+ tmp = start;
118
180
  if (seek2ch(&tmp, end, '?')) {
119
181
  if (args->on_path(args->parser, (char *)start, tmp - start))
120
182
  return -1;
@@ -131,62 +193,65 @@ inline static int consume_request_line(struct http1_fio_parser_args_s *args,
131
193
  if (args->on_path(args->parser, (char *)start, tmp - start))
132
194
  return -1;
133
195
  }
196
+ start_version:
134
197
  start = tmp + 1;
135
- if (start + 7 >= end)
198
+ if (start + 5 >= end) /* require "HTTP/" */
136
199
  return -1;
137
200
  if (args->on_http_version(args->parser, (char *)start, end - start))
138
201
  return -1;
202
+ /* */
203
+ if (host_start && args->on_header(args->parser, "host", 4, (char *)host_start,
204
+ host_end - host_start))
205
+ return -1;
139
206
  return 0;
140
207
  }
141
208
 
142
209
  inline static int consume_header(struct http1_fio_parser_args_s *args,
143
210
  uint8_t *start, uint8_t *end) {
144
- uint8_t t2 = 1;
145
- uint8_t *tmp = start;
211
+ uint8_t *end_name = start;
146
212
  /* divide header name from data */
147
- if (!seek2ch(&tmp, end, ':'))
213
+ if (!seek2ch(&end_name, end, ':'))
148
214
  return -1;
149
215
  #if HTTP_HEADERS_LOWERCASE
150
- for (uint8_t *t3 = start; t3 < tmp; t3++) {
151
- *t3 = tolower(*t3);
216
+ for (uint8_t *t = start; t < end_name; t++) {
217
+ *t = tolower(*t);
152
218
  }
153
219
  #endif
154
-
155
- tmp++;
156
- if (tmp[0] == ' ') {
157
- tmp++;
158
- t2++;
220
+ uint8_t *start_value = end_name + 1;
221
+ if (start_value[0] == ' ') {
222
+ start_value++;
159
223
  };
160
- #if HTTP_HEADERS_LOWERCASE
161
- if ((tmp - start) - t2 == 14 &&
224
+ #if ALLOW_UNALIGNED_MEMORY_ACCESS && HTTP_HEADERS_LOWERCASE
225
+ /* enable this section to test unaligned memory access */
226
+ if ((end_name - start) == 14 &&
162
227
  *((uint64_t *)start) == *((uint64_t *)"content-") &&
163
228
  *((uint64_t *)(start + 6)) == *((uint64_t *)"t-length")) {
164
229
  /* handle the special `content-length` header */
165
- args->parser->state.content_length = atol((char *)tmp);
166
- } else if ((tmp - start) - t2 == 17 &&
230
+ args->parser->state.content_length = atol((char *)start_value);
231
+ } else if ((end_name - start) == 17 &&
167
232
  *((uint64_t *)start) == *((uint64_t *)"transfer") &&
168
233
  *((uint64_t *)(start + 8)) == *((uint64_t *)"-encodin") &&
169
- *((uint32_t *)tmp) == *((uint32_t *)"chun") &&
170
- *((uint32_t *)(tmp + 3)) == *((uint32_t *)"nked")) {
234
+ *((uint32_t *)start_value) == *((uint32_t *)"chun") &&
235
+ *((uint32_t *)(start_value + 3)) == *((uint32_t *)"nked")) {
171
236
  /* handle the special `transfer-encoding: chunked` header */
172
237
  args->parser->state.reserved |= 64;
173
- } else if ((tmp - start) - t2 == 7 &&
238
+ } else if ((end_name - start) == 7 &&
174
239
  *((uint64_t *)start) == *((uint64_t *)"trailer")) {
175
240
  /* chunked data with trailer... */
176
241
  args->parser->state.reserved |= 64;
177
242
  args->parser->state.reserved |= 32;
178
243
  }
179
244
  #else
180
- if ((tmp - start) - t2 == 14 &&
245
+ if ((end_name - start) == 14 &&
181
246
  HEADER_NAME_IS_EQ((char *)start, "content-length", 14)) {
182
247
  /* handle the special `content-length` header */
183
- args->parser->state.content_length = atol((char *)tmp);
184
- } else if ((tmp - start) - t2 == 17 &&
248
+ args->parser->state.content_length = atol((char *)start_value);
249
+ } else if ((end_name - start) == 17 &&
185
250
  HEADER_NAME_IS_EQ((char *)start, "transfer-encoding", 17) &&
186
- memcmp(tmp, "chunked", 7)) {
251
+ memcmp(start_value, "chunked", 7)) {
187
252
  /* handle the special `transfer-encoding: chunked` header */
188
253
  args->parser->state.reserved |= 64;
189
- } else if ((tmp - start) - t2 == 7 &&
254
+ } else if ((end_name - start) == 7 &&
190
255
  HEADER_NAME_IS_EQ((char *)start, "trailer", 7)) {
191
256
  /* chunked data with trailer... */
192
257
  args->parser->state.reserved |= 64;
@@ -194,8 +259,8 @@ inline static int consume_header(struct http1_fio_parser_args_s *args,
194
259
  }
195
260
  #endif
196
261
  /* perform callback */
197
- if (args->on_header(args->parser, (char *)start, (tmp - start) - t2,
198
- (char *)tmp, end - tmp))
262
+ if (args->on_header(args->parser, (char *)start, (end_name - start),
263
+ (char *)start_value, end - start_value))
199
264
  return -1;
200
265
  return 0;
201
266
  }
@@ -228,14 +293,16 @@ inline static int consume_body_chunked(struct http1_fio_parser_args_s *args,
228
293
  while (*start < stop) {
229
294
  if (args->parser->state.content_length == 0) {
230
295
  size_t eol_len;
296
+ /* consume seperator */
297
+ while (*start < stop && (**start == '\n' || **start == '\r'))
298
+ ++(*start);
231
299
  /* collect chunked length */
232
300
  if (!(eol_len = seek2eol(&end, stop))) {
233
301
  /* requires length data to continue */
234
302
  return 0;
235
303
  }
236
304
  /* an empty EOL is possible in mid stream processing */
237
- if (*start + eol_len - 1 >= end && (*start = end) &&
238
- !seek2eol(&end, stop)) {
305
+ if (*start + eol_len > end && (*start = end) && !seek2eol(&end, stop)) {
239
306
  return 0;
240
307
  }
241
308
  args->parser->state.content_length = 0 - strtol((char *)*start, NULL, 16);
@@ -266,8 +333,6 @@ inline static int consume_body_chunked(struct http1_fio_parser_args_s *args,
266
333
  args->parser->state.read += (end - *start);
267
334
  args->parser->state.content_length += (end - *start);
268
335
  *start = end;
269
- if (args->parser->state.content_length == 0 && seek2eol(start, stop))
270
- (*start)++;
271
336
  }
272
337
  return 0;
273
338
  }
@@ -300,9 +365,8 @@ HTTP/1.1 parsre function
300
365
  #endif
301
366
 
302
367
  size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args) {
303
- if (DEBUG) {
304
- assert(args->parser && args->buffer);
305
- }
368
+ assert(args->parser && args->buffer);
369
+ args->parser->state.next = NULL;
306
370
  uint8_t *start = args->buffer;
307
371
  uint8_t *end = start;
308
372
  uint8_t *const stop = start + args->length;
@@ -325,7 +389,8 @@ re_eval:
325
389
  if (!(eol_len = seek2eol(&end, stop)))
326
390
  return CONSUMED;
327
391
 
328
- if (((uint32_t *)start)[0] == ((uint32_t *)"HTTP")[0]) {
392
+ if (start[0] == 'H' && start[1] == 'T' && start[2] == 'T' &&
393
+ start[3] == 'P') {
329
394
  /* HTTP response */
330
395
  if (consume_response_line(args, start, end - eol_len + 1))
331
396
  goto error;
@@ -341,32 +406,39 @@ re_eval:
341
406
  /* headers */
342
407
  case 1:
343
408
  do {
409
+ if (start + 1 >= stop)
410
+ return CONSUMED; /* buffer ended on header line */
411
+ if (*start == '\r' || *start == '\n') {
412
+ goto finished_headers; /* empty line, end of headers */
413
+ }
344
414
  if (!(eol_len = seek2eol(&end, stop)))
345
415
  return CONSUMED;
346
- /* test for header ending */
347
- if (*start == 0)
348
- goto finished_headers; /* break the do..while loop, not the switch
349
- statement */
350
416
  if (consume_header(args, start, end - eol_len + 1))
351
417
  goto error;
352
418
  end = start = end + 1;
353
419
  } while ((args->parser->state.reserved & 2) == 0);
354
420
  finished_headers:
355
- end = start = end + 1;
421
+ ++start;
422
+ if (*start == '\n')
423
+ ++start;
424
+ end = start;
356
425
  args->parser->state.reserved |= 2;
357
426
  /* fallthrough */
358
427
  /* request body */
359
428
  case 3: { /* 2 | 1 == 3 */
360
429
  int t3 = consume_body(args, &start);
361
- if (t3 == -1)
430
+ switch (t3) {
431
+ case -1:
362
432
  goto error;
363
- if (t3 == -2)
433
+ case -2:
364
434
  goto re_eval;
435
+ }
365
436
  break;
366
437
  }
367
438
  }
368
439
  /* are we done ? */
369
440
  if (args->parser->state.reserved & 4) {
441
+ args->parser->state.next = start;
370
442
  if (((args->parser->state.reserved & 128) ? args->on_response
371
443
  : args->on_request)(args->parser))
372
444
  goto error;
@@ -1,6 +1,6 @@
1
1
  #ifndef H_HTTP1_PARSER_H
2
2
  /*
3
- Copyright: Boaz segev, 2017
3
+ Copyright: Boaz Segev, 2017-2018
4
4
  License: MIT
5
5
 
6
6
  Feel free to copy, use and enjoy according to the license provided.
@@ -21,11 +21,19 @@ to maintain and that could be used for an HTTP/1.x client as well.
21
21
  #include <sys/types.h>
22
22
 
23
23
  #ifndef HTTP_HEADERS_LOWERCASE
24
- /** when defined, HTTP headers will be converted to lowercase and header
25
- * searches will be case sensitive. */
24
+ /**
25
+ * when defined, HTTP headers will be converted to lowercase and header
26
+ * searches will be case sensitive.
27
+ *
28
+ * this is required by facil.io and helps with HTTP/2 compatibility.
29
+ */
26
30
  #define HTTP_HEADERS_LOWERCASE 1
27
31
  #endif
28
32
 
33
+ #ifndef HTTP1_PARSER_CONVERT_EOL2NUL
34
+ #define HTTP1_PARSER_CONVERT_EOL2NUL 0
35
+ #endif
36
+
29
37
  #if HTTP_HEADERS_LOWERCASE
30
38
 
31
39
  #define HEADER_NAME_IS_EQ(var_name, const_name, len) \
@@ -41,7 +49,8 @@ typedef struct http1_parser_s {
41
49
  struct http1_parser_protected_read_only_state_s {
42
50
  ssize_t content_length; /* negative values indicate chuncked data state */
43
51
  ssize_t read; /* total number of bytes read so far (body only) */
44
- uint8_t reserved; /* for internal use */
52
+ uint8_t *next; /* the known position for the end of request/response */
53
+ uint8_t reserved; /* for internal use */
45
54
  } state;
46
55
  } http1_parser_s;
47
56
 
@@ -104,6 +113,8 @@ size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args);
104
113
 
105
114
  static inline __attribute__((unused)) size_t
106
115
  http1_fio_parser(struct http1_fio_parser_args_s args) {
116
+ if (!args.length)
117
+ return 0;
107
118
  return http1_fio_parser_fn(&args);
108
119
  }
109
120
  #if __STDC_VERSION__ >= 199901L