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.

@@ -0,0 +1,276 @@
1
+ /*
2
+ Copyright: Boaz segev, 2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef __GNU_SOURCE
8
+ #define __GNU_SOURCE
9
+ #endif
10
+
11
+ #include "http1_parser.h"
12
+ #include <ctype.h>
13
+ #include <stdio.h>
14
+ #include <string.h>
15
+
16
+ #define PREFER_MEMCHAR 0
17
+
18
+ /* *****************************************************************************
19
+ Seeking for characters in a string
20
+ ***************************************************************************** */
21
+
22
+ #if PREFER_MEMCHAR
23
+
24
+ /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
25
+ inline static uint8_t seek2ch(uint8_t **pos, uint8_t *const limit, uint8_t ch) {
26
+ /* This is library based alternative that is sometimes slower */
27
+ if (*pos >= limit || **pos == ch) {
28
+ return 0;
29
+ }
30
+ uint8_t *tmp = memchr(*pos, ch, limit - (*pos));
31
+ if (tmp) {
32
+ *pos = tmp;
33
+ *tmp = 0;
34
+ return 1;
35
+ }
36
+ *pos = limit;
37
+ return 0;
38
+ }
39
+
40
+ #else
41
+
42
+ /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
43
+ static inline uint8_t seek2ch(uint8_t **buffer, const uint8_t *const limit,
44
+ const uint8_t c) {
45
+ /* this single char lookup is better when target is closer... */
46
+ if (**buffer == c) {
47
+ **buffer = 0;
48
+ return 1;
49
+ }
50
+
51
+ uint64_t wanted = 0x0101010101010101ULL * c;
52
+ uint64_t *lpos = (uint64_t *)*buffer;
53
+ uint64_t *llimit = ((uint64_t *)limit) - 1;
54
+
55
+ for (; lpos < llimit; lpos++) {
56
+ const uint64_t eq = ~((*lpos) ^ wanted);
57
+ const uint64_t t0 = (eq & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
58
+ const uint64_t t1 = (eq & 0x8080808080808080llu);
59
+ if ((t0 & t1)) {
60
+ break;
61
+ }
62
+ }
63
+
64
+ *buffer = (uint8_t *)lpos;
65
+ while (*buffer < limit) {
66
+ if (**buffer == c) {
67
+ **buffer = 0;
68
+ return 1;
69
+ }
70
+ (*buffer)++;
71
+ }
72
+ return 0;
73
+ }
74
+
75
+ #endif
76
+
77
+ /* a helper that seeks the EOL, converts it to NUL and returns it's length */
78
+ inline static uint8_t seek2eol(uint8_t **pos, uint8_t *const limit) {
79
+ /* single char lookup using memchr might be better when target is far... */
80
+ if (!seek2ch(pos, limit, '\n'))
81
+ return 0;
82
+ if ((*pos)[-1] == '\r') {
83
+ (*pos)[-1] = 0;
84
+ return 2;
85
+ }
86
+ return 1;
87
+ }
88
+
89
+ /* *****************************************************************************
90
+ HTTP/1.1 parsre stages
91
+ ***************************************************************************** */
92
+
93
+ inline static int consume_response_line(struct http1_fio_parser_args_s *args,
94
+ uint8_t *start, uint8_t *end) {
95
+ args->parser->state.reserved |= 128;
96
+ uint8_t *tmp = start;
97
+ if (!seek2ch(&tmp, end, ' '))
98
+ return -1;
99
+ if (args->on_http_version(args->parser, (char *)start, tmp - start))
100
+ return -1;
101
+ tmp = start = tmp + 1;
102
+ if (!seek2ch(&tmp, end, ' '))
103
+ return -1;
104
+ if (args->on_status(args->parser, atol((char *)start), (char *)(tmp + 1),
105
+ end - tmp))
106
+ return -1;
107
+ return 0;
108
+ }
109
+
110
+ inline static int consume_request_line(struct http1_fio_parser_args_s *args,
111
+ uint8_t *start, uint8_t *end) {
112
+ uint8_t *tmp = start;
113
+ if (!seek2ch(&tmp, end, ' '))
114
+ return -1;
115
+ if (args->on_method(args->parser, (char *)start, tmp - start))
116
+ return -1;
117
+ tmp = start = tmp + 1;
118
+ if (seek2ch(&tmp, end, '?')) {
119
+ if (args->on_path(args->parser, (char *)start, tmp - start))
120
+ return -1;
121
+ tmp = start = tmp + 1;
122
+ if (!seek2ch(&tmp, end, ' '))
123
+ return -1;
124
+ if (tmp - start > 0 &&
125
+ args->on_query(args->parser, (char *)start, tmp - start))
126
+ return -1;
127
+ } else {
128
+ tmp = start;
129
+ if (!seek2ch(&tmp, end, ' '))
130
+ return -1;
131
+ if (args->on_path(args->parser, (char *)start, tmp - start))
132
+ return -1;
133
+ }
134
+ start = tmp + 1;
135
+ if (start + 7 >= end)
136
+ return -1;
137
+ if (args->on_http_version(args->parser, (char *)start, end - start))
138
+ return -1;
139
+ return 0;
140
+ }
141
+
142
+ inline static int consume_header(struct http1_fio_parser_args_s *args,
143
+ uint8_t *start, uint8_t *end) {
144
+ uint8_t t2 = 1;
145
+ uint8_t *tmp = start;
146
+ /* divide header name from data */
147
+ if (!seek2ch(&tmp, end, ':'))
148
+ return -1;
149
+ #if HTTP_HEADERS_LOWERCASE
150
+ for (uint8_t *t3 = start; t3 < tmp; t3++) {
151
+ *t3 = tolower(*t3);
152
+ }
153
+ #endif
154
+
155
+ tmp++;
156
+ if (tmp[0] == ' ') {
157
+ tmp++;
158
+ t2++;
159
+ };
160
+ #if HTTP_HEADERS_LOWERCASE
161
+ if ((tmp - start) - t2 == 14 &&
162
+ *((uint64_t *)start) == *((uint64_t *)"content-") &&
163
+ *((uint64_t *)(start + 6)) == *((uint64_t *)"t-length")) {
164
+ /* handle the special `content-length` header */
165
+ args->parser->state.content_length = atol((char *)tmp);
166
+ }
167
+ #else
168
+ if ((tmp - start) - t2 == 14 &&
169
+ HEADER_NAME_IS_EQ((char *)start, "content-length", 14)) {
170
+ /* handle the special `content-length` header */
171
+ args->parser->state.content_length = atol((char *)tmp);
172
+ }
173
+ #endif
174
+ /* perform callback */
175
+ if (args->on_header(args->parser, (char *)start, (tmp - start) - t2,
176
+ (char *)tmp, end - tmp))
177
+ return -1;
178
+ return 0;
179
+ }
180
+
181
+ /* *****************************************************************************
182
+ HTTP/1.1 parsre function
183
+ ***************************************************************************** */
184
+
185
+ size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args) {
186
+ uint8_t *start = args->buffer;
187
+ uint8_t *end = start;
188
+ uint8_t *const stop = start + args->length;
189
+ uint8_t eol_len = 0;
190
+ #define CONSUMED ((size_t)((uintptr_t)start - (uintptr_t)args->buffer))
191
+ // fprintf(stderr, "** resuming with at %p with %.*s...(%lu)\n", args->buffer,
192
+ // 4,
193
+ // start, args->length);
194
+ switch ((args->parser->state.reserved & 31)) {
195
+
196
+ /* request / response line */
197
+ case 0:
198
+ /* clear out any leadinng white space */
199
+ while (*start == '\r' || *start == '\r' || *start == ' ' || *start == 0) {
200
+ start++;
201
+ }
202
+ end = start;
203
+ /* make sure the whole line is available*/
204
+ if (!(eol_len = seek2eol(&end, stop)))
205
+ return CONSUMED;
206
+
207
+ if (start[0] >= '1' && start[0] <= '9') {
208
+ /* HTTP response */
209
+ if (consume_response_line(args, start, end - eol_len + 1))
210
+ goto error;
211
+ } else {
212
+ /* HTTP request */
213
+ if (consume_request_line(args, start, end - eol_len + 1))
214
+ goto error;
215
+ }
216
+ end = start = end + 1;
217
+ args->parser->state.reserved |= 1;
218
+
219
+ /* fallthrough */
220
+ /* headers */
221
+ case 1:
222
+ do {
223
+ if (!(eol_len = seek2eol(&end, stop)))
224
+ return CONSUMED;
225
+ /* test for header ending */
226
+ if (*start == 0)
227
+ goto finished_headers; /* break the do..while loop, not the switch
228
+ statement */
229
+ if (consume_header(args, start, end - eol_len + 1))
230
+ goto error;
231
+ end = start = end + 1;
232
+ } while ((args->parser->state.reserved & 2) == 0);
233
+ finished_headers:
234
+ end = start = end + 1;
235
+ args->parser->state.reserved |= 2;
236
+ if (args->parser->state.content_length == 0)
237
+ goto finish;
238
+ if (end >= stop)
239
+ return args->length;
240
+
241
+ /* fallthrough */
242
+ /* request body */
243
+ case 3: /* 2 | 1 == 3 */
244
+ end = start + args->parser->state.content_length - args->parser->state.read;
245
+ if (end > stop)
246
+ end = stop;
247
+ if (end == start)
248
+ return CONSUMED;
249
+ // fprintf(stderr, "Consuming body at (%lu/%lu):%.*s\n", end - start,
250
+ // args->parser->state.content_length, (int)(end - start), start);
251
+ if (args->on_body_chunk(args->parser, (char *)start, end - start))
252
+ goto error;
253
+ args->parser->state.read += (end - start);
254
+ start = end;
255
+ if (args->parser->state.content_length <= args->parser->state.read)
256
+ goto finish;
257
+ return CONSUMED;
258
+ break;
259
+ }
260
+
261
+ error:
262
+ args->on_error(args->parser);
263
+ args->parser->state =
264
+ (struct http1_parser_protected_read_only_state_s){0, 0, 0};
265
+ return args->length;
266
+
267
+ finish:
268
+ if (((args->parser->state.reserved & 128) ? args->on_response
269
+ : args->on_request)(args->parser))
270
+ goto error;
271
+ args->parser->state =
272
+ (struct http1_parser_protected_read_only_state_s){0, 0, 0};
273
+ return CONSUMED;
274
+ }
275
+
276
+ #undef CONSUMED
@@ -0,0 +1,111 @@
1
+ #ifndef H_HTTP1_PARSER_H
2
+ /*
3
+ Copyright: Boaz segev, 2017
4
+ License: MIT
5
+
6
+ Feel free to copy, use and enjoy according to the license provided.
7
+ */
8
+
9
+ /**
10
+ This is a callback based parser. It parses the skeleton of the HTTP/1.x protocol
11
+ and leaves most of the work (validation, error checks, etc') to the callbacks.
12
+
13
+ This is an attempt to replace the existing HTTP/1.x parser with something easier
14
+ to maintain and that could be used for an HTTP/1.x client as well.
15
+ */
16
+ #define H_HTTP1_PARSER_H
17
+ #include <stdint.h>
18
+ #include <stdlib.h>
19
+
20
+ #ifndef HTTP_HEADERS_LOWERCASE
21
+ /** when defined, HTTP headers will be converted to lowercase and header
22
+ * searches will be case sensitive. */
23
+ #define HTTP_HEADERS_LOWERCASE 1
24
+ #endif
25
+
26
+ #if HTTP_HEADERS_LOWERCASE
27
+
28
+ #define HEADER_NAME_IS_EQ(var_name, const_name, len) \
29
+ (!memcmp((var_name), (const_name), (len)))
30
+ #else
31
+ #define HEADER_NAME_IS_EQ(var_name, const_name, len) \
32
+ (!strncasecmp((var_name), (const_name), (len)))
33
+ #endif
34
+
35
+ /** this struct contains the state of the parser. */
36
+ typedef struct http1_parser_s {
37
+ void *udata;
38
+ struct http1_parser_protected_read_only_state_s {
39
+ size_t content_length;
40
+ size_t read;
41
+ uint8_t reserved;
42
+ } state;
43
+ } http1_parser_s;
44
+
45
+ /**
46
+ * Available options for the parsing function.
47
+ *
48
+ * Callbacks should return 0 unless an error occured.
49
+ */
50
+ struct http1_fio_parser_args_s {
51
+ /** REQUIRED: the parser object that manages the parser's state. */
52
+ http1_parser_s *parser;
53
+ /** REQUIRED: the data to be parsed. */
54
+ void *buffer;
55
+ /** REQUIRED: the length of the data to be parsed. */
56
+ size_t length;
57
+ /** called when a request was received. */
58
+ int (*const on_request)(http1_parser_s *parser);
59
+ /** called when a response was received. */
60
+ int (*const on_response)(http1_parser_s *parser);
61
+ /** called when a request method is parsed. */
62
+ int (*const on_method)(http1_parser_s *parser, char *method,
63
+ size_t method_len);
64
+ /** called when a response status is parsed. the status_str is the string
65
+ * without the prefixed numerical status indicator.*/
66
+ int (*const on_status)(http1_parser_s *parser, size_t status,
67
+ char *status_str, size_t len);
68
+ /** called when a request path (excluding query) is parsed. */
69
+ int (*const on_path)(http1_parser_s *parser, char *path, size_t path_len);
70
+ /** called when a request path (excluding query) is parsed. */
71
+ int (*const on_query)(http1_parser_s *parser, char *query, size_t query_len);
72
+ /** called when a the HTTP/1.x version is parsed. */
73
+ int (*const on_http_version)(http1_parser_s *parser, char *version,
74
+ size_t len);
75
+ /** called when a header is parsed. */
76
+ int (*const on_header)(http1_parser_s *parser, char *name, size_t name_len,
77
+ char *data, size_t data_len);
78
+ /** called when a body chunk is parsed. */
79
+ int (*const on_body_chunk)(http1_parser_s *parser, char *data,
80
+ size_t data_len);
81
+ /** called when a protocol error occured. */
82
+ int (*const on_error)(http1_parser_s *parser);
83
+ };
84
+
85
+ /**
86
+ * Returns the amount of data actually consumed by the parser.
87
+ *
88
+ * The value 0 indicates there wasn't enough data to be parsed and the same
89
+ * buffer (with more data) should be resubmitted.
90
+ *
91
+ * A value smaller than the buffer size indicates that EITHER a request /
92
+ * response was detected OR that the leftover could not be consumed because more
93
+ * data was required.
94
+ *
95
+ * Simply resubmit the reminder of the data to continue parsing.
96
+ *
97
+ * A request / response callback automatically stops the parsing process,
98
+ * allowing the user to adjust or refresh the state of the data.
99
+ */
100
+ size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args);
101
+
102
+ static inline __attribute__((unused)) size_t
103
+ http1_fio_parser(struct http1_fio_parser_args_s args) {
104
+ return http1_fio_parser_fn(&args);
105
+ }
106
+ #if __STDC_VERSION__ >= 199901L
107
+ #define http1_fio_parser(...) \
108
+ http1_fio_parser((struct http1_fio_parser_args_s){__VA_ARGS__})
109
+ #endif
110
+
111
+ #endif
@@ -85,11 +85,11 @@ initialize:
85
85
  return (http_response_s *)http1_response_pool.pool_mem;
