isomorfeus-iodine 0.7.44

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.gitignore +20 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +32 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1038 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/LIMITS.md +41 -0
  11. data/README.md +782 -0
  12. data/Rakefile +44 -0
  13. data/SPEC-PubSub-Draft.md +159 -0
  14. data/SPEC-WebSocket-Draft.md +239 -0
  15. data/bin/console +22 -0
  16. data/bin/info.md +353 -0
  17. data/bin/mustache_bench.rb +100 -0
  18. data/bin/poc/Gemfile.lock +23 -0
  19. data/bin/poc/README.md +37 -0
  20. data/bin/poc/config.ru +66 -0
  21. data/bin/poc/gemfile +1 -0
  22. data/bin/poc/www/index.html +57 -0
  23. data/examples/async_task.ru +92 -0
  24. data/examples/config.ru +56 -0
  25. data/examples/echo.ru +59 -0
  26. data/examples/hello.ru +29 -0
  27. data/examples/pubsub_engine.ru +81 -0
  28. data/examples/redis.ru +70 -0
  29. data/examples/shootout.ru +73 -0
  30. data/examples/sub-protocols.ru +90 -0
  31. data/examples/tcp_client.rb +66 -0
  32. data/examples/x-sendfile.ru +14 -0
  33. data/exe/iodine +277 -0
  34. data/ext/iodine/extconf.rb +109 -0
  35. data/ext/iodine/fio.c +11985 -0
  36. data/ext/iodine/fio.h +6373 -0
  37. data/ext/iodine/fio_cli.c +431 -0
  38. data/ext/iodine/fio_cli.h +189 -0
  39. data/ext/iodine/fio_json_parser.h +687 -0
  40. data/ext/iodine/fio_siphash.c +157 -0
  41. data/ext/iodine/fio_siphash.h +37 -0
  42. data/ext/iodine/fio_tls.h +129 -0
  43. data/ext/iodine/fio_tls_missing.c +649 -0
  44. data/ext/iodine/fio_tls_openssl.c +1056 -0
  45. data/ext/iodine/fio_tmpfile.h +50 -0
  46. data/ext/iodine/fiobj.h +44 -0
  47. data/ext/iodine/fiobj4fio.h +21 -0
  48. data/ext/iodine/fiobj_ary.c +333 -0
  49. data/ext/iodine/fiobj_ary.h +139 -0
  50. data/ext/iodine/fiobj_data.c +1185 -0
  51. data/ext/iodine/fiobj_data.h +167 -0
  52. data/ext/iodine/fiobj_hash.c +409 -0
  53. data/ext/iodine/fiobj_hash.h +176 -0
  54. data/ext/iodine/fiobj_json.c +622 -0
  55. data/ext/iodine/fiobj_json.h +68 -0
  56. data/ext/iodine/fiobj_mem.h +71 -0
  57. data/ext/iodine/fiobj_mustache.c +317 -0
  58. data/ext/iodine/fiobj_mustache.h +62 -0
  59. data/ext/iodine/fiobj_numbers.c +344 -0
  60. data/ext/iodine/fiobj_numbers.h +127 -0
  61. data/ext/iodine/fiobj_str.c +433 -0
  62. data/ext/iodine/fiobj_str.h +172 -0
  63. data/ext/iodine/fiobject.c +620 -0
  64. data/ext/iodine/fiobject.h +654 -0
  65. data/ext/iodine/hpack.h +1923 -0
  66. data/ext/iodine/http.c +2754 -0
  67. data/ext/iodine/http.h +1002 -0
  68. data/ext/iodine/http1.c +912 -0
  69. data/ext/iodine/http1.h +29 -0
  70. data/ext/iodine/http1_parser.h +873 -0
  71. data/ext/iodine/http_internal.c +1278 -0
  72. data/ext/iodine/http_internal.h +237 -0
  73. data/ext/iodine/http_mime_parser.h +350 -0
  74. data/ext/iodine/iodine.c +1430 -0
  75. data/ext/iodine/iodine.h +63 -0
  76. data/ext/iodine/iodine_caller.c +218 -0
  77. data/ext/iodine/iodine_caller.h +27 -0
  78. data/ext/iodine/iodine_connection.c +933 -0
  79. data/ext/iodine/iodine_connection.h +55 -0
  80. data/ext/iodine/iodine_defer.c +420 -0
  81. data/ext/iodine/iodine_defer.h +6 -0
  82. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  83. data/ext/iodine/iodine_helpers.c +282 -0
  84. data/ext/iodine/iodine_helpers.h +12 -0
  85. data/ext/iodine/iodine_http.c +1171 -0
  86. data/ext/iodine/iodine_http.h +23 -0
  87. data/ext/iodine/iodine_json.c +302 -0
  88. data/ext/iodine/iodine_json.h +6 -0
  89. data/ext/iodine/iodine_mustache.c +567 -0
  90. data/ext/iodine/iodine_mustache.h +6 -0
  91. data/ext/iodine/iodine_pubsub.c +580 -0
  92. data/ext/iodine/iodine_pubsub.h +26 -0
  93. data/ext/iodine/iodine_rack_io.c +281 -0
  94. data/ext/iodine/iodine_rack_io.h +20 -0
  95. data/ext/iodine/iodine_store.c +142 -0
  96. data/ext/iodine/iodine_store.h +20 -0
  97. data/ext/iodine/iodine_tcp.c +346 -0
  98. data/ext/iodine/iodine_tcp.h +13 -0
  99. data/ext/iodine/iodine_tls.c +261 -0
  100. data/ext/iodine/iodine_tls.h +13 -0
  101. data/ext/iodine/mustache_parser.h +1546 -0
  102. data/ext/iodine/redis_engine.c +957 -0
  103. data/ext/iodine/redis_engine.h +79 -0
  104. data/ext/iodine/resp_parser.h +317 -0
  105. data/ext/iodine/websocket_parser.h +505 -0
  106. data/ext/iodine/websockets.c +735 -0
  107. data/ext/iodine/websockets.h +185 -0
  108. data/isomorfeus-iodine.gemspec +42 -0
  109. data/lib/iodine/connection.rb +61 -0
  110. data/lib/iodine/json.rb +42 -0
  111. data/lib/iodine/mustache.rb +113 -0
  112. data/lib/iodine/pubsub.rb +55 -0
  113. data/lib/iodine/rack_utils.rb +43 -0
  114. data/lib/iodine/tls.rb +16 -0
  115. data/lib/iodine/version.rb +3 -0
  116. data/lib/iodine.rb +274 -0
  117. data/lib/rack/handler/iodine.rb +33 -0
  118. data/logo.png +0 -0
  119. metadata +271 -0
