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 +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +1 -1
- data/ext/iodine/http.c +34 -4
- data/ext/iodine/http.h +8 -0
- data/ext/iodine/http1.c +269 -160
- data/ext/iodine/http1_parser.c +276 -0
- data/ext/iodine/http1_parser.h +111 -0
- data/ext/iodine/http1_response.c +8 -8
- data/ext/iodine/http_response.c +1 -16
- data/lib/iodine/version.rb +1 -1
- metadata +4 -4
- data/ext/iodine/http1_simple_parser.c +0 -496
- data/ext/iodine/http1_simple_parser.h +0 -68
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08f011b1bc43d3c47d266fc0e2dab7379708e5de'
|
4
|
+
data.tar.gz: 1197234946e68f0515ce51d3f8858d948a0a77ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aab2abfb29dea3efd0041c31abfbe93b3aac2fa21cf48fa48629263d58d6b636ff054d43acc33b3c7e24904a7240e42ef6e865218b1530bce530bf785adc4ef6
|
7
|
+
data.tar.gz: 4ed2825719fc986132ecbdb28d3c56c2bc23e752f1d67df794a92fc12c5f89c84c0b914ef7ad49bece19d2f01ac3790ddce73b858fe024a1fffaeab985eba6aa
|
data/CHANGELOG.md
CHANGED
@@ -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)
|
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
|
|
data/ext/iodine/http.c
CHANGED
@@ -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",
|
219
|
-
|
220
|
-
|
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') \
|
data/ext/iodine/http.h
CHANGED
@@ -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
|
|
data/ext/iodine/http1.c
CHANGED
@@ -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 "
|
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 *
|
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
|
-
|
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
|
-
|
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
|
-
|
115
|
-
static
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
119
|
+
pr->request.request.settings = settings;
|
194
120
|
// make sure udata to NULL, making it available for the user
|
195
|
-
|
196
|
-
// call request callback
|
197
|
-
if (pr
|
198
|
-
(
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
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
|
-
|
207
|
-
|
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(&
|
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, &
|
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(
|
246
|
-
request
|
247
|
-
|
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(&
|
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, &
|
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
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
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
|
-
|
284
|
-
|
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\
|
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;
|