iodine 0.4.7 → 0.4.8

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 481aeb644c0f6d2b38f2c00bc98412cc867e77d5
4
- data.tar.gz: bd3eebc92151a3f2d4f41456c6d9402e7cc005fb
3
+ metadata.gz: '08f011b1bc43d3c47d266fc0e2dab7379708e5de'
4
+ data.tar.gz: 1197234946e68f0515ce51d3f8858d948a0a77ef
5
5
  SHA512:
6
- metadata.gz: 4f31a4c09c7cbfe4df0a9f1df10c9c12004c7e92e3a2cbe1b513afff12c4c5670a457e0f4f6e8cf710ae5cb677d6b3c319f30baac802aefac054f8c8ccda058b
7
- data.tar.gz: 9332123f1197af323c1b1af3a5fb1c5bbce7619b6586c1fb6ca049d220d9ebe6780637f5212588b55552e1bf17a07823cf5aedd1062dbf072095b1418df71cd5
6
+ metadata.gz: aab2abfb29dea3efd0041c31abfbe93b3aac2fa21cf48fa48629263d58d6b636ff054d43acc33b3c7e24904a7240e42ef6e865218b1530bce530bf785adc4ef6
7
+ data.tar.gz: 4ed2825719fc986132ecbdb28d3c56c2bc23e752f1d67df794a92fc12c5f89c84c0b914ef7ad49bece19d2f01ac3790ddce73b858fe024a1fffaeab985eba6aa
@@ -6,6 +6,14 @@ Please notice that this change log contains changes for upcoming releases as wel
6
6
 
7
7
  ## Changes:
8
8
 
9
+ #### Change log v.0.4.8
10
+
11
+ **Change**: (`facil.io`) the internal HTTP parser was replaced with something easier to read, for maintainability reasons. Performance seems to be unaffected.
12
+
13
+ **Fix**: HTTP request logging included an extra info line which was a debug/testing message inherited from `facil.io` v.0.5.3-pre-release. This is now removed.
14
+
15
+ **Performance**: The `now` HTTP Date string is now cached for up to 2 seconds, improving performance for `Date`, `Last-Modified` and Iodine logging messages that relate to the current time. However, it's likely that Rack will write it's own date string, masking this feature.
16
+
9
17
  #### Change log v.0.4.7
10
18
 
11
19
  **Update**: Now using `facil.io` edge (stripped down v.0.5.3).
data/README.md CHANGED
@@ -399,7 +399,7 @@ Yes, please, here are some thoughts:
399
399
 
