rage-iodine 1.7.58

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.github/workflows/ruby.yml +42 -0
  4. data/.gitignore +20 -0
  5. data/.rspec +2 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1098 -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 +23 -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/bates/README.md +3 -0
  25. data/examples/bates/config.ru +342 -0
  26. data/examples/bates/david+bold.pdf +0 -0
  27. data/examples/bates/public/drop-pdf.png +0 -0
  28. data/examples/bates/public/index.html +600 -0
  29. data/examples/config.ru +59 -0
  30. data/examples/echo.ru +59 -0
  31. data/examples/etag.ru +16 -0
  32. data/examples/hello.ru +29 -0
  33. data/examples/pubsub_engine.ru +81 -0
  34. data/examples/rack3.ru +12 -0
  35. data/examples/redis.ru +70 -0
  36. data/examples/shootout.ru +73 -0
  37. data/examples/sub-protocols.ru +90 -0
  38. data/examples/tcp_client.rb +66 -0
  39. data/examples/x-sendfile.ru +14 -0
  40. data/exe/iodine +280 -0
  41. data/ext/iodine/extconf.rb +110 -0
  42. data/ext/iodine/fio.c +12096 -0
  43. data/ext/iodine/fio.h +6390 -0
  44. data/ext/iodine/fio_cli.c +431 -0
  45. data/ext/iodine/fio_cli.h +189 -0
  46. data/ext/iodine/fio_json_parser.h +687 -0
  47. data/ext/iodine/fio_siphash.c +157 -0
  48. data/ext/iodine/fio_siphash.h +37 -0
  49. data/ext/iodine/fio_tls.h +129 -0
  50. data/ext/iodine/fio_tls_missing.c +649 -0
  51. data/ext/iodine/fio_tls_openssl.c +1056 -0
  52. data/ext/iodine/fio_tmpfile.h +50 -0
  53. data/ext/iodine/fiobj.h +44 -0
  54. data/ext/iodine/fiobj4fio.h +21 -0
  55. data/ext/iodine/fiobj_ary.c +333 -0
  56. data/ext/iodine/fiobj_ary.h +139 -0
  57. data/ext/iodine/fiobj_data.c +1185 -0
  58. data/ext/iodine/fiobj_data.h +167 -0
  59. data/ext/iodine/fiobj_hash.c +409 -0
  60. data/ext/iodine/fiobj_hash.h +176 -0
  61. data/ext/iodine/fiobj_json.c +622 -0
  62. data/ext/iodine/fiobj_json.h +68 -0
  63. data/ext/iodine/fiobj_mem.h +71 -0
  64. data/ext/iodine/fiobj_mustache.c +317 -0
  65. data/ext/iodine/fiobj_mustache.h +62 -0
  66. data/ext/iodine/fiobj_numbers.c +344 -0
  67. data/ext/iodine/fiobj_numbers.h +127 -0
  68. data/ext/iodine/fiobj_str.c +433 -0
  69. data/ext/iodine/fiobj_str.h +172 -0
  70. data/ext/iodine/fiobject.c +620 -0
  71. data/ext/iodine/fiobject.h +654 -0
  72. data/ext/iodine/hpack.h +1923 -0
  73. data/ext/iodine/http.c +2736 -0
  74. data/ext/iodine/http.h +1019 -0
  75. data/ext/iodine/http1.c +825 -0
  76. data/ext/iodine/http1.h +29 -0
  77. data/ext/iodine/http1_parser.h +1835 -0
  78. data/ext/iodine/http_internal.c +1279 -0
  79. data/ext/iodine/http_internal.h +248 -0
  80. data/ext/iodine/http_mime_parser.h +350 -0
  81. data/ext/iodine/iodine.c +1433 -0
  82. data/ext/iodine/iodine.h +64 -0
  83. data/ext/iodine/iodine_caller.c +218 -0
  84. data/ext/iodine/iodine_caller.h +27 -0
  85. data/ext/iodine/iodine_connection.c +941 -0
  86. data/ext/iodine/iodine_connection.h +55 -0
  87. data/ext/iodine/iodine_defer.c +420 -0
  88. data/ext/iodine/iodine_defer.h +6 -0
  89. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  90. data/ext/iodine/iodine_helpers.c +282 -0
  91. data/ext/iodine/iodine_helpers.h +12 -0
  92. data/ext/iodine/iodine_http.c +1280 -0
  93. data/ext/iodine/iodine_http.h +23 -0
  94. data/ext/iodine/iodine_json.c +302 -0
  95. data/ext/iodine/iodine_json.h +6 -0
  96. data/ext/iodine/iodine_mustache.c +567 -0
  97. data/ext/iodine/iodine_mustache.h +6 -0
  98. data/ext/iodine/iodine_pubsub.c +580 -0
  99. data/ext/iodine/iodine_pubsub.h +26 -0
  100. data/ext/iodine/iodine_rack_io.c +273 -0
  101. data/ext/iodine/iodine_rack_io.h +20 -0
  102. data/ext/iodine/iodine_store.c +142 -0
  103. data/ext/iodine/iodine_store.h +20 -0
  104. data/ext/iodine/iodine_tcp.c +346 -0
  105. data/ext/iodine/iodine_tcp.h +13 -0
  106. data/ext/iodine/iodine_tls.c +261 -0
  107. data/ext/iodine/iodine_tls.h +13 -0
  108. data/ext/iodine/mustache_parser.h +1546 -0
  109. data/ext/iodine/redis_engine.c +957 -0
  110. data/ext/iodine/redis_engine.h +79 -0
  111. data/ext/iodine/resp_parser.h +317 -0
  112. data/ext/iodine/scheduler.c +173 -0
  113. data/ext/iodine/scheduler.h +6 -0
  114. data/ext/iodine/websocket_parser.h +506 -0
  115. data/ext/iodine/websockets.c +752 -0
  116. data/ext/iodine/websockets.h +185 -0
  117. data/iodine.gemspec +50 -0
  118. data/lib/iodine/connection.rb +61 -0
  119. data/lib/iodine/json.rb +42 -0
  120. data/lib/iodine/mustache.rb +113 -0
  121. data/lib/iodine/pubsub.rb +55 -0
  122. data/lib/iodine/rack_utils.rb +43 -0
  123. data/lib/iodine/tls.rb +16 -0
  124. data/lib/iodine/version.rb +3 -0
  125. data/lib/iodine.rb +274 -0
  126. data/lib/rack/handler/iodine.rb +33 -0
  127. data/logo.png +0 -0
  128. metadata +284 -0