@@ -0,0 +1,237 @@
1
+ /*
2
+ Copyright: Boaz Segev, 2016-2019
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef H_HTTP_INTERNAL_H
8
+ #define H_HTTP_INTERNAL_H
9
+
10
+ #include <fio.h>
11
+ /* subscription lists have a long lifetime */
12
+ #define FIO_FORCE_MALLOC_TMP 1
13
+ #define FIO_INCLUDE_LINKED_LIST
14
+ #include <fio.h>
15
+
16
+ #include <http.h>
17
+
18
+ #ifndef __MINGW32__
19
+ #include <arpa/inet.h>
20
+ #endif
21
+ #include <errno.h>
22
+
23
+ /* *****************************************************************************
24
+ Types
25
+ ***************************************************************************** */
26
+
27
+ typedef struct http_fio_protocol_s http_fio_protocol_s;
28
+ typedef struct http_vtable_s http_vtable_s;
29
+
30
+ struct http_vtable_s {
31
+ /** Should send existing headers and data */
32
+ int (*const http_send_body)(http_s *h, void *data, uintptr_t length);
33
+ /** Should send existing headers and file */
34
+ int (*const http_sendfile)(http_s *h, int fd, uintptr_t length,
35
+ uintptr_t offset);
36
+ /** Should send existing headers and data and prepare for streaming */
37
+ int (*const http_stream)(http_s *h, void *data, uintptr_t length);
38
+ /** Should send existing headers or complete streaming */
39
+ void (*const http_finish)(http_s *h);
40
+ /** Push for data. */
41
+ int (*const http_push_data)(http_s *h, void *data, uintptr_t length,
42
+ FIOBJ mime_type);
43
+ /** Upgrades a connection to Websockets. */
44
+ int (*const http2websocket)(http_s *h, websocket_settings_s *arg);
45
+ /** Push for files. */
46
+ int (*const http_push_file)(http_s *h, FIOBJ filename, FIOBJ mime_type);
47
+ /** Pauses the request / response handling. */
48
+ void (*http_on_pause)(http_s *, http_fio_protocol_s *);
49
+
50
+ /** Resumes a request / response handling. */
51
+ void (*http_on_resume)(http_s *, http_fio_protocol_s *);
52
+ /** hijacks the socket aaway from the protocol. */
53
+ intptr_t (*http_hijack)(http_s *h, fio_str_info_s *leftover);
54
+
55
+ /** Upgrades an HTTP connection to an EventSource (SSE) connection. */
56
+ int (*http_upgrade2sse)(http_s *h, http_sse_s *sse);
57
+ /** Writes data to an EventSource (SSE) connection. MUST free the FIOBJ. */
58
+ int (*http_sse_write)(http_sse_s *sse, FIOBJ str);
59
+ /** Closes an EventSource (SSE) connection. */
60
+ int (*http_sse_close)(http_sse_s *sse);
61
+ };
62
+
63
+ struct http_fio_protocol_s {
64
+ fio_protocol_s protocol; /* facil.io protocol */
65
+ intptr_t uuid; /* socket uuid */
66
+ http_settings_s *settings; /* pointer to HTTP settings */
67
+ };
68
+
69
+ #define http2protocol(h) ((http_fio_protocol_s *)h->private_data.flag)
70
+
71
+ /* *****************************************************************************
72
+ Constants that shouldn't be accessed by the users (`fiobj_dup` required).
73
+ ***************************************************************************** */
74
+
75
+ extern FIOBJ HTTP_HEADER_ACCEPT_RANGES;
76
+ extern FIOBJ HTTP_HEADER_WS_SEC_CLIENT_KEY;
77
+ extern FIOBJ HTTP_HEADER_WS_SEC_KEY;
78
+ extern FIOBJ HTTP_HVALUE_BYTES;
79
+ extern FIOBJ HTTP_HVALUE_CLOSE;
80
+ extern FIOBJ HTTP_HVALUE_CONTENT_TYPE_DEFAULT;
81
+ extern FIOBJ HTTP_HVALUE_GZIP;
82
+ extern FIOBJ HTTP_HVALUE_KEEP_ALIVE;
83
+ extern FIOBJ HTTP_HVALUE_MAX_AGE;
84
+ extern FIOBJ HTTP_HVALUE_NO_CACHE;
85
+ extern FIOBJ HTTP_HVALUE_SSE_MIME;
86
+ extern FIOBJ HTTP_HVALUE_WEBSOCKET;
87
+ extern FIOBJ HTTP_HVALUE_WS_SEC_VERSION;
88
+ extern FIOBJ HTTP_HVALUE_WS_UPGRADE;
89
+ extern FIOBJ HTTP_HVALUE_WS_VERSION;
90
+
91
+ /* *****************************************************************************
92
+ HTTP request/response object management
93
+ ***************************************************************************** */
94
+
95
+ static inline void http_s_new(http_s *h, http_fio_protocol_s *owner,
96
+ http_vtable_s *vtbl) {
97
+ *h = (http_s){
98
+ .private_data =
99
+ {
100
+ .vtbl = vtbl,
101
+ .flag = (uintptr_t)owner,
102
+ .out_headers = fiobj_hash_new(),
103
+ },
104
+ .headers = fiobj_hash_new(),
105
+ .received_at = fio_last_tick(),
106
+ .status = 200,
107
+ };
108
+ }
109
+
110
+ static inline void http_s_destroy(http_s *h, uint8_t log) {
111
+ if (log && h->status && !h->status_str) {
112
+ http_write_log(h);
113
+ }
114
+ fiobj_free(h->method);
115
+ fiobj_free(h->status_str);
116
+ fiobj_free(h->private_data.out_headers);
117
+ fiobj_free(h->headers);
118
+ fiobj_free(h->version);
119
+ fiobj_free(h->query);
120
+ fiobj_free(h->path);
121
+ fiobj_free(h->cookies);
122
+ fiobj_free(h->body);
123
+ fiobj_free(h->params);
124
+
125
+ *h = (http_s){
126
+ .private_data.vtbl = h->private_data.vtbl,
127
+ .private_data.flag = h->private_data.flag,
128
+ };
129
+ }
130
+
131
+ static inline void http_s_clear(http_s *h, uint8_t log) {
132
+ http_s_destroy(h, log);
133
+ http_s_new(h, (http_fio_protocol_s *)h->private_data.flag,
134
+ h->private_data.vtbl);
135
+ }
136
+
137
+ /** tests handle validity */
138
+ #define HTTP_INVALID_HANDLE(h) \
139
+ (!(h) || (!(h)->method && !(h)->status_str && (h)->status))
140
+
141
+ /* *****************************************************************************
142
+ Request / Response Handlers
143
+ ***************************************************************************** */
144
+
145
+ /** Use this function to handle HTTP requests.*/
146
+ void http_on_request_handler______internal(http_s *h,
147
+ http_settings_s *settings);
148
+
149
+ void http_on_response_handler______internal(http_s *h,
150
+ http_settings_s *settings);
151
+ int http_send_error2(size_t error, intptr_t uuid, http_settings_s *settings);
152
+
153
+ /* *****************************************************************************
154
+ EventSource Support (SSE)
155
+ ***************************************************************************** */
156
+
157
+ typedef struct http_sse_internal_s {
158
+ http_sse_s sse; /* the user SSE settings */
159
+ intptr_t uuid; /* the socket's uuid */
160
+ http_vtable_s *vtable; /* the protocol's vtable */
161
+ uintptr_t id; /* the SSE identifier */
162
+ fio_ls_s subscriptions; /* Subscription List */
163
+ fio_lock_i lock; /* Subscription List lock */
164
+ size_t ref; /* reference count */
165
+ } http_sse_internal_s;
166
+
167
+ static inline void http_sse_init(http_sse_internal_s *sse, intptr_t uuid,
168
+ http_vtable_s *vtbl, http_sse_s *args) {
169
+ *sse = (http_sse_internal_s){
170
+ .sse = *args,
171
+ .uuid = uuid,
172
+ .subscriptions = FIO_LS_INIT(sse->subscriptions),
173
+ .vtable = vtbl,
174
+ .ref = 1,
175
+ };
176
+ }
177
+
178
+ static inline void http_sse_try_free(http_sse_internal_s *sse) {
179
+ if (fio_atomic_sub(&sse->ref, 1))
180
+ return;
181
+ fio_free(sse);
182
+ }
183
+
184
+ static inline void http_sse_destroy(http_sse_internal_s *sse) {
185
+ while (fio_ls_any(&sse->subscriptions)) {
186
+ void *sub = fio_ls_pop(&sse->subscriptions);
187
+ fio_unsubscribe(sub);
188
+ }
189
+ if (sse->sse.on_close)
190
+ sse->sse.on_close(&sse->sse);
191
+ sse->uuid = -1;
192
+ http_sse_try_free(sse);
193
+ }
194
+
195
+ /* *****************************************************************************
196
+ Helpers
197
+ ***************************************************************************** */
198
+
199
+ /** sets an outgoing header only if it doesn't exist */
200
+ static inline void set_header_if_missing(FIOBJ hash, FIOBJ name, FIOBJ value) {
201
+ FIOBJ old = fiobj_hash_replace(hash, name, value);
202
+ if (!old)
203
+ return;
204
+ fiobj_hash_replace(hash, name, old);
205
+ fiobj_free(value);
206
+ }
207
+
208
+ /** sets an outgoing header, collecting duplicates in an Array (i.e. cookies)
209
+ */
210
+ static inline void set_header_add(FIOBJ hash, FIOBJ name, FIOBJ value) {
211
+ FIOBJ old = fiobj_hash_replace(hash, name, value);
212
+ if (!old)
213
+ return;
214
+ if (!value) {
215
+ fiobj_free(old);
216
+ return;
217
+ }
218
+ if (!FIOBJ_TYPE_IS(old, FIOBJ_T_ARRAY)) {
219
+ FIOBJ tmp = fiobj_ary_new();
220
+ fiobj_ary_push(tmp, old);
221
+ old = tmp;
222
+ }
223
+ if (FIOBJ_TYPE_IS(value, FIOBJ_T_ARRAY)) {
224
+ for (size_t i = 0; i < fiobj_ary_count(value); ++i) {
225
+ fiobj_ary_push(old, fiobj_dup(fiobj_ary_index(value, i)));
226
+ }
227
+ /* frees `value` */
228
+ fiobj_hash_set(hash, name, old);
229
+ return;
230
+ }
231
+ /* value will be owned by both hash and array */
232
+ fiobj_ary_push(old, value);
233
+ /* don't free `value` (leave in array) */
234
+ fiobj_hash_replace(hash, name, old);
235
+ }
236
+
237
+ #endif /* H_HTTP_INTERNAL_H */
@@ -0,0 +1,350 @@
1
+ /*
2
+ Copyright: Boaz Segev, 2018-2019
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef H_HTTP_MIME_PARSER_H
8
+ #define H_HTTP_MIME_PARSER_H
9
+ #include <stdint.h>
10
+ #include <stdlib.h>
11
+ #include <string.h>
12
+
13
+ /* *****************************************************************************
14
+ Known Limitations:
15
+
16
+ - Doesn't support nested multipart form structures (i.e., multi-file selection).
17
+ See: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
18
+
19
+ To circumvent limitation, initialize a new parser to parse nested multiparts.
20
+ ***************************************************************************** */
21
+
22
+ /* *****************************************************************************
23
+ The HTTP MIME Multipart Form Parser Type
24
+ ***************************************************************************** */
25
+
26
+ /** all data id read-only / for internal use */
27
+ typedef struct {
28
+ char *boundary;
29
+ size_t boundary_len;
30
+ uint8_t in_obj;
31
+ uint8_t done;
32
+ uint8_t error;
33
+ } http_mime_parser_s;
34
+
35
+ /* *****************************************************************************
36
+ Callbacks to be implemented.
37
+ ***************************************************************************** */
38
+
39
+ /** Called when all the data is available at once. */
40
+ static void http_mime_parser_on_data(http_mime_parser_s *parser, void *name,
41
+ size_t name_len, void *filename,
42
+ size_t filename_len, void *mimetype,
43
+ size_t mimetype_len, void *value,
44
+ size_t value_len);
45
+
46
+ /** Called when the data didn't fit in the buffer. Data will be streamed. */
47
+ static void http_mime_parser_on_partial_start(
48
+ http_mime_parser_s *parser, void *name, size_t name_len, void *filename,
49
+ size_t filename_len, void *mimetype, size_t mimetype_len);
50
+
51
+ /** Called when partial data is available. */
52
+ static void http_mime_parser_on_partial_data(http_mime_parser_s *parser,
53
+ void *value, size_t value_len);
54
+
55
+ /** Called when the partial data is complete. */
56
+ static void http_mime_parser_on_partial_end(http_mime_parser_s *parser);
57
+
58
+ /**
59
+ * Called when URL decoding is required.
60
+ *
61
+ * Should support inplace decoding (`dest == encoded`).
62
+ *
63
+ * Should return the length of the decoded string.
64
+ */
65
+ static size_t http_mime_decode_url(char *dest, const char *encoded,
66
+ size_t length);
67
+
68
+ /* *****************************************************************************
69
+ API
70
+ ***************************************************************************** */
71
+
72
+ /**
73
+ * Takes the HTTP Content-Type header and initializes the parser data.
74
+ *
75
+ * Note: the Content-Type header should persist in memory while the parser is in
76
+ * use.
77
+ */
78
+ static int http_mime_parser_init(http_mime_parser_s *parser, char *content_type,
79
+ size_t len);
80
+
81
+ /**
82
+ * Consumes data from a streaming buffer.
83
+ *
84
+ * The data might be partially consumed, in which case the unconsumed data
85
+ * should be resent to the parser as more data becomes available.
86
+ *
87
+ * Note: test the `parser->done` and `parser->error` flags between iterations.
88
+ */
89
+ static size_t http_mime_parse(http_mime_parser_s *parser, void *buffer,
90
+ size_t length);
91
+
92
+ /* *****************************************************************************
93
+ Implementations
94
+ ***************************************************************************** */
95
+
96
+ /** takes the HTTP Content-Type header and initializes the parser data. */
97
+ static int http_mime_parser_init(http_mime_parser_s *parser, char *content_type,
98
+ size_t len) {
99
+ *parser = (http_mime_parser_s){.done = 0};
100
+ if (len < 14 || strncasecmp("multipart/form", content_type, 14))
101
+ return -1;
102
+ char *cut = memchr(content_type, ';', len);
103
+ while (cut) {
104
+ ++cut;
105
+ len -= (size_t)(cut - content_type);
106
+ while (len && cut[0] == ' ') {
107
+ --len;
108
+ ++cut;
109
+ }
110
+ if (len <= 9)
111
+ return -1;
112
+ if (strncasecmp("boundary=", cut, 9)) {
113
+ content_type = cut;
114
+ cut = memchr(cut, ';', len);
115
+ continue;
116
+ }
117
+ cut += 9;
118
+ len -= 9;
119
+ content_type = cut;
120
+ parser->boundary = content_type;
121
+ if ((cut = memchr(content_type, ';', len)))
122
+ parser->boundary_len = (size_t)(cut - content_type);
123
+ else
124
+ parser->boundary_len = len;
125
+ return 0;
126
+ }
127
+ return -1;
128
+ }
129
+
130
+ /**
131
+ * Consumes data from a streaming buffer.
132
+ *
133
+ * The data might be partially consumed, in which case the unconsumed data
134
+ * should be resent to the parser as more data becomes available.
135
+ *
136
+ * Note: test the `parser->done` and `parser->error` flags between iterations.
137
+ */
138
+ static size_t http_mime_parse(http_mime_parser_s *parser, void *buffer,
139
+ size_t length) {
140
+ int first_run = 1;
141
+ char *pos = buffer;
142
+ const char *stop = pos + length;
143
+ if (!length)
144
+ goto end_of_data;
145
+ consume_partial:
146
+ if (parser->in_obj) {
147
+ /* we're in an object longer than the buffer */
148
+ char *start = pos;
149
+ char *end = start;
150
+ do {
151
+ end = memchr(end, '\n', (size_t)(stop - end));
152
+ } while (end && ++end &&
153
+ (size_t)(stop - end) >= (4 + parser->boundary_len) &&
154
+ (end[0] != '-' || end[1] != '-' ||
155
+ memcmp(end + 2, parser->boundary, parser->boundary_len)));
156
+ if (!end) {
157
+ end = (char *)stop;
158
+ pos = end;
159
+ if (end - start)
160
+ http_mime_parser_on_partial_data(parser, start, (size_t)(end - start));
161
+ goto end_of_data;
162
+ } else if (end + 4 + parser->boundary_len >= stop) {
163
+ end -= 2;
164
+ if (end[0] == '\r')
165
+ --end;
166
+ pos = end;
167
+ if (end - start)
168
+ http_mime_parser_on_partial_data(parser, start, (size_t)(end - start));
169
+ goto end_of_data;
170
+ }
171
+ size_t len = (end - start) - 1;
172
+ if (start[len - 1] == '\r')
173
+ --len;
174
+ if (len)
175
+ http_mime_parser_on_partial_data(parser, start, len);
176
+ http_mime_parser_on_partial_end(parser);
177
+ pos = end;
178
+ parser->in_obj = 0;
179
+ first_run = 0;
180
+ } else if (length < (4 + parser->boundary_len) || pos[0] != '-' ||
181
+ pos[1] != '-' ||
182
+ memcmp(pos + 2, parser->boundary, parser->boundary_len))
183
+ goto error;
184
+ /* We're at a boundary */
185
+ while (pos < stop) {
186
+ char *start;
187
+ char *end;
188
+ char *name = NULL;
189
+ uint32_t name_len = 0;
190
+ char *value = NULL;
191
+ uint32_t value_len = 0;
192
+ char *filename = NULL;
193
+ uint32_t filename_len = 0;
194
+ char *mime = NULL;
195
+ uint32_t mime_len = 0;
196
+ uint8_t header_count = 0;
197
+ /* test for ending */
198
+ if (pos[2 + parser->boundary_len] == '-' &&
199
+ pos[3 + parser->boundary_len] == '-') {
200
+ pos += 5 + parser->boundary_len;
201
+ if (pos > stop)
202
+ pos = (char *)stop;
203
+ else if (pos < stop && pos[0] == '\n')
204
+ ++pos;
205
+ goto done;
206
+ }
207
+ start = pos + 3 + parser->boundary_len;
208
+ if (start[0] == '\n') {
209
+ /* should be true, unless new line marker was just '\n' */
210
+ ++start;
211
+ }
212
+ /* consume headers */
213
+ while (start + 4 < stop && start[0] != '\n' && start[1] != '\n') {
214
+ end = memchr(start, '\n', (size_t)(stop - start));
215
+ if (!end) {
216
+ if (first_run)
217
+ goto error;
218
+ goto end_of_data;
219
+ }
220
+ if (end - start > 29 && !strncasecmp(start, "content-disposition:", 20)) {
221
+ /* content-disposition header */
222
+ start = memchr(start + 20, ';', end - (start + 20));
223
+ // if (!start)
224
+ // start = end + 1;
225
+ while (start) {
226
+ ++start;
227
+ if (start[0] == ' ')
228
+ ++start;
229
+ if (start + 6 < end && !strncasecmp(start, "name=", 5)) {
230
+ name = start + 5;
231
+ if (name[0] == '"')
232
+ ++name;
233
+ start = memchr(name, ';', (size_t)(end - start));
234
+ if (!start) {
235
+ name_len = (size_t)(end - name);
236
+ if (name[name_len - 1] == '\r')
237
+ --name_len;
238
+ } else {
239
+ name_len = (size_t)(start - name);
240
+ }
241
+ if (name[name_len - 1] == '"')
242
+ --name_len;
243
+ } else if (start + 9 < end && !strncasecmp(start, "filename", 8)) {
244
+ uint8_t encoded = 0;
245
+ start += 8;
246
+ if (start[0] == '*') {
247
+ encoded = 1;
248
+ ++start;
249
+ }
250
+ if (start[0] != '=')
251
+ goto error;
252
+ ++start;
253
+ if (start[0] == ' ')
254
+ ++start;
255
+ if (start[0] == '"')
256
+ ++start;
257
+ if (filename && !encoded) {
258
+ /* prefer URL encoded version */
259
+ start = memchr(filename, ';', (size_t)(end - start));
260
+ continue;
261
+ }
262
+ filename = start;
263
+ start = memchr(filename, ';', (size_t)(end - start));
264
+ if (!start) {
265
+ filename_len = (size_t)((end - filename));
266
+ if (filename[filename_len - 1] == '\r') {
267
+ --filename_len;
268
+ }
269
+ } else {
270
+ filename_len = (size_t)(start - filename);
271
+ }
272
+ if (filename[filename_len - 1] == '"')
273
+ --filename_len;
274
+ if (encoded) {
275
+ ssize_t new_len =
276
+ http_mime_decode_url(filename, filename, filename_len);
277
+ if (new_len > 0)
278
+ filename_len = new_len;
279
+ }
280
+ } else {
281
+ start = memchr(start, ';', (size_t)(end - start));
282
+ }
283
+ }
284
+ } else if (end - start > 14 && !strncasecmp(start, "content-type:", 13)) {
285
+ /* content-type header */
286
+ start += 13;
287
+ if (start[0] == ' ')
288
+ ++start;
289
+ mime = start;
290
+ start = memchr(start, ';', (size_t)(end - start));
291
+ if (!start) {
292
+ mime_len = (size_t)(end - mime);
293
+ if (mime[mime_len - 1] == '\r')
294
+ --mime_len;
295
+ } else {
296
+ mime_len = (size_t)(start - mime);
297
+ }
298
+ }
299
+ start = end + 1;
300
+ if (header_count++ > 4)
301
+ goto error;
302
+ }
303
+ if (!name) {
304
+ if (start + 4 >= stop)
305
+ goto end_of_data;
306
+ goto error;
307
+ }
308
+
309
+ /* advance to end of boundry */
310
+ ++start;
311
+ if (start[0] == '\n')
312
+ ++start;
313
+ value = start;
314
+ end = start;
315
+ do {
316
+ end = memchr(end, '\n', (size_t)(stop - end));
317
+ } while (end && ++end &&
318
+ (size_t)(stop - end) >= (4 + parser->boundary_len) &&
319
+ (end[0] != '-' || end[1] != '-' ||
320
+ memcmp(end + 2, parser->boundary, parser->boundary_len)));
321
+ if (!end || end + 4 + parser->boundary_len >= stop) {
322
+ if (first_run) {
323
+ http_mime_parser_on_partial_start(parser, name, name_len, filename,
324
+ filename_len, mime, mime_len);
325
+ parser->in_obj = 1;
326
+ pos = value;
327
+ goto consume_partial;
328
+ }
329
+ goto end_of_data;
330
+ }
331
+ value_len = (size_t)((end - value) - 1);
332
+ if (value[value_len - 1] == '\r')
333
+ --value_len;
334
+ pos = end;
335
+ http_mime_parser_on_data(parser, name, name_len, filename, filename_len,
336
+ mime, mime_len, value, value_len);
337
+ first_run = 0;
338
+ }
339
+ end_of_data:
340
+ return (size_t)((uintptr_t)pos - (uintptr_t)buffer);
341
+ done:
342
+ parser->done = 1;
343
+ parser->error = 0;
344
+ return (size_t)((uintptr_t)pos - (uintptr_t)buffer);
345
+ error:
346
+ parser->done = 0;
347
+ parser->error = 1;
348
+ return (size_t)((uintptr_t)pos - (uintptr_t)buffer);
349
+ }
350
+ #endif