15 |
16 | #define MAX_EXPIRE_HOURS = 87600
17 |
18 | #define FILE_TYPE_COUNT 10
19 |
20 | typedef struct _mime_type {
21 | char *content_type;
22 | char *exts[FILE_TYPE_COUNT];
23 | } mime_type_t;
24 |
25 | mime_type_t standard_types[] = {
26 | {"text/html", {"html", "htm", "shtml", NULL}},
27 | {"text/css", {"css", NULL}},
28 | {"text/xml", {"xml", NULL}},
29 | {"text/plain", {"txt", NULL}},
30 | {"image/png", {"png", NULL}},
31 | {"image/gif", {"gif", NULL}},
32 | {"image/tiff", {"tif", "tiff", NULL}},
33 | {"image/jpeg", {"jpg", "jpeg", NULL}},
34 | {"image/x-ms-bmp", {"bmp", NULL}},
35 | {"image/svg+xml", {"svg", "svgz", NULL}},
36 | {"application/x-javascript", {"js", NULL}}
37 | };
38 |
39 | static struct hsearch_data std_mime_type_hash;
40 |
41 | /*
42 | * dir_filter uses this flag to decide whether to filter out
43 | * hidden files (dot files). Since C does not have closure, we
44 | * have to use a global variable to hack it.
45 | * It is set to the value of a
46 | * mod_static_conf_t.show_hidden_file before listing the file.
47 | */
48 | static int show_hidden_file = 0;
49 |
50 | static void *mod_static_conf_create(json_value *conf_value);
51 | static void mod_static_conf_destroy(void *conf);
52 |
53 | static int static_file_write_content(request_t *req,
54 | response_t *resp,
55 | handler_ctx_t *ctx);
56 | static int static_file_cleanup(request_t *req,
57 | response_t *resp,
58 | handler_ctx_t *ctx);
59 |
60 | static int static_file_handle_error(response_t *resp, int fd);
61 | static void handle_content_type(response_t *resp, const char *filepath);
62 | static int handle_cache(request_t *req, response_t *resp,
63 | const struct stat *st, const mod_static_conf_t *conf);
64 | static int handle_range(request_t *req, response_t *resp,
65 | size_t *offset, size_t *size);
66 | static char* generate_etag(const struct stat *st);
67 | static int try_open_file(const char *path, int *fd, struct stat *st);
68 | static int static_file_listdir(response_t *resp, const char *path,
69 | const char *realpath);
70 | static int dir_filter(const struct dirent *ent);
71 |
72 | /* Module descriptor */
73 | module_t mod_static = {
74 | "static",
75 | mod_static_init,
76 | mod_static_conf_create,
77 | mod_static_conf_destroy,
78 | static_file_handle
79 | };
80 |
81 | int mod_static_init() {
82 | int i;
83 | int j;
84 | size_t size;
85 | ENTRY item, *ret;
86 | char **ext = NULL;
87 |
88 | size = sizeof(standard_types) / sizeof(standard_types[0]);
89 |
90 | bzero(&std_mime_type_hash, sizeof(struct hsearch_data));
91 | if (hcreate_r(size * 2, &std_mime_type_hash) == 0) {
92 | error("Error creating standard MIME type hash");
93 | return -1;
94 | }
95 | for (i = 0; i < size; i++) {
96 | for (ext = standard_types[i].exts, j = 0;
97 | *ext != NULL && j < FILE_TYPE_COUNT;
98 | ext++, j++) {
99 | item.key = *ext;
100 | item.data = standard_types[i].content_type;
101 | debug("Registering standard MIME type %s:%s",
102 | *ext, standard_types[i].content_type);
103 | if (hsearch_r(item, ENTER, &ret, &std_mime_type_hash) == 0) {
104 | error("Error entering standard MIME type");
105 | }
106 | }
107 | }
108 | return 0;
109 | }
110 |
111 | static void *mod_static_conf_create(json_value *conf_value) {
112 | mod_static_conf_t *conf = NULL;
113 | DECLARE_CONF_VARIABLES()
114 |
115 | conf = (mod_static_conf_t*) calloc(1, sizeof(mod_static_conf_t));
116 | if (conf == NULL) {
117 | return NULL;
118 | }
119 |
120 | BEGIN_CONF_HANDLE(conf_value)
121 | ON_STRING_CONF("root", conf->root)
122 | ON_STRING_CONF("index", conf->index)
123 | ON_BOOLEAN_CONF("list_dir", conf->enable_list_dir)
124 | ON_INTEGER_CONF("expires", conf->expire_hours)
125 | ON_BOOLEAN_CONF("etag", conf->enable_etag)
126 | ON_BOOLEAN_CONF("range_request", conf->enable_range_req)
127 | END_CONF_HANDLE()
128 | return conf;
129 | }
130 |
131 | static void mod_static_conf_destroy(void *conf) {
132 | free(conf);
133 | }
134 |
135 | static int try_open_file(const char *path, int *fdptr, struct stat *st) {
136 | int fd, res;
137 |
138 | debug("Try opening file: %s", path);
139 | res = stat(path, st);
140 | if (res < 0) {
141 | return -1;
142 | }
143 |
144 | if (S_ISDIR(st->st_mode)) {
145 | return 1;
146 | } else if (S_ISREG(st->st_mode)) {
147 | fd = open(path, O_RDONLY);
148 | if (fd < 0) {
149 | return -1;
150 | }
151 | *fdptr = fd;
152 | return 0;
153 | } else {
154 | return -1;
155 | }
156 | }
157 |
158 | const char *listdir_header =
159 | ""
160 | "Index of %s"
161 | ""
162 | "Index of %s
"
163 | "
"
164 | "";
165 |
166 | const char *listdir_file = "%s\r\n";
167 | const char *listdir_dir = "%s/\r\n";
168 |
169 | const char *listdir_footer =
170 | "
"
171 | "
"
172 | "Powered by "
173 | "%s"
174 | "
"
175 | ""
176 | "";
177 |
178 | static int static_file_listdir(response_t *resp, const char *path,
179 | const char *realpath) {
180 | struct dirent **ent_list, *ent;
181 | int ent_len;
182 | char buf[2048];
183 | int pos = 0, i;
184 |
185 | debug("Opening dir: %s", realpath);
186 | if ((ent_len = scandir(realpath, &ent_list, dir_filter, versionsort)) < 0) {
187 | return static_file_handle_error(resp, -1);
188 | }
189 | resp->status = STATUS_OK;
190 | resp->connection = CONN_CLOSE;
191 | response_set_header(resp, "Content-Type", "text/html; charset=UTF-8");
192 | response_send_headers(resp, NULL);
193 | pos += snprintf(buf, 2048, listdir_header, path, path);
194 | for (i = 0; i < ent_len; i++) {
195 | ent = ent_list[i];
196 | pos += snprintf(buf + pos, 2048 - pos,
197 | ent->d_type == DT_DIR ? listdir_dir : listdir_file,
198 | ent->d_name, ent->d_name);
199 | if (2048 - pos < 255) {
200 | response_write(resp, buf, pos, NULL);
201 | pos = 0;
202 | }
203 | free(ent);
204 | }
205 | free(ent_list);
206 | pos += snprintf(buf + pos, 2048 - pos, listdir_footer, _BREEZE_NAME);
207 | response_write(resp, buf, pos, NULL);
208 |
209 | return HANDLER_DONE;
210 | }
211 |
212 | static int dir_filter(const struct dirent *ent) {
213 | const char *name = ent->d_name;
214 | if (name[0] == '.') {
215 | if (name[1] == '\0')
216 | return 0; // Skip "."
217 | else if (name[1] == '.' && name[2] == '\0')
218 | return 1; // Show ".." for parent dir
219 | else if (!show_hidden_file)
220 | return 0;
221 | }
222 | return 1;
223 | }
224 |
225 | int static_file_handle(request_t *req, response_t *resp,
226 | handler_ctx_t *ctx) {
227 | mod_static_conf_t *conf;
228 | char path[2048];
229 | int fd = -1, res, use_301;
230 | struct stat st;
231 | size_t len, pathlen, filesize, fileoffset;
232 | ctx_state_t val;
233 |
234 | conf = (mod_static_conf_t*) ctx->conf;
235 | len = strlen(conf->root);
236 | strncpy(path, conf->root, 2048);
237 | if (path[len - 1] == '/') {
238 | path[len - 1] = '\0';
239 | }
240 | if (req->path[0] != '/') {
241 | return response_send_status(resp, STATUS_BAD_REQUEST);
242 | }
243 | strncat(path, req->path, 2048 - len);
244 | debug("Request path: %s, real file path: %s", req->path, path);
245 | res = try_open_file(path, &fd, &st);
246 | if (res < 0) {
247 | return static_file_handle_error(resp, fd);
248 | } else if (res > 0) { // Is a directory, try index files.
249 | pathlen = strlen(path);
250 | use_301 = 0;
251 | if (path[pathlen - 1] != '/') {
252 | path[pathlen] = '/';
253 | path[pathlen + 1] = '\0';
254 | pathlen++;
255 | use_301 = 1;
256 | }
257 | //for (i = 0; i < 10 && res != 0 && conf->index[i] != NULL; i++) {
258 | // path[pathlen] = '\0';
259 | // strncat(path, conf->index[i], 2048 - pathlen);
260 | // res = try_open_file(path, &fd, &st);
261 | //}
262 | path[pathlen] = '\0';
263 | strncat(path, conf->index, 2048 - pathlen);
264 | res = try_open_file(path, &fd, &st);
265 | path[pathlen] = '\0';
266 | if (res != 0) {
267 | if (conf->enable_list_dir) {
268 | if (use_301) {
269 | // TODO Support HTTPS
270 | snprintf(path, 2048, "http://%s%s/", req->host, req->path);
271 | response_set_header(resp, "Location", path);
272 | resp->status = STATUS_MOVED;
273 | resp->content_length = 0;
274 | response_send_headers(resp, NULL);
275 | return HANDLER_DONE;
276 | }
277 | show_hidden_file = conf->show_hidden_file;
278 | return static_file_listdir(resp, req->path, path);
279 | } else {
280 | return static_file_handle_error(resp, fd);
281 | }
282 | }
283 | }
284 |
285 | fileoffset = 0;
286 | filesize = st.st_size;
287 | res = handle_range(req, resp, &fileoffset, &filesize);
288 | if (res < 0) {
289 | resp->status = STATUS_OK;
290 | } else if (res == 0) {
291 | resp->status = STATUS_PARTIAL_CONTENT;
292 | } else {
293 | return response_send_status(resp, STATUS_RANGE_NOT_SATISFIABLE);
294 | }
295 |
296 | resp->content_length = filesize;
297 | handle_content_type(resp, path);
298 | if (handle_cache(req, resp, &st, conf)) {
299 | return response_send_status(resp, STATUS_NOT_MODIFIED);
300 | }
301 |
302 | val.as_int = fd;
303 | context_push(ctx, val);
304 | val.as_long = fileoffset;
305 | context_push(ctx, val);
306 | val.as_long = filesize;
307 | context_push(ctx, val);
308 | debug("sending headers");
309 | response_send_headers(resp, static_file_write_content);
310 | return HANDLER_UNFISHED;
311 | }
312 |
313 | /*
314 | * Return values:
315 | * 0: valid range request
316 | * 1: range not satisfiable (should return 416)
317 | * -1: syntactically invalid range (ignore, return full content)
318 | */
319 | static int handle_range(request_t *req, response_t *resp,
320 | size_t *offset, size_t *size) {
321 | const char *range_spec;
322 | char buf[100], *pos;
323 | size_t len, total_size = *size, off, sz, end;
324 | int idx;
325 |
326 | range_spec = request_get_header(req, "range");
327 | if (range_spec == NULL) {
328 | return -1;
329 | }
330 | len = strlen(range_spec);
331 | if (len < 8) {
332 | return -1;
333 | }
334 | if (strstr(range_spec, "bytes=") != range_spec) {
335 | error("Only byte ranges are supported(error range:%s)", range_spec);
336 | return -1;
337 | }
338 | strncpy(buf, range_spec + 6, 100);
339 | len = strlen(buf);
340 | if (index(buf, ',') != NULL) {
341 | error("Multiple ranges are not supported.");
342 | return -1;
343 | }
344 | pos = index(buf, '-');
345 | if (pos == NULL) {
346 | error("Invalid range spec: %s.", range_spec);
347 | return -1;
348 | }
349 | idx = pos - buf;
350 | if (idx == 0) {
351 | sz = atol(buf + 1);
352 | end = total_size;
353 | off = total_size - sz;
354 | } else if (idx == len - 1) {
355 | buf[idx] = '\0';
356 | off = atol(buf);
357 | end = total_size;
358 | sz = total_size - off;
359 | } else {
360 | buf[idx] = '\0';
361 | off = atol(buf);
362 | end = atol(buf + idx + 1);
363 | sz = end - off + 1;
364 | }
365 |
366 | if (end < off)
367 | return -1;
368 | if (off >= total_size || sz > total_size)
369 | return 1;
370 |
371 | response_set_header_printf(resp, "Content-Range", "bytes %ld-%ld/%ld",
372 | off, off + sz - 1, total_size);
373 | *offset = off;
374 | *size = sz;
375 | return 0;
376 | }
377 |
378 | static void handle_content_type(response_t *resp, const char *filepath) {
379 | char *content_type = NULL, ext[20];
380 | int dot_pos = -1;
381 | int i;
382 | size_t len = strlen(filepath);
383 | ENTRY item, *ret;
384 |
385 | for (i = len - 1; i > 0; i--) {
386 | if (filepath[i] == '.') {
387 | dot_pos = i;
388 | break;
389 | }
390 | }
391 |
392 | if (dot_pos < 0) {
393 | // No '.' found in the file name (no extension part)
394 | return;
395 | }
396 |
397 | strncpy(ext, filepath + 1 + dot_pos, 20);
398 | strlowercase(ext, ext, 20);
399 | debug("File extension: %s", ext);
400 |
401 | item.key = ext;
402 | if (hsearch_r(item, FIND, &ret, &std_mime_type_hash) == 0) {
403 | return;
404 | }
405 | content_type = (char*) ret->data;
406 | if (content_type != NULL) {
407 | debug("Content type: %s", content_type);
408 | response_set_header(resp, "Content-Type", content_type);
409 | }
410 | }
411 |
412 | static int handle_cache(request_t *req, response_t *resp,
413 | const struct stat *st, const mod_static_conf_t *conf) {
414 | const char *if_mod_since, *if_none_match;
415 | char *etag;
416 | time_t mtime, req_mtime;
417 | int not_modified = 0;
418 | char *buf;
419 | char *cache_control;
420 |
421 | mtime = st->st_mtime;
422 | if_mod_since = request_get_header(req, "if-modified-since");
423 | if (if_mod_since != NULL &&
424 | parse_http_date(if_mod_since, &req_mtime) == 0 &&
425 | req_mtime == mtime) {
426 | debug("Resource not modified");
427 | not_modified = 1;
428 | }
429 | buf = response_alloc(resp, 32);
430 | format_http_date(&mtime, buf, 32);
431 | response_set_header(resp, "Last-Modified", buf);
432 |
433 | if (conf->enable_etag) {
434 | etag = generate_etag(st);
435 | if (not_modified) {
436 | if_none_match = request_get_header(req, "if-none-match");
437 | if (if_none_match == NULL ||
438 | strcmp(etag, if_none_match) != 0) {
439 | not_modified = 0;
440 | }
441 | }
442 | response_set_header(resp, "ETag", etag);
443 | }
444 |
445 | if (conf->expire_hours >= 0) {
446 | buf = response_alloc(resp, 32);
447 | mtime += conf->expire_hours * 3600;
448 | format_http_date(&mtime, buf, 32);
449 | response_set_header(resp, "Expires", buf);
450 | cache_control = response_alloc(resp, 20);
451 | snprintf(cache_control, 20, "max-age=%d", conf->expire_hours * 3600);
452 | } else {
453 | cache_control = "no-cache";
454 | }
455 | response_set_header(resp, "Cache-Control", cache_control);
456 | return not_modified;
457 | }
458 |
459 | static char* generate_etag(const struct stat *st) {
460 | char tag_buf[128];
461 | snprintf(tag_buf, 128, "etag-%ld-%zu", st->st_mtime, st->st_size);
462 | return crypt(tag_buf, "$1$breeze") + 10; // Skip the $id$salt part
463 | }
464 |
465 | static int static_file_write_content(request_t *req, response_t *resp, handler_ctx_t *ctx) {
466 | int fd;
467 | size_t size, offset;
468 |
469 | size = context_pop(ctx)->as_long;
470 | offset = context_pop(ctx)->as_long;
471 | fd = context_peek(ctx)->as_int;
472 | debug("writing file");
473 | if (response_send_file(resp, fd, offset, size, static_file_cleanup) < 0) {
474 | error("Error sending file");
475 | return response_send_status(resp, STATUS_NOT_FOUND);
476 | }
477 | return HANDLER_UNFISHED;
478 | }
479 |
480 | static int static_file_cleanup(request_t *req, response_t *resp, handler_ctx_t *ctx) {
481 | int fd;
482 |
483 | debug("cleaning up");
484 | fd = context_pop(ctx)->as_int;
485 | close(fd);
486 | return HANDLER_DONE;
487 | }
488 |
489 | static int static_file_handle_error(response_t *resp, int fd) {
490 | int err = errno;
491 | if (fd > 0)
492 | (void) close(fd); // Don't care the failure
493 | switch(err) {
494 | case EACCES:
495 | case EISDIR:
496 | return response_send_status(resp, STATUS_FORBIDDEN);
497 | case ENOENT:
498 | default:
499 | return response_send_status(resp, STATUS_NOT_FOUND);
500 | }
501 | }
502 |
503 |
504 |
--------------------------------------------------------------------------------
/src/iostream.c:
--------------------------------------------------------------------------------
1 | #include "iostream.h"
2 | #include "ioloop.h"
3 | #include "buffer.h"
4 | #include "log.h"
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | enum READ_OP_TYPES {
18 | READ_BYTES = 1,
19 | READ_UNTIL = 2
20 | };
21 |
22 | enum STREAM_STATE {
23 | NORMAL = 1,
24 | CLOSED = 2
25 | };
26 |
27 | enum WRITE_STATE {
28 | NOT_WRITING,
29 | WRITE_BUFFER,
30 | SEND_FILE
31 | };
32 |
33 | #define is_reading(stream) ((stream)->read_callback != NULL)
34 | #define is_writing(stream) ((stream)->write_callback != NULL)
35 | #define is_closed(stream) ((stream)->state == CLOSED)
36 |
37 | #define check_reading(stream) \
38 | if (is_reading(stream)) { \
39 | return -1; \
40 | }
41 |
42 | #define check_writing(stream) \
43 | if (is_writing(stream)) { \
44 | return -1; \
45 | }
46 |
47 | static void _handle_io_events(ioloop_t *loop, int fd, unsigned int events, void *args);
48 | static void _handle_error(iostream_t *stream, unsigned int events);
49 | static int _handle_read(iostream_t *stream);
50 | static int _handle_write(iostream_t *stream);
51 | static int _handle_sendfile(iostream_t *stream);
52 | static int _add_event(iostream_t *stream, unsigned int events);
53 |
54 | static ssize_t _read_from_socket(iostream_t *stream);
55 | static int _read_from_buffer(iostream_t *stream);
56 | static ssize_t _write_to_buffer(iostream_t *stream, void *data, size_t len);
57 | static int _write_to_socket(iostream_t *stream);
58 | static ssize_t _write_to_socket_direct(iostream_t *stream, void *data, size_t len);
59 |
60 | static void _finish_stream_callback(ioloop_t *loop, void *args);
61 | static void _finish_read_callback(ioloop_t *loop, void *args);
62 | static void _finish_write_callback(ioloop_t *loop, void *args);
63 | static void _close_callback(ioloop_t *loop, void *args);
64 | static void _destroy_callback(ioloop_t *loop, void *args);
65 |
66 | static void _stream_consumer_func(void *data, size_t len, void *args);
67 |
68 | iostream_t *iostream_create(ioloop_t *loop,
69 | int sockfd,
70 | size_t read_buf_capacity,
71 | size_t write_buf_capacity,
72 | void *user_data) {
73 | iostream_t *stream;
74 | buffer_t *in_buf = NULL, *out_buf = NULL;
75 |
76 | stream = (iostream_t*) calloc(1, sizeof(iostream_t));
77 | if (stream == NULL) {
78 | error("Error allocating memory for IO stream");
79 | goto error;
80 | }
81 | bzero(stream, sizeof(iostream_t));
82 |
83 | in_buf = buffer_create(read_buf_capacity);
84 | if (in_buf == NULL ) {
85 | error("Error creating read buffer");
86 | goto error;
87 | }
88 | out_buf = buffer_create(write_buf_capacity);
89 | if (out_buf == NULL) {
90 | error("Error creating write buffer");
91 | goto error;
92 | }
93 |
94 | stream->events = EPOLLERR;
95 | stream->write_buf = out_buf;
96 | stream->write_buf_cap = write_buf_capacity;
97 | stream->read_buf = in_buf;
98 | stream->read_buf_cap = read_buf_capacity;
99 | stream->fd = sockfd;
100 | stream->state = NORMAL;
101 | stream->ioloop = loop;
102 | stream->read_callback = NULL;
103 | stream->write_callback = NULL;
104 | stream->close_callback = NULL;
105 | stream->error_callback = NULL;
106 | stream->sendfile_fd = -1;
107 | stream->user_data = user_data;
108 |
109 | if (ioloop_add_handler(stream->ioloop,
110 | stream->fd,
111 | stream->events,
112 | _handle_io_events,
113 | stream) < 0) {
114 | error("Error add EPOLLERR event");
115 | goto error;
116 | }
117 |
118 | return stream;
119 |
120 | error:
121 | if (in_buf != NULL)
122 | buffer_destroy(in_buf);
123 | if (out_buf != NULL)
124 | buffer_destroy(out_buf);
125 | free(stream);
126 | return NULL;
127 | }
128 |
129 | int iostream_close(iostream_t *stream) {
130 | if (is_closed(stream)) {
131 | return -1;
132 | }
133 | stream->state = CLOSED;
134 | // Defer the close action to next loop, because there may be
135 | // pending read/write operations.
136 | ioloop_add_callback(stream->ioloop, _close_callback, stream);
137 | return 0;
138 | }
139 |
140 | static void _close_callback(ioloop_t *loop, void *args) {
141 | iostream_t *stream = (iostream_t*) args;
142 | ioloop_remove_handler(stream->ioloop, stream->fd);
143 | stream->close_callback(stream);
144 | close(stream->fd);
145 | // Defer the destroy action to next loop, in case there are
146 | // pending callbacks of this stream.
147 | ioloop_add_callback(stream->ioloop, _destroy_callback, stream);
148 | }
149 |
150 | static void _destroy_callback(ioloop_t *loop, void *args) {
151 | iostream_t *stream = (iostream_t*) args;
152 | debug("IO stream(fd:%d) destroyed.", stream->fd);
153 | iostream_destroy(stream);
154 | }
155 |
156 | int iostream_destroy(iostream_t *stream) {
157 | buffer_destroy(stream->read_buf);
158 | buffer_destroy(stream->write_buf);
159 | free(stream);
160 | return 0;
161 | }
162 |
163 | int iostream_read_bytes(iostream_t *stream,
164 | size_t sz,
165 | read_handler callback,
166 | read_handler stream_callback) {
167 | check_reading(stream);
168 | if (sz == 0) {
169 | return -1;
170 | }
171 | stream->read_callback = callback;
172 | stream->stream_callback = stream_callback;
173 | stream->read_bytes = sz;
174 | stream->read_type = READ_BYTES;
175 | for (;;) {
176 | if (_read_from_buffer(stream)) {
177 | return 0;
178 | }
179 | if (is_closed(stream)) {
180 | return -1;
181 | }
182 | if (_read_from_socket(stream) == 0) {
183 | break;
184 | }
185 | }
186 | _add_event(stream, EPOLLIN);
187 | return 0;
188 | }
189 |
190 | int iostream_read_until(iostream_t *stream, char *delimiter, read_handler callback) {
191 | check_reading(stream);
192 | assert(*delimiter != '\0');
193 | stream->read_callback = callback;
194 | stream->stream_callback = NULL;
195 | stream->read_delimiter = delimiter;
196 | stream->read_type = READ_UNTIL;
197 | for (;;) {
198 | if (_read_from_buffer(stream)) {
199 | return 0;
200 | }
201 | if (is_closed(stream)) {
202 | return -1;
203 | }
204 | if (_read_from_socket(stream) == 0) {
205 | break;
206 | }
207 | }
208 | _add_event(stream, EPOLLIN);
209 | return 0;
210 | }
211 |
212 | int iostream_write(iostream_t *stream, void *data, size_t len, write_handler callback) {
213 | ssize_t n;
214 | // Allow appending data to existing writing action
215 | if (is_writing(stream) && callback != stream->write_callback) {
216 | return -1;
217 | }
218 |
219 | if (len == 0) {
220 | return -1;
221 | }
222 |
223 | stream->write_callback = callback;
224 | stream->write_state = WRITE_BUFFER;
225 | n = _write_to_socket_direct(stream, data, len);
226 | if (n < 0) {
227 | return -1;
228 | } else if (n == len) {
229 | // Lucky! All the data are written directly, skipping the buffer completely.
230 | return 0;
231 | }
232 |
233 | if (_write_to_buffer(stream, (char*)data + n, len - n) < 0) {
234 | return -1;
235 | }
236 |
237 | // Try to write to the socket
238 | if (_write_to_socket(stream) > 0) {
239 | return 0;
240 | }
241 | _add_event(stream, EPOLLOUT);
242 | return 0;
243 | }
244 |
245 | int iostream_sendfile(iostream_t *stream, int in_fd,
246 | size_t offset, size_t len,
247 | write_handler callback) {
248 | struct stat st;
249 |
250 | check_writing(stream);
251 | if (len == 0) {
252 | return -1;
253 | }
254 | if (fstat(in_fd, &st) < 0) {
255 | error("The file to send is not valid");
256 | return -2;
257 | }
258 |
259 | switch (st.st_mode & S_IFMT) {
260 | case S_IFREG:
261 | case S_IFLNK:
262 | break;
263 |
264 | default:
265 | error("Unsupported file type: %d", st.st_mode);
266 | return -3;
267 | }
268 | stream->sendfile_fd = in_fd;
269 | stream->sendfile_len = len;
270 | stream->sendfile_offset = offset;
271 | stream->write_state = SEND_FILE;
272 | stream->write_callback = callback;
273 |
274 | if(_handle_sendfile(stream)) {
275 | return 0;
276 | }
277 | _add_event(stream, EPOLLOUT);
278 | return 0;
279 | }
280 |
281 | int iostream_set_error_handler(iostream_t *stream, error_handler callback) {
282 | stream->error_callback = callback;
283 | return 0;
284 | }
285 |
286 | int iostream_set_close_handler(iostream_t *stream, close_handler callback) {
287 | stream->close_callback = callback;
288 | return 0;
289 | }
290 |
291 | static void _handle_error(iostream_t *stream, unsigned int events) {
292 | error_handler err_cb = stream->error_callback;
293 | if (err_cb != NULL)
294 | stream->error_callback(stream, events);
295 | iostream_close(stream);
296 | }
297 |
298 | static void _handle_io_events(ioloop_t *loop,
299 | int fd,
300 | unsigned int events,
301 | void *args) {
302 | iostream_t *stream = (iostream_t*) args;
303 |
304 | if (events & EPOLLIN) {
305 | if (_handle_read(stream)) {
306 | stream->events &= ~EPOLLIN;
307 | }
308 | }
309 | if (events & EPOLLOUT) {
310 | if (_handle_write(stream)) {
311 | stream->events &= ~EPOLLOUT;
312 | }
313 | }
314 | if (events & EPOLLERR) {
315 | _handle_error(stream, events);
316 | return;
317 | }
318 | if (events & EPOLLHUP) {
319 | iostream_close(stream);
320 | return;
321 | }
322 |
323 | if (is_closed(stream)) {
324 | error("Stream closed");
325 | return;
326 | }
327 |
328 | ioloop_update_handler(stream->ioloop, stream->fd, stream->events);
329 | }
330 |
331 | static int _handle_read(iostream_t *stream) {
332 | if (!is_reading(stream)) {
333 | return 0;
334 | }
335 | _read_from_socket(stream);
336 | return _read_from_buffer(stream);
337 | }
338 |
339 | static int _handle_write(iostream_t *stream) {
340 | switch (stream->write_state) {
341 | case WRITE_BUFFER:
342 | return _write_to_socket(stream);
343 |
344 | case SEND_FILE:
345 | return _handle_sendfile(stream);
346 |
347 | default:
348 | return -1;
349 | }
350 | }
351 |
352 | static int _handle_sendfile(iostream_t *stream) {
353 | ssize_t sz;
354 |
355 | sz = sendfile(stream->fd, stream->sendfile_fd,
356 | &stream->sendfile_offset, stream->sendfile_len);
357 |
358 | if (sz < 0) {
359 | if (errno == EAGAIN || errno == EWOULDBLOCK) {
360 | return 0;
361 | } else {
362 | iostream_close(stream);
363 | return -1;
364 | }
365 | } else if (sz == 0 && stream->sendfile_len > 0) {
366 | // The lengh maybe longer than the actual available size. In
367 | // this case finish the write immediately.
368 | stream->sendfile_offset = 0;
369 | ioloop_add_callback(stream->ioloop, _finish_write_callback, stream);
370 | return 1;
371 | }
372 |
373 | stream->sendfile_len -= sz;
374 |
375 | if (stream->sendfile_len == 0) {
376 | stream->sendfile_offset = 0;
377 | ioloop_add_callback(stream->ioloop, _finish_write_callback, stream);
378 | return 1;
379 | }
380 |
381 | return 0;
382 | }
383 |
384 | static int _add_event(iostream_t *stream, unsigned int event) {
385 | if ((stream->events & event) == 0) {
386 | stream->events |= event;
387 | return ioloop_update_handler(stream->ioloop, stream->fd, stream->events);
388 | }
389 | return -1;
390 | }
391 |
392 | #define READ_SIZE 1024
393 |
394 | static ssize_t _read_from_socket(iostream_t *stream) {
395 | ssize_t n;
396 | n = buffer_fill(stream->read_buf, stream->fd);
397 | if (n < 0) {
398 | iostream_close(stream);
399 | return -1;
400 | }
401 | stream->read_buf_size += n;
402 | return n;
403 | }
404 |
405 |
406 | #define LOCAL_BUFSIZE 4096
407 |
408 | static int _read_from_buffer(iostream_t *stream) {
409 | int res = 0, idx;
410 |
411 | switch(stream->read_type) {
412 | case READ_BYTES:
413 | if (stream->stream_callback != NULL) {
414 | // Streaming mode, offer data
415 | if (stream->read_bytes <= 0) {
416 | res = 1;
417 | } else {
418 | ioloop_add_callback(stream->ioloop, _finish_stream_callback, stream);
419 | }
420 | } else if (stream->read_buf_size >= stream->read_bytes
421 | || buffer_is_full(stream->read_buf)
422 | || (stream->state == CLOSED && stream->read_buf_size > 0)) {
423 | ioloop_add_callback(stream->ioloop, _finish_read_callback, stream);
424 | res = 1;
425 | }
426 | break;
427 |
428 | case READ_UNTIL:
429 | idx = buffer_locate(stream->read_buf, stream->read_delimiter);
430 | if (idx > 0
431 | || buffer_is_full(stream->read_buf)
432 | || (stream->state == CLOSED && stream->read_buf_size > 0)) {
433 | stream->read_bytes = idx > 0
434 | ? (idx + strlen(stream->read_delimiter))
435 | : stream->read_buf_size;
436 | ioloop_add_callback(stream->ioloop, _finish_read_callback, stream);
437 | res = 1;
438 | }
439 | break;
440 | }
441 |
442 | return res;
443 | }
444 |
445 |
446 | static void _finish_stream_callback(ioloop_t *loop, void *args) {
447 | iostream_t *stream = (iostream_t*) args;
448 | read_handler callback = stream->read_callback;
449 |
450 | buffer_consume(stream->read_buf, stream->read_bytes, _stream_consumer_func, stream);
451 | if (stream->read_bytes <= 0) {
452 | stream->read_callback = NULL;
453 | stream->read_bytes = 0;
454 | // When streaming ends, call the read_callback with NULL to indicate the finish.
455 | callback(stream, NULL, 0);
456 | }
457 | }
458 |
459 | static void _finish_read_callback(ioloop_t *loop, void *args) {
460 | char local_buf[LOCAL_BUFSIZE];
461 | iostream_t *stream = (iostream_t*) args;
462 | read_handler callback = stream->read_callback;
463 | size_t n;
464 |
465 | // Normal mode, call read callback
466 | n = buffer_get(stream->read_buf, stream->read_bytes, local_buf, LOCAL_BUFSIZE);
467 | // assert(n == stream->read_bytes);
468 | callback = stream->read_callback;
469 | stream->read_callback = NULL;
470 | stream->read_bytes = 0;
471 | stream->read_buf_size -= n;
472 | callback(stream, local_buf, n);
473 | }
474 |
475 | static void _finish_write_callback(ioloop_t *loop, void *args) {
476 | iostream_t *stream = (iostream_t*) args;
477 | write_handler callback = stream->write_callback;
478 |
479 | stream->write_callback = NULL;
480 | stream->write_state = NOT_WRITING;
481 | if (callback != NULL) {
482 | callback(stream);
483 | }
484 | }
485 |
486 | static void _stream_consumer_func(void *data, size_t len, void *args) {
487 | iostream_t *stream = (iostream_t*) args;
488 | stream->read_bytes -= len;
489 | stream->read_buf_size -= len;
490 | stream->stream_callback(stream, data, len);
491 | }
492 |
493 | static ssize_t _write_to_buffer(iostream_t *stream, void *data, size_t len) {
494 | if (len > stream->write_buf_cap - stream->write_buf_size) {
495 | return -1;
496 | }
497 | if (buffer_put(stream->write_buf, data, len) < 0) {
498 | return -1;
499 | }
500 | stream->write_buf_size += len;
501 | return 0;
502 | }
503 |
504 | #define WRITE_CHUNK_SIZE 1024
505 |
506 | static int _write_to_socket(iostream_t *stream) {
507 | ssize_t n;
508 |
509 | n = buffer_flush(stream->write_buf, stream->fd);
510 | if (n < 0) {
511 | iostream_close(stream);
512 | return -1;
513 | }
514 | stream->write_buf_size -= n;
515 |
516 | if (stream->write_buf_size == 0) {
517 | ioloop_add_callback(stream->ioloop, _finish_write_callback, stream);
518 | return 1;
519 | } else {
520 | return 0;
521 | }
522 | }
523 |
524 | static ssize_t _write_to_socket_direct(iostream_t *stream, void *data, size_t len) {
525 | ssize_t n;
526 |
527 | if (stream->write_buf_size > 0) {
528 | // If there is data in the buffer, we could not write to socket directly.
529 | return 0;
530 | }
531 |
532 | n = write(stream->fd, data, len);
533 | if (n < 0) {
534 | if (errno == EAGAIN || errno == EWOULDBLOCK) {
535 | return 0;
536 | } else {
537 | iostream_close(stream);
538 | return -1;
539 | }
540 | }
541 |
542 | if (n == len) {
543 | // If we could write all the data once, call the callback function now.
544 | ioloop_add_callback(stream->ioloop, _finish_write_callback, stream);
545 | }
546 |
547 | return n;
548 | }
549 |
550 |
--------------------------------------------------------------------------------
/src/http.c:
--------------------------------------------------------------------------------
1 | #include "common.h"
2 | #include "http.h"
3 | #include "log.h"
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | typedef enum _parser_state {
10 | PARSER_STATE_BAD_REQUEST = -1,
11 | PARSER_STATE_COMPLETE = 0,
12 | PARSER_STATE_METHOD,
13 | PARSER_STATE_PATH,
14 | PARSER_STATE_QUERY_STR,
15 | PARSER_STATE_VERSION,
16 | PARSER_STATE_HEADER_NAME,
17 | PARSER_STATE_HEADER_COLON,
18 | PARSER_STATE_HEADER_VALUE,
19 | PARSER_STATE_HEADER_CR,
20 | PARSER_STATE_HEADER_LF,
21 | PARSER_STATE_HEADER_COMPLETE_CR,
22 | } parser_state_e;
23 |
24 |
25 | typedef void (*http_header_callback)(request_t *req, http_header_t *header);
26 |
27 | typedef struct _header_command {
28 | char *lower_name;
29 | http_header_callback callback;
30 | } header_command_t;
31 |
32 | static struct hsearch_data std_headers_hash;
33 | static int std_headers_hash_initialized = 0;
34 |
35 | static http_version_e _resolve_http_version(const char* version_str);
36 | static int init_std_headers_hash();
37 | static void handle_common_header(request_t *req, int header_index);
38 | static void set_common_headers(response_t *resp);
39 | static void on_write_finished(iostream_t *stream);
40 |
41 | inline static const char* str_http_ver(http_version_e ver) {
42 | switch (ver) {
43 | case HTTP_VERSION_1_1:
44 | return "1.1";
45 |
46 | case HTTP_VERSION_1_0:
47 | return "1.0";
48 |
49 | case HTTP_VERSION_0_9:
50 | return "0.9";
51 |
52 | default:
53 | return "1.1";
54 | }
55 | }
56 |
57 | request_t* request_create(connection_t *conn) {
58 | request_t *req;
59 | req = (request_t*) calloc(1, sizeof(request_t));
60 | if (req == NULL) {
61 | return NULL;
62 | }
63 | bzero(req, sizeof(request_t));
64 | if (hcreate_r(MAX_HEADER_SIZE, &req->_header_hash) == 0) {
65 | error("Error creating header hash table");
66 | free(req);
67 | return NULL;
68 | }
69 | req->_conn = conn;
70 | return req;
71 | }
72 |
73 | int request_reset(request_t *req) {
74 | connection_t *conn = req->_conn;
75 | hdestroy_r(&req->_header_hash);
76 | bzero(req, sizeof(request_t));
77 | if (hcreate_r(MAX_HEADER_SIZE, &req->_header_hash) == 0) {
78 | error("Error creating header hash table");
79 | return -1;
80 | }
81 | req->_conn = conn;
82 | return 0;
83 | }
84 |
85 | int request_destroy(request_t *req) {
86 | hdestroy_r(&req->_header_hash);
87 | free(req);
88 | return 0;
89 | }
90 |
91 | const char* request_get_header(request_t *request, const char *header_name) {
92 | ENTRY item, *ret;
93 | char header_name_lower[64];
94 |
95 | strlowercase(header_name, header_name_lower, 64);
96 | item.key = header_name_lower;
97 |
98 | if (hsearch_r(item, FIND, &ret, &request->_header_hash) == 0) {
99 | return NULL;
100 | }
101 |
102 | return ((http_header_t*) ret->data)->value;
103 | }
104 |
105 | #define START_NEW_TOKEN(tok, req) \
106 | (tok = (req)->_buffer + (req)->_buf_idx)
107 |
108 | #define FILL_NEXT_CHAR(req, ch) \
109 | ((req)->_buffer[req->_buf_idx++] = (ch))
110 |
111 | #define FINISH_CUR_TOKEN(req) \
112 | ((req)->_buffer[(req)->_buf_idx++] = '\0' )
113 |
114 | #define EXPECT_CHAR(state, ch, expected_ch, next_state) \
115 | if ((ch) == (expected_ch)) { \
116 | state = (next_state); \
117 | } else { \
118 | state = PARSER_STATE_BAD_REQUEST; \
119 | }
120 |
121 |
122 | int request_parse_headers(request_t *req,
123 | const char *data,
124 | const size_t data_len,
125 | size_t *consumed) {
126 | http_version_e ver;
127 | int i, rc;
128 | char ch;
129 | char *cur_token = req->_buffer;
130 | parser_state_e state = PARSER_STATE_METHOD;
131 |
132 | req->_buffer[0] = '\0';
133 | req->_buf_idx = 0;
134 | req->header_count = 0;
135 |
136 | if (std_headers_hash_initialized == 0) {
137 | init_std_headers_hash();
138 | }
139 |
140 | for (i = 0; i < data_len;){
141 |
142 | if (state == PARSER_STATE_COMPLETE ||
143 | state == PARSER_STATE_BAD_REQUEST) {
144 | break;
145 | }
146 | ch = data[i++];
147 |
148 | switch(state) {
149 |
150 | case PARSER_STATE_METHOD:
151 | if (ch == ' ') {
152 | FINISH_CUR_TOKEN(req);
153 | req->method = cur_token;
154 | state = PARSER_STATE_PATH;
155 | START_NEW_TOKEN(cur_token, req);
156 | } else if (ch < 'A' || ch > 'Z') {
157 | state = PARSER_STATE_BAD_REQUEST;
158 | } else {
159 | FILL_NEXT_CHAR(req, ch);
160 | }
161 | break;
162 |
163 | case PARSER_STATE_PATH:
164 | if (ch == '?') {
165 | FINISH_CUR_TOKEN(req);
166 | req->path = cur_token;
167 | state = PARSER_STATE_QUERY_STR;
168 | START_NEW_TOKEN(cur_token, req);
169 | } else if (ch == ' ') {
170 | FINISH_CUR_TOKEN(req);
171 | req->path = cur_token;
172 | state = PARSER_STATE_VERSION;
173 | START_NEW_TOKEN(cur_token, req);
174 | } else {
175 | FILL_NEXT_CHAR(req, ch);
176 | }
177 | break;
178 |
179 | case PARSER_STATE_QUERY_STR:
180 | if (ch == ' ') {
181 | FINISH_CUR_TOKEN(req);
182 | req->query_str = cur_token;
183 | state = PARSER_STATE_VERSION;
184 | START_NEW_TOKEN(cur_token, req);
185 | } else {
186 | FILL_NEXT_CHAR(req, ch);
187 | }
188 | break;
189 |
190 | case PARSER_STATE_VERSION:
191 | switch (ch) {
192 | // For HTTP part in the request line, e.g. GET / HTTP/1.1
193 | case 'H':
194 | case 'T':
195 | case 'P':
196 |
197 | // Currently only 0.9, 1.0 and 1.1 are supported.
198 | case '0':
199 | case '1':
200 | case '9':
201 | case '.':
202 | FILL_NEXT_CHAR(req, ch);
203 | break;
204 |
205 | case '/':
206 | FINISH_CUR_TOKEN(req);
207 | if (strcmp("HTTP", cur_token) != 0) {
208 | state = PARSER_STATE_BAD_REQUEST;
209 | break;
210 | }
211 | START_NEW_TOKEN(cur_token, req);
212 | break;
213 |
214 | case '\r':
215 | FINISH_CUR_TOKEN(req);
216 | ver = _resolve_http_version(cur_token);
217 | if (ver == HTTP_VERSION_UNKNOW) {
218 | state = PARSER_STATE_BAD_REQUEST;
219 | break;
220 | }
221 | req->version = ver;
222 | state = PARSER_STATE_HEADER_CR;
223 | START_NEW_TOKEN(cur_token, req);
224 | break;
225 | }
226 | break;
227 |
228 | case PARSER_STATE_HEADER_NAME:
229 | if (ch == ':') {
230 | FINISH_CUR_TOKEN(req);
231 | req->headers[req->header_count].name = cur_token;
232 | state = PARSER_STATE_HEADER_COLON;
233 | START_NEW_TOKEN(cur_token, req);
234 | } else {
235 | FILL_NEXT_CHAR(req, ch);
236 | }
237 | break;
238 |
239 | case PARSER_STATE_HEADER_COLON:
240 | EXPECT_CHAR(state, ch, ' ', PARSER_STATE_HEADER_VALUE);
241 | break;
242 |
243 | case PARSER_STATE_HEADER_VALUE:
244 | if (ch == '\r') {
245 | FINISH_CUR_TOKEN(req);
246 | req->headers[req->header_count].value = cur_token;
247 | handle_common_header(req, req->header_count);
248 | req->header_count++;
249 | state = PARSER_STATE_HEADER_CR;
250 | START_NEW_TOKEN(cur_token, req);
251 | } else {
252 | FILL_NEXT_CHAR(req, ch);
253 | }
254 | break;
255 |
256 | case PARSER_STATE_HEADER_CR:
257 | EXPECT_CHAR(state, ch, '\n', PARSER_STATE_HEADER_LF);
258 | break;
259 |
260 | case PARSER_STATE_HEADER_LF:
261 | // Another CR after a header LF, meanning the header end is met.
262 | if (ch == '\r') {
263 | state = PARSER_STATE_HEADER_COMPLETE_CR;
264 | } else {
265 | state = PARSER_STATE_HEADER_NAME;
266 | FILL_NEXT_CHAR(req, ch);
267 | }
268 | break;
269 |
270 | case PARSER_STATE_HEADER_COMPLETE_CR:
271 | EXPECT_CHAR(state, ch, '\n', PARSER_STATE_COMPLETE);
272 | break;
273 |
274 | default:
275 | error("Unexpected state: %d", state);
276 | break;
277 |
278 | }
279 | }
280 |
281 | *consumed = i;
282 |
283 | switch (state) {
284 | case PARSER_STATE_COMPLETE:
285 | rc = STATUS_COMPLETE;
286 | break;
287 |
288 | case PARSER_STATE_BAD_REQUEST:
289 | rc = STATUS_ERROR;
290 | break;
291 |
292 | default:
293 | rc = STATUS_INCOMPLETE;
294 | break;
295 | }
296 |
297 | return rc;
298 | }
299 |
300 | static http_version_e _resolve_http_version(const char* version_str) {
301 | if (strcmp(version_str, "1.1") == 0) {
302 | return HTTP_VERSION_1_1;
303 | } else if (strcmp(version_str, "1.0") == 0) {
304 | return HTTP_VERSION_1_0;
305 | } else if (strcmp(version_str, "0.9") == 0) {
306 | return HTTP_VERSION_0_9;
307 | }
308 |
309 | return HTTP_VERSION_UNKNOW;
310 | }
311 |
312 | static void _handle_content_len(request_t *req, http_header_t *header) {
313 | size_t content_length;
314 |
315 | content_length = (size_t) atol(header->value);
316 | req->content_length = content_length;
317 | }
318 |
319 | static void _handle_host(request_t *req, http_header_t *header) {
320 | req->host = header->value;
321 | }
322 |
323 | static void _handle_connection(request_t *req, http_header_t *header) {
324 | if (strcasecmp("keep-alive", header->value) == 0) {
325 | req->connection = CONN_KEEP_ALIVE;
326 | } else {
327 | req->connection = CONN_CLOSE;
328 | }
329 | }
330 |
331 | static header_command_t std_headers[] = {
332 | { "accept", NULL },
333 | { "accept-charset", NULL },
334 | { "accept-datetime", NULL },
335 | { "accept-encoding", NULL },
336 | { "accept-language", NULL },
337 | { "accept-ranges", NULL },
338 | { "access-control-allow-origin", NULL },
339 | { "age", NULL },
340 | { "allow", NULL },
341 | { "authorization", NULL },
342 | { "cache-control", NULL },
343 | { "connection", _handle_connection },
344 | { "content-disposition", NULL },
345 | { "content-encoding", NULL },
346 | { "content-language", NULL },
347 | { "content-length", _handle_content_len },
348 | { "content-location", NULL },
349 | { "content-md5", NULL },
350 | { "content-range", NULL },
351 | { "content-security-policy", NULL },
352 | { "content-type", NULL },
353 | { "cookie", NULL },
354 | { "dnt", NULL },
355 | { "date", NULL },
356 | { "etag", NULL },
357 | { "expect", NULL },
358 | { "expires", NULL },
359 | { "from", NULL },
360 | { "front-end-https", NULL },
361 | { "host", _handle_host },
362 | { "if-match", NULL },
363 | { "if-modified-since", NULL },
364 | { "if-none-match", NULL },
365 | { "if-range", NULL },
366 | { "if-unmodified-since", NULL },
367 | { "last-modified", NULL },
368 | { "link", NULL },
369 | { "location", NULL },
370 | { "max-forwards", NULL },
371 | { "origin", NULL },
372 | { "p3p", NULL },
373 | { "pragma", NULL },
374 | { "proxy-authenticate", NULL },
375 | { "proxy-authorization", NULL },
376 | { "proxy-connection", NULL },
377 | { "range", NULL },
378 | { "referer", NULL },
379 | { "refresh", NULL },
380 | { "retry-after", NULL },
381 | { "server", NULL },
382 | { "set-cookie", NULL },
383 | { "status", NULL },
384 | { "strict-transport-security", NULL },
385 | { "te", NULL },
386 | { "trailer", NULL },
387 | { "transfer-encoding", NULL },
388 | { "upgrade", NULL },
389 | { "user-agent", NULL },
390 | { "vary", NULL },
391 | { "via", NULL },
392 | { "www-authenticate", NULL },
393 | { "warning", NULL },
394 | { "x-att-deviceid", NULL },
395 | { "x-content-security-policy", NULL },
396 | { "x-content-type-options", NULL },
397 | { "x-forwarded-for", NULL },
398 | { "x-forwarded-proto", NULL },
399 | { "x-frame-options", NULL },
400 | { "x-powered-by", NULL },
401 | { "x-requested-with", NULL },
402 | { "x-wap-profile", NULL },
403 | { "x-webkit-csp", NULL },
404 | { "x-xss-protection", NULL },
405 | { "x-ua-compatible", NULL },
406 | };
407 |
408 | static int init_std_headers_hash() {
409 | int i;
410 | size_t size;
411 | ENTRY item, *ret;
412 |
413 | size = sizeof(std_headers)/sizeof(std_headers[0]);
414 |
415 | bzero(&std_headers_hash, sizeof(struct hsearch_data));
416 | if (hcreate_r(sizeof(std_headers) * 2, &std_headers_hash) == 0) {
417 | error("Error creating standard headers hash");
418 | return -1;
419 | }
420 | for (i = 0; i < size; i++) {
421 | item.key = std_headers[i].lower_name;
422 | item.data = std_headers[i].callback;
423 | if (hsearch_r(item, ENTER, &ret, &std_headers_hash) == 0) {
424 | error("Error entering standard header %s to hash", item.key);
425 | }
426 | }
427 | std_headers_hash_initialized = 1;
428 | return 0;
429 | }
430 |
431 | static void handle_common_header(request_t *req, int header_index) {
432 | ENTRY ent, *ret;
433 | http_header_t *header;
434 | char header_lowercase[64];
435 | http_header_callback callback = NULL;
436 |
437 | header = req->headers + header_index;
438 | strlowercase(header->name, header_lowercase, 64);
439 | ent.key = header_lowercase;
440 | if (hsearch_r(ent, FIND, &ret, &std_headers_hash) == 0) {
441 | return;
442 | }
443 |
444 | callback = (http_header_callback) ret->data;
445 | if (callback != NULL) {
446 | callback(req, header);
447 | }
448 |
449 | ent.key = ret->key;
450 | ent.data = header;
451 |
452 | hsearch_r(ent, ENTER, &ret, &req->_header_hash);
453 | }
454 |
455 |
456 | // HTTP common status codes
457 |
458 | // 1xx informational
459 | http_status_t STATUS_CONTINUE = {100, "Continue"};
460 |
461 | // 2xx success
462 | http_status_t STATUS_OK = {200, "OK"};
463 | http_status_t STATUS_CREATED = {201, "Created"};
464 | http_status_t STATUS_ACCEPTED = {202, "Accepted"};
465 | http_status_t STATUS_NO_CONTENT = {204, "No Content"};
466 | http_status_t STATUS_PARTIAL_CONTENT = {206, "Partial Content"};
467 |
468 | // 3xx redirection
469 | http_status_t STATUS_MOVED = {301, "Moved Permanently"};
470 | http_status_t STATUS_FOUND = {302, "Found"};
471 | http_status_t STATUS_SEE_OTHER = {303, "See Other"};
472 | http_status_t STATUS_NOT_MODIFIED = {304, "Not Modified"};
473 |
474 | // 4xx client errors
475 | http_status_t STATUS_BAD_REQUEST = {400, "Bad Request"};
476 | http_status_t STATUS_UNAUTHORIZED = {401, "Unauthorized"};
477 | http_status_t STATUS_FORBIDDEN = {403, "Forbidden"};
478 | http_status_t STATUS_NOT_FOUND = {404, "Not Found"};
479 | http_status_t STATUS_METHOD_NOT_ALLOWED = {405, "Method Not Allowed"};
480 | http_status_t STATUS_RANGE_NOT_SATISFIABLE = {416, "Range Not Satisfiable"};
481 |
482 | // 5xx server errors
483 | http_status_t STATUS_INTERNAL_ERROR = {500, "Internal Server Error"};
484 | http_status_t STATUS_NOT_IMPLEMENTED = {501, "Not Implemented"};
485 | http_status_t STATUS_BAD_GATEWAY = {502, "Bad Gateway"};
486 | http_status_t STATUS_SERVICE_UNAVAILABLE = {503, "Service Unavailable"};
487 | http_status_t STATUS_GATEWAY_TIMEOUT = {504, "Gateway Timeout"};
488 |
489 |
490 | response_t* response_create(connection_t *conn) {
491 | response_t *resp;
492 |
493 | resp = (response_t*) calloc(1, sizeof(response_t));
494 | if (resp == NULL) {
495 | return NULL;
496 | }
497 |
498 | bzero(resp, sizeof(response_t));
499 | if (hcreate_r(MAX_HEADER_SIZE, &resp->_header_hash) == 0) {
500 | error("Error creating header hash table");
501 | free(resp);
502 | return NULL;
503 | }
504 |
505 | resp->_conn = conn;
506 | // Content Length of -1 means we do not handle content length
507 | resp->content_length = -1;
508 | resp->connection = CONN_KEEP_ALIVE;
509 | return resp;
510 | }
511 |
512 | int response_reset(response_t *resp) {
513 | connection_t *conn = resp->_conn;
514 | hdestroy_r(&resp->_header_hash);
515 | bzero(resp, sizeof(response_t));
516 | if (hcreate_r(MAX_HEADER_SIZE, &resp->_header_hash) == 0) {
517 | error("Error creating header hash table");
518 | return -1;
519 | }
520 | resp->_conn = conn;
521 | resp->content_length = -1;
522 | resp->connection = CONN_KEEP_ALIVE;
523 | return 0;
524 | }
525 |
526 | int response_destroy(response_t *resp) {
527 | hdestroy_r(&resp->_header_hash);
528 | free(resp);
529 | return 0;
530 | }
531 |
532 | const char* response_get_header(response_t *resp, const char *header_name) {
533 | ENTRY item, *ret;
534 | char header_name_lower[64];
535 |
536 | strlowercase(header_name, header_name_lower, 64);
537 | item.key = header_name_lower;
538 |
539 | if (hsearch_r(item, FIND, &ret, &resp->_header_hash) == 0) {
540 | return NULL;
541 | }
542 |
543 | return ((http_header_t*) ret->data)->value;
544 | }
545 |
546 | char* response_alloc(response_t *resp, size_t n) {
547 | char *res;
548 | if (resp->_buf_idx + n > RESPONSE_BUFFER_SIZE) {
549 | return NULL;
550 | }
551 | res = resp->_buffer + resp->_buf_idx;
552 | resp->_buf_idx += n;
553 | return res;
554 | }
555 |
556 | int response_set_header(response_t *resp, char *header_name, char *header_value) {
557 | ENTRY ent, *ret;
558 | http_header_t *header;
559 | char header_lowercase[64];
560 | char *keybuf;
561 | size_t n;
562 |
563 | if (resp->_header_sent) {
564 | return -1;
565 | }
566 | strlowercase(header_name, header_lowercase, 64);
567 | ent.key = header_lowercase;
568 | if (hsearch_r(ent, FIND, &ret, &resp->_header_hash) != 0) {
569 | header = (http_header_t*) ret->data;
570 | header->value = header_value;
571 | return 0;
572 | }
573 |
574 | header = resp->headers + resp->header_count++;
575 | header->name = header_name;
576 | header->value = header_value;
577 |
578 | if (hsearch_r(ent, FIND, &ret, &std_headers_hash) != 0) {
579 | ent.key = ret->key;
580 | } else {
581 | n = strlen(header_lowercase) + 1;
582 | keybuf = response_alloc(resp, n);
583 | if (keybuf == NULL) {
584 | return -1;
585 | }
586 | ent.key = strncpy(keybuf, header_lowercase, n);
587 | }
588 | ent.data = header;
589 | if (hsearch_r(ent, ENTER, &ret, &resp->_header_hash) == 0) {
590 | return -1;
591 | }
592 | return 0;
593 | }
594 |
595 | int response_set_header_printf(response_t *resp, char* name,
596 | const char *fmt, ...) {
597 | char *buf = resp->_buffer + resp->_buf_idx;
598 | size_t max_len = RESPONSE_BUFFER_SIZE - resp->_buf_idx;
599 | int len = 0, res;
600 | va_list ap;
601 | va_start(ap, fmt);
602 | len = vsnprintf(buf, max_len, fmt, ap);
603 | va_end(ap);
604 | res = response_set_header(resp, name, buf);
605 | if (res == 0) {
606 | // Only truly allocate the buffer when the operation succeeds.
607 | resp->_buf_idx += (len + 1);
608 | }
609 | return res;
610 | }
611 |
612 | static void set_common_headers(response_t *resp) {
613 | char *keybuf, *tmbuf;
614 | size_t len = resp->content_length;
615 | int n;
616 |
617 | if (len >= 0) {
618 | keybuf = resp->_buffer + resp->_buf_idx;
619 | n = snprintf(keybuf,
620 | RESPONSE_BUFFER_SIZE - resp->_buf_idx,
621 | "%ld",
622 | resp->content_length);
623 |
624 | resp->_buf_idx += (n + 1);
625 | response_set_header(resp, "Content-Length", keybuf);
626 | } else {
627 | // No Content-Length header set, set connection header
628 | // to close
629 | // TODO Support chunked transfer encoding
630 | resp->connection = CONN_CLOSE;
631 | }
632 |
633 | switch(resp->connection) {
634 | case CONN_KEEP_ALIVE:
635 | //TODO Handle keep-alive time
636 | response_set_header(resp, "Connection", "keep-alive");
637 | break;
638 |
639 | default:
640 | response_set_header(resp, "Connection", "close");
641 | break;
642 | }
643 |
644 | response_set_header(resp, "Server", _BREEZE_NAME);
645 | tmbuf = response_alloc(resp, 32);
646 | if (current_http_date(tmbuf, 32) == 0) {
647 | response_set_header(resp, "Date", tmbuf);
648 | }
649 | }
650 |
651 | int response_send_headers(response_t *resp, handler_func next_handler) {
652 | char buffer[RESPONSE_BUFFER_SIZE];
653 | int i, n, buf_len = 0;
654 | http_header_t *header;
655 |
656 | if (resp->_header_sent) {
657 | return -1;
658 | }
659 |
660 | set_common_headers(resp);
661 | // Write status line
662 | n = snprintf(buffer,
663 | RESPONSE_BUFFER_SIZE,
664 | "HTTP/%s %d %s\r\n",
665 | str_http_ver(resp->version),
666 | resp->status.code,
667 | resp->status.msg);
668 | buf_len += n;
669 |
670 | // Write headers
671 | for (i = 0; i < resp->header_count; i++) {
672 | header = resp->headers + i;
673 | n = snprintf(buffer + buf_len,
674 | RESPONSE_BUFFER_SIZE - buf_len,
675 | "%s: %s\r\n",
676 | header->name,
677 | header->value);
678 | buf_len += n;
679 | }
680 |
681 | // Empty line between headers and body
682 | buffer[buf_len++] = '\r';
683 | buffer[buf_len++] = '\n';
684 |
685 | resp->_next_handler = next_handler;
686 | if (iostream_write(resp->_conn->stream, buffer, buf_len, on_write_finished) < 0) {
687 | return -1;
688 | }
689 | resp->_header_sent = 1;
690 | return 0;
691 | }
692 |
693 | int response_write(response_t *resp,
694 | char *data, size_t data_len,
695 | handler_func next_handler) {
696 | if (!resp->_header_sent) {
697 | return -1;
698 | }
699 | resp->_next_handler = next_handler;
700 | if (iostream_write(resp->_conn->stream,
701 | data, data_len, on_write_finished) < 0) {
702 | connection_close(resp->_conn);
703 | return -1;
704 | }
705 | return 0;
706 | }
707 |
708 | int response_send_file(response_t *resp,
709 | int fd,
710 | size_t offset,
711 | size_t size,
712 | handler_func next_handler) {
713 | if (!resp->_header_sent) {
714 | return -1;
715 | }
716 | resp->_next_handler = next_handler;
717 | if (iostream_sendfile(resp->_conn->stream, fd,
718 | offset, size, on_write_finished) < 0) {
719 | connection_close(resp->_conn);
720 | return -1;
721 | }
722 | return 0;
723 | }
724 |
725 | const char *status_msg_template = ""
726 | "%d %s"
727 | ""
728 | "%d %s
"
729 | "Please contact website administrator to report the problem."
730 | "
"
731 | ""
732 | "Powered by %s"
733 | ""
734 | ""
735 | "";
736 |
737 | int response_send_status(response_t *resp, http_status_t status) {
738 | size_t len = 0;
739 | char buf[1024];
740 |
741 | resp->status = status;
742 | response_set_header(resp, "Content-Type", "text/html");
743 | snprintf(buf, 1024, status_msg_template,
744 | status.code, status.msg,
745 | status.code, status.msg,
746 | _BREEZE_NAME);
747 | len = strlen(buf);
748 |
749 | resp->content_length = len;
750 | response_send_headers(resp, NULL);
751 | response_write(resp, buf, len, NULL);
752 | return HANDLER_DONE;
753 | }
754 |
755 | static void on_write_finished(iostream_t *stream) {
756 | connection_t *conn;
757 | handler_func handler;
758 | response_t *resp;
759 |
760 | conn = (connection_t*) stream->user_data;
761 | resp = conn->response;
762 | handler = resp->_next_handler;
763 |
764 | if (handler != NULL) {
765 | connection_run_handler(conn, handler);
766 | }
767 | if (resp->_done) {
768 | switch (resp->connection) {
769 | case CONN_CLOSE:
770 | connection_close(conn);
771 | break;
772 |
773 | case CONN_KEEP_ALIVE:
774 | default:
775 | if (request_reset(conn->request) < 0) {
776 | connection_close(conn);
777 | break;
778 | }
779 | if (response_reset(conn->response) < 0) {
780 | connection_close(conn);
781 | break;
782 | }
783 | if (context_reset(conn->context) < 0) {
784 | connection_close(conn);
785 | break;
786 | }
787 | conn->context->conf = conn->server->handler_conf;
788 | connection_run(conn);
789 | break;
790 | }
791 | }
792 | }
793 |
794 | handler_ctx_t* context_create() {
795 | handler_ctx_t *ctx = (handler_ctx_t*) calloc(1, sizeof(handler_ctx_t));
796 | return ctx;
797 | }
798 |
799 | int context_reset(handler_ctx_t *ctx) {
800 | ctx->_stat_top = 0;
801 | ctx->conf = NULL;
802 | return 0;
803 | }
804 |
805 | int context_destroy(handler_ctx_t *ctx) {
806 | free(ctx);
807 | return 0;
808 | }
809 |
810 | int context_push(handler_ctx_t *ctx, ctx_state_t stat) {
811 | if (ctx->_stat_top >= MAX_STATE_STACK_SIZE) {
812 | return -1;
813 | }
814 | ctx->_stat_stack[ctx->_stat_top] = stat;
815 | ctx->_stat_top++;
816 | return 0;
817 | }
818 |
819 | ctx_state_t* context_pop(handler_ctx_t *ctx) {
820 | if (ctx->_stat_top <= 0) {
821 | return NULL;
822 | }
823 | ctx->_stat_top--;
824 | return &ctx->_stat_stack[ctx->_stat_top];
825 | }
826 |
827 | ctx_state_t* context_peek(handler_ctx_t *ctx) {
828 | if (ctx->_stat_top <= 0) {
829 | return NULL;
830 | }
831 | return &ctx->_stat_stack[ctx->_stat_top - 1];
832 | }
833 |
--------------------------------------------------------------------------------
/src/json/json.c:
--------------------------------------------------------------------------------
1 | /* vim: set et ts=3 sw=3 ft=c:
2 | *
3 | * Copyright (C) 2012 James McLaughlin et al. All rights reserved.
4 | * https://github.com/udp/json-parser
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions
8 | * are met:
9 | *
10 | * 1. Redistributions of source code must retain the above copyright
11 | * notice, this list of conditions and the following disclaimer.
12 | *
13 | * 2. Redistributions in binary form must reproduce the above copyright
14 | * notice, this list of conditions and the following disclaimer in the
15 | * documentation and/or other materials provided with the distribution.
16 | *
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 | * SUCH DAMAGE.
28 | */
29 |
30 | #include "json.h"
31 |
32 | #ifdef _MSC_VER
33 | #ifndef _CRT_SECURE_NO_WARNINGS
34 | #define _CRT_SECURE_NO_WARNINGS
35 | #endif
36 | #endif
37 |
38 | #ifdef __cplusplus
39 | const struct _json_value json_value_none; /* zero-d by ctor */
40 | #else
41 | const struct _json_value json_value_none = { 0 };
42 | #endif
43 |
44 | #include
45 | #include
46 | #include
47 | #include
48 |
49 | typedef unsigned short json_uchar;
50 |
51 | static unsigned char hex_value (json_char c)
52 | {
53 | if (isdigit(c))
54 | return c - '0';
55 |
56 | switch (c) {
57 | case 'a': case 'A': return 0x0A;
58 | case 'b': case 'B': return 0x0B;
59 | case 'c': case 'C': return 0x0C;
60 | case 'd': case 'D': return 0x0D;
61 | case 'e': case 'E': return 0x0E;
62 | case 'f': case 'F': return 0x0F;
63 | default: return 0xFF;
64 | }
65 | }
66 |
67 | typedef struct
68 | {
69 | unsigned long used_memory;
70 |
71 | unsigned int uint_max;
72 | unsigned long ulong_max;
73 |
74 | json_settings settings;
75 | int first_pass;
76 |
77 | } json_state;
78 |
79 | static void * default_alloc (size_t size, int zero, void * user_data)
80 | {
81 | return zero ? calloc (size, 1) : malloc (size);
82 | }
83 |
84 | static void default_free (void * ptr, void * user_data)
85 | {
86 | free (ptr);
87 | }
88 |
89 | static void * json_alloc (json_state * state, unsigned long size, int zero)
90 | {
91 | if ((state->ulong_max - state->used_memory) < size)
92 | return 0;
93 |
94 | if (state->settings.max_memory
95 | && (state->used_memory += size) > state->settings.max_memory)
96 | {
97 | return 0;
98 | }
99 |
100 | return state->settings.mem_alloc (size, zero, state->settings.user_data);
101 | }
102 |
103 | static int new_value
104 | (json_state * state, json_value ** top, json_value ** root, json_value ** alloc, json_type type)
105 | {
106 | json_value * value;
107 | int values_size;
108 |
109 | if (!state->first_pass)
110 | {
111 | value = *top = *alloc;
112 | *alloc = (*alloc)->_reserved.next_alloc;
113 |
114 | if (!*root)
115 | *root = value;
116 |
117 | switch (value->type)
118 | {
119 | case json_array:
120 |
121 | if (! (value->u.array.values = (json_value **) json_alloc
122 | (state, value->u.array.length * sizeof (json_value *), 0)) )
123 | {
124 | return 0;
125 | }
126 |
127 | value->u.array.length = 0;
128 | break;
129 |
130 | case json_object:
131 |
132 | values_size = sizeof (*value->u.object.values) * value->u.object.length;
133 |
134 | if (! ((*(void **) &value->u.object.values) = json_alloc
135 | (state, values_size + ((unsigned long) value->u.object.values), 0)) )
136 | {
137 | return 0;
138 | }
139 |
140 | value->_reserved.object_mem = (*(char **) &value->u.object.values) + values_size;
141 |
142 | value->u.object.length = 0;
143 | break;
144 |
145 | case json_string:
146 |
147 | if (! (value->u.string.ptr = (json_char *) json_alloc
148 | (state, (value->u.string.length + 1) * sizeof (json_char), 0)) )
149 | {
150 | return 0;
151 | }
152 |
153 | value->u.string.length = 0;
154 | break;
155 |
156 | default:
157 | break;
158 | };
159 |
160 | return 1;
161 | }
162 |
163 | value = (json_value *) json_alloc (state, sizeof (json_value), 1);
164 |
165 | if (!value)
166 | return 0;
167 |
168 | if (!*root)
169 | *root = value;
170 |
171 | value->type = type;
172 | value->parent = *top;
173 |
174 | if (*alloc)
175 | (*alloc)->_reserved.next_alloc = value;
176 |
177 | *alloc = *top = value;
178 |
179 | return 1;
180 | }
181 |
182 | #define e_off \
183 | ((int) (i - cur_line_begin))
184 |
185 | #define whitespace \
186 | case '\n': ++ cur_line; cur_line_begin = i; \
187 | case ' ': case '\t': case '\r'
188 |
189 | #define string_add(b) \
190 | do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0);
191 |
192 | const static long
193 | flag_next = 1 << 0,
194 | flag_reproc = 1 << 1,
195 | flag_need_comma = 1 << 2,
196 | flag_seek_value = 1 << 3,
197 | flag_escaped = 1 << 4,
198 | flag_string = 1 << 5,
199 | flag_need_colon = 1 << 6,
200 | flag_done = 1 << 7,
201 | flag_num_negative = 1 << 8,
202 | flag_num_zero = 1 << 9,
203 | flag_num_e = 1 << 10,
204 | flag_num_e_got_sign = 1 << 11,
205 | flag_num_e_negative = 1 << 12;
206 |
207 | json_value * json_parse_ex (json_settings * settings,
208 | const json_char * json,
209 | size_t length,
210 | char * error_buf)
211 | {
212 | json_char error [128];
213 | unsigned int cur_line;
214 | const json_char * cur_line_begin, * i, * end;
215 | json_value * top, * root, * alloc = 0;
216 | json_state state = { 0 };
217 | long flags;
218 | long num_digits = 0, num_e = 0;
219 | json_int_t num_fraction = 0;
220 |
221 | /* Skip UTF-8 BOM
222 | */
223 | if (length >= 3 && json [0] == 0xEF
224 | && json [1] == 0xBB
225 | && json [2] == 0xBF)
226 | {
227 | json += 3;
228 | length -= 3;
229 | }
230 |
231 | error[0] = '\0';
232 | end = (json + length);
233 |
234 | memcpy (&state.settings, settings, sizeof (json_settings));
235 |
236 | if (!state.settings.mem_alloc)
237 | state.settings.mem_alloc = default_alloc;
238 |
239 | if (!state.settings.mem_free)
240 | state.settings.mem_free = default_free;
241 |
242 | memset (&state.uint_max, 0xFF, sizeof (state.uint_max));
243 | memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max));
244 |
245 | state.uint_max -= 8; /* limit of how much can be added before next check */
246 | state.ulong_max -= 8;
247 |
248 | for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass)
249 | {
250 | json_uchar uchar;
251 | unsigned char uc_b1, uc_b2, uc_b3, uc_b4;
252 | json_char * string = 0;
253 | unsigned int string_length = 0;
254 |
255 | top = root = 0;
256 | flags = flag_seek_value;
257 |
258 | cur_line = 1;
259 | cur_line_begin = json;
260 |
261 | for (i = json ;; ++ i)
262 | {
263 | json_char b = (i == end ? 0 : *i);
264 |
265 | if (flags & flag_done)
266 | {
267 | if (!b)
268 | break;
269 |
270 | switch (b)
271 | {
272 | whitespace:
273 | continue;
274 |
275 | default:
276 | sprintf (error, "%d:%d: Trailing garbage: `%c`", cur_line, e_off, b);
277 | goto e_failed;
278 | };
279 | }
280 |
281 | if (flags & flag_string)
282 | {
283 | if (!b)
284 | { sprintf (error, "Unexpected EOF in string (at %d:%d)", cur_line, e_off);
285 | goto e_failed;
286 | }
287 |
288 | if (string_length > state.uint_max)
289 | goto e_overflow;
290 |
291 | if (flags & flag_escaped)
292 | {
293 | flags &= ~ flag_escaped;
294 |
295 | switch (b)
296 | {
297 | case 'b': string_add ('\b'); break;
298 | case 'f': string_add ('\f'); break;
299 | case 'n': string_add ('\n'); break;
300 | case 'r': string_add ('\r'); break;
301 | case 't': string_add ('\t'); break;
302 | case 'u':
303 |
304 | if ((uc_b1 = hex_value (*++ i)) == 0xFF || (uc_b2 = hex_value (*++ i)) == 0xFF
305 | || (uc_b3 = hex_value (*++ i)) == 0xFF || (uc_b4 = hex_value (*++ i)) == 0xFF)
306 | {
307 | sprintf (error, "Invalid character value `%c` (at %d:%d)", b, cur_line, e_off);
308 | goto e_failed;
309 | }
310 |
311 | uc_b1 = uc_b1 * 16 + uc_b2;
312 | uc_b2 = uc_b3 * 16 + uc_b4;
313 |
314 | uchar = ((json_char) uc_b1) * 256 + uc_b2;
315 |
316 | if (sizeof (json_char) >= sizeof (json_uchar) || (uc_b1 == 0 && uc_b2 <= 0x7F))
317 | {
318 | string_add ((json_char) uchar);
319 | break;
320 | }
321 |
322 | if (uchar <= 0x7FF)
323 | {
324 | if (state.first_pass)
325 | string_length += 2;
326 | else
327 | { string [string_length ++] = 0xC0 | ((uc_b2 & 0xC0) >> 6) | ((uc_b1 & 0x7) << 2);
328 | string [string_length ++] = 0x80 | (uc_b2 & 0x3F);
329 | }
330 |
331 | break;
332 | }
333 |
334 | if (state.first_pass)
335 | string_length += 3;
336 | else
337 | { string [string_length ++] = 0xE0 | ((uc_b1 & 0xF0) >> 4);
338 | string [string_length ++] = 0x80 | ((uc_b1 & 0xF) << 2) | ((uc_b2 & 0xC0) >> 6);
339 | string [string_length ++] = 0x80 | (uc_b2 & 0x3F);
340 | }
341 |
342 | break;
343 |
344 | default:
345 | string_add (b);
346 | };
347 |
348 | continue;
349 | }
350 |
351 | if (b == '\\')
352 | {
353 | flags |= flag_escaped;
354 | continue;
355 | }
356 |
357 | if (b == '"')
358 | {
359 | if (!state.first_pass)
360 | string [string_length] = 0;
361 |
362 | flags &= ~ flag_string;
363 | string = 0;
364 |
365 | switch (top->type)
366 | {
367 | case json_string:
368 |
369 | top->u.string.length = string_length;
370 | flags |= flag_next;
371 |
372 | break;
373 |
374 | case json_object:
375 |
376 | if (state.first_pass)
377 | (*(json_char **) &top->u.object.values) += string_length + 1;
378 | else
379 | {
380 | top->u.object.values [top->u.object.length].name
381 | = (json_char *) top->_reserved.object_mem;
382 |
383 | (*(json_char **) &top->_reserved.object_mem) += string_length + 1;
384 | }
385 |
386 | flags |= flag_seek_value | flag_need_colon;
387 | continue;
388 |
389 | default:
390 | break;
391 | };
392 | }
393 | else
394 | {
395 | string_add (b);
396 | continue;
397 | }
398 | }
399 |
400 | if (flags & flag_seek_value)
401 | {
402 | switch (b)
403 | {
404 | whitespace:
405 | continue;
406 |
407 | case ']':
408 |
409 | if (top->type == json_array)
410 | flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next;
411 | else if (!state.settings.settings & json_relaxed_commas)
412 | { sprintf (error, "%d:%d: Unexpected ]", cur_line, e_off);
413 | goto e_failed;
414 | }
415 |
416 | break;
417 |
418 | default:
419 |
420 | if (flags & flag_need_comma)
421 | {
422 | if (b == ',')
423 | { flags &= ~ flag_need_comma;
424 | continue;
425 | }
426 | else
427 | { sprintf (error, "%d:%d: Expected , before %c", cur_line, e_off, b);
428 | goto e_failed;
429 | }
430 | }
431 |
432 | if (flags & flag_need_colon)
433 | {
434 | if (b == ':')
435 | { flags &= ~ flag_need_colon;
436 | continue;
437 | }
438 | else
439 | { sprintf (error, "%d:%d: Expected : before %c", cur_line, e_off, b);
440 | goto e_failed;
441 | }
442 | }
443 |
444 | flags &= ~ flag_seek_value;
445 |
446 | switch (b)
447 | {
448 | case '{':
449 |
450 | if (!new_value (&state, &top, &root, &alloc, json_object))
451 | goto e_alloc_failure;
452 |
453 | continue;
454 |
455 | case '[':
456 |
457 | if (!new_value (&state, &top, &root, &alloc, json_array))
458 | goto e_alloc_failure;
459 |
460 | flags |= flag_seek_value;
461 | continue;
462 |
463 | case '"':
464 |
465 | if (!new_value (&state, &top, &root, &alloc, json_string))
466 | goto e_alloc_failure;
467 |
468 | flags |= flag_string;
469 |
470 | string = top->u.string.ptr;
471 | string_length = 0;
472 |
473 | continue;
474 |
475 | case 't':
476 |
477 | if ((end - i) < 3 || *(++ i) != 'r' || *(++ i) != 'u' || *(++ i) != 'e')
478 | goto e_unknown_value;
479 |
480 | if (!new_value (&state, &top, &root, &alloc, json_boolean))
481 | goto e_alloc_failure;
482 |
483 | top->u.boolean = 1;
484 |
485 | flags |= flag_next;
486 | break;
487 |
488 | case 'f':
489 |
490 | if ((end - i) < 4 || *(++ i) != 'a' || *(++ i) != 'l' || *(++ i) != 's' || *(++ i) != 'e')
491 | goto e_unknown_value;
492 |
493 | if (!new_value (&state, &top, &root, &alloc, json_boolean))
494 | goto e_alloc_failure;
495 |
496 | flags |= flag_next;
497 | break;
498 |
499 | case 'n':
500 |
501 | if ((end - i) < 3 || *(++ i) != 'u' || *(++ i) != 'l' || *(++ i) != 'l')
502 | goto e_unknown_value;
503 |
504 | if (!new_value (&state, &top, &root, &alloc, json_null))
505 | goto e_alloc_failure;
506 |
507 | flags |= flag_next;
508 | break;
509 |
510 | default:
511 |
512 | if (isdigit (b) || b == '-')
513 | {
514 | if (!new_value (&state, &top, &root, &alloc, json_integer))
515 | goto e_alloc_failure;
516 |
517 | if (!state.first_pass)
518 | {
519 | while (isdigit (b) || b == '+' || b == '-'
520 | || b == 'e' || b == 'E' || b == '.')
521 | {
522 | if ( (++ i) == end)
523 | {
524 | b = 0;
525 | break;
526 | }
527 |
528 | b = *i;
529 | }
530 |
531 | flags |= flag_next | flag_reproc;
532 | break;
533 | }
534 |
535 | flags &= ~ (flag_num_negative | flag_num_e |
536 | flag_num_e_got_sign | flag_num_e_negative |
537 | flag_num_zero);
538 |
539 | num_digits = 0;
540 | num_fraction = 0;
541 | num_e = 0;
542 |
543 | if (b != '-')
544 | {
545 | flags |= flag_reproc;
546 | break;
547 | }
548 |
549 | flags |= flag_num_negative;
550 | continue;
551 | }
552 | else
553 | { sprintf (error, "%d:%d: Unexpected %c when seeking value", cur_line, e_off, b);
554 | goto e_failed;
555 | }
556 | };
557 | };
558 | }
559 | else
560 | {
561 | switch (top->type)
562 | {
563 | case json_object:
564 |
565 | switch (b)
566 | {
567 | whitespace:
568 | continue;
569 |
570 | case '"':
571 |
572 | if (flags & flag_need_comma && (!state.settings.settings & json_relaxed_commas))
573 | {
574 | sprintf (error, "%d:%d: Expected , before \"", cur_line, e_off);
575 | goto e_failed;
576 | }
577 |
578 | flags |= flag_string;
579 |
580 | string = (json_char *) top->_reserved.object_mem;
581 | string_length = 0;
582 |
583 | break;
584 |
585 | case '}':
586 |
587 | flags = (flags & ~ flag_need_comma) | flag_next;
588 | break;
589 |
590 | case ',':
591 |
592 | if (flags & flag_need_comma)
593 | {
594 | flags &= ~ flag_need_comma;
595 | break;
596 | }
597 |
598 | default:
599 |
600 | sprintf (error, "%d:%d: Unexpected `%c` in object", cur_line, e_off, b);
601 | goto e_failed;
602 | };
603 |
604 | break;
605 |
606 | case json_integer:
607 | case json_double:
608 |
609 | if (isdigit (b))
610 | {
611 | ++ num_digits;
612 |
613 | if (top->type == json_integer || flags & flag_num_e)
614 | {
615 | if (! (flags & flag_num_e))
616 | {
617 | if (flags & flag_num_zero)
618 | { sprintf (error, "%d:%d: Unexpected `0` before `%c`", cur_line, e_off, b);
619 | goto e_failed;
620 | }
621 |
622 | if (num_digits == 1 && b == '0')
623 | flags |= flag_num_zero;
624 | }
625 | else
626 | {
627 | flags |= flag_num_e_got_sign;
628 | num_e = (num_e * 10) + (b - '0');
629 | continue;
630 | }
631 |
632 | top->u.integer = (top->u.integer * 10) + (b - '0');
633 | continue;
634 | }
635 |
636 | num_fraction = (num_fraction * 10) + (b - '0');
637 | continue;
638 | }
639 |
640 | if (b == '+' || b == '-')
641 | {
642 | if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign))
643 | {
644 | flags |= flag_num_e_got_sign;
645 |
646 | if (b == '-')
647 | flags |= flag_num_e_negative;
648 |
649 | continue;
650 | }
651 | }
652 | else if (b == '.' && top->type == json_integer)
653 | {
654 | if (!num_digits)
655 | { sprintf (error, "%d:%d: Expected digit before `.`", cur_line, e_off);
656 | goto e_failed;
657 | }
658 |
659 | top->type = json_double;
660 | top->u.dbl = (double) top->u.integer;
661 |
662 | num_digits = 0;
663 | continue;
664 | }
665 |
666 | if (! (flags & flag_num_e))
667 | {
668 | if (top->type == json_double)
669 | {
670 | if (!num_digits)
671 | { sprintf (error, "%d:%d: Expected digit after `.`", cur_line, e_off);
672 | goto e_failed;
673 | }
674 |
675 | top->u.dbl += ((double) num_fraction) / (pow (10, (double) num_digits));
676 | }
677 |
678 | if (b == 'e' || b == 'E')
679 | {
680 | flags |= flag_num_e;
681 |
682 | if (top->type == json_integer)
683 | {
684 | top->type = json_double;
685 | top->u.dbl = (double) top->u.integer;
686 | }
687 |
688 | num_digits = 0;
689 | flags &= ~ flag_num_zero;
690 |
691 | continue;
692 | }
693 | }
694 | else
695 | {
696 | if (!num_digits)
697 | { sprintf (error, "%d:%d: Expected digit after `e`", cur_line, e_off);
698 | goto e_failed;
699 | }
700 |
701 | top->u.dbl *= pow (10, (double) (flags & flag_num_e_negative ? - num_e : num_e));
702 | }
703 |
704 | if (flags & flag_num_negative)
705 | {
706 | if (top->type == json_integer)
707 | top->u.integer = - top->u.integer;
708 | else
709 | top->u.dbl = - top->u.dbl;
710 | }
711 |
712 | flags |= flag_next | flag_reproc;
713 | break;
714 |
715 | default:
716 | break;
717 | };
718 | }
719 |
720 | if (flags & flag_reproc)
721 | {
722 | flags &= ~ flag_reproc;
723 | -- i;
724 | }
725 |
726 | if (flags & flag_next)
727 | {
728 | flags = (flags & ~ flag_next) | flag_need_comma;
729 |
730 | if (!top->parent)
731 | {
732 | /* root value done */
733 |
734 | flags |= flag_done;
735 | continue;
736 | }
737 |
738 | if (top->parent->type == json_array)
739 | flags |= flag_seek_value;
740 |
741 | if (!state.first_pass)
742 | {
743 | json_value * parent = top->parent;
744 |
745 | switch (parent->type)
746 | {
747 | case json_object:
748 |
749 | parent->u.object.values
750 | [parent->u.object.length].value = top;
751 |
752 | break;
753 |
754 | case json_array:
755 |
756 | parent->u.array.values
757 | [parent->u.array.length] = top;
758 |
759 | break;
760 |
761 | default:
762 | break;
763 | };
764 | }
765 |
766 | if ( (++ top->parent->u.array.length) > state.uint_max)
767 | goto e_overflow;
768 |
769 | top = top->parent;
770 |
771 | continue;
772 | }
773 | }
774 |
775 | alloc = root;
776 | }
777 |
778 | return root;
779 |
780 | e_unknown_value:
781 |
782 | sprintf (error, "%d:%d: Unknown value", cur_line, e_off);
783 | goto e_failed;
784 |
785 | e_alloc_failure:
786 |
787 | strcpy (error, "Memory allocation failure");
788 | goto e_failed;
789 |
790 | e_overflow:
791 |
792 | sprintf (error, "%d:%d: Too long (caught overflow)", cur_line, e_off);
793 | goto e_failed;
794 |
795 | e_failed:
796 |
797 | if (error_buf)
798 | {
799 | if (*error)
800 | strcpy (error_buf, error);
801 | else
802 | strcpy (error_buf, "Unknown error");
803 | }
804 |
805 | if (state.first_pass)
806 | alloc = root;
807 |
808 | while (alloc)
809 | {
810 | top = alloc->_reserved.next_alloc;
811 | state.settings.mem_free (alloc, state.settings.user_data);
812 | alloc = top;
813 | }
814 |
815 | if (!state.first_pass)
816 | json_value_free_ex (&state.settings, root);
817 |
818 | return 0;
819 | }
820 |
821 | json_value * json_parse (const json_char * json, size_t length)
822 | {
823 | json_settings settings = { 0 };
824 | return json_parse_ex (&settings, json, length, 0);
825 | }
826 |
827 | void json_value_free_ex (json_settings * settings, json_value * value)
828 | {
829 | json_value * cur_value;
830 |
831 | if (!value)
832 | return;
833 |
834 | value->parent = 0;
835 |
836 | while (value)
837 | {
838 | switch (value->type)
839 | {
840 | case json_array:
841 |
842 | if (!value->u.array.length)
843 | {
844 | settings->mem_free (value->u.array.values, settings->user_data);
845 | break;
846 | }
847 |
848 | value = value->u.array.values [-- value->u.array.length];
849 | continue;
850 |
851 | case json_object:
852 |
853 | if (!value->u.object.length)
854 | {
855 | settings->mem_free (value->u.object.values, settings->user_data);
856 | break;
857 | }
858 |
859 | value = value->u.object.values [-- value->u.object.length].value;
860 | continue;
861 |
862 | case json_string:
863 |
864 | settings->mem_free (value->u.string.ptr, settings->user_data);
865 | break;
866 |
867 | default:
868 | break;
869 | };
870 |
871 | cur_value = value;
872 | value = value->parent;
873 | settings->mem_free (cur_value, settings->user_data);
874 | }
875 | }
876 |
877 | void json_value_free (json_value * value)
878 | {
879 | json_settings settings = { 0 };
880 | settings.mem_free = default_free;
881 | json_value_free_ex (&settings, value);
882 | }
883 |
884 |
--------------------------------------------------------------------------------