86
86
  }
87
87
 
88
- static void http1_response_deffered_destroy(void *rs_, void *ignr) {
88
+ static void http1_response_deferred_destroy(void *rs_, void *ignr) {
89
89
  (void)(ignr);
90
90
  http1_response_s *rs = rs_;
91
91
  if (spn_trylock(&rs->lock)) {
92
- defer(http1_response_deffered_destroy, rs, NULL);
92
+ defer(http1_response_deferred_destroy, rs, NULL);
93
93
  return;
94
94
  }
95
95
  rs->use_count -= 1;
@@ -118,7 +118,7 @@ use_free:
118
118
  /** Destroys the response object. No data is sent.*/
119
119
  void http1_response_destroy(http_response_s *rs) {
120
120
  ((http1_response_s *)rs)->dest_count++;
121
- http1_response_deffered_destroy(rs, NULL);
121
+ http1_response_deferred_destroy(rs, NULL);
122
122
  }
123
123
 
124
124
  /* *****************************************************************************
@@ -162,15 +162,15 @@ static void http1_response_finalize_headers(http1_response_s *rs) {
162
162
  rs->response.date = rs->response.last_modified;
163
163
  struct tm t;
164
164
  /* date header */
165
- http_gmtime(&rs->response.date, &t);
166
165
  h1p_protected_copy(rs, "Date: ", 6);
167
- rs->buffer_end += http_date2str(rs->buffer + rs->buffer_end, &t);
166
+ rs->buffer_end +=
167
+ http_time2str(rs->buffer + rs->buffer_end, rs->response.date);
168
168
  rs->buffer[rs->buffer_end++] = '\r';
169
169
  rs->buffer[rs->buffer_end++] = '\n';
170
170
  /* last-modified header */
171
- http_gmtime(&rs->response.last_modified, &t);
172
171
  h1p_protected_copy(rs, "Last-Modified: ", 15);
173
- rs->buffer_end += http_date2str(rs->buffer + rs->buffer_end, &t);
172
+ rs->buffer_end +=
173
+ http_time2str(rs->buffer + rs->buffer_end, rs->response.last_modified);
174
174
  rs->buffer[rs->buffer_end++] = '\r';
175
175
  rs->buffer[rs->buffer_end++] = '\n';
176
176
  }
@@ -229,7 +229,7 @@ void http1_response_finish(http_response_s *rs) {
229
229
  http1_response_send_headers((http1_response_s *)rs);
230
230
  if (rs->should_close)
231
231
  sock_close(rs->fd);
232
- http1_response_deffered_destroy(rs, NULL);
232
+ http1_response_deferred_destroy(rs, NULL);
233
233
  }
234
234
 
235
235
  /* *****************************************************************************
@@ -485,20 +485,6 @@ static void http_response_log_finish(http_response_s *response) {
485
485
  ? ((get_clock_mili() - response->clock_start) / CLOCK_RESOLUTION)
486
486
  : 0;
487
487
 
488
- /* pre-print log message every 1 or 2 seconds or so. */
489
- static __thread time_t cached_tick;
490
- static __thread char cached_httpdate[48];
491
- static __thread size_t chached_len;
492
- time_t last_tick = facil_last_tick();
493
- if (last_tick > cached_tick) {
494
- struct tm tm;
495
- cached_tick = last_tick | 1;
496
- http_gmtime(&last_tick, &tm);
497
- chached_len = http_date2str(cached_httpdate, &tm);
498
- fprintf(stderr, "Updated date cache to (%lu) %s\n", chached_len,
499
- cached_httpdate);
500
- }
501
-
502
488
  // TODO Guess IP address from headers (forwarded) where possible
503
489
  sock_peer_addr_s addrinfo = sock_peer_addr(response->fd);
504
490
 
@@ -519,8 +505,7 @@ static void http_response_log_finish(http_response_s *response) {
519
505
  }
520
506
  memcpy(buffer + pos, " - - [", 6);
521
507
  pos += 6;
522
- memcpy(buffer + pos, cached_httpdate, chached_len);
523
- pos += chached_len;
508
+ pos += http_time2str(buffer + pos, facil_last_tick());
524
509
  buffer[pos++] = ']';
525
510
  buffer[pos++] = ' ';
526
511
  buffer[pos++] = '"';