Library for Firebase, PUT, PATCH, POST, GET, DELETE operations supported, (others are available, todo). Based on Mbed's https-example. Tested on STM32F767 using ETHERNET and ESP8266 WIFI interfaces and STM32F446 using ESP8266 WIFI interface.

Dependents:   Firebase-Example

Committer:
star297
Date:
Sun May 10 08:08:23 2020 +0000
Revision:
3:4d7c08734a91
Parent:
0:768ae9838086
added PATCH function

Who changed what in which revision?

UserRevisionLine numberNew contents of line
star297 0:768ae9838086 1 /* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
star297 0:768ae9838086 2 *
star297 0:768ae9838086 3 * Additional changes are licensed under the same terms as NGINX and
star297 0:768ae9838086 4 * copyright Joyent, Inc. and other Node contributors. All rights reserved.
star297 0:768ae9838086 5 *
star297 0:768ae9838086 6 * Permission is hereby granted, free of charge, to any person obtaining a copy
star297 0:768ae9838086 7 * of this software and associated documentation files (the "Software"), to
star297 0:768ae9838086 8 * deal in the Software without restriction, including without limitation the
star297 0:768ae9838086 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
star297 0:768ae9838086 10 * sell copies of the Software, and to permit persons to whom the Software is
star297 0:768ae9838086 11 * furnished to do so, subject to the following conditions:
star297 0:768ae9838086 12 *
star297 0:768ae9838086 13 * The above copyright notice and this permission notice shall be included in
star297 0:768ae9838086 14 * all copies or substantial portions of the Software.
star297 0:768ae9838086 15 *
star297 0:768ae9838086 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
star297 0:768ae9838086 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
star297 0:768ae9838086 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
star297 0:768ae9838086 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
star297 0:768ae9838086 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
star297 0:768ae9838086 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
star297 0:768ae9838086 22 * IN THE SOFTWARE.
star297 0:768ae9838086 23 */
star297 0:768ae9838086 24 #include "http_parser.h"
star297 0:768ae9838086 25 #include <assert.h>
star297 0:768ae9838086 26 #include <stddef.h>
star297 0:768ae9838086 27 #include <ctype.h>
star297 0:768ae9838086 28 #include <stdlib.h>
star297 0:768ae9838086 29 #include <string.h>
star297 0:768ae9838086 30 #include <limits.h>
star297 0:768ae9838086 31
star297 0:768ae9838086 32 #ifndef ULLONG_MAX
star297 0:768ae9838086 33 # define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
star297 0:768ae9838086 34 #endif
star297 0:768ae9838086 35
star297 0:768ae9838086 36 #ifndef MIN
star297 0:768ae9838086 37 # define MIN(a,b) ((a) < (b) ? (a) : (b))
star297 0:768ae9838086 38 #endif
star297 0:768ae9838086 39
star297 0:768ae9838086 40 #ifndef ARRAY_SIZE
star297 0:768ae9838086 41 # define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
star297 0:768ae9838086 42 #endif
star297 0:768ae9838086 43
star297 0:768ae9838086 44 #ifndef BIT_AT
star297 0:768ae9838086 45 # define BIT_AT(a, i) \
star297 0:768ae9838086 46 (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
star297 0:768ae9838086 47 (1 << ((unsigned int) (i) & 7))))
star297 0:768ae9838086 48 #endif
star297 0:768ae9838086 49
star297 0:768ae9838086 50 #ifndef ELEM_AT
star297 0:768ae9838086 51 # define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
star297 0:768ae9838086 52 #endif
star297 0:768ae9838086 53
star297 0:768ae9838086 54 #define SET_ERRNO(e) \
star297 0:768ae9838086 55 do { \
star297 0:768ae9838086 56 parser->http_errno = (e); \
star297 0:768ae9838086 57 } while(0)
star297 0:768ae9838086 58
star297 0:768ae9838086 59 #define CURRENT_STATE() p_state
star297 0:768ae9838086 60 #define UPDATE_STATE(V) p_state = (enum state) (V);
star297 0:768ae9838086 61 #define RETURN(V) \
star297 0:768ae9838086 62 do { \
star297 0:768ae9838086 63 parser->state = CURRENT_STATE(); \
star297 0:768ae9838086 64 return (V); \
star297 0:768ae9838086 65 } while (0);
star297 0:768ae9838086 66 #define REEXECUTE() \
star297 0:768ae9838086 67 goto reexecute; \
star297 0:768ae9838086 68
star297 0:768ae9838086 69
star297 0:768ae9838086 70 #ifdef __GNUC__
star297 0:768ae9838086 71 # define LIKELY(X) __builtin_expect(!!(X), 1)
star297 0:768ae9838086 72 # define UNLIKELY(X) __builtin_expect(!!(X), 0)
star297 0:768ae9838086 73 #else
star297 0:768ae9838086 74 # define LIKELY(X) (X)
star297 0:768ae9838086 75 # define UNLIKELY(X) (X)
star297 0:768ae9838086 76 #endif
star297 0:768ae9838086 77
star297 0:768ae9838086 78
star297 0:768ae9838086 79 /* Run the notify callback FOR, returning ER if it fails */
star297 0:768ae9838086 80 #define CALLBACK_NOTIFY_(FOR, ER) \
star297 0:768ae9838086 81 do { \
star297 0:768ae9838086 82 assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
star297 0:768ae9838086 83 \
star297 0:768ae9838086 84 if (LIKELY(settings->on_##FOR)) { \
star297 0:768ae9838086 85 parser->state = CURRENT_STATE(); \
star297 0:768ae9838086 86 if (UNLIKELY(0 != settings->on_##FOR(parser))) { \
star297 0:768ae9838086 87 SET_ERRNO(HPE_CB_##FOR); \
star297 0:768ae9838086 88 } \
star297 0:768ae9838086 89 UPDATE_STATE(parser->state); \
star297 0:768ae9838086 90 \
star297 0:768ae9838086 91 /* We either errored above or got paused; get out */ \
star297 0:768ae9838086 92 if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
star297 0:768ae9838086 93 return (ER); \
star297 0:768ae9838086 94 } \
star297 0:768ae9838086 95 } \
star297 0:768ae9838086 96 } while (0)
star297 0:768ae9838086 97
star297 0:768ae9838086 98 /* Run the notify callback FOR and consume the current byte */
star297 0:768ae9838086 99 #define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1)
star297 0:768ae9838086 100
star297 0:768ae9838086 101 /* Run the notify callback FOR and don't consume the current byte */
star297 0:768ae9838086 102 #define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data)
star297 0:768ae9838086 103
star297 0:768ae9838086 104 /* Run data callback FOR with LEN bytes, returning ER if it fails */
star297 0:768ae9838086 105 #define CALLBACK_DATA_(FOR, LEN, ER) \
star297 0:768ae9838086 106 do { \
star297 0:768ae9838086 107 assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
star297 0:768ae9838086 108 \
star297 0:768ae9838086 109 if (FOR##_mark) { \
star297 0:768ae9838086 110 if (LIKELY(settings->on_##FOR)) { \
star297 0:768ae9838086 111 parser->state = CURRENT_STATE(); \
star297 0:768ae9838086 112 if (UNLIKELY(0 != \
star297 0:768ae9838086 113 settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
star297 0:768ae9838086 114 SET_ERRNO(HPE_CB_##FOR); \
star297 0:768ae9838086 115 } \
star297 0:768ae9838086 116 UPDATE_STATE(parser->state); \
star297 0:768ae9838086 117 \
star297 0:768ae9838086 118 /* We either errored above or got paused; get out */ \
star297 0:768ae9838086 119 if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
star297 0:768ae9838086 120 return (ER); \
star297 0:768ae9838086 121 } \
star297 0:768ae9838086 122 } \
star297 0:768ae9838086 123 FOR##_mark = NULL; \
star297 0:768ae9838086 124 } \
star297 0:768ae9838086 125 } while (0)
star297 0:768ae9838086 126
star297 0:768ae9838086 127 /* Run the data callback FOR and consume the current byte */
star297 0:768ae9838086 128 #define CALLBACK_DATA(FOR) \
star297 0:768ae9838086 129 CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
star297 0:768ae9838086 130
star297 0:768ae9838086 131 /* Run the data callback FOR and don't consume the current byte */
star297 0:768ae9838086 132 #define CALLBACK_DATA_NOADVANCE(FOR) \
star297 0:768ae9838086 133 CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
star297 0:768ae9838086 134
star297 0:768ae9838086 135 /* Set the mark FOR; non-destructive if mark is already set */
star297 0:768ae9838086 136 #define MARK(FOR) \
star297 0:768ae9838086 137 do { \
star297 0:768ae9838086 138 if (!FOR##_mark) { \
star297 0:768ae9838086 139 FOR##_mark = p; \
star297 0:768ae9838086 140 } \
star297 0:768ae9838086 141 } while (0)
star297 0:768ae9838086 142
star297 0:768ae9838086 143 /* Don't allow the total size of the HTTP headers (including the status
star297 0:768ae9838086 144 * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
star297 0:768ae9838086 145 * embedders against denial-of-service attacks where the attacker feeds
star297 0:768ae9838086 146 * us a never-ending header that the embedder keeps buffering.
star297 0:768ae9838086 147 *
star297 0:768ae9838086 148 * This check is arguably the responsibility of embedders but we're doing
star297 0:768ae9838086 149 * it on the embedder's behalf because most won't bother and this way we
star297 0:768ae9838086 150 * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
star297 0:768ae9838086 151 * than any reasonable request or response so this should never affect
star297 0:768ae9838086 152 * day-to-day operation.
star297 0:768ae9838086 153 */
star297 0:768ae9838086 154 #define COUNT_HEADER_SIZE(V) \
star297 0:768ae9838086 155 do { \
star297 0:768ae9838086 156 parser->nread += (V); \
star297 0:768ae9838086 157 if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \
star297 0:768ae9838086 158 SET_ERRNO(HPE_HEADER_OVERFLOW); \
star297 0:768ae9838086 159 goto error; \
star297 0:768ae9838086 160 } \
star297 0:768ae9838086 161 } while (0)
star297 0:768ae9838086 162
star297 0:768ae9838086 163
star297 0:768ae9838086 164 #define PROXY_CONNECTION "proxy-connection"
star297 0:768ae9838086 165 #define CONNECTION "connection"
star297 0:768ae9838086 166 #define CONTENT_LENGTH "content-length"
star297 0:768ae9838086 167 #define TRANSFER_ENCODING "transfer-encoding"
star297 0:768ae9838086 168 #define UPGRADE "upgrade"
star297 0:768ae9838086 169 #define CHUNKED "chunked"
star297 0:768ae9838086 170 #define KEEP_ALIVE "keep-alive"
star297 0:768ae9838086 171 #define CLOSE "close"
star297 0:768ae9838086 172
star297 0:768ae9838086 173
star297 0:768ae9838086 174 static const char *method_strings[] =
star297 0:768ae9838086 175 {
star297 0:768ae9838086 176 #define XX(num, name, string) #string,
star297 0:768ae9838086 177 HTTP_METHOD_MAP(XX)
star297 0:768ae9838086 178 #undef XX
star297 0:768ae9838086 179 };
star297 0:768ae9838086 180
star297 0:768ae9838086 181
star297 0:768ae9838086 182 /* Tokens as defined by rfc 2616. Also lowercases them.
star297 0:768ae9838086 183 * token = 1*<any CHAR except CTLs or separators>
star297 0:768ae9838086 184 * separators = "(" | ")" | "<" | ">" | "@"
star297 0:768ae9838086 185 * | "," | ";" | ":" | "\" | <">
star297 0:768ae9838086 186 * | "/" | "[" | "]" | "?" | "="
star297 0:768ae9838086 187 * | "{" | "}" | SP | HT
star297 0:768ae9838086 188 */
star297 0:768ae9838086 189 static const char tokens[256] = {
star297 0:768ae9838086 190 /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
star297 0:768ae9838086 191 0, 0, 0, 0, 0, 0, 0, 0,
star297 0:768ae9838086 192 /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
star297 0:768ae9838086 193 0, 0, 0, 0, 0, 0, 0, 0,
star297 0:768ae9838086 194 /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
star297 0:768ae9838086 195 0, 0, 0, 0, 0, 0, 0, 0,
star297 0:768ae9838086 196 /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
star297 0:768ae9838086 197 0, 0, 0, 0, 0, 0, 0, 0,
star297 0:768ae9838086 198 /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
star297 0:768ae9838086 199 0, '!', 0, '#', '$', '%', '&', '\'',
star297 0:768ae9838086 200 /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
star297 0:768ae9838086 201 0, 0, '*', '+', 0, '-', '.', 0,
star297 0:768ae9838086 202 /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
star297 0:768ae9838086 203 '0', '1', '2', '3', '4', '5', '6', '7',
star297 0:768ae9838086 204 /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
star297 0:768ae9838086 205 '8', '9', 0, 0, 0, 0, 0, 0,
star297 0:768ae9838086 206 /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
star297 0:768ae9838086 207 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
star297 0:768ae9838086 208 /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
star297 0:768ae9838086 209 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
star297 0:768ae9838086 210 /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
star297 0:768ae9838086 211 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
star297 0:768ae9838086 212 /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
star297 0:768ae9838086 213 'x', 'y', 'z', 0, 0, 0, '^', '_',
star297 0:768ae9838086 214 /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
star297 0:768ae9838086 215 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
star297 0:768ae9838086 216 /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
star297 0:768ae9838086 217 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
star297 0:768ae9838086 218 /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
star297 0:768ae9838086 219 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
star297 0:768ae9838086 220 /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
star297 0:768ae9838086 221 'x', 'y', 'z', 0, '|', 0, '~', 0 };
star297 0:768ae9838086 222
star297 0:768ae9838086 223
star297 0:768ae9838086 224 static const int8_t unhex[256] =
star297 0:768ae9838086 225 {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
star297 0:768ae9838086 226 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
star297 0:768ae9838086 227 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
star297 0:768ae9838086 228 , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
star297 0:768ae9838086 229 ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
star297 0:768ae9838086 230 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
star297 0:768ae9838086 231 ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
star297 0:768ae9838086 232 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
star297 0:768ae9838086 233 };
star297 0:768ae9838086 234
star297 0:768ae9838086 235
star297 0:768ae9838086 236 #if HTTP_PARSER_STRICT
star297 0:768ae9838086 237 # define T(v) 0
star297 0:768ae9838086 238 #else
star297 0:768ae9838086 239 # define T(v) v
star297 0:768ae9838086 240 #endif
star297 0:768ae9838086 241
star297 0:768ae9838086 242
star297 0:768ae9838086 243 static const uint8_t normal_url_char[32] = {
star297 0:768ae9838086 244 /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
star297 0:768ae9838086 245 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
star297 0:768ae9838086 246 /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
star297 0:768ae9838086 247 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
star297 0:768ae9838086 248 /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
star297 0:768ae9838086 249 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
star297 0:768ae9838086 250 /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
star297 0:768ae9838086 251 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
star297 0:768ae9838086 252 /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
star297 0:768ae9838086 253 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
star297 0:768ae9838086 254 /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
star297 0:768ae9838086 255 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
star297 0:768ae9838086 256 /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
star297 0:768ae9838086 257 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
star297 0:768ae9838086 258 /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
star297 0:768ae9838086 259 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
star297 0:768ae9838086 260 /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
star297 0:768ae9838086 261 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
star297 0:768ae9838086 262 /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
star297 0:768ae9838086 263 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
star297 0:768ae9838086 264 /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
star297 0:768ae9838086 265 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
star297 0:768ae9838086 266 /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
star297 0:768ae9838086 267 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
star297 0:768ae9838086 268 /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
star297 0:768ae9838086 269 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
star297 0:768ae9838086 270 /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
star297 0:768ae9838086 271 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
star297 0:768ae9838086 272 /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
star297 0:768ae9838086 273 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
star297 0:768ae9838086 274 /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
star297 0:768ae9838086 275 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
star297 0:768ae9838086 276
star297 0:768ae9838086 277 #undef T
star297 0:768ae9838086 278
star297 0:768ae9838086 279 enum state
star297 0:768ae9838086 280 { s_dead = 1 /* important that this is > 0 */
star297 0:768ae9838086 281
star297 0:768ae9838086 282 , s_start_req_or_res
star297 0:768ae9838086 283 , s_res_or_resp_H
star297 0:768ae9838086 284 , s_start_res
star297 0:768ae9838086 285 , s_res_H
star297 0:768ae9838086 286 , s_res_HT
star297 0:768ae9838086 287 , s_res_HTT
star297 0:768ae9838086 288 , s_res_HTTP
star297 0:768ae9838086 289 , s_res_first_http_major
star297 0:768ae9838086 290 , s_res_http_major
star297 0:768ae9838086 291 , s_res_first_http_minor
star297 0:768ae9838086 292 , s_res_http_minor
star297 0:768ae9838086 293 , s_res_first_status_code
star297 0:768ae9838086 294 , s_res_status_code
star297 0:768ae9838086 295 , s_res_status_start
star297 0:768ae9838086 296 , s_res_status
star297 0:768ae9838086 297 , s_res_line_almost_done
star297 0:768ae9838086 298
star297 0:768ae9838086 299 , s_start_req
star297 0:768ae9838086 300
star297 0:768ae9838086 301 , s_req_method
star297 0:768ae9838086 302 , s_req_spaces_before_url
star297 0:768ae9838086 303 , s_req_schema
star297 0:768ae9838086 304 , s_req_schema_slash
star297 0:768ae9838086 305 , s_req_schema_slash_slash
star297 0:768ae9838086 306 , s_req_server_start
star297 0:768ae9838086 307 , s_req_server
star297 0:768ae9838086 308 , s_req_server_with_at
star297 0:768ae9838086 309 , s_req_path
star297 0:768ae9838086 310 , s_req_query_string_start
star297 0:768ae9838086 311 , s_req_query_string
star297 0:768ae9838086 312 , s_req_fragment_start
star297 0:768ae9838086 313 , s_req_fragment
star297 0:768ae9838086 314 , s_req_http_start
star297 0:768ae9838086 315 , s_req_http_H
star297 0:768ae9838086 316 , s_req_http_HT
star297 0:768ae9838086 317 , s_req_http_HTT
star297 0:768ae9838086 318 , s_req_http_HTTP
star297 0:768ae9838086 319 , s_req_first_http_major
star297 0:768ae9838086 320 , s_req_http_major
star297 0:768ae9838086 321 , s_req_first_http_minor
star297 0:768ae9838086 322 , s_req_http_minor
star297 0:768ae9838086 323 , s_req_line_almost_done
star297 0:768ae9838086 324
star297 0:768ae9838086 325 , s_header_field_start
star297 0:768ae9838086 326 , s_header_field
star297 0:768ae9838086 327 , s_header_value_discard_ws
star297 0:768ae9838086 328 , s_header_value_discard_ws_almost_done
star297 0:768ae9838086 329 , s_header_value_discard_lws
star297 0:768ae9838086 330 , s_header_value_start
star297 0:768ae9838086 331 , s_header_value
star297 0:768ae9838086 332 , s_header_value_lws
star297 0:768ae9838086 333
star297 0:768ae9838086 334 , s_header_almost_done
star297 0:768ae9838086 335
star297 0:768ae9838086 336 , s_chunk_size_start
star297 0:768ae9838086 337 , s_chunk_size
star297 0:768ae9838086 338 , s_chunk_parameters
star297 0:768ae9838086 339 , s_chunk_size_almost_done
star297 0:768ae9838086 340
star297 0:768ae9838086 341 , s_headers_almost_done
star297 0:768ae9838086 342 , s_headers_done
star297 0:768ae9838086 343
star297 0:768ae9838086 344 /* Important: 's_headers_done' must be the last 'header' state. All
star297 0:768ae9838086 345 * states beyond this must be 'body' states. It is used for overflow
star297 0:768ae9838086 346 * checking. See the PARSING_HEADER() macro.
star297 0:768ae9838086 347 */
star297 0:768ae9838086 348
star297 0:768ae9838086 349 , s_chunk_data
star297 0:768ae9838086 350 , s_chunk_data_almost_done
star297 0:768ae9838086 351 , s_chunk_data_done
star297 0:768ae9838086 352
star297 0:768ae9838086 353 , s_body_identity
star297 0:768ae9838086 354 , s_body_identity_eof
star297 0:768ae9838086 355
star297 0:768ae9838086 356 , s_message_done
star297 0:768ae9838086 357 };
star297 0:768ae9838086 358
star297 0:768ae9838086 359
star297 0:768ae9838086 360 #define PARSING_HEADER(state) (state <= s_headers_done)
star297 0:768ae9838086 361
star297 0:768ae9838086 362
star297 0:768ae9838086 363 enum header_states
star297 0:768ae9838086 364 { h_general = 0
star297 0:768ae9838086 365 , h_C
star297 0:768ae9838086 366 , h_CO
star297 0:768ae9838086 367 , h_CON
star297 0:768ae9838086 368
star297 0:768ae9838086 369 , h_matching_connection
star297 0:768ae9838086 370 , h_matching_proxy_connection
star297 0:768ae9838086 371 , h_matching_content_length
star297 0:768ae9838086 372 , h_matching_transfer_encoding
star297 0:768ae9838086 373 , h_matching_upgrade
star297 0:768ae9838086 374
star297 0:768ae9838086 375 , h_connection
star297 0:768ae9838086 376 , h_content_length
star297 0:768ae9838086 377 , h_transfer_encoding
star297 0:768ae9838086 378 , h_upgrade
star297 0:768ae9838086 379
star297 0:768ae9838086 380 , h_matching_transfer_encoding_chunked
star297 0:768ae9838086 381 , h_matching_connection_token_start
star297 0:768ae9838086 382 , h_matching_connection_keep_alive
star297 0:768ae9838086 383 , h_matching_connection_close
star297 0:768ae9838086 384 , h_matching_connection_upgrade
star297 0:768ae9838086 385 , h_matching_connection_token
star297 0:768ae9838086 386
star297 0:768ae9838086 387 , h_transfer_encoding_chunked
star297 0:768ae9838086 388 , h_connection_keep_alive
star297 0:768ae9838086 389 , h_connection_close
star297 0:768ae9838086 390 , h_connection_upgrade
star297 0:768ae9838086 391 };
star297 0:768ae9838086 392
star297 0:768ae9838086 393 enum http_host_state
star297 0:768ae9838086 394 {
star297 0:768ae9838086 395 s_http_host_dead = 1
star297 0:768ae9838086 396 , s_http_userinfo_start
star297 0:768ae9838086 397 , s_http_userinfo
star297 0:768ae9838086 398 , s_http_host_start
star297 0:768ae9838086 399 , s_http_host_v6_start
star297 0:768ae9838086 400 , s_http_host
star297 0:768ae9838086 401 , s_http_host_v6
star297 0:768ae9838086 402 , s_http_host_v6_end
star297 0:768ae9838086 403 , s_http_host_v6_zone_start
star297 0:768ae9838086 404 , s_http_host_v6_zone
star297 0:768ae9838086 405 , s_http_host_port_start
star297 0:768ae9838086 406 , s_http_host_port
star297 0:768ae9838086 407 };
star297 0:768ae9838086 408
star297 0:768ae9838086 409 /* Macros for character classes; depends on strict-mode */
star297 0:768ae9838086 410 #define CR '\r'
star297 0:768ae9838086 411 #define LF '\n'
star297 0:768ae9838086 412 #define LOWER(c) (unsigned char)(c | 0x20)
star297 0:768ae9838086 413 #define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
star297 0:768ae9838086 414 #define IS_NUM(c) ((c) >= '0' && (c) <= '9')
star297 0:768ae9838086 415 #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
star297 0:768ae9838086 416 #define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
star297 0:768ae9838086 417 #define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
star297 0:768ae9838086 418 (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
star297 0:768ae9838086 419 (c) == ')')
star297 0:768ae9838086 420 #define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
star297 0:768ae9838086 421 (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
star297 0:768ae9838086 422 (c) == '$' || (c) == ',')
star297 0:768ae9838086 423
star297 0:768ae9838086 424 #define STRICT_TOKEN(c) (tokens[(unsigned char)c])
star297 0:768ae9838086 425
star297 0:768ae9838086 426 #if HTTP_PARSER_STRICT
star297 0:768ae9838086 427 #define TOKEN(c) (tokens[(unsigned char)c])
star297 0:768ae9838086 428 #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
star297 0:768ae9838086 429 #define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
star297 0:768ae9838086 430 #else
star297 0:768ae9838086 431 #define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c])
star297 0:768ae9838086 432 #define IS_URL_CHAR(c) \
star297 0:768ae9838086 433 (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
star297 0:768ae9838086 434 #define IS_HOST_CHAR(c) \
star297 0:768ae9838086 435 (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
star297 0:768ae9838086 436 #endif
star297 0:768ae9838086 437
star297 0:768ae9838086 438 /**
star297 0:768ae9838086 439 * Verify that a char is a valid visible (printable) US-ASCII
star297 0:768ae9838086 440 * character or %x80-FF
star297 0:768ae9838086 441 **/
star297 0:768ae9838086 442 #define IS_HEADER_CHAR(ch) \
star297 0:768ae9838086 443 (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
star297 0:768ae9838086 444
star297 0:768ae9838086 445 #define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
star297 0:768ae9838086 446
star297 0:768ae9838086 447
star297 0:768ae9838086 448 #if HTTP_PARSER_STRICT
star297 0:768ae9838086 449 # define STRICT_CHECK(cond) \
star297 0:768ae9838086 450 do { \
star297 0:768ae9838086 451 if (cond) { \
star297 0:768ae9838086 452 SET_ERRNO(HPE_STRICT); \
star297 0:768ae9838086 453 goto error; \
star297 0:768ae9838086 454 } \
star297 0:768ae9838086 455 } while (0)
star297 0:768ae9838086 456 # define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
star297 0:768ae9838086 457 #else
star297 0:768ae9838086 458 # define STRICT_CHECK(cond)
star297 0:768ae9838086 459 # define NEW_MESSAGE() start_state
star297 0:768ae9838086 460 #endif
star297 0:768ae9838086 461
star297 0:768ae9838086 462
star297 0:768ae9838086 463 /* Map errno values to strings for human-readable output */
star297 0:768ae9838086 464 #define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
star297 0:768ae9838086 465 static struct {
star297 0:768ae9838086 466 const char *name;
star297 0:768ae9838086 467 const char *description;
star297 0:768ae9838086 468 } http_strerror_tab[] = {
star297 0:768ae9838086 469 HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
star297 0:768ae9838086 470 };
star297 0:768ae9838086 471 #undef HTTP_STRERROR_GEN
star297 0:768ae9838086 472
star297 0:768ae9838086 473 int http_message_needs_eof(const http_parser *parser);
star297 0:768ae9838086 474
star297 0:768ae9838086 475 /* Our URL parser.
star297 0:768ae9838086 476 *
star297 0:768ae9838086 477 * This is designed to be shared by http_parser_execute() for URL validation,
star297 0:768ae9838086 478 * hence it has a state transition + byte-for-byte interface. In addition, it
star297 0:768ae9838086 479 * is meant to be embedded in http_parser_parse_url(), which does the dirty
star297 0:768ae9838086 480 * work of turning state transitions URL components for its API.
star297 0:768ae9838086 481 *
star297 0:768ae9838086 482 * This function should only be invoked with non-space characters. It is
star297 0:768ae9838086 483 * assumed that the caller cares about (and can detect) the transition between
star297 0:768ae9838086 484 * URL and non-URL states by looking for these.
star297 0:768ae9838086 485 */
star297 0:768ae9838086 486 static enum state
star297 0:768ae9838086 487 parse_url_char(enum state s, const char ch)
star297 0:768ae9838086 488 {
star297 0:768ae9838086 489 if (ch == ' ' || ch == '\r' || ch == '\n') {
star297 0:768ae9838086 490 return s_dead;
star297 0:768ae9838086 491 }
star297 0:768ae9838086 492
star297 0:768ae9838086 493 #if HTTP_PARSER_STRICT
star297 0:768ae9838086 494 if (ch == '\t' || ch == '\f') {
star297 0:768ae9838086 495 return s_dead;
star297 0:768ae9838086 496 }
star297 0:768ae9838086 497 #endif
star297 0:768ae9838086 498
star297 0:768ae9838086 499 switch (s) {
star297 0:768ae9838086 500 case s_req_spaces_before_url:
star297 0:768ae9838086 501 /* Proxied requests are followed by scheme of an absolute URI (alpha).
star297 0:768ae9838086 502 * All methods except CONNECT are followed by '/' or '*'.
star297 0:768ae9838086 503 */
star297 0:768ae9838086 504
star297 0:768ae9838086 505 if (ch == '/' || ch == '*') {
star297 0:768ae9838086 506 return s_req_path;
star297 0:768ae9838086 507 }
star297 0:768ae9838086 508
star297 0:768ae9838086 509 if (IS_ALPHA(ch)) {
star297 0:768ae9838086 510 return s_req_schema;
star297 0:768ae9838086 511 }
star297 0:768ae9838086 512
star297 0:768ae9838086 513 break;
star297 0:768ae9838086 514
star297 0:768ae9838086 515 case s_req_schema:
star297 0:768ae9838086 516 if (IS_ALPHA(ch)) {
star297 0:768ae9838086 517 return s;
star297 0:768ae9838086 518 }
star297 0:768ae9838086 519
star297 0:768ae9838086 520 if (ch == ':') {
star297 0:768ae9838086 521 return s_req_schema_slash;
star297 0:768ae9838086 522 }
star297 0:768ae9838086 523
star297 0:768ae9838086 524 break;
star297 0:768ae9838086 525
star297 0:768ae9838086 526 case s_req_schema_slash:
star297 0:768ae9838086 527 if (ch == '/') {
star297 0:768ae9838086 528 return s_req_schema_slash_slash;
star297 0:768ae9838086 529 }
star297 0:768ae9838086 530
star297 0:768ae9838086 531 break;
star297 0:768ae9838086 532
star297 0:768ae9838086 533 case s_req_schema_slash_slash:
star297 0:768ae9838086 534 if (ch == '/') {
star297 0:768ae9838086 535 return s_req_server_start;
star297 0:768ae9838086 536 }
star297 0:768ae9838086 537
star297 0:768ae9838086 538 break;
star297 0:768ae9838086 539
star297 0:768ae9838086 540 case s_req_server_with_at:
star297 0:768ae9838086 541 if (ch == '@') {
star297 0:768ae9838086 542 return s_dead;
star297 0:768ae9838086 543 }
star297 0:768ae9838086 544
star297 0:768ae9838086 545 /* FALLTHROUGH */
star297 0:768ae9838086 546 case s_req_server_start:
star297 0:768ae9838086 547 case s_req_server:
star297 0:768ae9838086 548 if (ch == '/') {
star297 0:768ae9838086 549 return s_req_path;
star297 0:768ae9838086 550 }
star297 0:768ae9838086 551
star297 0:768ae9838086 552 if (ch == '?') {
star297 0:768ae9838086 553 return s_req_query_string_start;
star297 0:768ae9838086 554 }
star297 0:768ae9838086 555
star297 0:768ae9838086 556 if (ch == '@') {
star297 0:768ae9838086 557 return s_req_server_with_at;
star297 0:768ae9838086 558 }
star297 0:768ae9838086 559
star297 0:768ae9838086 560 if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
star297 0:768ae9838086 561 return s_req_server;
star297 0:768ae9838086 562 }
star297 0:768ae9838086 563
star297 0:768ae9838086 564 break;
star297 0:768ae9838086 565
star297 0:768ae9838086 566 case s_req_path:
star297 0:768ae9838086 567 if (IS_URL_CHAR(ch)) {
star297 0:768ae9838086 568 return s;
star297 0:768ae9838086 569 }
star297 0:768ae9838086 570
star297 0:768ae9838086 571 switch (ch) {
star297 0:768ae9838086 572 case '?':
star297 0:768ae9838086 573 return s_req_query_string_start;
star297 0:768ae9838086 574
star297 0:768ae9838086 575 case '#':
star297 0:768ae9838086 576 return s_req_fragment_start;
star297 0:768ae9838086 577 }
star297 0:768ae9838086 578
star297 0:768ae9838086 579 break;
star297 0:768ae9838086 580
star297 0:768ae9838086 581 case s_req_query_string_start:
star297 0:768ae9838086 582 case s_req_query_string:
star297 0:768ae9838086 583 if (IS_URL_CHAR(ch)) {
star297 0:768ae9838086 584 return s_req_query_string;
star297 0:768ae9838086 585 }
star297 0:768ae9838086 586
star297 0:768ae9838086 587 switch (ch) {
star297 0:768ae9838086 588 case '?':
star297 0:768ae9838086 589 /* allow extra '?' in query string */
star297 0:768ae9838086 590 return s_req_query_string;
star297 0:768ae9838086 591
star297 0:768ae9838086 592 case '#':
star297 0:768ae9838086 593 return s_req_fragment_start;
star297 0:768ae9838086 594 }
star297 0:768ae9838086 595
star297 0:768ae9838086 596 break;
star297 0:768ae9838086 597
star297 0:768ae9838086 598 case s_req_fragment_start:
star297 0:768ae9838086 599 if (IS_URL_CHAR(ch)) {
star297 0:768ae9838086 600 return s_req_fragment;
star297 0:768ae9838086 601 }
star297 0:768ae9838086 602
star297 0:768ae9838086 603 switch (ch) {
star297 0:768ae9838086 604 case '?':
star297 0:768ae9838086 605 return s_req_fragment;
star297 0:768ae9838086 606
star297 0:768ae9838086 607 case '#':
star297 0:768ae9838086 608 return s;
star297 0:768ae9838086 609 }
star297 0:768ae9838086 610
star297 0:768ae9838086 611 break;
star297 0:768ae9838086 612
star297 0:768ae9838086 613 case s_req_fragment:
star297 0:768ae9838086 614 if (IS_URL_CHAR(ch)) {
star297 0:768ae9838086 615 return s;
star297 0:768ae9838086 616 }
star297 0:768ae9838086 617
star297 0:768ae9838086 618 switch (ch) {
star297 0:768ae9838086 619 case '?':
star297 0:768ae9838086 620 case '#':
star297 0:768ae9838086 621 return s;
star297 0:768ae9838086 622 }
star297 0:768ae9838086 623
star297 0:768ae9838086 624 break;
star297 0:768ae9838086 625
star297 0:768ae9838086 626 default:
star297 0:768ae9838086 627 break;
star297 0:768ae9838086 628 }
star297 0:768ae9838086 629
star297 0:768ae9838086 630 /* We should never fall out of the switch above unless there's an error */
star297 0:768ae9838086 631 return s_dead;
star297 0:768ae9838086 632 }
star297 0:768ae9838086 633
star297 0:768ae9838086 634 uint32_t http_parser_execute (http_parser *parser,
star297 0:768ae9838086 635 const http_parser_settings *settings,
star297 0:768ae9838086 636 const char *data,
star297 0:768ae9838086 637 uint32_t len)
star297 0:768ae9838086 638 {
star297 0:768ae9838086 639 char c, ch;
star297 0:768ae9838086 640 int8_t unhex_val;
star297 0:768ae9838086 641 const char *p = data;
star297 0:768ae9838086 642 const char *header_field_mark = 0;
star297 0:768ae9838086 643 const char *header_value_mark = 0;
star297 0:768ae9838086 644 const char *url_mark = 0;
star297 0:768ae9838086 645 const char *body_mark = 0;
star297 0:768ae9838086 646 const char *status_mark = 0;
star297 0:768ae9838086 647 enum state p_state = (enum state) parser->state;
star297 0:768ae9838086 648 const unsigned int lenient = parser->lenient_http_headers;
star297 0:768ae9838086 649
star297 0:768ae9838086 650 /* We're in an error state. Don't bother doing anything. */
star297 0:768ae9838086 651 if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
star297 0:768ae9838086 652 return 0;
star297 0:768ae9838086 653 }
star297 0:768ae9838086 654
star297 0:768ae9838086 655 if (len == 0) {
star297 0:768ae9838086 656 switch (CURRENT_STATE()) {
star297 0:768ae9838086 657 case s_body_identity_eof:
star297 0:768ae9838086 658 /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
star297 0:768ae9838086 659 * we got paused.
star297 0:768ae9838086 660 */
star297 0:768ae9838086 661 CALLBACK_NOTIFY_NOADVANCE(message_complete);
star297 0:768ae9838086 662 return 0;
star297 0:768ae9838086 663
star297 0:768ae9838086 664 case s_dead:
star297 0:768ae9838086 665 case s_start_req_or_res:
star297 0:768ae9838086 666 case s_start_res:
star297 0:768ae9838086 667 case s_start_req:
star297 0:768ae9838086 668 return 0;
star297 0:768ae9838086 669
star297 0:768ae9838086 670 default:
star297 0:768ae9838086 671 SET_ERRNO(HPE_INVALID_EOF_STATE);
star297 0:768ae9838086 672 return 1;
star297 0:768ae9838086 673 }
star297 0:768ae9838086 674 }
star297 0:768ae9838086 675
star297 0:768ae9838086 676
star297 0:768ae9838086 677 if (CURRENT_STATE() == s_header_field)
star297 0:768ae9838086 678 header_field_mark = data;
star297 0:768ae9838086 679 if (CURRENT_STATE() == s_header_value)
star297 0:768ae9838086 680 header_value_mark = data;
star297 0:768ae9838086 681 switch (CURRENT_STATE()) {
star297 0:768ae9838086 682 case s_req_path:
star297 0:768ae9838086 683 case s_req_schema:
star297 0:768ae9838086 684 case s_req_schema_slash:
star297 0:768ae9838086 685 case s_req_schema_slash_slash:
star297 0:768ae9838086 686 case s_req_server_start:
star297 0:768ae9838086 687 case s_req_server:
star297 0:768ae9838086 688 case s_req_server_with_at:
star297 0:768ae9838086 689 case s_req_query_string_start:
star297 0:768ae9838086 690 case s_req_query_string:
star297 0:768ae9838086 691 case s_req_fragment_start:
star297 0:768ae9838086 692 case s_req_fragment:
star297 0:768ae9838086 693 url_mark = data;
star297 0:768ae9838086 694 break;
star297 0:768ae9838086 695 case s_res_status:
star297 0:768ae9838086 696 status_mark = data;
star297 0:768ae9838086 697 break;
star297 0:768ae9838086 698 default:
star297 0:768ae9838086 699 break;
star297 0:768ae9838086 700 }
star297 0:768ae9838086 701
star297 0:768ae9838086 702 for (p=data; p != data + len; p++) {
star297 0:768ae9838086 703 ch = *p;
star297 0:768ae9838086 704
star297 0:768ae9838086 705 if (PARSING_HEADER(CURRENT_STATE()))
star297 0:768ae9838086 706 COUNT_HEADER_SIZE(1);
star297 0:768ae9838086 707
star297 0:768ae9838086 708 reexecute:
star297 0:768ae9838086 709 switch (CURRENT_STATE()) {
star297 0:768ae9838086 710
star297 0:768ae9838086 711 case s_dead:
star297 0:768ae9838086 712 /* this state is used after a 'Connection: close' message
star297 0:768ae9838086 713 * the parser will error out if it reads another message
star297 0:768ae9838086 714 */
star297 0:768ae9838086 715 if (LIKELY(ch == CR || ch == LF))
star297 0:768ae9838086 716 break;
star297 0:768ae9838086 717
star297 0:768ae9838086 718 SET_ERRNO(HPE_CLOSED_CONNECTION);
star297 0:768ae9838086 719 goto error;
star297 0:768ae9838086 720
star297 0:768ae9838086 721 case s_start_req_or_res:
star297 0:768ae9838086 722 {
star297 0:768ae9838086 723 if (ch == CR || ch == LF)
star297 0:768ae9838086 724 break;
star297 0:768ae9838086 725 parser->flags = 0;
star297 0:768ae9838086 726 parser->content_length = ULLONG_MAX;
star297 0:768ae9838086 727
star297 0:768ae9838086 728 if (ch == 'H') {
star297 0:768ae9838086 729 UPDATE_STATE(s_res_or_resp_H);
star297 0:768ae9838086 730
star297 0:768ae9838086 731 CALLBACK_NOTIFY(message_begin);
star297 0:768ae9838086 732 } else {
star297 0:768ae9838086 733 parser->type = HTTP_REQUEST;
star297 0:768ae9838086 734 UPDATE_STATE(s_start_req);
star297 0:768ae9838086 735 REEXECUTE();
star297 0:768ae9838086 736 }
star297 0:768ae9838086 737
star297 0:768ae9838086 738 break;
star297 0:768ae9838086 739 }
star297 0:768ae9838086 740
star297 0:768ae9838086 741 case s_res_or_resp_H:
star297 0:768ae9838086 742 if (ch == 'T') {
star297 0:768ae9838086 743 parser->type = HTTP_RESPONSE;
star297 0:768ae9838086 744 UPDATE_STATE(s_res_HT);
star297 0:768ae9838086 745 } else {
star297 0:768ae9838086 746 if (UNLIKELY(ch != 'E')) {
star297 0:768ae9838086 747 SET_ERRNO(HPE_INVALID_CONSTANT);
star297 0:768ae9838086 748 goto error;
star297 0:768ae9838086 749 }
star297 0:768ae9838086 750
star297 0:768ae9838086 751 parser->type = HTTP_REQUEST;
star297 0:768ae9838086 752 parser->method = HTTP_HEAD;
star297 0:768ae9838086 753 parser->index = 2;
star297 0:768ae9838086 754 UPDATE_STATE(s_req_method);
star297 0:768ae9838086 755 }
star297 0:768ae9838086 756 break;
star297 0:768ae9838086 757
star297 0:768ae9838086 758 case s_start_res:
star297 0:768ae9838086 759 {
star297 0:768ae9838086 760 parser->flags = 0;
star297 0:768ae9838086 761 parser->content_length = ULLONG_MAX;
star297 0:768ae9838086 762
star297 0:768ae9838086 763 switch (ch) {
star297 0:768ae9838086 764 case 'H':
star297 0:768ae9838086 765 UPDATE_STATE(s_res_H);
star297 0:768ae9838086 766 break;
star297 0:768ae9838086 767
star297 0:768ae9838086 768 case CR:
star297 0:768ae9838086 769 case LF:
star297 0:768ae9838086 770 break;
star297 0:768ae9838086 771
star297 0:768ae9838086 772 default:
star297 0:768ae9838086 773 SET_ERRNO(HPE_INVALID_CONSTANT);
star297 0:768ae9838086 774 goto error;
star297 0:768ae9838086 775 }
star297 0:768ae9838086 776
star297 0:768ae9838086 777 CALLBACK_NOTIFY(message_begin);
star297 0:768ae9838086 778 break;
star297 0:768ae9838086 779 }
star297 0:768ae9838086 780
star297 0:768ae9838086 781 case s_res_H:
star297 0:768ae9838086 782 STRICT_CHECK(ch != 'T');
star297 0:768ae9838086 783 UPDATE_STATE(s_res_HT);
star297 0:768ae9838086 784 break;
star297 0:768ae9838086 785
star297 0:768ae9838086 786 case s_res_HT:
star297 0:768ae9838086 787 STRICT_CHECK(ch != 'T');
star297 0:768ae9838086 788 UPDATE_STATE(s_res_HTT);
star297 0:768ae9838086 789 break;
star297 0:768ae9838086 790
star297 0:768ae9838086 791 case s_res_HTT:
star297 0:768ae9838086 792 STRICT_CHECK(ch != 'P');
star297 0:768ae9838086 793 UPDATE_STATE(s_res_HTTP);
star297 0:768ae9838086 794 break;
star297 0:768ae9838086 795
star297 0:768ae9838086 796 case s_res_HTTP:
star297 0:768ae9838086 797 STRICT_CHECK(ch != '/');
star297 0:768ae9838086 798 UPDATE_STATE(s_res_first_http_major);
star297 0:768ae9838086 799 break;
star297 0:768ae9838086 800
star297 0:768ae9838086 801 case s_res_first_http_major:
star297 0:768ae9838086 802 if (UNLIKELY(ch < '0' || ch > '9')) {
star297 0:768ae9838086 803 SET_ERRNO(HPE_INVALID_VERSION);
star297 0:768ae9838086 804 goto error;
star297 0:768ae9838086 805 }
star297 0:768ae9838086 806
star297 0:768ae9838086 807 parser->http_major = ch - '0';
star297 0:768ae9838086 808 UPDATE_STATE(s_res_http_major);
star297 0:768ae9838086 809 break;
star297 0:768ae9838086 810
star297 0:768ae9838086 811 /* major HTTP version or dot */
star297 0:768ae9838086 812 case s_res_http_major:
star297 0:768ae9838086 813 {
star297 0:768ae9838086 814 if (ch == '.') {
star297 0:768ae9838086 815 UPDATE_STATE(s_res_first_http_minor);
star297 0:768ae9838086 816 break;
star297 0:768ae9838086 817 }
star297 0:768ae9838086 818
star297 0:768ae9838086 819 if (!IS_NUM(ch)) {
star297 0:768ae9838086 820 SET_ERRNO(HPE_INVALID_VERSION);
star297 0:768ae9838086 821 goto error;
star297 0:768ae9838086 822 }
star297 0:768ae9838086 823
star297 0:768ae9838086 824 parser->http_major *= 10;
star297 0:768ae9838086 825 parser->http_major += ch - '0';
star297 0:768ae9838086 826
star297 0:768ae9838086 827 if (UNLIKELY(parser->http_major > 999)) {
star297 0:768ae9838086 828 SET_ERRNO(HPE_INVALID_VERSION);
star297 0:768ae9838086 829 goto error;
star297 0:768ae9838086 830 }
star297 0:768ae9838086 831
star297 0:768ae9838086 832 break;
star297 0:768ae9838086 833 }
star297 0:768ae9838086 834
star297 0:768ae9838086 835 /* first digit of minor HTTP version */
star297 0:768ae9838086 836 case s_res_first_http_minor:
star297 0:768ae9838086 837 if (UNLIKELY(!IS_NUM(ch))) {
star297 0:768ae9838086 838 SET_ERRNO(HPE_INVALID_VERSION);
star297 0:768ae9838086 839 goto error;
star297 0:768ae9838086 840 }
star297 0:768ae9838086 841
star297 0:768ae9838086 842 parser->http_minor = ch - '0';
star297 0:768ae9838086 843 UPDATE_STATE(s_res_http_minor);
star297 0:768ae9838086 844 break;
star297 0:768ae9838086 845
star297 0:768ae9838086 846 /* minor HTTP version or end of request line */
star297 0:768ae9838086 847 case s_res_http_minor:
star297 0:768ae9838086 848 {
star297 0:768ae9838086 849 if (ch == ' ') {
star297 0:768ae9838086 850 UPDATE_STATE(s_res_first_status_code);
star297 0:768ae9838086 851 break;
star297 0:768ae9838086 852 }
star297 0:768ae9838086 853
star297 0:768ae9838086 854 if (UNLIKELY(!IS_NUM(ch))) {
star297 0:768ae9838086 855 SET_ERRNO(HPE_INVALID_VERSION);
star297 0:768ae9838086 856 goto error;
star297 0:768ae9838086 857 }
star297 0:768ae9838086 858
star297 0:768ae9838086 859 parser->http_minor *= 10;
star297 0:768ae9838086 860 parser->http_minor += ch - '0';
star297 0:768ae9838086 861
star297 0:768ae9838086 862 if (UNLIKELY(parser->http_minor > 999)) {
star297 0:768ae9838086 863 SET_ERRNO(HPE_INVALID_VERSION);
star297 0:768ae9838086 864 goto error;
star297 0:768ae9838086 865 }
star297 0:768ae9838086 866
star297 0:768ae9838086 867 break;
star297 0:768ae9838086 868 }
star297 0:768ae9838086 869
star297 0:768ae9838086 870 case s_res_first_status_code:
star297 0:768ae9838086 871 {
star297 0:768ae9838086 872 if (!IS_NUM(ch)) {
star297 0:768ae9838086 873 if (ch == ' ') {
star297 0:768ae9838086 874 break;
star297 0:768ae9838086 875 }
star297 0:768ae9838086 876
star297 0:768ae9838086 877 SET_ERRNO(HPE_INVALID_STATUS);
star297 0:768ae9838086 878 goto error;
star297 0:768ae9838086 879 }
star297 0:768ae9838086 880 parser->status_code = ch - '0';
star297 0:768ae9838086 881 UPDATE_STATE(s_res_status_code);
star297 0:768ae9838086 882 break;
star297 0:768ae9838086 883 }
star297 0:768ae9838086 884
star297 0:768ae9838086 885 case s_res_status_code:
star297 0:768ae9838086 886 {
star297 0:768ae9838086 887 if (!IS_NUM(ch)) {
star297 0:768ae9838086 888 switch (ch) {
star297 0:768ae9838086 889 case ' ':
star297 0:768ae9838086 890 UPDATE_STATE(s_res_status_start);
star297 0:768ae9838086 891 break;
star297 0:768ae9838086 892 case CR:
star297 0:768ae9838086 893 UPDATE_STATE(s_res_line_almost_done);
star297 0:768ae9838086 894 break;
star297 0:768ae9838086 895 case LF:
star297 0:768ae9838086 896 UPDATE_STATE(s_header_field_start);
star297 0:768ae9838086 897 break;
star297 0:768ae9838086 898 default:
star297 0:768ae9838086 899 SET_ERRNO(HPE_INVALID_STATUS);
star297 0:768ae9838086 900 goto error;
star297 0:768ae9838086 901 }
star297 0:768ae9838086 902 break;
star297 0:768ae9838086 903 }
star297 0:768ae9838086 904
star297 0:768ae9838086 905 parser->status_code *= 10;
star297 0:768ae9838086 906 parser->status_code += ch - '0';
star297 0:768ae9838086 907
star297 0:768ae9838086 908 if (UNLIKELY(parser->status_code > 999)) {
star297 0:768ae9838086 909 SET_ERRNO(HPE_INVALID_STATUS);
star297 0:768ae9838086 910 goto error;
star297 0:768ae9838086 911 }
star297 0:768ae9838086 912
star297 0:768ae9838086 913 break;
star297 0:768ae9838086 914 }
star297 0:768ae9838086 915
star297 0:768ae9838086 916 case s_res_status_start:
star297 0:768ae9838086 917 {
star297 0:768ae9838086 918 if (ch == CR) {
star297 0:768ae9838086 919 UPDATE_STATE(s_res_line_almost_done);
star297 0:768ae9838086 920 break;
star297 0:768ae9838086 921 }
star297 0:768ae9838086 922
star297 0:768ae9838086 923 if (ch == LF) {
star297 0:768ae9838086 924 UPDATE_STATE(s_header_field_start);
star297 0:768ae9838086 925 break;
star297 0:768ae9838086 926 }
star297 0:768ae9838086 927
star297 0:768ae9838086 928 MARK(status);
star297 0:768ae9838086 929 UPDATE_STATE(s_res_status);
star297 0:768ae9838086 930 parser->index = 0;
star297 0:768ae9838086 931 break;
star297 0:768ae9838086 932 }
star297 0:768ae9838086 933
star297 0:768ae9838086 934 case s_res_status:
star297 0:768ae9838086 935 if (ch == CR) {
star297 0:768ae9838086 936 UPDATE_STATE(s_res_line_almost_done);
star297 0:768ae9838086 937 CALLBACK_DATA(status);
star297 0:768ae9838086 938 break;
star297 0:768ae9838086 939 }
star297 0:768ae9838086 940
star297 0:768ae9838086 941 if (ch == LF) {
star297 0:768ae9838086 942 UPDATE_STATE(s_header_field_start);
star297 0:768ae9838086 943 CALLBACK_DATA(status);
star297 0:768ae9838086 944 break;
star297 0:768ae9838086 945 }
star297 0:768ae9838086 946
star297 0:768ae9838086 947 break;
star297 0:768ae9838086 948
star297 0:768ae9838086 949 case s_res_line_almost_done:
star297 0:768ae9838086 950 STRICT_CHECK(ch != LF);
star297 0:768ae9838086 951 UPDATE_STATE(s_header_field_start);
star297 0:768ae9838086 952 break;
star297 0:768ae9838086 953
star297 0:768ae9838086 954 case s_start_req:
star297 0:768ae9838086 955 {
star297 0:768ae9838086 956 if (ch == CR || ch == LF)
star297 0:768ae9838086 957 break;
star297 0:768ae9838086 958 parser->flags = 0;
star297 0:768ae9838086 959 parser->content_length = ULLONG_MAX;
star297 0:768ae9838086 960
star297 0:768ae9838086 961 if (UNLIKELY(!IS_ALPHA(ch))) {
star297 0:768ae9838086 962 SET_ERRNO(HPE_INVALID_METHOD);
star297 0:768ae9838086 963 goto error;
star297 0:768ae9838086 964 }
star297 0:768ae9838086 965
star297 0:768ae9838086 966 parser->method = (enum http_method) 0;
star297 0:768ae9838086 967 parser->index = 1;
star297 0:768ae9838086 968 switch (ch) {
star297 0:768ae9838086 969 case 'A': parser->method = HTTP_ACL; break;
star297 0:768ae9838086 970 case 'B': parser->method = HTTP_BIND; break;
star297 0:768ae9838086 971 case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
star297 0:768ae9838086 972 case 'D': parser->method = HTTP_DELETE; break;
star297 0:768ae9838086 973 case 'G': parser->method = HTTP_GET; break;
star297 0:768ae9838086 974 case 'H': parser->method = HTTP_HEAD; break;
star297 0:768ae9838086 975 case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
star297 0:768ae9838086 976 case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
star297 0:768ae9838086 977 case 'N': parser->method = HTTP_NOTIFY; break;
star297 0:768ae9838086 978 case 'O': parser->method = HTTP_OPTIONS; break;
star297 0:768ae9838086 979 case 'P': parser->method = HTTP_POST;
star297 0:768ae9838086 980 /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
star297 0:768ae9838086 981 break;
star297 0:768ae9838086 982 case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
star297 0:768ae9838086 983 case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
star297 0:768ae9838086 984 case 'T': parser->method = HTTP_TRACE; break;
star297 0:768ae9838086 985 case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
star297 0:768ae9838086 986 default:
star297 0:768ae9838086 987 SET_ERRNO(HPE_INVALID_METHOD);
star297 0:768ae9838086 988 goto error;
star297 0:768ae9838086 989 }
star297 0:768ae9838086 990 UPDATE_STATE(s_req_method);
star297 0:768ae9838086 991
star297 0:768ae9838086 992 CALLBACK_NOTIFY(message_begin);
star297 0:768ae9838086 993
star297 0:768ae9838086 994 break;
star297 0:768ae9838086 995 }
star297 0:768ae9838086 996
star297 0:768ae9838086 997 case s_req_method:
star297 0:768ae9838086 998 {
star297 0:768ae9838086 999 const char *matcher;
star297 0:768ae9838086 1000 if (UNLIKELY(ch == '\0')) {
star297 0:768ae9838086 1001 SET_ERRNO(HPE_INVALID_METHOD);
star297 0:768ae9838086 1002 goto error;
star297 0:768ae9838086 1003 }
star297 0:768ae9838086 1004
star297 0:768ae9838086 1005 matcher = method_strings[parser->method];
star297 0:768ae9838086 1006 if (ch == ' ' && matcher[parser->index] == '\0') {
star297 0:768ae9838086 1007 UPDATE_STATE(s_req_spaces_before_url);
star297 0:768ae9838086 1008 } else if (ch == matcher[parser->index]) {
star297 0:768ae9838086 1009 ; /* nada */
star297 0:768ae9838086 1010 } else if (IS_ALPHA(ch)) {
star297 0:768ae9838086 1011
star297 0:768ae9838086 1012 switch (parser->method << 16 | parser->index << 8 | ch) {
star297 0:768ae9838086 1013 #define XX(meth, pos, ch, new_meth) \
star297 0:768ae9838086 1014 case (HTTP_##meth << 16 | pos << 8 | ch): \
star297 0:768ae9838086 1015 parser->method = HTTP_##new_meth; break;
star297 0:768ae9838086 1016
star297 0:768ae9838086 1017 XX(POST, 1, 'U', PUT)
star297 0:768ae9838086 1018 XX(POST, 1, 'A', PATCH)
star297 0:768ae9838086 1019 XX(CONNECT, 1, 'H', CHECKOUT)
star297 0:768ae9838086 1020 XX(CONNECT, 2, 'P', COPY)
star297 0:768ae9838086 1021 XX(MKCOL, 1, 'O', MOVE)
star297 0:768ae9838086 1022 XX(MKCOL, 1, 'E', MERGE)
star297 0:768ae9838086 1023 XX(MKCOL, 2, 'A', MKACTIVITY)
star297 0:768ae9838086 1024 XX(MKCOL, 3, 'A', MKCALENDAR)
star297 0:768ae9838086 1025 XX(SUBSCRIBE, 1, 'E', SEARCH)
star297 0:768ae9838086 1026 XX(REPORT, 2, 'B', REBIND)
star297 0:768ae9838086 1027 XX(POST, 1, 'R', PROPFIND)
star297 0:768ae9838086 1028 XX(PROPFIND, 4, 'P', PROPPATCH)
star297 0:768ae9838086 1029 XX(PUT, 2, 'R', PURGE)
star297 0:768ae9838086 1030 XX(LOCK, 1, 'I', LINK)
star297 0:768ae9838086 1031 XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
star297 0:768ae9838086 1032 XX(UNLOCK, 2, 'B', UNBIND)
star297 0:768ae9838086 1033 XX(UNLOCK, 3, 'I', UNLINK)
star297 0:768ae9838086 1034 #undef XX
star297 0:768ae9838086 1035
star297 0:768ae9838086 1036 default:
star297 0:768ae9838086 1037 SET_ERRNO(HPE_INVALID_METHOD);
star297 0:768ae9838086 1038 goto error;
star297 0:768ae9838086 1039 }
star297 0:768ae9838086 1040 } else if (ch == '-' &&
star297 0:768ae9838086 1041 parser->index == 1 &&
star297 0:768ae9838086 1042 parser->method == HTTP_MKCOL) {
star297 0:768ae9838086 1043 parser->method = HTTP_MSEARCH;
star297 0:768ae9838086 1044 } else {
star297 0:768ae9838086 1045 SET_ERRNO(HPE_INVALID_METHOD);
star297 0:768ae9838086 1046 goto error;
star297 0:768ae9838086 1047 }
star297 0:768ae9838086 1048
star297 0:768ae9838086 1049 ++parser->index;
star297 0:768ae9838086 1050 break;
star297 0:768ae9838086 1051 }
star297 0:768ae9838086 1052
star297 0:768ae9838086 1053 case s_req_spaces_before_url:
star297 0:768ae9838086 1054 {
star297 0:768ae9838086 1055 if (ch == ' ') break;
star297 0:768ae9838086 1056
star297 0:768ae9838086 1057 MARK(url);
star297 0:768ae9838086 1058 if (parser->method == HTTP_CONNECT) {
star297 0:768ae9838086 1059 UPDATE_STATE(s_req_server_start);
star297 0:768ae9838086 1060 }
star297 0:768ae9838086 1061
star297 0:768ae9838086 1062 UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
star297 0:768ae9838086 1063 if (UNLIKELY(CURRENT_STATE() == s_dead)) {
star297 0:768ae9838086 1064 SET_ERRNO(HPE_INVALID_URL);
star297 0:768ae9838086 1065 goto error;
star297 0:768ae9838086 1066 }
star297 0:768ae9838086 1067
star297 0:768ae9838086 1068 break;
star297 0:768ae9838086 1069 }
star297 0:768ae9838086 1070
star297 0:768ae9838086 1071 case s_req_schema:
star297 0:768ae9838086 1072 case s_req_schema_slash:
star297 0:768ae9838086 1073 case s_req_schema_slash_slash:
star297 0:768ae9838086 1074 case s_req_server_start:
star297 0:768ae9838086 1075 {
star297 0:768ae9838086 1076 switch (ch) {
star297 0:768ae9838086 1077 /* No whitespace allowed here */
star297 0:768ae9838086 1078 case ' ':
star297 0:768ae9838086 1079 case CR:
star297 0:768ae9838086 1080 case LF:
star297 0:768ae9838086 1081 SET_ERRNO(HPE_INVALID_URL);
star297 0:768ae9838086 1082 goto error;
star297 0:768ae9838086 1083 default:
star297 0:768ae9838086 1084 UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
star297 0:768ae9838086 1085 if (UNLIKELY(CURRENT_STATE() == s_dead)) {
star297 0:768ae9838086 1086 SET_ERRNO(HPE_INVALID_URL);
star297 0:768ae9838086 1087 goto error;
star297 0:768ae9838086 1088 }
star297 0:768ae9838086 1089 }
star297 0:768ae9838086 1090
star297 0:768ae9838086 1091 break;
star297 0:768ae9838086 1092 }
star297 0:768ae9838086 1093
star297 0:768ae9838086 1094 case s_req_server:
star297 0:768ae9838086 1095 case s_req_server_with_at:
star297 0:768ae9838086 1096 case s_req_path:
star297 0:768ae9838086 1097 case s_req_query_string_start:
star297 0:768ae9838086 1098 case s_req_query_string:
star297 0:768ae9838086 1099 case s_req_fragment_start:
star297 0:768ae9838086 1100 case s_req_fragment:
star297 0:768ae9838086 1101 {
star297 0:768ae9838086 1102 switch (ch) {
star297 0:768ae9838086 1103 case ' ':
star297 0:768ae9838086 1104 UPDATE_STATE(s_req_http_start);
star297 0:768ae9838086 1105 CALLBACK_DATA(url);
star297 0:768ae9838086 1106 break;
star297 0:768ae9838086 1107 case CR:
star297 0:768ae9838086 1108 case LF:
star297 0:768ae9838086 1109 parser->http_major = 0;
star297 0:768ae9838086 1110 parser->http_minor = 9;
star297 0:768ae9838086 1111 UPDATE_STATE((ch == CR) ?
star297 0:768ae9838086 1112 s_req_line_almost_done :
star297 0:768ae9838086 1113 s_header_field_start);
star297 0:768ae9838086 1114 CALLBACK_DATA(url);
star297 0:768ae9838086 1115 break;
star297 0:768ae9838086 1116 default:
star297 0:768ae9838086 1117 UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
star297 0:768ae9838086 1118 if (UNLIKELY(CURRENT_STATE() == s_dead)) {
star297 0:768ae9838086 1119 SET_ERRNO(HPE_INVALID_URL);
star297 0:768ae9838086 1120 goto error;
star297 0:768ae9838086 1121 }
star297 0:768ae9838086 1122 }
star297 0:768ae9838086 1123 break;
star297 0:768ae9838086 1124 }
star297 0:768ae9838086 1125
star297 0:768ae9838086 1126 case s_req_http_start:
star297 0:768ae9838086 1127 switch (ch) {
star297 0:768ae9838086 1128 case 'H':
star297 0:768ae9838086 1129 UPDATE_STATE(s_req_http_H);
star297 0:768ae9838086 1130 break;
star297 0:768ae9838086 1131 case ' ':
star297 0:768ae9838086 1132 break;
star297 0:768ae9838086 1133 default:
star297 0:768ae9838086 1134 SET_ERRNO(HPE_INVALID_CONSTANT);
star297 0:768ae9838086 1135 goto error;
star297 0:768ae9838086 1136 }
star297 0:768ae9838086 1137 break;
star297 0:768ae9838086 1138
star297 0:768ae9838086 1139 case s_req_http_H:
star297 0:768ae9838086 1140 STRICT_CHECK(ch != 'T');
star297 0:768ae9838086 1141 UPDATE_STATE(s_req_http_HT);
star297 0:768ae9838086 1142 break;
star297 0:768ae9838086 1143
star297 0:768ae9838086 1144 case s_req_http_HT:
star297 0:768ae9838086 1145 STRICT_CHECK(ch != 'T');
star297 0:768ae9838086 1146 UPDATE_STATE(s_req_http_HTT);
star297 0:768ae9838086 1147 break;
star297 0:768ae9838086 1148
star297 0:768ae9838086 1149 case s_req_http_HTT:
star297 0:768ae9838086 1150 STRICT_CHECK(ch != 'P');
star297 0:768ae9838086 1151 UPDATE_STATE(s_req_http_HTTP);
star297 0:768ae9838086 1152 break;
star297 0:768ae9838086 1153
star297 0:768ae9838086 1154 case s_req_http_HTTP:
star297 0:768ae9838086 1155 STRICT_CHECK(ch != '/');
star297 0:768ae9838086 1156 UPDATE_STATE(s_req_first_http_major);
star297 0:768ae9838086 1157 break;
star297 0:768ae9838086 1158
star297 0:768ae9838086 1159 /* first digit of major HTTP version */
star297 0:768ae9838086 1160 case s_req_first_http_major:
star297 0:768ae9838086 1161 if (UNLIKELY(ch < '1' || ch > '9')) {
star297 0:768ae9838086 1162 SET_ERRNO(HPE_INVALID_VERSION);
star297 0:768ae9838086 1163 goto error;
star297 0:768ae9838086 1164 }
star297 0:768ae9838086 1165
star297 0:768ae9838086 1166 parser->http_major = ch - '0';
star297 0:768ae9838086 1167 UPDATE_STATE(s_req_http_major);
star297 0:768ae9838086 1168 break;
star297 0:768ae9838086 1169
star297 0:768ae9838086 1170 /* major HTTP version or dot */
star297 0:768ae9838086 1171 case s_req_http_major:
star297 0:768ae9838086 1172 {
star297 0:768ae9838086 1173 if (ch == '.') {
star297 0:768ae9838086 1174 UPDATE_STATE(s_req_first_http_minor);
star297 0:768ae9838086 1175 break;
star297 0:768ae9838086 1176 }
star297 0:768ae9838086 1177
star297 0:768ae9838086 1178 if (UNLIKELY(!IS_NUM(ch))) {
star297 0:768ae9838086 1179 SET_ERRNO(HPE_INVALID_VERSION);
star297 0:768ae9838086 1180 goto error;
star297 0:768ae9838086 1181 }
star297 0:768ae9838086 1182
star297 0:768ae9838086 1183 parser->http_major *= 10;
star297 0:768ae9838086 1184 parser->http_major += ch - '0';
star297 0:768ae9838086 1185
star297 0:768ae9838086 1186 if (UNLIKELY(parser->http_major > 999)) {
star297 0:768ae9838086 1187 SET_ERRNO(HPE_INVALID_VERSION);
star297 0:768ae9838086 1188 goto error;
star297 0:768ae9838086 1189 }
star297 0:768ae9838086 1190
star297 0:768ae9838086 1191 break;
star297 0:768ae9838086 1192 }
star297 0:768ae9838086 1193
star297 0:768ae9838086 1194 /* first digit of minor HTTP version */
star297 0:768ae9838086 1195 case s_req_first_http_minor:
star297 0:768ae9838086 1196 if (UNLIKELY(!IS_NUM(ch))) {
star297 0:768ae9838086 1197 SET_ERRNO(HPE_INVALID_VERSION);
star297 0:768ae9838086 1198 goto error;
star297 0:768ae9838086 1199 }
star297 0:768ae9838086 1200
star297 0:768ae9838086 1201 parser->http_minor = ch - '0';
star297 0:768ae9838086 1202 UPDATE_STATE(s_req_http_minor);
star297 0:768ae9838086 1203 break;
star297 0:768ae9838086 1204
star297 0:768ae9838086 1205 /* minor HTTP version or end of request line */
star297 0:768ae9838086 1206 case s_req_http_minor:
star297 0:768ae9838086 1207 {
star297 0:768ae9838086 1208 if (ch == CR) {
star297 0:768ae9838086 1209 UPDATE_STATE(s_req_line_almost_done);
star297 0:768ae9838086 1210 break;
star297 0:768ae9838086 1211 }
star297 0:768ae9838086 1212
star297 0:768ae9838086 1213 if (ch == LF) {
star297 0:768ae9838086 1214 UPDATE_STATE(s_header_field_start);
star297 0:768ae9838086 1215 break;
star297 0:768ae9838086 1216 }
star297 0:768ae9838086 1217
star297 0:768ae9838086 1218 /* XXX allow spaces after digit? */
star297 0:768ae9838086 1219
star297 0:768ae9838086 1220 if (UNLIKELY(!IS_NUM(ch))) {
star297 0:768ae9838086 1221 SET_ERRNO(HPE_INVALID_VERSION);
star297 0:768ae9838086 1222 goto error;
star297 0:768ae9838086 1223 }
star297 0:768ae9838086 1224
star297 0:768ae9838086 1225 parser->http_minor *= 10;
star297 0:768ae9838086 1226 parser->http_minor += ch - '0';
star297 0:768ae9838086 1227
star297 0:768ae9838086 1228 if (UNLIKELY(parser->http_minor > 999)) {
star297 0:768ae9838086 1229 SET_ERRNO(HPE_INVALID_VERSION);
star297 0:768ae9838086 1230 goto error;
star297 0:768ae9838086 1231 }
star297 0:768ae9838086 1232
star297 0:768ae9838086 1233 break;
star297 0:768ae9838086 1234 }
star297 0:768ae9838086 1235
star297 0:768ae9838086 1236 /* end of request line */
star297 0:768ae9838086 1237 case s_req_line_almost_done:
star297 0:768ae9838086 1238 {
star297 0:768ae9838086 1239 if (UNLIKELY(ch != LF)) {
star297 0:768ae9838086 1240 SET_ERRNO(HPE_LF_EXPECTED);
star297 0:768ae9838086 1241 goto error;
star297 0:768ae9838086 1242 }
star297 0:768ae9838086 1243
star297 0:768ae9838086 1244 UPDATE_STATE(s_header_field_start);
star297 0:768ae9838086 1245 break;
star297 0:768ae9838086 1246 }
star297 0:768ae9838086 1247
star297 0:768ae9838086 1248 case s_header_field_start:
star297 0:768ae9838086 1249 {
star297 0:768ae9838086 1250 if (ch == CR) {
star297 0:768ae9838086 1251 UPDATE_STATE(s_headers_almost_done);
star297 0:768ae9838086 1252 break;
star297 0:768ae9838086 1253 }
star297 0:768ae9838086 1254
star297 0:768ae9838086 1255 if (ch == LF) {
star297 0:768ae9838086 1256 /* they might be just sending \n instead of \r\n so this would be
star297 0:768ae9838086 1257 * the second \n to denote the end of headers*/
star297 0:768ae9838086 1258 UPDATE_STATE(s_headers_almost_done);
star297 0:768ae9838086 1259 REEXECUTE();
star297 0:768ae9838086 1260 }
star297 0:768ae9838086 1261
star297 0:768ae9838086 1262 c = TOKEN(ch);
star297 0:768ae9838086 1263
star297 0:768ae9838086 1264 if (UNLIKELY(!c)) {
star297 0:768ae9838086 1265 SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
star297 0:768ae9838086 1266 goto error;
star297 0:768ae9838086 1267 }
star297 0:768ae9838086 1268
star297 0:768ae9838086 1269 MARK(header_field);
star297 0:768ae9838086 1270
star297 0:768ae9838086 1271 parser->index = 0;
star297 0:768ae9838086 1272 UPDATE_STATE(s_header_field);
star297 0:768ae9838086 1273
star297 0:768ae9838086 1274 switch (c) {
star297 0:768ae9838086 1275 case 'c':
star297 0:768ae9838086 1276 parser->header_state = h_C;
star297 0:768ae9838086 1277 break;
star297 0:768ae9838086 1278
star297 0:768ae9838086 1279 case 'p':
star297 0:768ae9838086 1280 parser->header_state = h_matching_proxy_connection;
star297 0:768ae9838086 1281 break;
star297 0:768ae9838086 1282
star297 0:768ae9838086 1283 case 't':
star297 0:768ae9838086 1284 parser->header_state = h_matching_transfer_encoding;
star297 0:768ae9838086 1285 break;
star297 0:768ae9838086 1286
star297 0:768ae9838086 1287 case 'u':
star297 0:768ae9838086 1288 parser->header_state = h_matching_upgrade;
star297 0:768ae9838086 1289 break;
star297 0:768ae9838086 1290
star297 0:768ae9838086 1291 default:
star297 0:768ae9838086 1292 parser->header_state = h_general;
star297 0:768ae9838086 1293 break;
star297 0:768ae9838086 1294 }
star297 0:768ae9838086 1295 break;
star297 0:768ae9838086 1296 }
star297 0:768ae9838086 1297
star297 0:768ae9838086 1298 case s_header_field:
star297 0:768ae9838086 1299 {
star297 0:768ae9838086 1300 const char* start = p;
star297 0:768ae9838086 1301 for (; p != data + len; p++) {
star297 0:768ae9838086 1302 ch = *p;
star297 0:768ae9838086 1303 c = TOKEN(ch);
star297 0:768ae9838086 1304
star297 0:768ae9838086 1305 if (!c)
star297 0:768ae9838086 1306 break;
star297 0:768ae9838086 1307
star297 0:768ae9838086 1308 switch (parser->header_state) {
star297 0:768ae9838086 1309 case h_general:
star297 0:768ae9838086 1310 break;
star297 0:768ae9838086 1311
star297 0:768ae9838086 1312 case h_C:
star297 0:768ae9838086 1313 parser->index++;
star297 0:768ae9838086 1314 parser->header_state = (c == 'o' ? h_CO : h_general);
star297 0:768ae9838086 1315 break;
star297 0:768ae9838086 1316
star297 0:768ae9838086 1317 case h_CO:
star297 0:768ae9838086 1318 parser->index++;
star297 0:768ae9838086 1319 parser->header_state = (c == 'n' ? h_CON : h_general);
star297 0:768ae9838086 1320 break;
star297 0:768ae9838086 1321
star297 0:768ae9838086 1322 case h_CON:
star297 0:768ae9838086 1323 parser->index++;
star297 0:768ae9838086 1324 switch (c) {
star297 0:768ae9838086 1325 case 'n':
star297 0:768ae9838086 1326 parser->header_state = h_matching_connection;
star297 0:768ae9838086 1327 break;
star297 0:768ae9838086 1328 case 't':
star297 0:768ae9838086 1329 parser->header_state = h_matching_content_length;
star297 0:768ae9838086 1330 break;
star297 0:768ae9838086 1331 default:
star297 0:768ae9838086 1332 parser->header_state = h_general;
star297 0:768ae9838086 1333 break;
star297 0:768ae9838086 1334 }
star297 0:768ae9838086 1335 break;
star297 0:768ae9838086 1336
star297 0:768ae9838086 1337 /* connection */
star297 0:768ae9838086 1338
star297 0:768ae9838086 1339 case h_matching_connection:
star297 0:768ae9838086 1340 parser->index++;
star297 0:768ae9838086 1341 if (parser->index > sizeof(CONNECTION)-1
star297 0:768ae9838086 1342 || c != CONNECTION[parser->index]) {
star297 0:768ae9838086 1343 parser->header_state = h_general;
star297 0:768ae9838086 1344 } else if (parser->index == sizeof(CONNECTION)-2) {
star297 0:768ae9838086 1345 parser->header_state = h_connection;
star297 0:768ae9838086 1346 }
star297 0:768ae9838086 1347 break;
star297 0:768ae9838086 1348
star297 0:768ae9838086 1349 /* proxy-connection */
star297 0:768ae9838086 1350
star297 0:768ae9838086 1351 case h_matching_proxy_connection:
star297 0:768ae9838086 1352 parser->index++;
star297 0:768ae9838086 1353 if (parser->index > sizeof(PROXY_CONNECTION)-1
star297 0:768ae9838086 1354 || c != PROXY_CONNECTION[parser->index]) {
star297 0:768ae9838086 1355 parser->header_state = h_general;
star297 0:768ae9838086 1356 } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
star297 0:768ae9838086 1357 parser->header_state = h_connection;
star297 0:768ae9838086 1358 }
star297 0:768ae9838086 1359 break;
star297 0:768ae9838086 1360
star297 0:768ae9838086 1361 /* content-length */
star297 0:768ae9838086 1362
star297 0:768ae9838086 1363 case h_matching_content_length:
star297 0:768ae9838086 1364 parser->index++;
star297 0:768ae9838086 1365 if (parser->index > sizeof(CONTENT_LENGTH)-1
star297 0:768ae9838086 1366 || c != CONTENT_LENGTH[parser->index]) {
star297 0:768ae9838086 1367 parser->header_state = h_general;
star297 0:768ae9838086 1368 } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
star297 0:768ae9838086 1369 parser->header_state = h_content_length;
star297 0:768ae9838086 1370 }
star297 0:768ae9838086 1371 break;
star297 0:768ae9838086 1372
star297 0:768ae9838086 1373 /* transfer-encoding */
star297 0:768ae9838086 1374
star297 0:768ae9838086 1375 case h_matching_transfer_encoding:
star297 0:768ae9838086 1376 parser->index++;
star297 0:768ae9838086 1377 if (parser->index > sizeof(TRANSFER_ENCODING)-1
star297 0:768ae9838086 1378 || c != TRANSFER_ENCODING[parser->index]) {
star297 0:768ae9838086 1379 parser->header_state = h_general;
star297 0:768ae9838086 1380 } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
star297 0:768ae9838086 1381 parser->header_state = h_transfer_encoding;
star297 0:768ae9838086 1382 }
star297 0:768ae9838086 1383 break;
star297 0:768ae9838086 1384
star297 0:768ae9838086 1385 /* upgrade */
star297 0:768ae9838086 1386
star297 0:768ae9838086 1387 case h_matching_upgrade:
star297 0:768ae9838086 1388 parser->index++;
star297 0:768ae9838086 1389 if (parser->index > sizeof(UPGRADE)-1
star297 0:768ae9838086 1390 || c != UPGRADE[parser->index]) {
star297 0:768ae9838086 1391 parser->header_state = h_general;
star297 0:768ae9838086 1392 } else if (parser->index == sizeof(UPGRADE)-2) {
star297 0:768ae9838086 1393 parser->header_state = h_upgrade;
star297 0:768ae9838086 1394 }
star297 0:768ae9838086 1395 break;
star297 0:768ae9838086 1396
star297 0:768ae9838086 1397 case h_connection:
star297 0:768ae9838086 1398 case h_content_length:
star297 0:768ae9838086 1399 case h_transfer_encoding:
star297 0:768ae9838086 1400 case h_upgrade:
star297 0:768ae9838086 1401 if (ch != ' ') parser->header_state = h_general;
star297 0:768ae9838086 1402 break;
star297 0:768ae9838086 1403
star297 0:768ae9838086 1404 default:
star297 0:768ae9838086 1405 assert(0 && "Unknown header_state");
star297 0:768ae9838086 1406 break;
star297 0:768ae9838086 1407 }
star297 0:768ae9838086 1408 }
star297 0:768ae9838086 1409
star297 0:768ae9838086 1410 COUNT_HEADER_SIZE(p - start);
star297 0:768ae9838086 1411
star297 0:768ae9838086 1412 if (p == data + len) {
star297 0:768ae9838086 1413 --p;
star297 0:768ae9838086 1414 break;
star297 0:768ae9838086 1415 }
star297 0:768ae9838086 1416
star297 0:768ae9838086 1417 if (ch == ':') {
star297 0:768ae9838086 1418 UPDATE_STATE(s_header_value_discard_ws);
star297 0:768ae9838086 1419 CALLBACK_DATA(header_field);
star297 0:768ae9838086 1420 break;
star297 0:768ae9838086 1421 }
star297 0:768ae9838086 1422
star297 0:768ae9838086 1423 SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
star297 0:768ae9838086 1424 goto error;
star297 0:768ae9838086 1425 }
star297 0:768ae9838086 1426
star297 0:768ae9838086 1427 case s_header_value_discard_ws:
star297 0:768ae9838086 1428 if (ch == ' ' || ch == '\t') break;
star297 0:768ae9838086 1429
star297 0:768ae9838086 1430 if (ch == CR) {
star297 0:768ae9838086 1431 UPDATE_STATE(s_header_value_discard_ws_almost_done);
star297 0:768ae9838086 1432 break;
star297 0:768ae9838086 1433 }
star297 0:768ae9838086 1434
star297 0:768ae9838086 1435 if (ch == LF) {
star297 0:768ae9838086 1436 UPDATE_STATE(s_header_value_discard_lws);
star297 0:768ae9838086 1437 break;
star297 0:768ae9838086 1438 }
star297 0:768ae9838086 1439
star297 0:768ae9838086 1440 /* FALLTHROUGH */
star297 0:768ae9838086 1441
star297 0:768ae9838086 1442 case s_header_value_start:
star297 0:768ae9838086 1443 {
star297 0:768ae9838086 1444 MARK(header_value);
star297 0:768ae9838086 1445
star297 0:768ae9838086 1446 UPDATE_STATE(s_header_value);
star297 0:768ae9838086 1447 parser->index = 0;
star297 0:768ae9838086 1448
star297 0:768ae9838086 1449 c = LOWER(ch);
star297 0:768ae9838086 1450
star297 0:768ae9838086 1451 switch (parser->header_state) {
star297 0:768ae9838086 1452 case h_upgrade:
star297 0:768ae9838086 1453 parser->flags |= F_UPGRADE;
star297 0:768ae9838086 1454 parser->header_state = h_general;
star297 0:768ae9838086 1455 break;
star297 0:768ae9838086 1456
star297 0:768ae9838086 1457 case h_transfer_encoding:
star297 0:768ae9838086 1458 /* looking for 'Transfer-Encoding: chunked' */
star297 0:768ae9838086 1459 if ('c' == c) {
star297 0:768ae9838086 1460 parser->header_state = h_matching_transfer_encoding_chunked;
star297 0:768ae9838086 1461 } else {
star297 0:768ae9838086 1462 parser->header_state = h_general;
star297 0:768ae9838086 1463 }
star297 0:768ae9838086 1464 break;
star297 0:768ae9838086 1465
star297 0:768ae9838086 1466 case h_content_length:
star297 0:768ae9838086 1467 if (UNLIKELY(!IS_NUM(ch))) {
star297 0:768ae9838086 1468 SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
star297 0:768ae9838086 1469 goto error;
star297 0:768ae9838086 1470 }
star297 0:768ae9838086 1471
star297 0:768ae9838086 1472 if (parser->flags & F_CONTENTLENGTH) {
star297 0:768ae9838086 1473 SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
star297 0:768ae9838086 1474 goto error;
star297 0:768ae9838086 1475 }
star297 0:768ae9838086 1476
star297 0:768ae9838086 1477 parser->flags |= F_CONTENTLENGTH;
star297 0:768ae9838086 1478 parser->content_length = ch - '0';
star297 0:768ae9838086 1479 break;
star297 0:768ae9838086 1480
star297 0:768ae9838086 1481 case h_connection:
star297 0:768ae9838086 1482 /* looking for 'Connection: keep-alive' */
star297 0:768ae9838086 1483 if (c == 'k') {
star297 0:768ae9838086 1484 parser->header_state = h_matching_connection_keep_alive;
star297 0:768ae9838086 1485 /* looking for 'Connection: close' */
star297 0:768ae9838086 1486 } else if (c == 'c') {
star297 0:768ae9838086 1487 parser->header_state = h_matching_connection_close;
star297 0:768ae9838086 1488 } else if (c == 'u') {
star297 0:768ae9838086 1489 parser->header_state = h_matching_connection_upgrade;
star297 0:768ae9838086 1490 } else {
star297 0:768ae9838086 1491 parser->header_state = h_matching_connection_token;
star297 0:768ae9838086 1492 }
star297 0:768ae9838086 1493 break;
star297 0:768ae9838086 1494
star297 0:768ae9838086 1495 /* Multi-value `Connection` header */
star297 0:768ae9838086 1496 case h_matching_connection_token_start:
star297 0:768ae9838086 1497 break;
star297 0:768ae9838086 1498
star297 0:768ae9838086 1499 default:
star297 0:768ae9838086 1500 parser->header_state = h_general;
star297 0:768ae9838086 1501 break;
star297 0:768ae9838086 1502 }
star297 0:768ae9838086 1503 break;
star297 0:768ae9838086 1504 }
star297 0:768ae9838086 1505
star297 0:768ae9838086 1506 case s_header_value:
star297 0:768ae9838086 1507 {
star297 0:768ae9838086 1508 const char* start = p;
star297 0:768ae9838086 1509 enum header_states h_state = (enum header_states) parser->header_state;
star297 0:768ae9838086 1510 for (; p != data + len; p++) {
star297 0:768ae9838086 1511 ch = *p;
star297 0:768ae9838086 1512 if (ch == CR) {
star297 0:768ae9838086 1513 UPDATE_STATE(s_header_almost_done);
star297 0:768ae9838086 1514 parser->header_state = h_state;
star297 0:768ae9838086 1515 CALLBACK_DATA(header_value);
star297 0:768ae9838086 1516 break;
star297 0:768ae9838086 1517 }
star297 0:768ae9838086 1518
star297 0:768ae9838086 1519 if (ch == LF) {
star297 0:768ae9838086 1520 UPDATE_STATE(s_header_almost_done);
star297 0:768ae9838086 1521 COUNT_HEADER_SIZE(p - start);
star297 0:768ae9838086 1522 parser->header_state = h_state;
star297 0:768ae9838086 1523 CALLBACK_DATA_NOADVANCE(header_value);
star297 0:768ae9838086 1524 REEXECUTE();
star297 0:768ae9838086 1525 }
star297 0:768ae9838086 1526
star297 0:768ae9838086 1527 if (!lenient && !IS_HEADER_CHAR(ch)) {
star297 0:768ae9838086 1528 SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
star297 0:768ae9838086 1529 goto error;
star297 0:768ae9838086 1530 }
star297 0:768ae9838086 1531
star297 0:768ae9838086 1532 c = LOWER(ch);
star297 0:768ae9838086 1533
star297 0:768ae9838086 1534 switch (h_state) {
star297 0:768ae9838086 1535 case h_general:
star297 0:768ae9838086 1536 {
star297 0:768ae9838086 1537 const char* p_cr;
star297 0:768ae9838086 1538 const char* p_lf;
star297 0:768ae9838086 1539 uint32_t limit = data + len - p;
star297 0:768ae9838086 1540
star297 0:768ae9838086 1541 limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
star297 0:768ae9838086 1542
star297 0:768ae9838086 1543 p_cr = (const char*) memchr(p, CR, limit);
star297 0:768ae9838086 1544 p_lf = (const char*) memchr(p, LF, limit);
star297 0:768ae9838086 1545 if (p_cr != NULL) {
star297 0:768ae9838086 1546 if (p_lf != NULL && p_cr >= p_lf)
star297 0:768ae9838086 1547 p = p_lf;
star297 0:768ae9838086 1548 else
star297 0:768ae9838086 1549 p = p_cr;
star297 0:768ae9838086 1550 } else if (UNLIKELY(p_lf != NULL)) {
star297 0:768ae9838086 1551 p = p_lf;
star297 0:768ae9838086 1552 } else {
star297 0:768ae9838086 1553 p = data + len;
star297 0:768ae9838086 1554 }
star297 0:768ae9838086 1555 --p;
star297 0:768ae9838086 1556
star297 0:768ae9838086 1557 break;
star297 0:768ae9838086 1558 }
star297 0:768ae9838086 1559
star297 0:768ae9838086 1560 case h_connection:
star297 0:768ae9838086 1561 case h_transfer_encoding:
star297 0:768ae9838086 1562 assert(0 && "Shouldn't get here.");
star297 0:768ae9838086 1563 break;
star297 0:768ae9838086 1564
star297 0:768ae9838086 1565 case h_content_length:
star297 0:768ae9838086 1566 {
star297 0:768ae9838086 1567 uint64_t t;
star297 0:768ae9838086 1568
star297 0:768ae9838086 1569 if (ch == ' ') break;
star297 0:768ae9838086 1570
star297 0:768ae9838086 1571 if (UNLIKELY(!IS_NUM(ch))) {
star297 0:768ae9838086 1572 SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
star297 0:768ae9838086 1573 parser->header_state = h_state;
star297 0:768ae9838086 1574 goto error;
star297 0:768ae9838086 1575 }
star297 0:768ae9838086 1576
star297 0:768ae9838086 1577 t = parser->content_length;
star297 0:768ae9838086 1578 t *= 10;
star297 0:768ae9838086 1579 t += ch - '0';
star297 0:768ae9838086 1580
star297 0:768ae9838086 1581 /* Overflow? Test against a conservative limit for simplicity. */
star297 0:768ae9838086 1582 if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
star297 0:768ae9838086 1583 SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
star297 0:768ae9838086 1584 parser->header_state = h_state;
star297 0:768ae9838086 1585 goto error;
star297 0:768ae9838086 1586 }
star297 0:768ae9838086 1587
star297 0:768ae9838086 1588 parser->content_length = t;
star297 0:768ae9838086 1589 break;
star297 0:768ae9838086 1590 }
star297 0:768ae9838086 1591
star297 0:768ae9838086 1592 /* Transfer-Encoding: chunked */
star297 0:768ae9838086 1593 case h_matching_transfer_encoding_chunked:
star297 0:768ae9838086 1594 parser->index++;
star297 0:768ae9838086 1595 if (parser->index > sizeof(CHUNKED)-1
star297 0:768ae9838086 1596 || c != CHUNKED[parser->index]) {
star297 0:768ae9838086 1597 h_state = h_general;
star297 0:768ae9838086 1598 } else if (parser->index == sizeof(CHUNKED)-2) {
star297 0:768ae9838086 1599 h_state = h_transfer_encoding_chunked;
star297 0:768ae9838086 1600 }
star297 0:768ae9838086 1601 break;
star297 0:768ae9838086 1602
star297 0:768ae9838086 1603 case h_matching_connection_token_start:
star297 0:768ae9838086 1604 /* looking for 'Connection: keep-alive' */
star297 0:768ae9838086 1605 if (c == 'k') {
star297 0:768ae9838086 1606 h_state = h_matching_connection_keep_alive;
star297 0:768ae9838086 1607 /* looking for 'Connection: close' */
star297 0:768ae9838086 1608 } else if (c == 'c') {
star297 0:768ae9838086 1609 h_state = h_matching_connection_close;
star297 0:768ae9838086 1610 } else if (c == 'u') {
star297 0:768ae9838086 1611 h_state = h_matching_connection_upgrade;
star297 0:768ae9838086 1612 } else if (STRICT_TOKEN(c)) {
star297 0:768ae9838086 1613 h_state = h_matching_connection_token;
star297 0:768ae9838086 1614 } else if (c == ' ' || c == '\t') {
star297 0:768ae9838086 1615 /* Skip lws */
star297 0:768ae9838086 1616 } else {
star297 0:768ae9838086 1617 h_state = h_general;
star297 0:768ae9838086 1618 }
star297 0:768ae9838086 1619 break;
star297 0:768ae9838086 1620
star297 0:768ae9838086 1621 /* looking for 'Connection: keep-alive' */
star297 0:768ae9838086 1622 case h_matching_connection_keep_alive:
star297 0:768ae9838086 1623 parser->index++;
star297 0:768ae9838086 1624 if (parser->index > sizeof(KEEP_ALIVE)-1
star297 0:768ae9838086 1625 || c != KEEP_ALIVE[parser->index]) {
star297 0:768ae9838086 1626 h_state = h_matching_connection_token;
star297 0:768ae9838086 1627 } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
star297 0:768ae9838086 1628 h_state = h_connection_keep_alive;
star297 0:768ae9838086 1629 }
star297 0:768ae9838086 1630 break;
star297 0:768ae9838086 1631
star297 0:768ae9838086 1632 /* looking for 'Connection: close' */
star297 0:768ae9838086 1633 case h_matching_connection_close:
star297 0:768ae9838086 1634 parser->index++;
star297 0:768ae9838086 1635 if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
star297 0:768ae9838086 1636 h_state = h_matching_connection_token;
star297 0:768ae9838086 1637 } else if (parser->index == sizeof(CLOSE)-2) {
star297 0:768ae9838086 1638 h_state = h_connection_close;
star297 0:768ae9838086 1639 }
star297 0:768ae9838086 1640 break;
star297 0:768ae9838086 1641
star297 0:768ae9838086 1642 /* looking for 'Connection: upgrade' */
star297 0:768ae9838086 1643 case h_matching_connection_upgrade:
star297 0:768ae9838086 1644 parser->index++;
star297 0:768ae9838086 1645 if (parser->index > sizeof(UPGRADE) - 1 ||
star297 0:768ae9838086 1646 c != UPGRADE[parser->index]) {
star297 0:768ae9838086 1647 h_state = h_matching_connection_token;
star297 0:768ae9838086 1648 } else if (parser->index == sizeof(UPGRADE)-2) {
star297 0:768ae9838086 1649 h_state = h_connection_upgrade;
star297 0:768ae9838086 1650 }
star297 0:768ae9838086 1651 break;
star297 0:768ae9838086 1652
star297 0:768ae9838086 1653 case h_matching_connection_token:
star297 0:768ae9838086 1654 if (ch == ',') {
star297 0:768ae9838086 1655 h_state = h_matching_connection_token_start;
star297 0:768ae9838086 1656 parser->index = 0;
star297 0:768ae9838086 1657 }
star297 0:768ae9838086 1658 break;
star297 0:768ae9838086 1659
star297 0:768ae9838086 1660 case h_transfer_encoding_chunked:
star297 0:768ae9838086 1661 if (ch != ' ') h_state = h_general;
star297 0:768ae9838086 1662 break;
star297 0:768ae9838086 1663
star297 0:768ae9838086 1664 case h_connection_keep_alive:
star297 0:768ae9838086 1665 case h_connection_close:
star297 0:768ae9838086 1666 case h_connection_upgrade:
star297 0:768ae9838086 1667 if (ch == ',') {
star297 0:768ae9838086 1668 if (h_state == h_connection_keep_alive) {
star297 0:768ae9838086 1669 parser->flags |= F_CONNECTION_KEEP_ALIVE;
star297 0:768ae9838086 1670 } else if (h_state == h_connection_close) {
star297 0:768ae9838086 1671 parser->flags |= F_CONNECTION_CLOSE;
star297 0:768ae9838086 1672 } else if (h_state == h_connection_upgrade) {
star297 0:768ae9838086 1673 parser->flags |= F_CONNECTION_UPGRADE;
star297 0:768ae9838086 1674 }
star297 0:768ae9838086 1675 h_state = h_matching_connection_token_start;
star297 0:768ae9838086 1676 parser->index = 0;
star297 0:768ae9838086 1677 } else if (ch != ' ') {
star297 0:768ae9838086 1678 h_state = h_matching_connection_token;
star297 0:768ae9838086 1679 }
star297 0:768ae9838086 1680 break;
star297 0:768ae9838086 1681
star297 0:768ae9838086 1682 default:
star297 0:768ae9838086 1683 UPDATE_STATE(s_header_value);
star297 0:768ae9838086 1684 h_state = h_general;
star297 0:768ae9838086 1685 break;
star297 0:768ae9838086 1686 }
star297 0:768ae9838086 1687 }
star297 0:768ae9838086 1688 parser->header_state = h_state;
star297 0:768ae9838086 1689
star297 0:768ae9838086 1690 COUNT_HEADER_SIZE(p - start);
star297 0:768ae9838086 1691
star297 0:768ae9838086 1692 if (p == data + len)
star297 0:768ae9838086 1693 --p;
star297 0:768ae9838086 1694 break;
star297 0:768ae9838086 1695 }
star297 0:768ae9838086 1696
star297 0:768ae9838086 1697 case s_header_almost_done:
star297 0:768ae9838086 1698 {
star297 0:768ae9838086 1699 if (UNLIKELY(ch != LF)) {
star297 0:768ae9838086 1700 SET_ERRNO(HPE_LF_EXPECTED);
star297 0:768ae9838086 1701 goto error;
star297 0:768ae9838086 1702 }
star297 0:768ae9838086 1703
star297 0:768ae9838086 1704 UPDATE_STATE(s_header_value_lws);
star297 0:768ae9838086 1705 break;
star297 0:768ae9838086 1706 }
star297 0:768ae9838086 1707
star297 0:768ae9838086 1708 case s_header_value_lws:
star297 0:768ae9838086 1709 {
star297 0:768ae9838086 1710 if (ch == ' ' || ch == '\t') {
star297 0:768ae9838086 1711 UPDATE_STATE(s_header_value_start);
star297 0:768ae9838086 1712 REEXECUTE();
star297 0:768ae9838086 1713 }
star297 0:768ae9838086 1714
star297 0:768ae9838086 1715 /* finished the header */
star297 0:768ae9838086 1716 switch (parser->header_state) {
star297 0:768ae9838086 1717 case h_connection_keep_alive:
star297 0:768ae9838086 1718 parser->flags |= F_CONNECTION_KEEP_ALIVE;
star297 0:768ae9838086 1719 break;
star297 0:768ae9838086 1720 case h_connection_close:
star297 0:768ae9838086 1721 parser->flags |= F_CONNECTION_CLOSE;
star297 0:768ae9838086 1722 break;
star297 0:768ae9838086 1723 case h_transfer_encoding_chunked:
star297 0:768ae9838086 1724 parser->flags |= F_CHUNKED;
star297 0:768ae9838086 1725 break;
star297 0:768ae9838086 1726 case h_connection_upgrade:
star297 0:768ae9838086 1727 parser->flags |= F_CONNECTION_UPGRADE;
star297 0:768ae9838086 1728 break;
star297 0:768ae9838086 1729 default:
star297 0:768ae9838086 1730 break;
star297 0:768ae9838086 1731 }
star297 0:768ae9838086 1732
star297 0:768ae9838086 1733 UPDATE_STATE(s_header_field_start);
star297 0:768ae9838086 1734 REEXECUTE();
star297 0:768ae9838086 1735 }
star297 0:768ae9838086 1736
star297 0:768ae9838086 1737 case s_header_value_discard_ws_almost_done:
star297 0:768ae9838086 1738 {
star297 0:768ae9838086 1739 STRICT_CHECK(ch != LF);
star297 0:768ae9838086 1740 UPDATE_STATE(s_header_value_discard_lws);
star297 0:768ae9838086 1741 break;
star297 0:768ae9838086 1742 }
star297 0:768ae9838086 1743
star297 0:768ae9838086 1744 case s_header_value_discard_lws:
star297 0:768ae9838086 1745 {
star297 0:768ae9838086 1746 if (ch == ' ' || ch == '\t') {
star297 0:768ae9838086 1747 UPDATE_STATE(s_header_value_discard_ws);
star297 0:768ae9838086 1748 break;
star297 0:768ae9838086 1749 } else {
star297 0:768ae9838086 1750 switch (parser->header_state) {
star297 0:768ae9838086 1751 case h_connection_keep_alive:
star297 0:768ae9838086 1752 parser->flags |= F_CONNECTION_KEEP_ALIVE;
star297 0:768ae9838086 1753 break;
star297 0:768ae9838086 1754 case h_connection_close:
star297 0:768ae9838086 1755 parser->flags |= F_CONNECTION_CLOSE;
star297 0:768ae9838086 1756 break;
star297 0:768ae9838086 1757 case h_connection_upgrade:
star297 0:768ae9838086 1758 parser->flags |= F_CONNECTION_UPGRADE;
star297 0:768ae9838086 1759 break;
star297 0:768ae9838086 1760 case h_transfer_encoding_chunked:
star297 0:768ae9838086 1761 parser->flags |= F_CHUNKED;
star297 0:768ae9838086 1762 break;
star297 0:768ae9838086 1763 default:
star297 0:768ae9838086 1764 break;
star297 0:768ae9838086 1765 }
star297 0:768ae9838086 1766
star297 0:768ae9838086 1767 /* header value was empty */
star297 0:768ae9838086 1768 MARK(header_value);
star297 0:768ae9838086 1769 UPDATE_STATE(s_header_field_start);
star297 0:768ae9838086 1770 CALLBACK_DATA_NOADVANCE(header_value);
star297 0:768ae9838086 1771 REEXECUTE();
star297 0:768ae9838086 1772 }
star297 0:768ae9838086 1773 }
star297 0:768ae9838086 1774
star297 0:768ae9838086 1775 case s_headers_almost_done:
star297 0:768ae9838086 1776 {
star297 0:768ae9838086 1777 STRICT_CHECK(ch != LF);
star297 0:768ae9838086 1778
star297 0:768ae9838086 1779 if (parser->flags & F_TRAILING) {
star297 0:768ae9838086 1780 /* End of a chunked request */
star297 0:768ae9838086 1781 UPDATE_STATE(s_message_done);
star297 0:768ae9838086 1782 CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
star297 0:768ae9838086 1783 REEXECUTE();
star297 0:768ae9838086 1784 }
star297 0:768ae9838086 1785
star297 0:768ae9838086 1786 /* Cannot use chunked encoding and a content-length header together
star297 0:768ae9838086 1787 per the HTTP specification. */
star297 0:768ae9838086 1788 if ((parser->flags & F_CHUNKED) &&
star297 0:768ae9838086 1789 (parser->flags & F_CONTENTLENGTH)) {
star297 0:768ae9838086 1790 SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
star297 0:768ae9838086 1791 goto error;
star297 0:768ae9838086 1792 }
star297 0:768ae9838086 1793
star297 0:768ae9838086 1794 UPDATE_STATE(s_headers_done);
star297 0:768ae9838086 1795
star297 0:768ae9838086 1796 /* Set this here so that on_headers_complete() callbacks can see it */
star297 0:768ae9838086 1797 parser->upgrade =
star297 0:768ae9838086 1798 ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) ==
star297 0:768ae9838086 1799 (F_UPGRADE | F_CONNECTION_UPGRADE) ||
star297 0:768ae9838086 1800 parser->method == HTTP_CONNECT);
star297 0:768ae9838086 1801
star297 0:768ae9838086 1802 /* Here we call the headers_complete callback. This is somewhat
star297 0:768ae9838086 1803 * different than other callbacks because if the user returns 1, we
star297 0:768ae9838086 1804 * will interpret that as saying that this message has no body. This
star297 0:768ae9838086 1805 * is needed for the annoying case of recieving a response to a HEAD
star297 0:768ae9838086 1806 * request.
star297 0:768ae9838086 1807 *
star297 0:768ae9838086 1808 * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
star297 0:768ae9838086 1809 * we have to simulate it by handling a change in errno below.
star297 0:768ae9838086 1810 */
star297 0:768ae9838086 1811 if (settings->on_headers_complete) {
star297 0:768ae9838086 1812 switch (settings->on_headers_complete(parser)) {
star297 0:768ae9838086 1813 case 0:
star297 0:768ae9838086 1814 break;
star297 0:768ae9838086 1815
star297 0:768ae9838086 1816 case 2:
star297 0:768ae9838086 1817 parser->upgrade = 1;
star297 0:768ae9838086 1818
star297 0:768ae9838086 1819 case 1:
star297 0:768ae9838086 1820 parser->flags |= F_SKIPBODY;
star297 0:768ae9838086 1821 break;
star297 0:768ae9838086 1822
star297 0:768ae9838086 1823 default:
star297 0:768ae9838086 1824 SET_ERRNO(HPE_CB_headers_complete);
star297 0:768ae9838086 1825 RETURN(p - data); /* Error */
star297 0:768ae9838086 1826 }
star297 0:768ae9838086 1827 }
star297 0:768ae9838086 1828
star297 0:768ae9838086 1829 if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
star297 0:768ae9838086 1830 RETURN(p - data);
star297 0:768ae9838086 1831 }
star297 0:768ae9838086 1832
star297 0:768ae9838086 1833 REEXECUTE();
star297 0:768ae9838086 1834 }
star297 0:768ae9838086 1835
star297 0:768ae9838086 1836 case s_headers_done:
star297 0:768ae9838086 1837 {
star297 0:768ae9838086 1838 int hasBody;
star297 0:768ae9838086 1839 STRICT_CHECK(ch != LF);
star297 0:768ae9838086 1840
star297 0:768ae9838086 1841 parser->nread = 0;
star297 0:768ae9838086 1842
star297 0:768ae9838086 1843 hasBody = parser->flags & F_CHUNKED ||
star297 0:768ae9838086 1844 (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
star297 0:768ae9838086 1845 if (parser->upgrade && (parser->method == HTTP_CONNECT ||
star297 0:768ae9838086 1846 (parser->flags & F_SKIPBODY) || !hasBody)) {
star297 0:768ae9838086 1847 /* Exit, the rest of the message is in a different protocol. */
star297 0:768ae9838086 1848 UPDATE_STATE(NEW_MESSAGE());
star297 0:768ae9838086 1849 CALLBACK_NOTIFY(message_complete);
star297 0:768ae9838086 1850 RETURN((p - data) + 1);
star297 0:768ae9838086 1851 }
star297 0:768ae9838086 1852
star297 0:768ae9838086 1853 if (parser->flags & F_SKIPBODY) {
star297 0:768ae9838086 1854 UPDATE_STATE(NEW_MESSAGE());
star297 0:768ae9838086 1855 CALLBACK_NOTIFY(message_complete);
star297 0:768ae9838086 1856 } else if (parser->flags & F_CHUNKED) {
star297 0:768ae9838086 1857 /* chunked encoding - ignore Content-Length header */
star297 0:768ae9838086 1858 UPDATE_STATE(s_chunk_size_start);
star297 0:768ae9838086 1859 } else {
star297 0:768ae9838086 1860 if (parser->content_length == 0) {
star297 0:768ae9838086 1861 /* Content-Length header given but zero: Content-Length: 0\r\n */
star297 0:768ae9838086 1862 UPDATE_STATE(NEW_MESSAGE());
star297 0:768ae9838086 1863 CALLBACK_NOTIFY(message_complete);
star297 0:768ae9838086 1864 } else if (parser->content_length != ULLONG_MAX) {
star297 0:768ae9838086 1865 /* Content-Length header given and non-zero */
star297 0:768ae9838086 1866 UPDATE_STATE(s_body_identity);
star297 0:768ae9838086 1867 } else {
star297 0:768ae9838086 1868 if (!http_message_needs_eof(parser)) {
star297 0:768ae9838086 1869 /* Assume content-length 0 - read the next */
star297 0:768ae9838086 1870 UPDATE_STATE(NEW_MESSAGE());
star297 0:768ae9838086 1871 CALLBACK_NOTIFY(message_complete);
star297 0:768ae9838086 1872 } else {
star297 0:768ae9838086 1873 /* Read body until EOF */
star297 0:768ae9838086 1874 UPDATE_STATE(s_body_identity_eof);
star297 0:768ae9838086 1875 }
star297 0:768ae9838086 1876 }
star297 0:768ae9838086 1877 }
star297 0:768ae9838086 1878
star297 0:768ae9838086 1879 break;
star297 0:768ae9838086 1880 }
star297 0:768ae9838086 1881
star297 0:768ae9838086 1882 case s_body_identity:
star297 0:768ae9838086 1883 {
star297 0:768ae9838086 1884 uint64_t to_read = MIN(parser->content_length,
star297 0:768ae9838086 1885 (uint64_t) ((data + len) - p));
star297 0:768ae9838086 1886
star297 0:768ae9838086 1887 assert(parser->content_length != 0
star297 0:768ae9838086 1888 && parser->content_length != ULLONG_MAX);
star297 0:768ae9838086 1889
star297 0:768ae9838086 1890 /* The difference between advancing content_length and p is because
star297 0:768ae9838086 1891 * the latter will automaticaly advance on the next loop iteration.
star297 0:768ae9838086 1892 * Further, if content_length ends up at 0, we want to see the last
star297 0:768ae9838086 1893 * byte again for our message complete callback.
star297 0:768ae9838086 1894 */
star297 0:768ae9838086 1895 MARK(body);
star297 0:768ae9838086 1896 parser->content_length -= to_read;
star297 0:768ae9838086 1897 p += to_read - 1;
star297 0:768ae9838086 1898
star297 0:768ae9838086 1899 if (parser->content_length == 0) {
star297 0:768ae9838086 1900 UPDATE_STATE(s_message_done);
star297 0:768ae9838086 1901
star297 0:768ae9838086 1902 /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
star297 0:768ae9838086 1903 *
star297 0:768ae9838086 1904 * The alternative to doing this is to wait for the next byte to
star297 0:768ae9838086 1905 * trigger the data callback, just as in every other case. The
star297 0:768ae9838086 1906 * problem with this is that this makes it difficult for the test
star297 0:768ae9838086 1907 * harness to distinguish between complete-on-EOF and
star297 0:768ae9838086 1908 * complete-on-length. It's not clear that this distinction is
star297 0:768ae9838086 1909 * important for applications, but let's keep it for now.
star297 0:768ae9838086 1910 */
star297 0:768ae9838086 1911 CALLBACK_DATA_(body, p - body_mark + 1, p - data);
star297 0:768ae9838086 1912 REEXECUTE();
star297 0:768ae9838086 1913 }
star297 0:768ae9838086 1914
star297 0:768ae9838086 1915 break;
star297 0:768ae9838086 1916 }
star297 0:768ae9838086 1917
star297 0:768ae9838086 1918 /* read until EOF */
star297 0:768ae9838086 1919 case s_body_identity_eof:
star297 0:768ae9838086 1920 MARK(body);
star297 0:768ae9838086 1921 p = data + len - 1;
star297 0:768ae9838086 1922
star297 0:768ae9838086 1923 break;
star297 0:768ae9838086 1924
star297 0:768ae9838086 1925 case s_message_done:
star297 0:768ae9838086 1926 UPDATE_STATE(NEW_MESSAGE());
star297 0:768ae9838086 1927 CALLBACK_NOTIFY(message_complete);
star297 0:768ae9838086 1928 if (parser->upgrade) {
star297 0:768ae9838086 1929 /* Exit, the rest of the message is in a different protocol. */
star297 0:768ae9838086 1930 RETURN((p - data) + 1);
star297 0:768ae9838086 1931 }
star297 0:768ae9838086 1932 break;
star297 0:768ae9838086 1933
star297 0:768ae9838086 1934 case s_chunk_size_start:
star297 0:768ae9838086 1935 {
star297 0:768ae9838086 1936 assert(parser->nread == 1);
star297 0:768ae9838086 1937 assert(parser->flags & F_CHUNKED);
star297 0:768ae9838086 1938
star297 0:768ae9838086 1939 unhex_val = unhex[(unsigned char)ch];
star297 0:768ae9838086 1940 if (UNLIKELY(unhex_val == -1)) {
star297 0:768ae9838086 1941 SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
star297 0:768ae9838086 1942 goto error;
star297 0:768ae9838086 1943 }
star297 0:768ae9838086 1944
star297 0:768ae9838086 1945 parser->content_length = unhex_val;
star297 0:768ae9838086 1946 UPDATE_STATE(s_chunk_size);
star297 0:768ae9838086 1947 break;
star297 0:768ae9838086 1948 }
star297 0:768ae9838086 1949
star297 0:768ae9838086 1950 case s_chunk_size:
star297 0:768ae9838086 1951 {
star297 0:768ae9838086 1952 uint64_t t;
star297 0:768ae9838086 1953
star297 0:768ae9838086 1954 assert(parser->flags & F_CHUNKED);
star297 0:768ae9838086 1955
star297 0:768ae9838086 1956 if (ch == CR) {
star297 0:768ae9838086 1957 UPDATE_STATE(s_chunk_size_almost_done);
star297 0:768ae9838086 1958 break;
star297 0:768ae9838086 1959 }
star297 0:768ae9838086 1960
star297 0:768ae9838086 1961 unhex_val = unhex[(unsigned char)ch];
star297 0:768ae9838086 1962
star297 0:768ae9838086 1963 if (unhex_val == -1) {
star297 0:768ae9838086 1964 if (ch == ';' || ch == ' ') {
star297 0:768ae9838086 1965 UPDATE_STATE(s_chunk_parameters);
star297 0:768ae9838086 1966 break;
star297 0:768ae9838086 1967 }
star297 0:768ae9838086 1968
star297 0:768ae9838086 1969 SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
star297 0:768ae9838086 1970 goto error;
star297 0:768ae9838086 1971 }
star297 0:768ae9838086 1972
star297 0:768ae9838086 1973 t = parser->content_length;
star297 0:768ae9838086 1974 t *= 16;
star297 0:768ae9838086 1975 t += unhex_val;
star297 0:768ae9838086 1976
star297 0:768ae9838086 1977 /* Overflow? Test against a conservative limit for simplicity. */
star297 0:768ae9838086 1978 if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
star297 0:768ae9838086 1979 SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
star297 0:768ae9838086 1980 goto error;
star297 0:768ae9838086 1981 }
star297 0:768ae9838086 1982
star297 0:768ae9838086 1983 parser->content_length = t;
star297 0:768ae9838086 1984 break;
star297 0:768ae9838086 1985 }
star297 0:768ae9838086 1986
star297 0:768ae9838086 1987 case s_chunk_parameters:
star297 0:768ae9838086 1988 {
star297 0:768ae9838086 1989 assert(parser->flags & F_CHUNKED);
star297 0:768ae9838086 1990 /* just ignore this shit. TODO check for overflow */
star297 0:768ae9838086 1991 if (ch == CR) {
star297 0:768ae9838086 1992 UPDATE_STATE(s_chunk_size_almost_done);
star297 0:768ae9838086 1993 break;
star297 0:768ae9838086 1994 }
star297 0:768ae9838086 1995 break;
star297 0:768ae9838086 1996 }
star297 0:768ae9838086 1997
star297 0:768ae9838086 1998 case s_chunk_size_almost_done:
star297 0:768ae9838086 1999 {
star297 0:768ae9838086 2000 assert(parser->flags & F_CHUNKED);
star297 0:768ae9838086 2001 STRICT_CHECK(ch != LF);
star297 0:768ae9838086 2002
star297 0:768ae9838086 2003 parser->nread = 0;
star297 0:768ae9838086 2004
star297 0:768ae9838086 2005 if (parser->content_length == 0) {
star297 0:768ae9838086 2006 parser->flags |= F_TRAILING;
star297 0:768ae9838086 2007 UPDATE_STATE(s_header_field_start);
star297 0:768ae9838086 2008 } else {
star297 0:768ae9838086 2009 UPDATE_STATE(s_chunk_data);
star297 0:768ae9838086 2010 }
star297 0:768ae9838086 2011 CALLBACK_NOTIFY(chunk_header);
star297 0:768ae9838086 2012 break;
star297 0:768ae9838086 2013 }
star297 0:768ae9838086 2014
star297 0:768ae9838086 2015 case s_chunk_data:
star297 0:768ae9838086 2016 {
star297 0:768ae9838086 2017 uint64_t to_read = MIN(parser->content_length,
star297 0:768ae9838086 2018 (uint64_t) ((data + len) - p));
star297 0:768ae9838086 2019
star297 0:768ae9838086 2020 assert(parser->flags & F_CHUNKED);
star297 0:768ae9838086 2021 assert(parser->content_length != 0
star297 0:768ae9838086 2022 && parser->content_length != ULLONG_MAX);
star297 0:768ae9838086 2023
star297 0:768ae9838086 2024 /* See the explanation in s_body_identity for why the content
star297 0:768ae9838086 2025 * length and data pointers are managed this way.
star297 0:768ae9838086 2026 */
star297 0:768ae9838086 2027 MARK(body);
star297 0:768ae9838086 2028 parser->content_length -= to_read;
star297 0:768ae9838086 2029 p += to_read - 1;
star297 0:768ae9838086 2030
star297 0:768ae9838086 2031 if (parser->content_length == 0) {
star297 0:768ae9838086 2032 UPDATE_STATE(s_chunk_data_almost_done);
star297 0:768ae9838086 2033 }
star297 0:768ae9838086 2034
star297 0:768ae9838086 2035 break;
star297 0:768ae9838086 2036 }
star297 0:768ae9838086 2037
star297 0:768ae9838086 2038 case s_chunk_data_almost_done:
star297 0:768ae9838086 2039 assert(parser->flags & F_CHUNKED);
star297 0:768ae9838086 2040 assert(parser->content_length == 0);
star297 0:768ae9838086 2041 STRICT_CHECK(ch != CR);
star297 0:768ae9838086 2042 UPDATE_STATE(s_chunk_data_done);
star297 0:768ae9838086 2043 CALLBACK_DATA(body);
star297 0:768ae9838086 2044 break;
star297 0:768ae9838086 2045
star297 0:768ae9838086 2046 case s_chunk_data_done:
star297 0:768ae9838086 2047 assert(parser->flags & F_CHUNKED);
star297 0:768ae9838086 2048 STRICT_CHECK(ch != LF);
star297 0:768ae9838086 2049 parser->nread = 0;
star297 0:768ae9838086 2050 UPDATE_STATE(s_chunk_size_start);
star297 0:768ae9838086 2051 CALLBACK_NOTIFY(chunk_complete);
star297 0:768ae9838086 2052 break;
star297 0:768ae9838086 2053
star297 0:768ae9838086 2054 default:
star297 0:768ae9838086 2055 assert(0 && "unhandled state");
star297 0:768ae9838086 2056 SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
star297 0:768ae9838086 2057 goto error;
star297 0:768ae9838086 2058 }
star297 0:768ae9838086 2059 }
star297 0:768ae9838086 2060
star297 0:768ae9838086 2061 /* Run callbacks for any marks that we have leftover after we ran our of
star297 0:768ae9838086 2062 * bytes. There should be at most one of these set, so it's OK to invoke
star297 0:768ae9838086 2063 * them in series (unset marks will not result in callbacks).
star297 0:768ae9838086 2064 *
star297 0:768ae9838086 2065 * We use the NOADVANCE() variety of callbacks here because 'p' has already
star297 0:768ae9838086 2066 * overflowed 'data' and this allows us to correct for the off-by-one that
star297 0:768ae9838086 2067 * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
star297 0:768ae9838086 2068 * value that's in-bounds).
star297 0:768ae9838086 2069 */
star297 0:768ae9838086 2070
star297 0:768ae9838086 2071 assert(((header_field_mark ? 1 : 0) +
star297 0:768ae9838086 2072 (header_value_mark ? 1 : 0) +
star297 0:768ae9838086 2073 (url_mark ? 1 : 0) +
star297 0:768ae9838086 2074 (body_mark ? 1 : 0) +
star297 0:768ae9838086 2075 (status_mark ? 1 : 0)) <= 1);
star297 0:768ae9838086 2076
star297 0:768ae9838086 2077 CALLBACK_DATA_NOADVANCE(header_field);
star297 0:768ae9838086 2078 CALLBACK_DATA_NOADVANCE(header_value);
star297 0:768ae9838086 2079 CALLBACK_DATA_NOADVANCE(url);
star297 0:768ae9838086 2080 CALLBACK_DATA_NOADVANCE(body);
star297 0:768ae9838086 2081 CALLBACK_DATA_NOADVANCE(status);
star297 0:768ae9838086 2082
star297 0:768ae9838086 2083 RETURN(len);
star297 0:768ae9838086 2084
star297 0:768ae9838086 2085 error:
star297 0:768ae9838086 2086 if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
star297 0:768ae9838086 2087 SET_ERRNO(HPE_UNKNOWN);
star297 0:768ae9838086 2088 }
star297 0:768ae9838086 2089
star297 0:768ae9838086 2090 RETURN(p - data);
star297 0:768ae9838086 2091 }
star297 0:768ae9838086 2092
star297 0:768ae9838086 2093
star297 0:768ae9838086 2094 /* Does the parser need to see an EOF to find the end of the message? */
star297 0:768ae9838086 2095 int
star297 0:768ae9838086 2096 http_message_needs_eof (const http_parser *parser)
star297 0:768ae9838086 2097 {
star297 0:768ae9838086 2098 if (parser->type == HTTP_REQUEST) {
star297 0:768ae9838086 2099 return 0;
star297 0:768ae9838086 2100 }
star297 0:768ae9838086 2101
star297 0:768ae9838086 2102 /* See RFC 2616 section 4.4 */
star297 0:768ae9838086 2103 if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
star297 0:768ae9838086 2104 parser->status_code == 204 || /* No Content */
star297 0:768ae9838086 2105 parser->status_code == 304 || /* Not Modified */
star297 0:768ae9838086 2106 parser->flags & F_SKIPBODY) { /* response to a HEAD request */
star297 0:768ae9838086 2107 return 0;
star297 0:768ae9838086 2108 }
star297 0:768ae9838086 2109
star297 0:768ae9838086 2110 if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
star297 0:768ae9838086 2111 return 0;
star297 0:768ae9838086 2112 }
star297 0:768ae9838086 2113
star297 0:768ae9838086 2114 return 1;
star297 0:768ae9838086 2115 }
star297 0:768ae9838086 2116
star297 0:768ae9838086 2117
star297 0:768ae9838086 2118 int
star297 0:768ae9838086 2119 http_should_keep_alive (const http_parser *parser)
star297 0:768ae9838086 2120 {
star297 0:768ae9838086 2121 if (parser->http_major > 0 && parser->http_minor > 0) {
star297 0:768ae9838086 2122 /* HTTP/1.1 */
star297 0:768ae9838086 2123 if (parser->flags & F_CONNECTION_CLOSE) {
star297 0:768ae9838086 2124 return 0;
star297 0:768ae9838086 2125 }
star297 0:768ae9838086 2126 } else {
star297 0:768ae9838086 2127 /* HTTP/1.0 or earlier */
star297 0:768ae9838086 2128 if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
star297 0:768ae9838086 2129 return 0;
star297 0:768ae9838086 2130 }
star297 0:768ae9838086 2131 }
star297 0:768ae9838086 2132
star297 0:768ae9838086 2133 return !http_message_needs_eof(parser);
star297 0:768ae9838086 2134 }
star297 0:768ae9838086 2135
star297 0:768ae9838086 2136
star297 0:768ae9838086 2137 const char *
star297 0:768ae9838086 2138 http_method_str (enum http_method m)
star297 0:768ae9838086 2139 {
star297 0:768ae9838086 2140 return ELEM_AT(method_strings, m, "<unknown>");
star297 0:768ae9838086 2141 }
star297 0:768ae9838086 2142
star297 0:768ae9838086 2143
star297 0:768ae9838086 2144 void
star297 0:768ae9838086 2145 http_parser_init (http_parser *parser, enum http_parser_type t)
star297 0:768ae9838086 2146 {
star297 0:768ae9838086 2147 void *data = parser->data; /* preserve application data */
star297 0:768ae9838086 2148 memset(parser, 0, sizeof(*parser));
star297 0:768ae9838086 2149 parser->data = data;
star297 0:768ae9838086 2150 parser->type = t;
star297 0:768ae9838086 2151 parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
star297 0:768ae9838086 2152 parser->http_errno = HPE_OK;
star297 0:768ae9838086 2153 }
star297 0:768ae9838086 2154
star297 0:768ae9838086 2155 void
star297 0:768ae9838086 2156 http_parser_settings_init(http_parser_settings *settings)
star297 0:768ae9838086 2157 {
star297 0:768ae9838086 2158 memset(settings, 0, sizeof(*settings));
star297 0:768ae9838086 2159 }
star297 0:768ae9838086 2160
star297 0:768ae9838086 2161 const char *
star297 0:768ae9838086 2162 http_errno_name(enum http_errno err) {
star297 0:768ae9838086 2163 assert(((uint32_t) err) < ARRAY_SIZE(http_strerror_tab));
star297 0:768ae9838086 2164 return http_strerror_tab[err].name;
star297 0:768ae9838086 2165 }
star297 0:768ae9838086 2166
star297 0:768ae9838086 2167 const char *
star297 0:768ae9838086 2168 http_errno_description(enum http_errno err) {
star297 0:768ae9838086 2169 assert(((uint32_t) err) < ARRAY_SIZE(http_strerror_tab));
star297 0:768ae9838086 2170 return http_strerror_tab[err].description;
star297 0:768ae9838086 2171 }
star297 0:768ae9838086 2172
star297 0:768ae9838086 2173 static enum http_host_state
star297 0:768ae9838086 2174 http_parse_host_char(enum http_host_state s, const char ch) {
star297 0:768ae9838086 2175 switch(s) {
star297 0:768ae9838086 2176 case s_http_userinfo:
star297 0:768ae9838086 2177 case s_http_userinfo_start:
star297 0:768ae9838086 2178 if (ch == '@') {
star297 0:768ae9838086 2179 return s_http_host_start;
star297 0:768ae9838086 2180 }
star297 0:768ae9838086 2181
star297 0:768ae9838086 2182 if (IS_USERINFO_CHAR(ch)) {
star297 0:768ae9838086 2183 return s_http_userinfo;
star297 0:768ae9838086 2184 }
star297 0:768ae9838086 2185 break;
star297 0:768ae9838086 2186
star297 0:768ae9838086 2187 case s_http_host_start:
star297 0:768ae9838086 2188 if (ch == '[') {
star297 0:768ae9838086 2189 return s_http_host_v6_start;
star297 0:768ae9838086 2190 }
star297 0:768ae9838086 2191
star297 0:768ae9838086 2192 if (IS_HOST_CHAR(ch)) {
star297 0:768ae9838086 2193 return s_http_host;
star297 0:768ae9838086 2194 }
star297 0:768ae9838086 2195
star297 0:768ae9838086 2196 break;
star297 0:768ae9838086 2197
star297 0:768ae9838086 2198 case s_http_host:
star297 0:768ae9838086 2199 if (IS_HOST_CHAR(ch)) {
star297 0:768ae9838086 2200 return s_http_host;
star297 0:768ae9838086 2201 }
star297 0:768ae9838086 2202
star297 0:768ae9838086 2203 /* FALLTHROUGH */
star297 0:768ae9838086 2204 case s_http_host_v6_end:
star297 0:768ae9838086 2205 if (ch == ':') {
star297 0:768ae9838086 2206 return s_http_host_port_start;
star297 0:768ae9838086 2207 }
star297 0:768ae9838086 2208
star297 0:768ae9838086 2209 break;
star297 0:768ae9838086 2210
star297 0:768ae9838086 2211 case s_http_host_v6:
star297 0:768ae9838086 2212 if (ch == ']') {
star297 0:768ae9838086 2213 return s_http_host_v6_end;
star297 0:768ae9838086 2214 }
star297 0:768ae9838086 2215
star297 0:768ae9838086 2216 /* FALLTHROUGH */
star297 0:768ae9838086 2217 case s_http_host_v6_start:
star297 0:768ae9838086 2218 if (IS_HEX(ch) || ch == ':' || ch == '.') {
star297 0:768ae9838086 2219 return s_http_host_v6;
star297 0:768ae9838086 2220 }
star297 0:768ae9838086 2221
star297 0:768ae9838086 2222 if (s == s_http_host_v6 && ch == '%') {
star297 0:768ae9838086 2223 return s_http_host_v6_zone_start;
star297 0:768ae9838086 2224 }
star297 0:768ae9838086 2225 break;
star297 0:768ae9838086 2226
star297 0:768ae9838086 2227 case s_http_host_v6_zone:
star297 0:768ae9838086 2228 if (ch == ']') {
star297 0:768ae9838086 2229 return s_http_host_v6_end;
star297 0:768ae9838086 2230 }
star297 0:768ae9838086 2231
star297 0:768ae9838086 2232 /* FALLTHROUGH */
star297 0:768ae9838086 2233 case s_http_host_v6_zone_start:
star297 0:768ae9838086 2234 /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
star297 0:768ae9838086 2235 if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
star297 0:768ae9838086 2236 ch == '~') {
star297 0:768ae9838086 2237 return s_http_host_v6_zone;
star297 0:768ae9838086 2238 }
star297 0:768ae9838086 2239 break;
star297 0:768ae9838086 2240
star297 0:768ae9838086 2241 case s_http_host_port:
star297 0:768ae9838086 2242 case s_http_host_port_start:
star297 0:768ae9838086 2243 if (IS_NUM(ch)) {
star297 0:768ae9838086 2244 return s_http_host_port;
star297 0:768ae9838086 2245 }
star297 0:768ae9838086 2246
star297 0:768ae9838086 2247 break;
star297 0:768ae9838086 2248
star297 0:768ae9838086 2249 default:
star297 0:768ae9838086 2250 break;
star297 0:768ae9838086 2251 }
star297 0:768ae9838086 2252 return s_http_host_dead;
star297 0:768ae9838086 2253 }
star297 0:768ae9838086 2254
star297 0:768ae9838086 2255 static int
star297 0:768ae9838086 2256 http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
star297 0:768ae9838086 2257 enum http_host_state s;
star297 0:768ae9838086 2258
star297 0:768ae9838086 2259 const char *p;
star297 0:768ae9838086 2260 uint32_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
star297 0:768ae9838086 2261
star297 0:768ae9838086 2262 assert(u->field_set & (1 << UF_HOST));
star297 0:768ae9838086 2263
star297 0:768ae9838086 2264 u->field_data[UF_HOST].len = 0;
star297 0:768ae9838086 2265
star297 0:768ae9838086 2266 s = found_at ? s_http_userinfo_start : s_http_host_start;
star297 0:768ae9838086 2267
star297 0:768ae9838086 2268 for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
star297 0:768ae9838086 2269 enum http_host_state new_s = http_parse_host_char(s, *p);
star297 0:768ae9838086 2270
star297 0:768ae9838086 2271 if (new_s == s_http_host_dead) {
star297 0:768ae9838086 2272 return 1;
star297 0:768ae9838086 2273 }
star297 0:768ae9838086 2274
star297 0:768ae9838086 2275 switch(new_s) {
star297 0:768ae9838086 2276 case s_http_host:
star297 0:768ae9838086 2277 if (s != s_http_host) {
star297 0:768ae9838086 2278 u->field_data[UF_HOST].off = p - buf;
star297 0:768ae9838086 2279 }
star297 0:768ae9838086 2280 u->field_data[UF_HOST].len++;
star297 0:768ae9838086 2281 break;
star297 0:768ae9838086 2282
star297 0:768ae9838086 2283 case s_http_host_v6:
star297 0:768ae9838086 2284 if (s != s_http_host_v6) {
star297 0:768ae9838086 2285 u->field_data[UF_HOST].off = p - buf;
star297 0:768ae9838086 2286 }
star297 0:768ae9838086 2287 u->field_data[UF_HOST].len++;
star297 0:768ae9838086 2288 break;
star297 0:768ae9838086 2289
star297 0:768ae9838086 2290 case s_http_host_v6_zone_start:
star297 0:768ae9838086 2291 case s_http_host_v6_zone:
star297 0:768ae9838086 2292 u->field_data[UF_HOST].len++;
star297 0:768ae9838086 2293 break;
star297 0:768ae9838086 2294
star297 0:768ae9838086 2295 case s_http_host_port:
star297 0:768ae9838086 2296 if (s != s_http_host_port) {
star297 0:768ae9838086 2297 u->field_data[UF_PORT].off = p - buf;
star297 0:768ae9838086 2298 u->field_data[UF_PORT].len = 0;
star297 0:768ae9838086 2299 u->field_set |= (1 << UF_PORT);
star297 0:768ae9838086 2300 }
star297 0:768ae9838086 2301 u->field_data[UF_PORT].len++;
star297 0:768ae9838086 2302 break;
star297 0:768ae9838086 2303
star297 0:768ae9838086 2304 case s_http_userinfo:
star297 0:768ae9838086 2305 if (s != s_http_userinfo) {
star297 0:768ae9838086 2306 u->field_data[UF_USERINFO].off = p - buf ;
star297 0:768ae9838086 2307 u->field_data[UF_USERINFO].len = 0;
star297 0:768ae9838086 2308 u->field_set |= (1 << UF_USERINFO);
star297 0:768ae9838086 2309 }
star297 0:768ae9838086 2310 u->field_data[UF_USERINFO].len++;
star297 0:768ae9838086 2311 break;
star297 0:768ae9838086 2312
star297 0:768ae9838086 2313 default:
star297 0:768ae9838086 2314 break;
star297 0:768ae9838086 2315 }
star297 0:768ae9838086 2316 s = new_s;
star297 0:768ae9838086 2317 }
star297 0:768ae9838086 2318
star297 0:768ae9838086 2319 /* Make sure we don't end somewhere unexpected */
star297 0:768ae9838086 2320 switch (s) {
star297 0:768ae9838086 2321 case s_http_host_start:
star297 0:768ae9838086 2322 case s_http_host_v6_start:
star297 0:768ae9838086 2323 case s_http_host_v6:
star297 0:768ae9838086 2324 case s_http_host_v6_zone_start:
star297 0:768ae9838086 2325 case s_http_host_v6_zone:
star297 0:768ae9838086 2326 case s_http_host_port_start:
star297 0:768ae9838086 2327 case s_http_userinfo:
star297 0:768ae9838086 2328 case s_http_userinfo_start:
star297 0:768ae9838086 2329 return 1;
star297 0:768ae9838086 2330 default:
star297 0:768ae9838086 2331 break;
star297 0:768ae9838086 2332 }
star297 0:768ae9838086 2333
star297 0:768ae9838086 2334 return 0;
star297 0:768ae9838086 2335 }
star297 0:768ae9838086 2336
star297 0:768ae9838086 2337 void
star297 0:768ae9838086 2338 http_parser_url_init(struct http_parser_url *u) {
star297 0:768ae9838086 2339 memset(u, 0, sizeof(*u));
star297 0:768ae9838086 2340 }
star297 0:768ae9838086 2341
star297 0:768ae9838086 2342 int
star297 0:768ae9838086 2343 http_parser_parse_url(const char *buf, uint32_t buflen, int is_connect,
star297 0:768ae9838086 2344 struct http_parser_url *u)
star297 0:768ae9838086 2345 {
star297 0:768ae9838086 2346 enum state s;
star297 0:768ae9838086 2347 const char *p;
star297 0:768ae9838086 2348 enum http_parser_url_fields uf, old_uf;
star297 0:768ae9838086 2349 int found_at = 0;
star297 0:768ae9838086 2350
star297 0:768ae9838086 2351 u->port = u->field_set = 0;
star297 0:768ae9838086 2352 s = is_connect ? s_req_server_start : s_req_spaces_before_url;
star297 0:768ae9838086 2353 old_uf = UF_MAX;
star297 0:768ae9838086 2354
star297 0:768ae9838086 2355 for (p = buf; p < buf + buflen; p++) {
star297 0:768ae9838086 2356 s = parse_url_char(s, *p);
star297 0:768ae9838086 2357
star297 0:768ae9838086 2358 /* Figure out the next field that we're operating on */
star297 0:768ae9838086 2359 switch (s) {
star297 0:768ae9838086 2360 case s_dead:
star297 0:768ae9838086 2361 return 1;
star297 0:768ae9838086 2362
star297 0:768ae9838086 2363 /* Skip delimeters */
star297 0:768ae9838086 2364 case s_req_schema_slash:
star297 0:768ae9838086 2365 case s_req_schema_slash_slash:
star297 0:768ae9838086 2366 case s_req_server_start:
star297 0:768ae9838086 2367 case s_req_query_string_start:
star297 0:768ae9838086 2368 case s_req_fragment_start:
star297 0:768ae9838086 2369 continue;
star297 0:768ae9838086 2370
star297 0:768ae9838086 2371 case s_req_schema:
star297 0:768ae9838086 2372 uf = UF_SCHEMA;
star297 0:768ae9838086 2373 break;
star297 0:768ae9838086 2374
star297 0:768ae9838086 2375 case s_req_server_with_at:
star297 0:768ae9838086 2376 found_at = 1;
star297 0:768ae9838086 2377
star297 0:768ae9838086 2378 /* FALLTROUGH */
star297 0:768ae9838086 2379 case s_req_server:
star297 0:768ae9838086 2380 uf = UF_HOST;
star297 0:768ae9838086 2381 break;
star297 0:768ae9838086 2382
star297 0:768ae9838086 2383 case s_req_path:
star297 0:768ae9838086 2384 uf = UF_PATH;
star297 0:768ae9838086 2385 break;
star297 0:768ae9838086 2386
star297 0:768ae9838086 2387 case s_req_query_string:
star297 0:768ae9838086 2388 uf = UF_QUERY;
star297 0:768ae9838086 2389 break;
star297 0:768ae9838086 2390
star297 0:768ae9838086 2391 case s_req_fragment:
star297 0:768ae9838086 2392 uf = UF_FRAGMENT;
star297 0:768ae9838086 2393 break;
star297 0:768ae9838086 2394
star297 0:768ae9838086 2395 default:
star297 0:768ae9838086 2396 assert(!"Unexpected state");
star297 0:768ae9838086 2397 return 1;
star297 0:768ae9838086 2398 }
star297 0:768ae9838086 2399
star297 0:768ae9838086 2400 /* Nothing's changed; soldier on */
star297 0:768ae9838086 2401 if (uf == old_uf) {
star297 0:768ae9838086 2402 u->field_data[uf].len++;
star297 0:768ae9838086 2403 continue;
star297 0:768ae9838086 2404 }
star297 0:768ae9838086 2405
star297 0:768ae9838086 2406 u->field_data[uf].off = p - buf;
star297 0:768ae9838086 2407 u->field_data[uf].len = 1;
star297 0:768ae9838086 2408
star297 0:768ae9838086 2409 u->field_set |= (1 << uf);
star297 0:768ae9838086 2410 old_uf = uf;
star297 0:768ae9838086 2411 }
star297 0:768ae9838086 2412
star297 0:768ae9838086 2413 /* host must be present if there is a schema */
star297 0:768ae9838086 2414 /* parsing http:///toto will fail */
star297 0:768ae9838086 2415 if ((u->field_set & (1 << UF_SCHEMA)) &&
star297 0:768ae9838086 2416 (u->field_set & (1 << UF_HOST)) == 0) {
star297 0:768ae9838086 2417 return 1;
star297 0:768ae9838086 2418 }
star297 0:768ae9838086 2419
star297 0:768ae9838086 2420 if (u->field_set & (1 << UF_HOST)) {
star297 0:768ae9838086 2421 if (http_parse_host(buf, u, found_at) != 0) {
star297 0:768ae9838086 2422 return 1;
star297 0:768ae9838086 2423 }
star297 0:768ae9838086 2424 }
star297 0:768ae9838086 2425
star297 0:768ae9838086 2426 /* CONNECT requests can only contain "hostname:port" */
star297 0:768ae9838086 2427 if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
star297 0:768ae9838086 2428 return 1;
star297 0:768ae9838086 2429 }
star297 0:768ae9838086 2430
star297 0:768ae9838086 2431 if (u->field_set & (1 << UF_PORT)) {
star297 0:768ae9838086 2432 /* Don't bother with endp; we've already validated the string */
star297 0:768ae9838086 2433 unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
star297 0:768ae9838086 2434
star297 0:768ae9838086 2435 /* Ports have a max value of 2^16 */
star297 0:768ae9838086 2436 if (v > 0xffff) {
star297 0:768ae9838086 2437 return 1;
star297 0:768ae9838086 2438 }
star297 0:768ae9838086 2439
star297 0:768ae9838086 2440 u->port = (uint16_t) v;
star297 0:768ae9838086 2441 }
star297 0:768ae9838086 2442
star297 0:768ae9838086 2443 return 0;
star297 0:768ae9838086 2444 }
star297 0:768ae9838086 2445
star297 0:768ae9838086 2446 void
star297 0:768ae9838086 2447 http_parser_pause(http_parser *parser, int paused) {
star297 0:768ae9838086 2448 /* Users should only be pausing/unpausing a parser that is not in an error
star297 0:768ae9838086 2449 * state. In non-debug builds, there's not much that we can do about this
star297 0:768ae9838086 2450 * other than ignore it.
star297 0:768ae9838086 2451 */
star297 0:768ae9838086 2452 if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
star297 0:768ae9838086 2453 HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
star297 0:768ae9838086 2454 SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
star297 0:768ae9838086 2455 } else {
star297 0:768ae9838086 2456 assert(0 && "Attempting to pause parser in error state");
star297 0:768ae9838086 2457 }
star297 0:768ae9838086 2458 }
star297 0:768ae9838086 2459
star297 0:768ae9838086 2460 int
star297 0:768ae9838086 2461 http_body_is_final(const struct http_parser *parser) {
star297 0:768ae9838086 2462 return parser->state == s_message_done;
star297 0:768ae9838086 2463 }
star297 0:768ae9838086 2464
star297 0:768ae9838086 2465 unsigned long
star297 0:768ae9838086 2466 http_parser_version(void) {
star297 0:768ae9838086 2467 return HTTP_PARSER_VERSION_MAJOR * 0x10000 |
star297 0:768ae9838086 2468 HTTP_PARSER_VERSION_MINOR * 0x00100 |
star297 0:768ae9838086 2469 HTTP_PARSER_VERSION_PATCH * 0x00001;
star297 0:768ae9838086 2470 }