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
@@ -0,0 +1,226 @@
1
+ /*
2
+ Copyright: Boaz Segev, 2016-2018
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 "spnlock.inc"
11
+
12
+ #include "fio_llist.h"
13
+ #include "http.h"
14
+ #include "pubsub.h"
15
+
16
+ #include "fiobj4sock.h"
17
+
18
+ #include <arpa/inet.h>
19
+ #include <errno.h>
20
+
21
+ /* *****************************************************************************
22
+ Types
23
+ ***************************************************************************** */
24
+
25
+ typedef struct http_protocol_s http_protocol_s;
26
+ typedef struct http_vtable_s http_vtable_s;
27
+
28
+ struct http_vtable_s {
29
+ /** Should send existing headers and data */
30
+ int (*const http_send_body)(http_s *h, void *data, uintptr_t length);
31
+ /** Should send existing headers and file */
32
+ int (*const http_sendfile)(http_s *h, int fd, uintptr_t length,
33
+ uintptr_t offset);
34
+ /** Should send existing headers and data and prepare for streaming */
35
+ int (*const http_stream)(http_s *h, void *data, uintptr_t length);
36
+ /** Should send existing headers or complete streaming */
37
+ void (*const http_finish)(http_s *h);
38
+ /** Push for data. */
39
+ int (*const http_push_data)(http_s *h, void *data, uintptr_t length,
40
+ FIOBJ mime_type);
41
+ /** Upgrades a connection to Websockets. */
42
+ int (*const http2websocket)(websocket_settings_s *arg);
43
+ /** Push for files. */
44
+ int (*const http_push_file)(http_s *h, FIOBJ filename, FIOBJ mime_type);
45
+ /** Pauses the request / response handling. */
46
+ void (*http_on_pause)(http_s *, http_protocol_s *);
47
+
48
+ /** Resumes a request / response handling. */
49
+ void (*http_on_resume)(http_s *, http_protocol_s *);
50
+ /** hijacks the socket aaway from the protocol. */
51
+ intptr_t (*http_hijack)(http_s *h, fio_cstr_s *leftover);
52
+
53
+ /** Upgrades an HTTP connection to an EventSource (SSE) connection. */
54
+ int (*http_upgrade2sse)(http_s *h, http_sse_s *sse);
55
+ /** Writes data to an EventSource (SSE) connection. MUST free the FIOBJ. */
56
+ int (*http_sse_write)(http_sse_s *sse, FIOBJ str);
57
+ /** Closes an EventSource (SSE) connection. */
58
+ int (*http_sse_close)(http_sse_s *sse);
59
+ };
60
+
61
+ struct http_protocol_s {
62
+ protocol_s protocol; /* facil.io protocol */
63
+ intptr_t uuid; /* socket uuid */
64
+ http_settings_s *settings; /* pointer to HTTP settings */
65
+ };
66
+
67
+ #define http2protocol(h) ((http_protocol_s *)h->private_data.flag)
68
+
69
+ /* *****************************************************************************
70
+ Constants that shouldn't be accessed by the users (`fiobj_dup` required).
71
+ ***************************************************************************** */
72
+
73
+ extern FIOBJ HTTP_HEADER_ACCEPT_RANGES;
74
+ extern FIOBJ HTTP_HEADER_WS_SEC_CLIENT_KEY;
75
+ extern FIOBJ HTTP_HEADER_WS_SEC_KEY;
76
+ extern FIOBJ HTTP_HVALUE_BYTES;
77
+ extern FIOBJ HTTP_HVALUE_CLOSE;
78
+ extern FIOBJ HTTP_HVALUE_CONTENT_TYPE_DEFAULT;
79
+ extern FIOBJ HTTP_HVALUE_GZIP;
80
+ extern FIOBJ HTTP_HVALUE_KEEP_ALIVE;
81
+ extern FIOBJ HTTP_HVALUE_MAX_AGE;
82
+ extern FIOBJ HTTP_HVALUE_NO_CACHE;
83
+ extern FIOBJ HTTP_HVALUE_SSE_MIME;
84
+ extern FIOBJ HTTP_HVALUE_WEBSOCKET;
85
+ extern FIOBJ HTTP_HVALUE_WS_SEC_VERSION;
86
+ extern FIOBJ HTTP_HVALUE_WS_UPGRADE;
87
+ extern FIOBJ HTTP_HVALUE_WS_VERSION;
88
+
89
+ /* *****************************************************************************
90
+ HTTP request/response object management
91
+ ***************************************************************************** */
92
+
93
+ static inline void http_s_new(http_s *h, http_protocol_s *owner,
94
+ http_vtable_s *vtbl) {
95
+ *h = (http_s){
96
+ .private_data =
97
+ {
98
+ .vtbl = vtbl,
99
+ .flag = (uintptr_t)owner,
100
+ .out_headers = fiobj_hash_new(),
101
+ },
102
+ .headers = fiobj_hash_new(),
103
+ .received_at = facil_last_tick(),
104
+ .status = 200,
105
+ };
106
+ }
107
+
108
+ static inline void http_s_destroy(http_s *h, uint8_t log) {
109
+ if (log && h->status && !h->status_str) {
110
+ http_write_log(h);
111
+ }
112
+ fiobj_free(h->method);
113
+ fiobj_free(h->status_str);
114
+ fiobj_free(h->private_data.out_headers);
115
+ fiobj_free(h->headers);
116
+ fiobj_free(h->version);
117
+ fiobj_free(h->query);
118
+ fiobj_free(h->path);
119
+ fiobj_free(h->cookies);
120
+ fiobj_free(h->body);
121
+ fiobj_free(h->params);
122
+
123
+ *h = (http_s){
124
+ .private_data.vtbl = h->private_data.vtbl,
125
+ .private_data.flag = h->private_data.flag,
126
+ };
127
+ }
128
+
129
+ static inline void http_s_clear(http_s *h, uint8_t log) {
130
+ http_s_destroy(h, log);
131
+ http_s_new(h, (http_protocol_s *)h->private_data.flag, h->private_data.vtbl);
132
+ }
133
+
134
+ /** tests handle validity */
135
+ #define HTTP_INVALID_HANDLE(h) \
136
+ (!(h) || (!(h)->method && !(h)->status_str && (h)->status))
137
+
138
+ /* *****************************************************************************
139
+ Request / Response Handlers
140
+ ***************************************************************************** */
141
+
142
+ /** Use this function to handle HTTP requests.*/
143
+ void http_on_request_handler______internal(http_s *h,
144
+ http_settings_s *settings);
145
+
146
+ void http_on_response_handler______internal(http_s *h,
147
+ http_settings_s *settings);
148
+ int http_send_error2(size_t error, intptr_t uuid, http_settings_s *settings);
149
+
150
+ /* *****************************************************************************
151
+ EventSource Support (SSE)
152
+ ***************************************************************************** */
153
+
154
+ typedef struct http_sse_internal_s {
155
+ http_sse_s sse; /* the user SSE settings */
156
+ intptr_t uuid; /* the socket's uuid */
157
+ http_vtable_s *vtable; /* the protocol's vtable */
158
+ uintptr_t id; /* the SSE identifier */
159
+ fio_ls_s subscriptions; /* Subscription List */
160
+ spn_lock_i lock; /* Subscription List lock */
161
+ size_t ref; /* reference count */
162
+ } http_sse_internal_s;
163
+
164
+ static inline void http_sse_init(http_sse_internal_s *sse, intptr_t uuid,
165
+ http_vtable_s *vtbl, http_sse_s *args) {
166
+ *sse = (http_sse_internal_s){
167
+ .sse = *args,
168
+ .uuid = uuid,
169
+ .subscriptions = FIO_LS_INIT(sse->subscriptions),
170
+ .vtable = vtbl,
171
+ .ref = 1,
172
+ };
173
+ }
174
+
175
+ static inline void http_sse_try_free(http_sse_internal_s *sse) {
176
+ if (spn_sub(&sse->ref, 1))
177
+ return;
178
+ free(sse);
179
+ }
180
+
181
+ static inline void http_sse_destroy(http_sse_internal_s *sse) {
182
+ while (fio_ls_any(&sse->subscriptions)) {
183
+ void *sub = fio_ls_pop(&sse->subscriptions);
184
+ pubsub_unsubscribe(sub);
185
+ }
186
+ sse->uuid = -1;
187
+ http_sse_try_free(sse);
188
+ }
189
+
190
+ /* *****************************************************************************
191
+ Helpers
192
+ ***************************************************************************** */
193
+
194
+ #define HTTP_ASSERT(x, m) \
195
+ if (!x) \
196
+ perror("FATAL ERROR: (http)" m), exit(errno);
197
+
198
+ /** sets an outgoing header only if it doesn't exist */
199
+ static inline void set_header_if_missing(FIOBJ hash, FIOBJ name, FIOBJ value) {
200
+ FIOBJ old = fiobj_hash_replace(hash, name, value);
201
+ if (!old)
202
+ return;
203
+ fiobj_hash_replace(hash, name, old);
204
+ fiobj_free(value);
205
+ }
206
+
207
+ /** sets an outgoing header, collecting duplicates in an Array (i.e. cookies)
208
+ */
209
+ static inline void set_header_add(FIOBJ hash, FIOBJ name, FIOBJ value) {
210
+ FIOBJ old = fiobj_hash_replace(hash, name, value);
211
+ if (!old)
212
+ return;
213
+ if (!value) {
214
+ fiobj_free(old);
215
+ return;
216
+ }
217
+ if (!FIOBJ_TYPE_IS(old, FIOBJ_T_ARRAY)) {
218
+ FIOBJ tmp = fiobj_ary_new();
219
+ fiobj_ary_push(tmp, old);
220
+ old = tmp;
221
+ }
222
+ fiobj_ary_push(old, value);
223
+ fiobj_hash_replace(hash, name, old);
224
+ }
225
+
226
+ #endif /* H_HTTP_INTERNAL_H */
@@ -0,0 +1,341 @@
1
+ /*
2
+ Copyright: Boaz Segev, 2018
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
+ The HTTP MIME Multipart Form Parser Type
15
+ ***************************************************************************** */
16
+
17
+ /** all data id read-only / for internal use */
18
+ typedef struct {
19
+ char *boundary;
20
+ size_t boundary_len;
21
+ uint8_t in_obj;
22
+ uint8_t done;
23
+ uint8_t error;
24
+ } http_mime_parser_s;
25
+
26
+ /* *****************************************************************************
27
+ Callbacks to be implemented.
28
+ ***************************************************************************** */
29
+
30
+ /** Called when all the data is available at once. */
31
+ static void http_mime_parser_on_data(http_mime_parser_s *parser, void *name,
32
+ size_t name_len, void *filename,
33
+ size_t filename_len, void *mimetype,
34
+ size_t mimetype_len, void *value,
35
+ size_t value_len);
36
+
37
+ /** Called when the data didn't fit in the buffer. Data will be streamed. */
38
+ static void http_mime_parser_on_partial_start(
39
+ http_mime_parser_s *parser, void *name, size_t name_len, void *filename,
40
+ size_t filename_len, void *mimetype, size_t mimetype_len);
41
+
42
+ /** Called when partial data is available. */
43
+ static void http_mime_parser_on_partial_data(http_mime_parser_s *parser,
44
+ void *value, size_t value_len);
45
+
46
+ /** Called when the partial data is complete. */
47
+ static void http_mime_parser_on_partial_end(http_mime_parser_s *parser);
48
+
49
+ /**
50
+ * Called when URL decoding is required.
51
+ *
52
+ * Should support inplace decoding (`dest == encoded`).
53
+ *
54
+ * Should return the length of the decoded string.
55
+ */
56
+ static size_t http_mime_decode_url(char *dest, const char *encoded,
57
+ size_t length);
58
+
59
+ /* *****************************************************************************
60
+ API
61
+ ***************************************************************************** */
62
+
63
+ /**
64
+ * Takes the HTTP Content-Type header and initializes the parser data.
65
+ *
66
+ * Note: the Content-Type header should persist in memory while the parser is in
67
+ * use.
68
+ */
69
+ static int http_mime_parser_init(http_mime_parser_s *parser, char *content_type,
70
+ size_t len);
71
+
72
+ /**
73
+ * Consumes data from a streaming buffer.
74
+ *
75
+ * The data might be partially consumed, in which case the unconsumed data
76
+ * should be resent to the parser as more data becomes available.
77
+ *
78
+ * Note: test the `parser->done` and `parser->error` flags between iterations.
79
+ */
80
+ static size_t http_mime_parse(http_mime_parser_s *parser, void *buffer,
81
+ size_t length);
82
+
83
+ /* *****************************************************************************
84
+ Implementations
85
+ ***************************************************************************** */
86
+
87
+ /** takes the HTTP Content-Type header and initializes the parser data. */
88
+ static int http_mime_parser_init(http_mime_parser_s *parser, char *content_type,
89
+ size_t len) {
90
+ *parser = (http_mime_parser_s){.done = 0};
91
+ if (len < 14 || strncasecmp("multipart/form", content_type, 14))
92
+ return -1;
93
+ char *cut = memchr(content_type, ';', len);
94
+ while (cut) {
95
+ ++cut;
96
+ len -= (size_t)(cut - content_type);
97
+ while (len && cut[0] == ' ') {
98
+ --len;
99
+ ++cut;
100
+ }
101
+ if (len <= 9)
102
+ return -1;
103
+ if (strncasecmp("boundary=", cut, 9)) {
104
+ content_type = cut;
105
+ cut = memchr(cut, ';', len);
106
+ continue;
107
+ }
108
+ cut += 9;
109
+ len -= 9;
110
+ content_type = cut;
111
+ parser->boundary = content_type;
112
+ if ((cut = memchr(content_type, ';', len)))
113
+ parser->boundary_len = (size_t)(cut - content_type);
114
+ else
115
+ parser->boundary_len = len;
116
+ return 0;
117
+ }
118
+ return -1;
119
+ }
120
+
121
+ /**
122
+ * Consumes data from a streaming buffer.
123
+ *
124
+ * The data might be partially consumed, in which case the unconsumed data
125
+ * should be resent to the parser as more data becomes available.
126
+ *
127
+ * Note: test the `parser->done` and `parser->error` flags between iterations.
128
+ */
129
+ static size_t http_mime_parse(http_mime_parser_s *parser, void *buffer,
130
+ size_t length) {
131
+ int first_run = 1;
132
+ char *pos = buffer;
133
+ const char *stop = pos + length;
134
+ if (!length)
135
+ goto end_of_data;
136
+ consume_partial:
137
+ if (parser->in_obj) {
138
+ /* we're in an object longer than the buffer */
139
+ char *start = pos;
140
+ char *end = start;
141
+ do {
142
+ end = memchr(end, '\n', (size_t)(stop - end));
143
+ } while (end && ++end &&
144
+ (size_t)(stop - end) >= (4 + parser->boundary_len) &&
145
+ (end[0] != '-' || end[1] != '-' ||
146
+ memcmp(end + 2, parser->boundary, parser->boundary_len)));
147
+ if (!end) {
148
+ end = (char *)stop;
149
+ pos = end;
150
+ if (end - start)
151
+ http_mime_parser_on_partial_data(parser, start, (size_t)(end - start));
152
+ goto end_of_data;
153
+ } else if (end + 4 + parser->boundary_len >= stop) {
154
+ end -= 2;
155
+ if (end[0] == '\r')
156
+ --end;
157
+ pos = end;
158
+ if (end - start)
159
+ http_mime_parser_on_partial_data(parser, start, (size_t)(end - start));
160
+ goto end_of_data;
161
+ }
162
+ size_t len = (end - start) - 1;
163
+ if (start[len - 1] == '\r')
164
+ --len;
165
+ if (len)
166
+ http_mime_parser_on_partial_data(parser, start, len);
167
+ http_mime_parser_on_partial_end(parser);
168
+ pos = end;
169
+ parser->in_obj = 0;
170
+ first_run = 0;
171
+ } else if (length < (4 + parser->boundary_len) || pos[0] != '-' ||
172
+ pos[1] != '-' ||
173
+ memcmp(pos + 2, parser->boundary, parser->boundary_len))
174
+ goto error;
175
+ /* We're at a boundary */
176
+ while (pos < stop) {
177
+ char *start;
178
+ char *end;
179
+ char *name = NULL;
180
+ uint32_t name_len = 0;
181
+ char *value = NULL;
182
+ uint32_t value_len = 0;
183
+ char *filename = NULL;
184
+ uint32_t filename_len = 0;
185
+ char *mime = NULL;
186
+ uint32_t mime_len = 0;
187
+ uint8_t header_count = 0;
188
+ /* test for ending */
189
+ if (pos[2 + parser->boundary_len] == '-' &&
190
+ pos[3 + parser->boundary_len] == '-') {
191
+ pos += 5 + parser->boundary_len;
192
+ if (pos > stop)
193
+ pos = (char *)stop;
194
+ else if (pos < stop && pos[0] == '\n')
195
+ ++pos;
196
+ goto done;
197
+ }
198
+ start = pos + 3 + parser->boundary_len;
199
+ if (start[0] == '\n') {
200
+ /* should be true, unless new line marker was just '\n' */
201
+ ++start;
202
+ }
203
+ /* consume headers */
204
+ while (start + 4 < stop && start[0] != '\n' && start[1] != '\n') {
205
+ end = memchr(start, '\n', (size_t)(stop - start));
206
+ if (!end) {
207
+ if (first_run)
208
+ goto error;
209
+ goto end_of_data;
210
+ }
211
+ if (end - start > 29 && !strncasecmp(start, "content-disposition:", 20)) {
212
+ /* content-disposition header */
213
+ start = memchr(start + 20, ';', end - (start + 20));
214
+ // if (!start)
215
+ // start = end + 1;
216
+ while (start) {
217
+ ++start;
218
+ if (start[0] == ' ')
219
+ ++start;
220
+ if (start + 6 < end && !strncasecmp(start, "name=", 5)) {
221
+ name = start + 5;
222
+ if (name[0] == '"')
223
+ ++name;
224
+ start = memchr(name, ';', (size_t)(end - start));
225
+ if (!start) {
226
+ name_len = (size_t)(end - name);
227
+ if (name[name_len - 1] == '\r')
228
+ --name_len;
229
+ } else {
230
+ name_len = (size_t)(start - name);
231
+ }
232
+ if (name[name_len - 1] == '"')
233
+ --name_len;
234
+ } else if (start + 9 < end && !strncasecmp(start, "filename", 8)) {
235
+ uint8_t encoded = 0;
236
+ start += 8;
237
+ if (start[0] == '*') {
238
+ encoded = 1;
239
+ ++start;
240
+ }
241
+ if (start[0] != '=')
242
+ goto error;
243
+ ++start;
244
+ if (start[0] == ' ')
245
+ ++start;
246
+ if (start[0] == '"')
247
+ ++start;
248
+ if (filename && !encoded) {
249
+ /* prefer URL encoded version */
250
+ start = memchr(filename, ';', (size_t)(end - start));
251
+ continue;
252
+ }
253
+ filename = start;
254
+ start = memchr(filename, ';', (size_t)(end - start));
255
+ if (!start) {
256
+ filename_len = (size_t)((end - filename));
257
+ if (filename[filename_len - 1] == '\r') {
258
+ --filename_len;
259
+ }
260
+ } else {
261
+ filename_len = (size_t)(start - filename);
262
+ }
263
+ if (filename[filename_len - 1] == '"')
264
+ --filename_len;
265
+ if (encoded) {
266
+ ssize_t new_len =
267
+ http_mime_decode_url(filename, filename, filename_len);
268
+ if (new_len > 0)
269
+ filename_len = new_len;
270
+ }
271
+ } else {
272
+ start = memchr(start, ';', (size_t)(end - start));
273
+ }
274
+ }
275
+ } else if (end - start > 14 && !strncasecmp(start, "content-type:", 13)) {
276
+ /* content-type header */
277
+ start += 13;
278
+ if (start[0] == ' ')
279
+ ++start;
280
+ mime = start;
281
+ start = memchr(start, ';', (size_t)(end - start));
282
+ if (!start) {
283
+ mime_len = (size_t)(end - mime);
284
+ if (mime[mime_len - 1] == '\r')
285
+ --mime_len;
286
+ } else {
287
+ mime_len = (size_t)(start - mime);
288
+ }
289
+ }
290
+ start = end + 1;
291
+ if (header_count++ > 4)
292
+ goto error;
293
+ }
294
+ if (!name) {
295
+ if (start + 4 >= stop)
296
+ goto end_of_data;
297
+ goto error;
298
+ }
299
+
300
+ /* advance to end of boundry */
301
+ ++start;
302
+ if (start[0] == '\n')
303
+ ++start;
304
+ value = start;
305
+ end = start;
306
+ do {
307
+ end = memchr(end, '\n', (size_t)(stop - end));
308
+ } while (end && ++end &&
309
+ (size_t)(stop - end) >= (4 + parser->boundary_len) &&
310
+ (end[0] != '-' || end[1] != '-' ||
311
+ memcmp(end + 2, parser->boundary, parser->boundary_len)));
312
+ if (!end || end + 4 + parser->boundary_len >= stop) {
313
+ if (first_run) {
314
+ http_mime_parser_on_partial_start(parser, name, name_len, filename,
315
+ filename_len, mime, mime_len);
316
+ parser->in_obj = 1;
317
+ pos = value;
318
+ goto consume_partial;
319
+ }
320
+ goto end_of_data;
321
+ }
322
+ value_len = (size_t)((end - value) - 1);
323
+ if (value[value_len - 1] == '\r')
324
+ --value_len;
325
+ pos = end;
326
+ http_mime_parser_on_data(parser, name, name_len, filename, filename_len,
327
+ mime, mime_len, value, value_len);
328
+ first_run = 0;
329
+ }
330
+ end_of_data:
331
+ return (size_t)((uintptr_t)pos - (uintptr_t)buffer);
332
+ done:
333
+ parser->done = 1;
334
+ parser->error = 0;
335
+ return (size_t)((uintptr_t)pos - (uintptr_t)buffer);
336
+ error:
337
+ parser->done = 0;
338
+ parser->error = 1;
339
+ return (size_t)((uintptr_t)pos - (uintptr_t)buffer);
340
+ }
341
+ #endif