Bug Summary

File:lwan-request.c
Location:line 777, column 29
Description:The left operand of '==' is a garbage value

Annotated Source Code

1/*
2 * lwan - simple web server
3 * Copyright (c) 2012-2014 Leandro A. F. Pereira <leandro@hardinfo.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#define _GNU_SOURCE
21#include <assert.h>
22#include <errno(*__errno_location ()).h>
23#include <inttypes.h>
24#include <stddef.h>
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28#include <arpa/inet.h>
29
30#include "lwan.h"
31#include "lwan-config.h"
32#include "lwan-http-authorize.h"
33
34typedef enum {
35 FINALIZER_DONE,
36 FINALIZER_TRY_AGAIN,
37 FINALIZER_YIELD_TRY_AGAIN,
38 FINALIZER_ERROR_TOO_LARGE
39} lwan_read_finalizer_t;
40
41typedef struct lwan_request_parse_t_ lwan_request_parse_t;
42
43struct lwan_request_parse_t_ {
44 lwan_value_t buffer;
45 lwan_value_t query_string;
46 lwan_value_t if_modified_since;
47 lwan_value_t range;
48 lwan_value_t accept_encoding;
49 lwan_value_t fragment;
50 lwan_value_t content_length;
51 lwan_value_t post_data;
52 lwan_value_t content_type;
53 lwan_value_t authorization;
54 char connection;
55};
56
57static char _decode_hex_digit(char ch) __attribute__((pure));
58static bool_Bool _is_hex_digit(char ch) __attribute__((pure));
59static unsigned long _has_zero_byte(unsigned long n) __attribute__((pure));
60static unsigned long _is_space(char ch) __attribute__((pure));
61static char *_ignore_leading_whitespace(char *buffer) __attribute__((pure));
62
63static ALWAYS_INLINEinline __attribute__((always_inline)) char *
64_identify_http_method(lwan_request_t *request, char *buffer)
65{
66 enum {
67 HTTP_STR_GET = MULTICHAR_CONSTANT('G','E','T',' ')((int32_t)(('G') | ('E') << 8 | ('T') << 16 | (' '
) << 24))
,
68 HTTP_STR_HEAD = MULTICHAR_CONSTANT('H','E','A','D')((int32_t)(('H') | ('E') << 8 | ('A') << 16 | ('D'
) << 24))
,
69 HTTP_STR_POST = MULTICHAR_CONSTANT('P','O','S','T')((int32_t)(('P') | ('O') << 8 | ('S') << 16 | ('T'
) << 24))
70 };
71
72 STRING_SWITCH(buffer)switch (*((int32_t *)(buffer))) {
73 case HTTP_STR_GET:
74 request->flags |= REQUEST_METHOD_GET;
75 return buffer + 4;
76 case HTTP_STR_HEAD:
77 request->flags |= REQUEST_METHOD_HEAD;
78 return buffer + 5;
79 case HTTP_STR_POST:
80 request->flags |= REQUEST_METHOD_POST;
81 return buffer + 5;
82 }
83 return NULL((void*)0);
84}
85
86static ALWAYS_INLINEinline __attribute__((always_inline)) char
87_decode_hex_digit(char ch)
88{
89 return (char)((ch <= '9') ? ch - '0' : (ch & 7) + 9);
90}
91
92static ALWAYS_INLINEinline __attribute__((always_inline)) bool_Bool
93_is_hex_digit(char ch)
94{
95 return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
96}
97
98static size_t
99_url_decode(char *str)
100{
101 if (UNLIKELY(!str)__builtin_expect(((!str)), (0)))
102 return 0;
103
104 char *ch, *decoded;
105 for (decoded = ch = str; *ch; ch++) {
106 if (*ch == '%' && LIKELY(_is_hex_digit(ch[1]) && _is_hex_digit(ch[2]))__builtin_expect((!!(_is_hex_digit(ch[1]) && _is_hex_digit
(ch[2]))), (1))
) {
107 char tmp = (char)(_decode_hex_digit(ch[1]) << 4 | _decode_hex_digit(ch[2]));
108 if (UNLIKELY(!tmp)__builtin_expect(((!tmp)), (0)))
109 return 0;
110 *decoded++ = tmp;
111 ch += 2;
112 } else if (*ch == '+') {
113 *decoded++ = ' ';
114 } else {
115 *decoded++ = *ch;
116 }
117 }
118
119 *decoded = '\0';
120 return (size_t)(decoded - str);
121}
122
123static int
124_key_value_compare_qsort_key(const void *a, const void *b)
125{
126 return strcmp(((lwan_key_value_t *)a)->key, ((lwan_key_value_t *)b)->key);
127}
128
129#define DECODE_AND_ADD() \
130 do { \
131 if (LIKELY(_url_decode(key))__builtin_expect((!!(_url_decode(key))), (1))) { \
132 kvs[values].key = key; \
133 if (LIKELY(_url_decode(value))__builtin_expect((!!(_url_decode(value))), (1))) \
134 kvs[values].value = value; \
135 else \
136 kvs[values].value = ""; \
137 ++values; \
138 if (UNLIKELY(values >= N_ELEMENTS(kvs))__builtin_expect(((values >= (sizeof(kvs) / sizeof(kvs[0])
))), (0))
) \
139 goto oom; \
140 } \
141 } while(0)
142
143static void
144_parse_urlencoded_keyvalues(lwan_request_t *request,
145 lwan_value_t *helper_value, lwan_key_value_t **base, size_t *len)
146{
147 if (!helper_value->value)
148 return;
149
150 char *key = helper_value->value;
151 char *value = NULL((void*)0);
152 size_t values = 0;
153 lwan_key_value_t kvs[256];
154
155 for (char *ch = key; *ch; ch++) {
156 switch (*ch) {
157 case '=':
158 *ch = '\0';
159 value = ch + 1;
160 break;
161 case '&':
162 case ';':
163 *ch = '\0';
164 DECODE_AND_ADD();
165 key = ch + 1;
166 value = NULL((void*)0);
167 }
168 }
169
170 DECODE_AND_ADD();
171oom:
172 kvs[values].key = kvs[values].value = NULL((void*)0);
173
174 lwan_key_value_t *kv = coro_malloc(request->conn->coro,
175 (1 + values) * sizeof(lwan_key_value_t));
176 if (LIKELY(kv)__builtin_expect((!!(kv)), (1))) {
177 qsort(kvs, values, sizeof(lwan_key_value_t), _key_value_compare_qsort_key);
178 *base = memcpy(kv, kvs, (1 + values) * sizeof(lwan_key_value_t));
179 *len = values;
180 }
181}
182
183#undef DECODE_AND_ADD
184
185static void
186_parse_query_string(lwan_request_t *request, lwan_request_parse_t *helper)
187{
188 _parse_urlencoded_keyvalues(request, &helper->query_string,
189 &request->query_params.base, &request->query_params.len);
190}
191
192static void
193_parse_post_data(lwan_request_t *request, lwan_request_parse_t *helper)
194{
195 static const char content_type[] = "application/x-www-form-urlencoded";
196
197 if (helper->content_type.len != sizeof(content_type) - 1)
198 return;
199 if (UNLIKELY(strcmp(helper->content_type.value, content_type))__builtin_expect(((strcmp(helper->content_type.value, content_type
))), (0))
)
200 return;
201
202 _parse_urlencoded_keyvalues(request, &helper->post_data,
203 &request->post_data.base, &request->post_data.len);
204}
205
206static char *
207_identify_http_path(lwan_request_t *request, char *buffer,
208 lwan_request_parse_t *helper)
209{
210 char *end_of_line = memchr(buffer, '\r',
211 (helper->buffer.len - (size_t)(buffer - helper->buffer.value)));
212 if (UNLIKELY(!end_of_line)__builtin_expect(((!end_of_line)), (0)))
213 return NULL((void*)0);
214 *end_of_line = '\0';
215
216 char *space = end_of_line - sizeof("HTTP/X.X");
217 if (UNLIKELY(*(space + 1) != 'H')__builtin_expect(((*(space + 1) != 'H')), (0))) /* assume HTTP/X.Y */
218 return NULL((void*)0);
219 *space = '\0';
220
221 if (UNLIKELY(*(space + 6) != '1')__builtin_expect(((*(space + 6) != '1')), (0)))
222 return NULL((void*)0);
223
224 if (*(space + 8) == '0')
225 request->flags |= REQUEST_IS_HTTP_1_0;
226
227 if (UNLIKELY(*buffer != '/')__builtin_expect(((*buffer != '/')), (0)))
228 return NULL((void*)0);
229
230 request->url.value = buffer;
231 request->url.len = (size_t)(space - buffer);
232
233 /* Most of the time, fragments are small -- so search backwards */
234 char *fragment = memrchr(buffer, '#', request->url.len);
235 if (fragment) {
236 *fragment = '\0';
237 helper->fragment.value = fragment + 1;
238 helper->fragment.len = (size_t)(space - fragment - 1);
239 request->url.len -= helper->fragment.len + 1;
240 }
241
242 /* Most of the time, query string values are larger than the URL, so
243 search from the beginning */
244 char *query_string = memchr(buffer, '?', request->url.len);
245 if (query_string) {
246 *query_string = '\0';
247 helper->query_string.value = query_string + 1;
248 helper->query_string.len = (size_t)((fragment ? fragment : space) - query_string - 1);
249 request->url.len -= helper->query_string.len + 1;
250 }
251
252 request->original_url.value = buffer;
253 request->original_url.len = request->url.len;
254
255 return end_of_line + 1;
256}
257
258#define MATCH_HEADER(hdr) \
259 do { \
260 char *end; \
261 p += sizeof(hdr) - 1; \
262 if (p >= buffer_end) /* reached the end of header blocks */ \
263 goto end; \
264 if (UNLIKELY(*p++ != ':')__builtin_expect(((*p++ != ':')), (0))) /* not the header we're looking for */ \
265 goto did_not_match; \
266 if (UNLIKELY(*p++ != ' ')__builtin_expect(((*p++ != ' ')), (0))) /* not the header we're looking for */ \
267 goto did_not_match; \
268 if (LIKELY(end = strchr(p, '\r'))__builtin_expect((!!(end = strchr(p, '\r'))), (1))) { \
269 *end = '\0'; \
270 value = p; \
271 p = end + 1; \
272 length = (size_t)(end - value); \
273 if (UNLIKELY(*p != '\n')__builtin_expect(((*p != '\n')), (0))) \
274 goto did_not_match; \
275 } else goto did_not_match; /* couldn't find line end */ \
276 } while (0)
277
278#define CASE_HEADER(hdr_const,hdr_name) \
279 case hdr_const: MATCH_HEADER(hdr_name);
280
281static char *
282_parse_headers(lwan_request_parse_t *helper, char *buffer, char *buffer_end)
283{
284 enum {
285 HTTP_HDR_CONNECTION = MULTICHAR_CONSTANT_L('C','o','n','n')(((int32_t)(('C') | ('o') << 8 | ('n') << 16 | ('n'
) << 24)) | 0x20202020)
,
286 HTTP_HDR_RANGE = MULTICHAR_CONSTANT_L('R','a','n','g')(((int32_t)(('R') | ('a') << 8 | ('n') << 16 | ('g'
) << 24)) | 0x20202020)
,
287 HTTP_HDR_IF_MODIFIED_SINCE = MULTICHAR_CONSTANT_L('I','f','-','M')(((int32_t)(('I') | ('f') << 8 | ('-') << 16 | ('M'
) << 24)) | 0x20202020)
,
288 HTTP_HDR_ACCEPT = MULTICHAR_CONSTANT_L('A','c','c','e')(((int32_t)(('A') | ('c') << 8 | ('c') << 16 | ('e'
) << 24)) | 0x20202020)
,
289 HTTP_HDR_CONTENT = MULTICHAR_CONSTANT_L('C','o','n','t')(((int32_t)(('C') | ('o') << 8 | ('n') << 16 | ('t'
) << 24)) | 0x20202020)
,
290 HTTP_HDR_ENCODING = MULTICHAR_CONSTANT_L('-','E','n','c')(((int32_t)(('-') | ('E') << 8 | ('n') << 16 | ('c'
) << 24)) | 0x20202020)
,
291 HTTP_HDR_LENGTH = MULTICHAR_CONSTANT_L('-','L','e','n')(((int32_t)(('-') | ('L') << 8 | ('e') << 16 | ('n'
) << 24)) | 0x20202020)
,
292 HTTP_HDR_TYPE = MULTICHAR_CONSTANT_L('-','T','y','p')(((int32_t)(('-') | ('T') << 8 | ('y') << 16 | ('p'
) << 24)) | 0x20202020)
,
293 HTTP_HDR_AUTHORIZATION = MULTICHAR_CONSTANT_L('A','u','t','h')(((int32_t)(('A') | ('u') << 8 | ('t') << 16 | ('h'
) << 24)) | 0x20202020)
,
294 };
295
296 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
297 return NULL((void*)0);
298
299 for (char *p = buffer; *p; buffer = ++p) {
300 char *value;
301 size_t length;
302
303retry:
304 if ((p + sizeof(int32_t)) >= buffer_end)
305 break;
306
307 STRING_SWITCH_L(p)switch (*((int32_t *)(p)) | 0x20202020) {
308 CASE_HEADER(HTTP_HDR_CONNECTION, "Connection")
309 helper->connection = (*value | 0x20);
310 break;
311 CASE_HEADER(HTTP_HDR_IF_MODIFIED_SINCE, "If-Modified-Since")
312 helper->if_modified_since.value = value;
313 helper->if_modified_since.len = length;
314 break;
315 CASE_HEADER(HTTP_HDR_RANGE, "Range")
316 helper->range.value = value;
317 helper->range.len = length;
318 break;
319 CASE_HEADER(HTTP_HDR_AUTHORIZATION, "Authorization")
320 helper->authorization.value = value;
321 helper->authorization.len = length;
322 break;
323 CASE_HEADER(HTTP_HDR_ENCODING, "-Encoding")
324 helper->accept_encoding.value = value;
325 helper->accept_encoding.len = length;
326 break;
327 CASE_HEADER(HTTP_HDR_TYPE, "-Type")
328 helper->content_type.value = value;
329 helper->content_type.len = length;
330 break;
331 CASE_HEADER(HTTP_HDR_LENGTH, "-Length")
332 helper->content_length.value = value;
333 helper->content_length.len = length;
334 break;
335 case HTTP_HDR_CONTENT:
336 p += sizeof("Content") - 1;
337 goto retry;
338 case HTTP_HDR_ACCEPT:
339 p += sizeof("Accept") - 1;
340 goto retry;
341 }
342did_not_match:
343 p = memchr(p, '\n', (size_t)(buffer_end - p));
344 if (!p)
345 break;
346 }
347
348end:
349 return buffer;
350}
351
352#undef CASE_HEADER
353#undef MATCH_HEADER
354
355static void
356_parse_if_modified_since(lwan_request_t *request, lwan_request_parse_t *helper)
357{
358 if (UNLIKELY(!helper->if_modified_since.len)__builtin_expect(((!helper->if_modified_since.len)), (0)))
359 return;
360
361 struct tm t;
362 char *processed = strptime(helper->if_modified_since.value,
363 "%a, %d %b %Y %H:%M:%S GMT", &t);
364
365 if (UNLIKELY(!processed)__builtin_expect(((!processed)), (0)))
366 return;
367 if (UNLIKELY(*processed)__builtin_expect(((*processed)), (0)))
368 return;
369
370 request->header.if_modified_since = timegm(&t);
371}
372
373static void
374_parse_range(lwan_request_t *request, lwan_request_parse_t *helper)
375{
376 if (UNLIKELY(helper->range.len <= (sizeof("bytes=") - 1))__builtin_expect(((helper->range.len <= (sizeof("bytes="
) - 1))), (0))
)
377 return;
378
379 char *range = helper->range.value;
380 if (UNLIKELY(strncmp(range, "bytes=", sizeof("bytes=") - 1))__builtin_expect(((strncmp(range, "bytes=", sizeof("bytes=") -
1))), (0))
)
381 return;
382
383 range += sizeof("bytes=") - 1;
384 off_t from, to;
385
386 if (sscanf(range, "%"PRIu64"l" "u""-%"PRIu64"l" "u", &from, &to) == 2) {
387 request->header.range.from = from;
388 request->header.range.to = to;
389 } else if (sscanf(range, "-%"PRIu64"l" "u", &to) == 1) {
390 request->header.range.from = 0;
391 request->header.range.to = to;
392 } else if (sscanf(range, "%"PRIu64"l" "u""-", &from) == 1) {
393 request->header.range.from = from;
394 request->header.range.to = -1;
395 } else {
396 request->header.range.from = -1;
397 request->header.range.to = -1;
398 }
399}
400
401static void
402_parse_accept_encoding(lwan_request_t *request, lwan_request_parse_t *helper)
403{
404 if (!helper->accept_encoding.len)
405 return;
406
407 enum {
408 ENCODING_DEFL1 = MULTICHAR_CONSTANT('d','e','f','l')((int32_t)(('d') | ('e') << 8 | ('f') << 16 | ('l'
) << 24))
,
409 ENCODING_DEFL2 = MULTICHAR_CONSTANT(' ','d','e','f')((int32_t)((' ') | ('d') << 8 | ('e') << 16 | ('f'
) << 24))
410 };
411
412 for (char *p = helper->accept_encoding.value; p && *p; p++) {
413 STRING_SWITCH(p)switch (*((int32_t *)(p))) {
414 case ENCODING_DEFL1:
415 case ENCODING_DEFL2:
416 request->flags |= REQUEST_ACCEPT_DEFLATE;
417 return;
418 }
419
420 if (!(p = strchr(p, ',')))
421 break;
422 }
423}
424
425static ALWAYS_INLINEinline __attribute__((always_inline)) unsigned long
426_has_zero_byte(unsigned long n)
427{
428 return ((n - 0x01010101UL) & ~n) & 0x80808080UL;
429}
430
431static ALWAYS_INLINEinline __attribute__((always_inline)) unsigned long
432_is_space(char ch)
433{
434 return _has_zero_byte((0x1010101UL * (unsigned long)ch) ^ 0x090a0d20UL);
435}
436
437static ALWAYS_INLINEinline __attribute__((always_inline)) char *
438_ignore_leading_whitespace(char *buffer)
439{
440 while (*buffer && _is_space(*buffer))
441 buffer++;
442 return buffer;
443}
444
445static ALWAYS_INLINEinline __attribute__((always_inline)) void
446_compute_keep_alive_flag(lwan_request_t *request, lwan_request_parse_t *helper)
447{
448 bool_Bool is_keep_alive;
449 if (!(request->flags & REQUEST_IS_HTTP_1_0))
450 is_keep_alive = (helper->connection != 'c');
451 else
452 is_keep_alive = (helper->connection == 'k');
453 if (is_keep_alive)
454 request->conn->flags |= CONN_KEEP_ALIVE;
455 else
456 request->conn->flags &= ~CONN_KEEP_ALIVE;
457}
458
459static lwan_http_status_t _read_from_request_socket(lwan_request_t *request,
460 lwan_value_t *buffer, const size_t buffer_size,
461 lwan_read_finalizer_t (*finalizer)(size_t total_read, size_t buffer_size, lwan_value_t *buffer))
462{
463 ssize_t n;
464 size_t total_read = 0;
465
466 while(true1) {
467 n = read(request->fd, buffer->value + total_read,
468 (size_t)(buffer_size - total_read));
469 /* Client has shutdown orderly, nothing else to do; kill coro */
470 if (UNLIKELY(n == 0)__builtin_expect(((n == 0)), (0))) {
471 coro_yield(request->conn->coro, CONN_CORO_ABORT);
472 ASSERT_NOT_REACHED()((!"Not reached") ? (void) (0) : __assert_fail ("!\"Not reached\""
, "/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-request.c"
, 472, __PRETTY_FUNCTION__))
;
473 }
474
475 if (UNLIKELY(n < 0)__builtin_expect(((n < 0)), (0))) {
476 switch (errno(*__errno_location ())) {
477 case EAGAIN11:
478 case EINTR4:
479yield_and_read_again:
480 /* Toggle write events so the scheduler thinks we're in a
481 * "can read" state (and thus resumable). */
482 request->conn->flags ^= CONN_WRITE_EVENTS;
483 /* Yield 1 so the scheduler doesn't kill the coroutine. */
484 coro_yield(request->conn->coro, CONN_CORO_MAY_RESUME);
485 /* Put the WRITE_EVENTS flag back on. */
486 request->conn->flags ^= CONN_WRITE_EVENTS;
487 /* We can probably read again, so try it */
488 continue;
489 }
490
491 /* Unexpected error before reading anything */
492 if (UNLIKELY(!total_read)__builtin_expect(((!total_read)), (0)))
493 return HTTP_BAD_REQUEST;
494
495 /* Unexpected error, kill coro */
496 coro_yield(request->conn->coro, CONN_CORO_ABORT);
497 ASSERT_NOT_REACHED()((!"Not reached") ? (void) (0) : __assert_fail ("!\"Not reached\""
, "/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-request.c"
, 497, __PRETTY_FUNCTION__))
;
498 }
499
500 total_read += (size_t)n;
501 buffer->value[total_read] = '\0';
502
503 switch (finalizer(total_read, buffer_size, buffer)) {
504 case FINALIZER_YIELD_TRY_AGAIN: goto yield_and_read_again;
505 case FINALIZER_ERROR_TOO_LARGE: return HTTP_TOO_LARGE;
506 case FINALIZER_DONE: goto out;
507 case FINALIZER_TRY_AGAIN: continue;
508 }
509 }
510
511out:
512 buffer->len = (size_t)total_read;
513 return HTTP_OK;
514}
515
516static lwan_read_finalizer_t _read_request_finalizer(size_t total_read,
517 size_t buffer_size, lwan_value_t *buffer)
518{
519 if (UNLIKELY(total_read < 4)__builtin_expect(((total_read < 4)), (0)))
520 return FINALIZER_YIELD_TRY_AGAIN;
521
522 if (UNLIKELY(total_read == buffer_size)__builtin_expect(((total_read == buffer_size)), (0)))
523 return FINALIZER_ERROR_TOO_LARGE;
524
525 if (LIKELY(!memcmp(buffer->value + total_read - 4, "\r\n\r\n", 4))__builtin_expect((!!(!memcmp(buffer->value + total_read - 4
, "\r\n\r\n", 4))), (1))
)
526 return FINALIZER_DONE;
527
528 char *post_data_separator = strrchr(buffer->value, '\n');
529 if (post_data_separator) {
530 if (LIKELY(!memcmp(post_data_separator - 3, "\r\n\r", 3))__builtin_expect((!!(!memcmp(post_data_separator - 3, "\r\n\r"
, 3))), (1))
)
531 return FINALIZER_DONE;
532 }
533
534 return FINALIZER_TRY_AGAIN;
535}
536
537static ALWAYS_INLINEinline __attribute__((always_inline)) lwan_http_status_t
538_read_request(lwan_request_t *request, lwan_request_parse_t *helper)
539{
540 return _read_from_request_socket(request, &helper->buffer,
541 DEFAULT_BUFFER_SIZE4096, _read_request_finalizer);
542}
543
544static lwan_read_finalizer_t
545_read_post_data_finalizer(size_t total_read, size_t buffer_size,
546 lwan_value_t *buffer __attribute__((unused)))
547{
548 if (LIKELY(total_read == buffer_size)__builtin_expect((!!(total_read == buffer_size)), (1)))
549 return FINALIZER_DONE;
550 return FINALIZER_YIELD_TRY_AGAIN;
551}
552
553static lwan_http_status_t
554_read_post_data(lwan_request_t *request, lwan_request_parse_t *helper, char
555 *buffer)
556{
557 long parsed_length;
558
559 if (!helper->content_length.value)
560 return HTTP_BAD_REQUEST;
561 parsed_length = parse_long(helper->content_length.value, DEFAULT_BUFFER_SIZE4096);
562 if (UNLIKELY(parsed_length > DEFAULT_BUFFER_SIZE)__builtin_expect(((parsed_length > 4096)), (0)))
563 return HTTP_TOO_LARGE;
564 if (UNLIKELY(parsed_length < 0)__builtin_expect(((parsed_length < 0)), (0)))
565 return HTTP_BAD_REQUEST;
566
567 size_t post_data_size = (size_t)parsed_length;
568 size_t curr_post_data_len =
569 (helper->buffer.len - (size_t)(buffer - helper->buffer.value));
570 if (curr_post_data_len == post_data_size) {
571 helper->post_data.value = buffer;
572 helper->post_data.len = (size_t)post_data_size;
573
574 return HTTP_OK;
575 }
576
577 helper->post_data.value = coro_malloc(request->conn->coro, (size_t)post_data_size);
578 if (!helper->post_data.value)
579 return HTTP_INTERNAL_ERROR;
580
581 memcpy(helper->post_data.value, buffer, (size_t)curr_post_data_len);
582 helper->post_data.len = (size_t)curr_post_data_len;
583 helper->post_data.value += curr_post_data_len;
584
585 lwan_http_status_t status = _read_from_request_socket(request,
586 &helper->post_data,
587 post_data_size - curr_post_data_len,
588 _read_post_data_finalizer);
589 if (status != HTTP_OK)
590 return status;
591
592 helper->post_data.value -= curr_post_data_len;
593 return HTTP_OK;
594}
595
596static lwan_http_status_t
597_parse_http_request(lwan_request_t *request, lwan_request_parse_t *helper)
598{
599 char *buffer;
600
601 buffer = _ignore_leading_whitespace(helper->buffer.value);
602 if (UNLIKELY(!*buffer)__builtin_expect(((!*buffer)), (0)))
603 return HTTP_BAD_REQUEST;
604
605 buffer = _identify_http_method(request, buffer);
606 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
607 return HTTP_NOT_ALLOWED;
608
609 buffer = _identify_http_path(request, buffer, helper);
610 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
611 return HTTP_BAD_REQUEST;
612
613 buffer = _parse_headers(helper, buffer, helper->buffer.value + helper->buffer.len);
614 if (UNLIKELY(!buffer)__builtin_expect(((!buffer)), (0)))
615 return HTTP_BAD_REQUEST;
616
617 size_t decoded_len = _url_decode(request->url.value);
618 if (UNLIKELY(!decoded_len)__builtin_expect(((!decoded_len)), (0)))
619 return HTTP_BAD_REQUEST;
620 request->original_url.len = request->url.len = decoded_len;
621
622 _compute_keep_alive_flag(request, helper);
623
624 if (request->flags & REQUEST_METHOD_POST) {
625 lwan_http_status_t status = _read_post_data(request, helper, buffer);
626 if (UNLIKELY(status != HTTP_OK)__builtin_expect(((status != HTTP_OK)), (0)))
627 return status;
628 }
629
630 return HTTP_OK;
631}
632
633static lwan_http_status_t
634_prepare_for_response(lwan_url_map_t *url_map,
635 lwan_request_t *request,
636 lwan_request_parse_t *helper)
637{
638 if (url_map->flags & HANDLER_PARSE_QUERY_STRING)
639 _parse_query_string(request, helper);
640
641 if (url_map->flags & HANDLER_PARSE_IF_MODIFIED_SINCE)
642 _parse_if_modified_since(request, helper);
643
644 if (url_map->flags & HANDLER_PARSE_RANGE)
645 _parse_range(request, helper);
646
647 if (url_map->flags & HANDLER_PARSE_ACCEPT_ENCODING)
648 _parse_accept_encoding(request, helper);
649
650 if (request->flags & REQUEST_METHOD_POST) {
651 if (url_map->flags & HANDLER_PARSE_POST_DATA)
652 _parse_post_data(request, helper);
653 else
654 return HTTP_NOT_ALLOWED;
655 }
656
657 if (url_map->flags & HANDLER_MUST_AUTHORIZE) {
658 if (!lwan_http_authorize(request,
659 &helper->authorization,
660 url_map->authorization.realm,
661 url_map->authorization.password_file))
662 return HTTP_NOT_AUTHORIZED;
663 }
664
665 if (url_map->flags & HANDLER_REMOVE_LEADING_SLASH) {
666 while (*request->url.value == '/' && request->url.len > 0) {
667 ++request->url.value;
668 --request->url.len;
669 }
670 }
671
672 return HTTP_OK;
673}
674
675void
676lwan_process_request(lwan_t *l, lwan_request_t *request)
677{
678 lwan_http_status_t status;
679 lwan_url_map_t *url_map;
680 char buffer[DEFAULT_BUFFER_SIZE4096];
681 lwan_request_parse_t helper = {
682 .buffer = {
683 .value = buffer,
684 .len = 0
685 }
686 };
687
688 status = _read_request(request, &helper);
689 if (UNLIKELY(status != HTTP_OK)__builtin_expect(((status != HTTP_OK)), (0))) {
690 /* If status is anything but a bad request at this point, give up. */
691 if (status != HTTP_BAD_REQUEST)
692 lwan_default_response(request, status);
693
694 return;
695 }
696
697 status = _parse_http_request(request, &helper);
698 if (UNLIKELY(status != HTTP_OK)__builtin_expect(((status != HTTP_OK)), (0))) {
699 lwan_default_response(request, status);
700 return;
701 }
702
703 url_map = lwan_trie_lookup_prefix(l->url_map_trie, request->url.value);
704 if (UNLIKELY(!url_map)__builtin_expect(((!url_map)), (0))) {
705 lwan_default_response(request, HTTP_NOT_FOUND);
706 return;
707 }
708
709 request->url.value += url_map->prefix_len;
710 request->url.len -= url_map->prefix_len;
711
712 status = _prepare_for_response(url_map, request, &helper);
713 if (UNLIKELY(status != HTTP_OK)__builtin_expect(((status != HTTP_OK)), (0))) {
714 lwan_default_response(request, status);
715 return;
716 }
717
718 status = url_map->callback(request, &request->response, url_map->data);
719 lwan_response(request, status);
720}
721
722static const char *
723_value_array_bsearch(lwan_key_value_t *base, const size_t len, const char *key)
724{
725 if (UNLIKELY(!len)__builtin_expect(((!len)), (0)))
726 return NULL((void*)0);
727
728 size_t lower_bound = 0;
729 size_t upper_bound = len;
730 size_t key_len = strlen(key);
731
732 while (lower_bound < upper_bound) {
733 /* lower_bound + upper_bound will never overflow */
734 size_t idx = (lower_bound + upper_bound) / 2;
735 lwan_key_value_t *ptr = base + idx;
736 int cmp = strncmp(key, ptr->key, key_len);
737 if (LIKELY(!cmp)__builtin_expect((!!(!cmp)), (1)))
738 return ptr->value;
739 if (cmp > 0)
740 lower_bound = idx + 1;
741 else
742 upper_bound = idx;
743 }
744
745 return NULL((void*)0);
746}
747
748ALWAYS_INLINEinline __attribute__((always_inline)) const char *
749lwan_request_get_query_param(lwan_request_t *request, const char *key)
750{
751 return _value_array_bsearch(request->query_params.base,
752 request->query_params.len, key);
753}
754
755ALWAYS_INLINEinline __attribute__((always_inline)) const char *
756lwan_request_get_post_param(lwan_request_t *request, const char *key)
757{
758 return _value_array_bsearch(request->post_data.base,
759 request->post_data.len, key);
760}
761
762ALWAYS_INLINEinline __attribute__((always_inline)) int
763lwan_connection_get_fd(lwan_connection_t *conn)
764{
765 return (int)(ptrdiff_t)(conn - conn->thread->lwan->conns);
766}
767
768const char *
769lwan_request_get_remote_address(lwan_request_t *request,
770 char buffer[static INET6_ADDRSTRLEN46])
771{
772 struct sockaddr_storage sock_addr;
773 socklen_t sock_len = sizeof(struct sockaddr_storage);
774 if (UNLIKELY(getpeername(request->fd, (struct sockaddr *)&sock_addr, &sock_len) < 0)__builtin_expect(((getpeername(request->fd, (struct sockaddr
*)&sock_addr, &sock_len) < 0)), (0))
)
1
Taking false branch
775 return NULL((void*)0);
776
777 if (sock_addr.ss_family == AF_INET2)
2
The left operand of '==' is a garbage value
778 return inet_ntop(AF_INET2, &((struct sockaddr_in *)&sock_addr)->sin_addr,
779 buffer, INET6_ADDRSTRLEN46);
780 return inet_ntop(AF_INET610, &((struct sockaddr_in6 *)&sock_addr)->sin6_addr,
781 buffer, INET6_ADDRSTRLEN46);
782}