File Coverage

picohttpparser-git/picohttpparser.c
Criterion Covered Total %
statement 88 153 57.5
branch 78 170 45.8
condition n/a
subroutine n/a
pod n/a
total 166 323 51.3


line stmt bran cond sub pod time code
1             #include
2             #include "picohttpparser.h"
3              
4             /* $Id: 2bb2f4c32652b53c0f54838e068c05293c70cfd6 $ */
5              
6             #if __GNUC__ >= 3
7             # define likely(x) __builtin_expect(!!(x), 1)
8             # define unlikely(x) __builtin_expect(!!(x), 0)
9             #else
10             # define likely(x) (x)
11             # define unlikely(x) (x)
12             #endif
13              
14             #define CHECK_EOF() \
15             if (buf == buf_end) { \
16             *ret = -2; \
17             return NULL; \
18             }
19              
20             #define EXPECT_CHAR(ch) \
21             CHECK_EOF(); \
22             if (*buf++ != ch) { \
23             *ret = -1; \
24             return NULL; \
25             }
26              
27             #define ADVANCE_TOKEN(tok, toklen) do { \
28             const char* tok_start = buf; \
29             for (; ; ++buf) { \
30             CHECK_EOF(); \
31             if (*buf == ' ') { \
32             break; \
33             } else if (*buf == '\015' || *buf == '\012') { \
34             *ret = -1; \
35             return NULL; \
36             } \
37             } \
38             tok = tok_start; \
39             toklen = buf - tok_start; \
40             } while (0)
41              
42 383           static const char* get_token_to_eol(const char* buf, const char* buf_end,
43             const char** token, size_t* token_len,
44             int* ret)
45             {
46 383           const char* token_start = buf;
47            
48             while (1) {
49 456 100         if (likely(buf_end - buf >= 16)) {
50             unsigned i;
51 4412 100         for (i = 0; i < 16; i++, ++buf) {
52 4339 100         if (unlikely((unsigned char)*buf <= '\015')
53 299 50         && (*buf == '\015' || *buf == '\012')) {
    0          
54             goto EOL_FOUND;
55             }
56             }
57             } else {
58 366           for (; ; ++buf) {
59 450 50         CHECK_EOF();
60 450 100         if (unlikely((unsigned char)*buf <= '\015')
61 84 50         && (*buf == '\015' || *buf == '\012')) {
    0          
62             goto EOL_FOUND;
63             }
64 366           }
65             }
66 73           }
67             EOL_FOUND:
68 383 50         if (*buf == '\015') {
69 383           ++buf;
70 383 50         EXPECT_CHAR('\012');
    50          
71 383           *token_len = buf - 2 - token_start;
72             } else { /* should be: *buf == '\012' */
73 0           *token_len = buf - token_start;
74 0           ++buf;
75             }
76 383           *token = token_start;
77            
78 383           return buf;
79             }
80            
81 0           static const char* is_complete(const char* buf, const char* buf_end,
82             size_t last_len, int* ret)
83             {
84 0           int ret_cnt = 0;
85 0 0         buf = last_len < 3 ? buf : buf + last_len - 3;
86            
87             while (1) {
88 0 0         CHECK_EOF();
89 0 0         if (*buf == '\015') {
90 0           ++buf;
91 0 0         CHECK_EOF();
92 0 0         EXPECT_CHAR('\012');
    0          
93 0           ++ret_cnt;
94 0 0         } else if (*buf == '\012') {
95 0           ++buf;
96 0           ++ret_cnt;
97             } else {
98 0           ++buf;
99 0           ret_cnt = 0;
100             }
101 0 0         if (ret_cnt == 2) {
102 0           return buf;
103             }
104 0           }
105            
106             *ret = -2;
107             return NULL;
108             }
109              
110             /* *_buf is always within [buf, buf_end) upon success */
111 110           static const char* parse_int(const char* buf, const char* buf_end, int* value,
112             int* ret)
113             {
114             int v;
115 110 50         CHECK_EOF();
116 110 50         if (! ('0' <= *buf && *buf <= '9')) {
    50          
117 0           *ret = -1;
118 0           return NULL;
119             }
120 110           v = 0;
121 110           for (; ; ++buf) {
122 220 50         CHECK_EOF();
123 220 100         if ('0' <= *buf && *buf <= '9') {
    50          
124 110           v = v * 10 + *buf - '0';
125             } else {
126             break;
127             }
128 110           }
129            
130 110           *value = v;
131 110           return buf;
132             }
133              
134             /* returned pointer is always within [buf, buf_end), or null */
135 111           static const char* parse_http_version(const char* buf, const char* buf_end,
136             int* minor_version, int* ret)
137             {
138 111 50         EXPECT_CHAR('H'); EXPECT_CHAR('T'); EXPECT_CHAR('T'); EXPECT_CHAR('P');
    100          
    50          
    50          
    50          
    50          
    50          
    50          
139 110 50         EXPECT_CHAR('/'); EXPECT_CHAR('1'); EXPECT_CHAR('.');
    50          
    50          
    50          
    50          
    50          
140 110           return parse_int(buf, buf_end, minor_version, ret);
141             }
142              
143 110           static const char* parse_headers(const char* buf, const char* buf_end,
144             struct phr_header* headers,
145             size_t* num_headers, size_t max_headers,
146             int* ret)
147             {
148 383           for (; ; ++*num_headers) {
149 493 100         CHECK_EOF();
150 492 100         if (*buf == '\015') {
151 109           ++buf;
152 109 50         EXPECT_CHAR('\012');
    50          
153 109           break;
154 383 50         } else if (*buf == '\012') {
155 0           ++buf;
156 0           break;
157             }
158 383 50         if (*num_headers == max_headers) {
159 0           *ret = -1;
160 0           return NULL;
161             }
162 766 100         if (*num_headers == 0 || ! (*buf == ' ' || *buf == '\t')) {
    50          
    50          
163             /* parsing name, but do not discard SP before colon, see
164             * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
165 383           headers[*num_headers].name = buf;
166 3225           for (; ; ++buf) {
167 3608 50         CHECK_EOF();
168 3608 100         if (*buf == ':') {
169 383           break;
170 3225 50         } else if (*buf < ' ') {
171 0           *ret = -1;
172 0           return NULL;
173             }
174 3225           }
175 383           headers[*num_headers].name_len = buf - headers[*num_headers].name;
176 383           ++buf;
177 383           for (; ; ++buf) {
178 766 50         CHECK_EOF();
179 766 100         if (! (*buf == ' ' || *buf == '\t')) {
    50          
180 383           break;
181             }
182 383           }
183             } else {
184 0           headers[*num_headers].name = NULL;
185 0           headers[*num_headers].name_len = 0;
186             }
187 383 50         if ((buf = get_token_to_eol(buf, buf_end, &headers[*num_headers].value,
188 383           &headers[*num_headers].value_len, ret))
189             == NULL) {
190 0           return NULL;
191             }
192 383           }
193 109           return buf;
194             }
195              
196 111           const char* parse_request(const char* buf, const char* buf_end,
197             const char** method, size_t* method_len,
198             const char** path, size_t* path_len,
199             int* minor_version, struct phr_header* headers,
200             size_t* num_headers, size_t max_headers, int* ret)
201             {
202             /* skip first empty line (some clients add CRLF after POST content) */
203 111 50         CHECK_EOF();
204 111 50         if (*buf == '\015') {
205 0           ++buf;
206 0 0         EXPECT_CHAR('\012');
    0          
207 111 50         } else if (*buf == '\012') {
208 0           ++buf;
209             }
210            
211             /* parse request line */
212 455 50         ADVANCE_TOKEN(*method, *method_len);
    100          
    50          
    50          
213 111           ++buf;
214 783 50         ADVANCE_TOKEN(*path, *path_len);
    100          
    50          
    50          
215 111           ++buf;
216 111 100         if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
217 1           return NULL;
218             }
219 110 50         if (*buf == '\015') {
220 110           ++buf;
221 110 50         EXPECT_CHAR('\012');
    50          
222 0 0         } else if (*buf == '\012') {
223 0           ++buf;
224             } else {
225 0           *ret = -1;
226 0           return NULL;
227             }
228            
229 110           return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
230             }
231              
232 111           int phr_parse_request(const char* buf_start, size_t len, const char** method,
233             size_t* method_len, const char** path, size_t* path_len,
234             int* minor_version, struct phr_header* headers,
235             size_t* num_headers, size_t last_len)
236             {
237 111           const char * buf = buf_start, * buf_end = buf_start + len;
238 111           size_t max_headers = *num_headers;
239             int r;
240            
241 111           *method = NULL;
242 111           *method_len = 0;
243 111           *path = NULL;
244 111           *path_len = 0;
245 111           *minor_version = -1;
246 111           *num_headers = 0;
247            
248             /* if last_len != 0, check if the request is complete (a fast countermeasure
249             againt slowloris */
250 111 50         if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
    0          
251 0           return r;
252             }
253            
254 111 100         if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len,
255             minor_version, headers, num_headers, max_headers,
256             &r))
257             == NULL) {
258 2           return r;
259             }
260            
261 111           return buf - buf_start;
262             }
263              
264 0           static const char* parse_response(const char* buf, const char* buf_end,
265             int* minor_version, int* status,
266             const char** msg, size_t* msg_len,
267             struct phr_header* headers,
268             size_t* num_headers, size_t max_headers,
269             int* ret)
270             {
271             /* parse "HTTP/1.x" */
272 0 0         if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
273 0           return NULL;
274             }
275             /* skip space */
276 0 0         if (*buf++ != ' ') {
277 0           *ret = -1;
278 0           return NULL;
279             }
280             /* parse status code */
281 0 0         if ((buf = parse_int(buf, buf_end, status, ret)) == NULL) {
282 0           return NULL;
283             }
284             /* skip space */
285 0 0         if (*buf++ != ' ') {
286 0           *ret = -1;
287 0           return NULL;
288             }
289             /* get message */
290 0 0         if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
291 0           return NULL;
292             }
293            
294 0           return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
295             }
296              
297 0           int phr_parse_response(const char* buf_start, size_t len, int* minor_version,
298             int* status, const char** msg, size_t* msg_len,
299             struct phr_header* headers, size_t* num_headers,
300             size_t last_len)
301             {
302 0           const char * buf = buf_start, * buf_end = buf + len;
303 0           size_t max_headers = *num_headers;
304             int r;
305            
306 0           *minor_version = -1;
307 0           *status = 0;
308 0           *msg = NULL;
309 0           *msg_len = 0;
310 0           *num_headers = 0;
311            
312             /* if last_len != 0, check if the response is complete (a fast countermeasure
313             against slowloris */
314 0 0         if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
    0          
315 0           return r;
316             }
317            
318 0 0         if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len,
319             headers, num_headers, max_headers, &r))
320             == NULL) {
321 0           return r;
322             }
323            
324 0           return buf - buf_start;
325             }
326              
327             #undef CHECK_EOF
328             #undef EXPECT_CHAR
329             #undef ADVANCE_TOKEN