@@ -0,0 +1,248 @@
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 "fiobj_str.h"
11
+ #include "fiobject.h"
12
+ #include "iodine_store.h"
13
+ #include <fio.h>
14
+ /* subscription lists have a long lifetime */
15
+ #define FIO_FORCE_MALLOC_TMP 1
16
+ #define FIO_INCLUDE_LINKED_LIST
17
+ #include <fio.h>
18
+
19
+ #include <http.h>
20
+
21
+ #ifndef __MINGW32__
22
+ #include <arpa/inet.h>
23
+ #endif
24
+ #include <errno.h>
25
+
26
+ /* *****************************************************************************
27
+ Types
28
+ ***************************************************************************** */
29
+
30
+ typedef struct http_fio_protocol_s http_fio_protocol_s;
31
+ typedef struct http_vtable_s http_vtable_s;
32
+
33
+ struct http_vtable_s {
34
+ /** Should send existing headers and data */
35
+ int (*const http_send_body)(http_s *h, void *data, uintptr_t length);
36
+ /** Should send existing headers and file */
37
+ int (*const http_sendfile)(http_s *h, int fd, uintptr_t length,
38
+ uintptr_t offset);
39
+ /** Should send existing headers and data and prepare for streaming */
40
+ int (*const http_stream)(http_s *h, void *data, uintptr_t length);
41
+ /** Should send existing headers or complete streaming */
42
+ void (*const http_finish)(http_s *h);
43
+ /** Push for data. */
44
+ int (*const http_push_data)(http_s *h, void *data, uintptr_t length,
45
+ FIOBJ mime_type);
46
+ /** Upgrades a connection to Websockets. */
47
+ int (*const http2websocket)(http_s *h, websocket_settings_s *arg);
48
+ /** Push for files. */
49
+ int (*const http_push_file)(http_s *h, FIOBJ filename, FIOBJ mime_type);
50
+ /** Pauses the request / response handling. */
51
+ void (*http_on_pause)(http_s *, http_fio_protocol_s *);
52
+
53
+ /** Resumes a request / response handling. */
54
+ void (*http_on_resume)(http_s *, http_fio_protocol_s *);
55
+ /** hijacks the socket aaway from the protocol. */
56
+ intptr_t (*http_hijack)(http_s *h, fio_str_info_s *leftover);
57
+
58
+ /** Upgrades an HTTP connection to an EventSource (SSE) connection. */
59
+ int (*http_upgrade2sse)(http_s *h, http_sse_s *sse);
60
+ /** Writes data to an EventSource (SSE) connection. MUST free the FIOBJ. */
61
+ int (*http_sse_write)(http_sse_s *sse, FIOBJ str);
62
+ /** Closes an EventSource (SSE) connection. */
63
+ int (*http_sse_close)(http_sse_s *sse);
64
+ };
65
+
66
+ struct http_fio_protocol_s {
67
+ fio_protocol_s protocol; /* facil.io protocol */
68
+ intptr_t uuid; /* socket uuid */
69
+ http_settings_s *settings; /* pointer to HTTP settings */
70
+ };
71
+
72
+ #define http2protocol(h) ((http_fio_protocol_s *)h->private_data.flag)
73
+
74
+ /* *****************************************************************************
75
+ Constants that shouldn't be accessed by the users (`fiobj_dup` required).
76
+ ***************************************************************************** */
77
+
78
+ extern FIOBJ HTTP_HEADER_ACCEPT_RANGES;
79
+ extern FIOBJ HTTP_HEADER_WS_SEC_CLIENT_KEY;
80
+ extern FIOBJ HTTP_HEADER_WS_SEC_KEY;
81
+ extern FIOBJ HTTP_HVALUE_BYTES;
82
+ extern FIOBJ HTTP_HVALUE_CLOSE;
83
+ extern FIOBJ HTTP_HVALUE_CONTENT_TYPE_DEFAULT;
84
+ extern FIOBJ HTTP_HVALUE_GZIP;
85
+ extern FIOBJ HTTP_HVALUE_KEEP_ALIVE;
86
+ extern FIOBJ HTTP_HVALUE_MAX_AGE;
87
+ extern FIOBJ HTTP_HVALUE_NO_CACHE;
88
+ extern FIOBJ HTTP_HVALUE_SSE_MIME;
89
+ extern FIOBJ HTTP_HVALUE_WEBSOCKET;
90
+ extern FIOBJ HTTP_HVALUE_WS_SEC_VERSION;
91
+ extern FIOBJ HTTP_HVALUE_WS_UPGRADE;
92
+ extern FIOBJ HTTP_HVALUE_WS_VERSION;
93
+
94
+ /* *****************************************************************************
95
+ HTTP request/response object management
96
+ ***************************************************************************** */
97
+
98
+ static inline void http_s_new(http_s *h, http_fio_protocol_s *owner,
99
+ http_vtable_s *vtbl) {
100
+ FIOBJ request_id = fiobj_str_buf(14);
101
+ fiobj_str_write(request_id, "req:", 4);
102
+ fiobj_str_write_i(request_id, (uint32_t)fio_rand64());
103
+
104
+ *h = (http_s){
105
+ .private_data =
106
+ {
107
+ .vtbl = vtbl,
108
+ .flag = (uintptr_t)owner,
109
+ .out_headers = fiobj_hash_new(),
110
+ },
111
+ .headers = fiobj_hash_new(),
112
+ .received_at = fio_last_tick(),
113
+ .status = 200,
114
+ .request_id = request_id,
115
+ };
116
+ }
117
+
118
+ static inline void http_s_destroy(http_s *h, uint8_t log) {
119
+ if (log && h->status && !h->status_str) {
120
+ http_write_log(h);
121
+ }
122
+ fiobj_free(h->method);
123
+ fiobj_free(h->status_str);
124
+ fiobj_free(h->private_data.out_headers);
125
+ fiobj_free(h->headers);
126
+ fiobj_free(h->version);
127
+ fiobj_free(h->query);
128
+ fiobj_free(h->path);
129
+ fiobj_free(h->cookies);
130
+ fiobj_free(h->body);
131
+ fiobj_free(h->params);
132
+ fiobj_free(h->request_id);
133
+
134
+ IodineStore.remove((VALUE)h->fiber);
135
+
136
+ *h = (http_s){
137
+ .private_data.vtbl = h->private_data.vtbl,
138
+ .private_data.flag = h->private_data.flag,
139
+ };
140
+ }
141
+
142
+ static inline void http_s_clear(http_s *h, uint8_t log) {
143
+ http_s_destroy(h, log);
144
+ http_s_new(h, (http_fio_protocol_s *)h->private_data.flag,
145
+ h->private_data.vtbl);
146
+ }
147
+
148
+ /** tests handle validity */
149
+ #define HTTP_INVALID_HANDLE(h) \
150
+ (!(h) || (!(h)->method && !(h)->status_str && (h)->status))
151
+
152
+ /* *****************************************************************************
153
+ Request / Response Handlers
154
+ ***************************************************************************** */
155
+
156
+ /** Use this function to handle HTTP requests.*/
157
+ void http_on_request_handler______internal(http_s *h,
158
+ http_settings_s *settings);
159
+
160
+ void http_on_response_handler______internal(http_s *h,
161
+ http_settings_s *settings);
162
+ int http_send_error2(size_t error, intptr_t uuid, http_settings_s *settings);
163
+
164
+ /* *****************************************************************************
165
+ EventSource Support (SSE)
166
+ ***************************************************************************** */
167
+
168
+ typedef struct http_sse_internal_s {
169
+ http_sse_s sse; /* the user SSE settings */
170
+ intptr_t uuid; /* the socket's uuid */
171
+ http_vtable_s *vtable; /* the protocol's vtable */
172
+ uintptr_t id; /* the SSE identifier */
173
+ fio_ls_s subscriptions; /* Subscription List */
174
+ fio_lock_i lock; /* Subscription List lock */
175
+ size_t ref; /* reference count */
176
+ } http_sse_internal_s;
177
+
178
+ static inline void http_sse_init(http_sse_internal_s *sse, intptr_t uuid,
179
+ http_vtable_s *vtbl, http_sse_s *args) {
180
+ *sse = (http_sse_internal_s){
181
+ .sse = *args,
182
+ .uuid = uuid,
183
+ .subscriptions = FIO_LS_INIT(sse->subscriptions),
184
+ .vtable = vtbl,
185
+ .ref = 1,
186
+ };
187
+ }
188
+
189
+ static inline void http_sse_try_free(http_sse_internal_s *sse) {
190
+ if (fio_atomic_sub(&sse->ref, 1))
191
+ return;
192
+ fio_free(sse);
193
+ }
194
+
195
+ static inline void http_sse_destroy(http_sse_internal_s *sse) {
196
+ while (fio_ls_any(&sse->subscriptions)) {
197
+ void *sub = fio_ls_pop(&sse->subscriptions);
198
+ fio_unsubscribe(sub);
199
+ }
200
+ if (sse->sse.on_close)
201
+ sse->sse.on_close(&sse->sse);
202
+ sse->uuid = -1;
203
+ http_sse_try_free(sse);
204
+ }
205
+
206
+ /* *****************************************************************************
207
+ Helpers
208
+ ***************************************************************************** */
209
+
210
+ /** sets an outgoing header only if it doesn't exist */
211
+ static inline void set_header_if_missing(FIOBJ hash, FIOBJ name, FIOBJ value) {
212
+ FIOBJ old = fiobj_hash_replace(hash, name, value);
213
+ if (!old)
214
+ return;
215
+ fiobj_hash_replace(hash, name, old);
216
+ fiobj_free(value);
217
+ }
218
+
219
+ /** sets an outgoing header, collecting duplicates in an Array (i.e. cookies)
220
+ */
221
+ static inline void set_header_add(FIOBJ hash, FIOBJ name, FIOBJ value) {
222
+ FIOBJ old = fiobj_hash_replace(hash, name, value);
223
+ if (!old)
224
+ return;
225
+ if (!value) {
226
+ fiobj_free(old);
227
+ return;
228
+ }
229
+ if (!FIOBJ_TYPE_IS(old, FIOBJ_T_ARRAY)) {
230
+ FIOBJ tmp = fiobj_ary_new();
231
+ fiobj_ary_push(tmp, old);
232
+ old = tmp;
233
+ }
234
+ if (FIOBJ_TYPE_IS(value, FIOBJ_T_ARRAY)) {
235
+ for (size_t i = 0; i < fiobj_ary_count(value); ++i) {
236
+ fiobj_ary_push(old, fiobj_dup(fiobj_ary_index(value, i)));
237
+ }
238
+ /* frees `value` */
239
+ fiobj_hash_set(hash, name, old);
240
+ return;
241
+ }
242
+ /* value will be owned by both hash and array */
243
+ fiobj_ary_push(old, value);
244
+ /* don't free `value` (leave in array) */
245
+ fiobj_hash_replace(hash, name, old);
246
+ }
247
+
248
+ #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