400
400
  * If we can write a Java wrapper for [the `facil.io` C framework](https://github.com/boazsegev/facil.io), it would be nice... but it could be as big a project as the whole gem, as a lot of minor details are implemented within the bridge between these two languages.
401
401
 
402
- * PRs or issues related to [the `facil.io` C framework](https://github.com/boazsegev/facil.io) would be better placed in [the `facil.io` repository](https://github.com/boazsegev/facil.io).
402
+ * PRs or issues related to [the `facil.io` C framework](https://github.com/boazsegev/facil.io) should be placed in [the `facil.io` repository](https://github.com/boazsegev/facil.io).
403
403
 
404
404
  * Bug reports and pull requests are welcome on GitHub at https://github.com/boazsegev/iodine.
405
405
 
@@ -121,7 +121,7 @@ struct tm *http_gmtime(const time_t *timer, struct tm *tmbuf) {
121
121
  // static char * Months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
122
122
  // "Jul",
123
123
  // "Aug", "Sep", "Oct", "Nov", "Dec"};
124
- static uint8_t month_len[] = {
124
+ static const uint8_t month_len[] = {
125
125
  31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, // nonleap year
126
126
  31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // leap year
127
127
  };
@@ -215,9 +215,11 @@ struct tm *http_gmtime(const time_t *timer, struct tm *tmbuf) {
215
215
  return tmbuf;
216
216
  }
217
217
 
218
- static char *DAY_NAMES[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
219
- static char *MONTH_NAMES[] = {"Jan ", "Feb ", "Mar ", "Apr ", "May ", "Jun ",
220
- "Jul ", "Aug ", "Sep ", "Oct ", "Nov ", "Dec "};
218
+ static const char *DAY_NAMES[] = {"Sun", "Mon", "Tue", "Wed",
219
+ "Thu", "Fri", "Sat"};
220
+ static const char *MONTH_NAMES[] = {"Jan ", "Feb ", "Mar ", "Apr ",
221
+ "May ", "Jun ", "Jul ", "Aug ",
222
+ "Sep ", "Oct ", "Nov ", "Dec "};
221
223
  static const char *GMT_STR = "GMT";
222
224
 
223
225
  size_t http_date2str(char *target, struct tm *tmbuf) {
@@ -345,6 +347,34 @@ size_t http_date2rfc2109(char *target, struct tm *tmbuf) {
345
347
  return pos - target;
346
348
  }
347
349
 
350
+ /**
351
+ * Prints Unix time to a HTTP time formatted string.
352
+ *
353
+ * This variation implements chached results for faster processeing, at the
354
+ * price of a less accurate string.
355
+ */
356
+ size_t http_time2str(char *target, const time_t t) {
357
+ /* pre-print time every 1 or 2 seconds or so. */
358
+ static __thread time_t cached_tick;
359
+ static __thread char cached_httpdate[48];
360
+ static __thread size_t chached_len;
361
+ time_t last_tick = facil_last_tick();
362
+ if ((t | 7) < last_tick) {
363
+ /* this is a custom time, not "now", pass through */
364
+ struct tm tm;
365
+ http_gmtime(&t, &tm);
366
+ return http_date2str(target, &tm);
367
+ }
368
+ if (last_tick > cached_tick) {
369
+ struct tm tm;
370
+ cached_tick = last_tick | 1;
371
+ http_gmtime(&last_tick, &tm);
372
+ chached_len = http_date2str(cached_httpdate, &tm);
373
+ }
374
+ memcpy(target, cached_httpdate, chached_len);
375
+ return chached_len;
376
+ }
377
+
348
378
  /* Credit to Jonathan Leffler for the idea of a unified conditional */
349
379
  #define hex_val(c) \
350
380
  (((c) >= '0' && (c) <= '9') \
@@ -164,6 +164,14 @@ size_t http_date2rfc2109(char *target, struct tm *tmbuf);
164
164
  /** An alternative, RFC 2822 date representation. */
165
165
  size_t http_date2rfc2822(char *target, struct tm *tmbuf);
166
166
 
167
+ /**
168
+ * Prints Unix time to a HTTP time formatted string.
169
+ *
170
+ * This variation implements chached results for faster processeing, at the
171
+ * price of a less accurate string.
172
+ */
173
+ size_t http_time2str(char *target, const time_t t);
174
+
167
175
  /**
168
176
  A fast, inline alternative to `sprintf(dest, "%lu", num)`.
169
177
 
@@ -7,34 +7,35 @@ Feel free to copy, use and enjoy according to the license provided.
7
7
  #include "spnlock.inc"
8
8
 
9
9
  #include "http.h"
10
- #include "http1.h"
10
+ #include "http1_parser.h"
11
11
  #include "http1_request.h"
12
- #include "http1_simple_parser.h"
13
12
 
14
13
  #include <fcntl.h>
15
14
  #include <stdio.h>
16
15
  #include <string.h>
17
16
  #include <sys/stat.h>
18
17
 
19
- char *HTTP1_Protocol_String = "facil_http/1.1_protocol";
18
+ char *HTTP11_Protocol_String = "facil_http/1.1_protocol";
20
19
  /* *****************************************************************************
21
20
  HTTP/1.1 data structures
22
- */
21
+ ***************************************************************************** */
23
22
 
24
23
  typedef struct http1_protocol_s {
25
24
  protocol_s protocol;
26
25
  http_settings_s *settings;
26
+ http1_parser_s parser;
27
27
  void (*on_request)(http_request_s *request);
28
28
  struct http1_protocol_s *next;
29
29
  http1_request_s request;
30
- ssize_t len; /* used as a persistent socket `read` indication. */
30
+ size_t len; /* used as a persistent socket `read` indication. */
31
+ size_t refresh; /* a flag indicating a request callback was called. */
31
32
  } http1_protocol_s;
32
33
 
33
34
  static void http1_on_data(intptr_t uuid, http1_protocol_s *protocol);
34
35
 
35
36
  /* *****************************************************************************
36
37
  HTTP/1.1 pool
37
- */
38
+ ***************************************************************************** */
38
39
 
39
40
  static struct {
40
41
  spn_lock_i lock;
@@ -63,7 +64,8 @@ static inline void http1_set_protocol_data(http1_protocol_s *pr) {
63
64
  .on_data = (void (*)(intptr_t, protocol_s *))http1_on_data,
64
65
  .on_close = (void (*)(intptr_t uuid, protocol_s *))http1_free};
65
66
  pr->request.request = (http_request_s){.fd = 0, .http_version = HTTP_V1};
66
- pr->request.header_pos = pr->request.buffer_pos = pr->len = 0;
67
+ pr->refresh = pr->request.header_pos = pr->request.buffer_pos = pr->len = 0;
68
+ pr->parser = (http1_parser_s){.udata = pr};
67
69
  }
68
70
 
69
71
  static http1_protocol_s *http1_alloc(void) {
@@ -100,188 +102,295 @@ initialize:
100
102
  }
101
103
 
102
104
  /* *****************************************************************************
103
- HTTP callbacks
104
- */
105
- #define HTTP_BODY_CHUNK_SIZE 3072 // 4096
105
+ HTTP/1.1 parsre callbacks
106
+ ***************************************************************************** */
106
107
 
107
- static void http1_on_header_found(http_request_s *request,
108
- http_header_s *header) {
109
- ((http1_request_s *)request)
110
- ->headers[((http1_request_s *)request)->header_pos] = *header;
111
- ((http1_request_s *)request)->header_pos += 1;
112
- }
108
+ #define HTTP_BODY_CHUNK_SIZE 4096
113
109
 
114
- /* parse and call callback */
115
- static void http1_on_data(intptr_t uuid, http1_protocol_s *pr) {
116
- ssize_t result;
117
- char buff[HTTP_BODY_CHUNK_SIZE];
118
- http1_request_s *request = &pr->request;
119
- char *buffer = request->buffer;
120
- // handle requests with no file data
121
- if (request->request.body_file <= 0) {
122
- // request headers parsing
123
- if (pr->len == 0) {
124
- buffer = request->buffer;
125
- // make sure headers don't overflow
126
- pr->len = sock_read(uuid, buffer + request->buffer_pos,
127
- HTTP1_MAX_HEADER_SIZE - request->buffer_pos);
128
- // update buffer read position.
129
- request->buffer_pos += pr->len;
130
- // if (len > 0) {
131
- // fprintf(stderr, "\n----\nRead from socket, %lu bytes, total
132
- // %lu:\n",
133
- // len, request->buffer_pos);
134
- // for (size_t i = 0; i < request->buffer_pos; i++) {
135
- // fprintf(stderr, "%c", buffer[i] ? buffer[i] : '-');
136
- // }
137
- // fprintf(stderr, "\n");
138
- // }
139
- }
140
- if (pr->len <= 0)
141
- goto finished_reading;
142
-
143
- // parse headers
144
- result = http1_parse_request_headers(
145
- buffer, request->buffer_pos, &request->request, http1_on_header_found);
146
- // review result
147
- if (result >= 0) { // headers comeplete
148
- // are we done?
149
- if (request->request.content_length == 0 || request->request.body_str) {
150
- goto handle_request;
151
- }
152
- if (request->request.content_length > pr->settings->max_body_size) {
153
- goto body_to_big;
154
- }
155
- // initialize or submit body data
156
- result = http1_parse_request_body(buffer + result, pr->len - result,
157
- (http_request_s *)request);
158
- if (result >= 0) {
159
- request->buffer_pos += result;
160
- goto handle_request;
161
- } else if (result == -1) // parser error
162
- goto parser_error;
163
- goto parse_body;
164
- } else if (result == -1) // parser error
165
- goto parser_error;
166
- // assume incomplete (result == -2), even if wrong, we're right.
167
- pr->len = 0;
168
- goto postpone;
169
- }
170
- if (request->request.body_file > 0) {
171
- // fprintf(stderr, "Body File\n");
172
- parse_body:
173
- buffer = buff;
174
- // request body parsing
175
- pr->len = sock_read(uuid, buffer, HTTP_BODY_CHUNK_SIZE);
176
- if (pr->len <= 0)
177
- goto finished_reading;
178
- result = http1_parse_request_body(buffer, pr->len, &request->request);
179
- if (result >= 0) {
180
- goto handle_request;
181
- } else if (result == -1) // parser error
182
- goto parser_error;
183
- if (pr->len < HTTP_BODY_CHUNK_SIZE) // pause parser for more data
184
- goto finished_reading;
185
- goto parse_body;
186
- }
187
- goto postpone;
188
- handle_request:
189
- // review required headers / data
190
- if (request->request.host == NULL)
110
+ /** called when a request was received. */
111
+ static int http1_on_request(http1_parser_s *parser) {
112
+ http1_protocol_s *pr = parser->udata;
113
+ if (!pr)
114
+ return -1;
115
+ if (pr->request.request.host == NULL) {
191
116
  goto bad_request;
117
+ }
192
118
  http_settings_s *settings = pr->settings;
193
- request->request.settings = settings;
119
+ pr->request.request.settings = settings;
194
120
  // make sure udata to NULL, making it available for the user
195
- request->request.udata = NULL;
196
- // call request callback
197
- if (pr && settings &&
198
- (request->request.upgrade || settings->public_folder == NULL ||
199
- http_response_sendfile2(NULL, &request->request, settings->public_folder,
200
- settings->public_folder_length,
201
- request->request.path, request->request.path_len,
202
- settings->log_static))) {
203
- pr->on_request(&request->request);
204
- // fprintf(stderr, "Called on_request\n");
121
+ pr->request.request.udata = NULL;
122
+ // static file service or call request callback
123
+ if (pr->request.request.upgrade || settings->public_folder == NULL ||
124
+ http_response_sendfile2(
125
+ NULL, &pr->request.request, settings->public_folder,
126
+ settings->public_folder_length, pr->request.request.path,
127
+ pr->request.request.path_len, settings->log_static)) {
128
+ pr->on_request(&pr->request.request);
205
129
  }
206
- // rotate buffer for HTTP pipelining
207
- if ((ssize_t)request->buffer_pos <= result) {
208
- pr->len = 0;
209
- // fprintf(stderr, "\n----\nAll data consumed.\n");
210
- } else {
211
- memmove(request->buffer, buffer + result, request->buffer_pos - result);
212
- pr->len = request->buffer_pos - result;
213
- // fprintf(stderr, "\n----\ndata after move, %lu long:\n%.*s\n", len,
214
- // (int)len, request->buffer);
215
- }
216
- // fprintf(stderr, "data in buffer, %lu long:\n%.*s\n", len, (int)len,
217
- // request->buffer);
218
- // clear request state
219
- http1_request_clear(&request->request);
220
- request->buffer_pos = pr->len;
221
- // make sure to use the correct buffer.
222
- buffer = request->buffer;
223
- /* prevent this connection from hogging the thread by pipelining endless
224
- * requests.
225
- */
226
- postpone:
227
- facil_force_event(uuid, FIO_EVENT_ON_DATA);
228
- return;
229
- parser_error:
230
- if (request->request.headers_count >= HTTP1_MAX_HEADER_COUNT)
231
- goto too_big;
130
+ pr->refresh = 1;
131
+ return 0;
232
132
  bad_request:
233
133
  /* handle generally bad requests */
234
134
  {
235
- http_response_s *response = http_response_create(&request->request);
135
+ http_response_s *response = http_response_create(&pr->request.request);
136
+ if (pr->settings->log_static)
137
+ http_response_log_start(response);
236
138
  response->status = 400;
237
139
  if (!pr->settings->public_folder ||
238
- http_response_sendfile2(response, &request->request,
140
+ http_response_sendfile2(response, &pr->request.request,
239
141
  pr->settings->public_folder,
240
142
  pr->settings->public_folder_length, "400.html",
241
143
  8, pr->settings->log_static)) {
144
+ response->should_close = 1;
242
145
  http_response_write_body(response, "Bad Request", 11);
243
146
  http_response_finish(response);
244
147
  }
245
- sock_close(uuid);
246
- request->buffer_pos = 0;
247
- goto finished_reading;
148
+ sock_close(pr->request.request.fd);
149
+ pr->request.buffer_pos = 0;
150
+ }
151
+ return -1;
152
+ }
153
+ /** called when a response was received. */
154
+ static int http1_on_response(http1_parser_s *parser) {
155
+ return -1;
156
+ (void)parser;
157
+ }
158
+ /** called when a request method is parsed. */
159
+ static int http1_on_method(http1_parser_s *parser, char *method,
160
+ size_t method_len) {
161
+ http1_protocol_s *pr = parser->udata;
162
+ if (!pr)
163
+ return -1;
164
+ pr->request.request.method = method;
165
+ pr->request.request.method_len = method_len;
166
+ return 0;
167
+ }
168
+
169
+ /** called when a response status is parsed. the status_str is the string
170
+ * without the prefixed numerical status indicator.*/
171
+ static int http1_on_status(http1_parser_s *parser, size_t status,
172
+ char *status_str, size_t len) {
173
+ return -1;
174
+ (void)parser;
175
+ (void)status;
176
+ (void)status_str;
177
+ (void)len;
178
+ }
179
+
180
+ /** called when a request path (excluding query) is parsed. */
181
+ static int http1_on_path(http1_parser_s *parser, char *path, size_t path_len) {
182
+ http1_protocol_s *pr = parser->udata;
183
+ if (!pr)
184
+ return -1;
185
+ pr->request.request.path = path;
186
+ pr->request.request.path_len = path_len;
187
+ return 0;
188
+ }
189
+
190
+ /** called when a request path (excluding query) is parsed. */
191
+ static int http1_on_query(http1_parser_s *parser, char *query,
192
+ size_t query_len) {
193
+ http1_protocol_s *pr = parser->udata;
194
+ if (!pr)
195
+ return -1;
196
+ pr->request.request.query = query;
197
+ pr->request.request.query_len = query_len;
198
+ return 0;
199
+ }
200
+
201
+ /** called when a the HTTP/1.x version is parsed. */
202
+ static int http1_on_http_version(http1_parser_s *parser, char *version,
203
+ size_t len) {
204
+ http1_protocol_s *pr = parser->udata;
205
+ if (!pr)
206
+ return -1;
207
+ pr->request.request.version = version;
208
+ pr->request.request.version_len = len;
209
+ return 0;
210
+ }
211
+
212
+ /** called when a header is parsed. */
213
+ static int http1_on_header(http1_parser_s *parser, char *name, size_t name_len,
214
+ char *data, size_t data_len) {
215
+ http1_protocol_s *pr = parser->udata;
216
+ if (!pr || pr->request.header_pos >= HTTP1_MAX_HEADER_COUNT - 1)
217
+ goto too_big;
218
+
219
+ /** test for special headers that should be easily accessible **/
220
+ #if HTTP_HEADERS_LOWERCASE
221
+ if (name_len == 4 && *((uint32_t *)name) == *((uint32_t *)"host")) {
222
+ pr->request.request.host = data;
223
+ pr->request.request.host_len = data_len;
224
+ } else if (name_len == 12 && *((uint32_t *)name) == *((uint32_t *)"cont") &&
225
+ *((uint64_t *)(name + 4)) == *((uint64_t *)"ent-type")) {
226
+ pr->request.request.content_type = data;
227
+ pr->request.request.content_type_len = data_len;
228
+ } else if (name_len == 7 && *((uint64_t *)name) == *((uint64_t *)"upgrade")) {
229
+ pr->request.request.upgrade = data;
230
+ pr->request.request.upgrade_len = data_len;
231
+ } else if (name_len == 10 && *((uint32_t *)name) == *((uint32_t *)"conn") &&
232
+ *((uint64_t *)(name + 2)) == *((uint64_t *)"nnection")) {
233
+ pr->request.request.connection = data;
234
+ pr->request.request.connection_len = data_len;
248
235
  }
236
+ #else
237
+ if (name_len == 4 && HEADER_NAME_IS_EQ(name, "host", name_len)) {
238
+ pr->request.request.host = data;
239
+ pr->request.request.host_len = data_len;
240
+ } else if (name_len == 12 &&
241
+ HEADER_NAME_IS_EQ(name, "content-type", name_len)) {
242
+ pr->request.request.content_type = data;
243
+ pr->request.request.content_type_len = data_len;
244
+ } else if (name_len == 7 && HEADER_NAME_IS_EQ(name, "upgrade", name_len)) {
245
+ pr->request.request.upgrade = data;
246
+ pr->request.request.upgrade_len = data_len;
247
+ } else if (name_len == 10 &&
248
+ HEADER_NAME_IS_EQ(name, "connection", name_len)) {
249
+ pr->request.request.connection = data;
250
+ pr->request.request.connection_len = data_len;
251
+ }
252
+ #endif
253
+ pr->request.headers[pr->request.header_pos].name = name;
254
+ pr->request.headers[pr->request.header_pos].name_len = name_len;
255
+ pr->request.headers[pr->request.header_pos].data = data;
256
+ pr->request.headers[pr->request.header_pos].data_len = data_len;
257
+ pr->request.header_pos++;
258
+ pr->request.request.headers_count++;
259
+ return 0;
249
260
  too_big:
250
261
  /* handle oversized headers */
251
262
  {
252
- http_response_s *response = http_response_create(&request->request);
263
+ http_response_s *response = http_response_create(&pr->request.request);
264
+ if (pr->settings->log_static)
265
+ http_response_log_start(response);
253
266
  response->status = 431;
254
267
  if (!pr->settings->public_folder ||
255
- http_response_sendfile2(response, &request->request,
268
+ http_response_sendfile2(response, &pr->request.request,
256
269
  pr->settings->public_folder,
257
270
  pr->settings->public_folder_length, "431.html",
258
271
  8, pr->settings->log_static)) {
259
272
  http_response_write_body(response, "Request Header Fields Too Large", 31);
260
273
  http_response_finish(response);
261
274
  }
262
- sock_close(uuid);
263
- request->buffer_pos = 0;
264
- goto finished_reading;
265
- body_to_big:
266
- /* handle oversized body */
267
- {
268
- http_response_s *response = http_response_create(&request->request);
269
- response->status = 413;
270
- if (!pr->settings->public_folder ||
271
- http_response_sendfile2(response, &request->request,
272
- pr->settings->public_folder,
273
- pr->settings->public_folder_length,
274
- "413.html", 8, pr->settings->log_static)) {
275
- http_response_write_body(response, "Payload Too Large", 17);
276
- http_response_finish(response);
277
- }
278
- sock_close(uuid);
279
- request->buffer_pos = 0;
280
- goto finished_reading;
275
+ }
276
+ return -1;
277
+ }
278
+
279
+ /** called when a body chunk is parsed. */
280
+ static int http1_on_body_chunk(http1_parser_s *parser, char *data,
281
+ size_t data_len) {
282
+ http1_protocol_s *pr = parser->udata;
283
+ if (!pr)
284
+ return -1;
285
+ if (!parser->state.read) {
286
+ if (parser->state.content_length > pr->settings->max_body_size)
287
+ return -1;
288
+ pr->request.request.content_length = parser->state.content_length;
289
+ if (pr->request.buffer_pos + parser->state.content_length <
290
+ HTTP1_MAX_HEADER_SIZE) {
291
+ pr->request.request.body_str = data;
292
+ } else {
293
+ // create a temporary file to contain the data.
294
+ #ifdef P_tmpdir
295
+ #if defined(__linux__) /* linux doesn't end with a divider */
296
+ char template[] = P_tmpdir "/http_request_body_XXXXXXXX";
297
+ #else
298
+ char template[] = P_tmpdir "http_request_body_XXXXXXXX";
299
+ #endif
300
+ #else
301
+ char template[] = "/tmp/http_request_body_XXXXXXXX";
302
+ #endif
303
+ pr->request.request.body_file = mkstemp(template);
304
+ if (pr->request.request.body_file == -1)
305
+ return -1;
281
306
  }
282
307
  }
283
- finished_reading:
284
- pr->len = 0;
308
+ if (pr->request.request.body_file) {
309
+ if (write(pr->request.request.body_file, data, data_len) !=
310
+ (ssize_t)data_len)
311
+ return -1;
312
+ } else {
313
+ /* nothing to do... the parser and `on_data` are doing all the work */
314
+ }
315
+ return 0;
316
+ }
317
+
318
+ /** called when a protocol error occured. */
319
+ static int http1_on_error(http1_parser_s *parser) {
320
+ http1_protocol_s *pr = parser->udata;
321
+ if (!pr)
322
+ return -1;
323
+ sock_close(pr->request.request.fd);
324
+ http1_request_clear(&pr->request.request);
325
+ return 0;
326
+ }
327
+
328
+ /* *****************************************************************************
329
+ HTTP/1.1 protocol callbacks
330
+ ***************************************************************************** */
331
+
332
+ /* parse and call callback */
333
+ static void http1_on_data(intptr_t uuid, http1_protocol_s *pr) {
334
+ size_t consumed;
335
+ char buff[HTTP_BODY_CHUNK_SIZE];
336
+ http1_request_s *request = &pr->request;
337
+ char *buffer = request->buffer;
338
+ ssize_t tmp = 0;
339
+ // handle requests with no file data
340
+ if (request->request.body_file <= 0) {
341
+ // read into the request buffer.
342
+ tmp = sock_read(uuid, request->buffer + request->buffer_pos,
343
+ HTTP1_MAX_HEADER_SIZE - request->buffer_pos);
344
+ if (tmp > 0) {
345
+ request->buffer_pos += tmp;
346
+ pr->len += tmp;
347
+ } else
348
+ tmp = 0;
349
+ buffer = request->buffer + request->buffer_pos - pr->len;
350
+ } else {
351
+ buffer = buff;
352
+ tmp = sock_read(uuid, buffer, HTTP_BODY_CHUNK_SIZE);
353
+ if (tmp > 0) {
354
+ request->buffer_pos += tmp;
355
+ pr->len += tmp;
356
+ } else
357
+ tmp = 0;
358
+ }
359
+
360
+ if (pr->len == 0)
361
+ return;
362
+
363
+ // parse HTTP data
364
+ consumed =
365
+ http1_fio_parser(.parser = &pr->parser, .buffer = buffer,
366
+ .length = pr->len, .on_request = http1_on_request,
367
+ .on_response = http1_on_response,
368
+ .on_method = http1_on_method,
369
+ .on_status = http1_on_status, .on_path = http1_on_path,
370
+ .on_query = http1_on_query,
371
+ .on_http_version = http1_on_http_version,
372
+ .on_header = http1_on_header,
373
+ .on_body_chunk = http1_on_body_chunk,
374
+ .on_error = http1_on_error);
375
+
376
+ // handle leftovers, if any
377
+ if (pr->refresh) {
378
+ pr->refresh = 0;
379
+ if (pr->len > consumed) {
380
+ memmove(request->buffer, buffer + consumed, pr->len - consumed);
381
+ pr->len = pr->len - consumed;
382
+ http1_request_clear(&request->request);
383
+ request->buffer_pos = pr->len;
384
+ facil_force_event(uuid, FIO_EVENT_ON_DATA);
385
+ return;
386
+ }
387
+ http1_request_clear(&request->request);
388
+ pr->len = 0;
389
+ } else {
390
+ pr->len = pr->len - consumed;
391
+ }
392
+ if (tmp)
393
+ facil_force_event(uuid, FIO_EVENT_ON_DATA);
285
394
  }
286
395
 
287
396
  /* *****************************************************************************
@@ -353,8 +462,8 @@ is_busy:
353
462
 
354
463
  busy_no_file:
355
464
  sock_write(fd,
356
- "HTTP/1.1 503 Service Unavailable\r\nContent-Length: "
357
- "13\r\n\r\nServer Busy.",
465
+ "HTTP/1.1 503 Service Unavailable\r\n"
466
+ "Content-Length: 13\r\n\r\nServer Busy.",
358
467
  68);
359
468
  sock_close(fd);
360
469
  return NULL;