pico_http_parser 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -27,6 +27,12 @@
27
27
  #ifndef picohttpparser_h
28
28
  #define picohttpparser_h
29
29
 
30
+ #include <sys/types.h>
31
+
32
+ #ifdef _MSC_VER
33
+ #define ssize_t intptr_t
34
+ #endif
35
+
30
36
  /* $Id$ */
31
37
 
32
38
  #ifdef __cplusplus
@@ -36,25 +42,42 @@ extern "C" {
36
42
  /* contains name and value of a header (name == NULL if is a continuing line
37
43
  * of a multiline header */
38
44
  struct phr_header {
39
- const char* name;
40
- size_t name_len;
41
- const char* value;
42
- size_t value_len;
45
+ const char *name;
46
+ size_t name_len;
47
+ const char *value;
48
+ size_t value_len;
43
49
  };
44
50
 
45
- /* returns number of bytes cosumed if successful, -2 if request is partial,
51
+ /* returns number of bytes consumed if successful, -2 if request is partial,
46
52
  * -1 if failed */
47
- int phr_parse_request(const char* buf, size_t len, const char** method,
48
- size_t* method_len, const char** path,
49
- size_t* path_len, int* minor_version,
50
- struct phr_header* headers, size_t* num_headers,
51
- size_t last_len);
53
+ int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len,
54
+ int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len);
55
+
56
+ /* ditto */
57
+ int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
58
+ struct phr_header *headers, size_t *num_headers, size_t last_len);
52
59
 
53
60
  /* ditto */
54
- int phr_parse_response(const char* _buf, size_t len, int *minor_version,
55
- int *status, const char **msg, size_t *msg_len,
56
- struct phr_header* headers, size_t* num_headers,
57
- size_t last_len);
61
+ int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len);
62
+
63
+ /* should be zero-filled before start */
64
+ struct phr_chunked_decoder {
65
+ size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
66
+ char consume_trailer; /* if trailing headers should be consumed */
67
+ char _hex_count;
68
+ char _state;
69
+ };
70
+
71
+ /* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
72
+ * encoding headers. When the function returns without an error, bufsz is
73
+ * updated to the length of the decoded data available. Applications should
74
+ * repeatedly call the function while it returns -2 (incomplete) every time
75
+ * supplying newly arrived data. If the end of the chunked-encoded data is
76
+ * found, the function returns a non-negative number indicating the number of
77
+ * octets left undecoded at the tail of the supplied buffer. Returns -1 on
78
+ * error.
79
+ */
80
+ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz);
58
81
 
59
82
  #ifdef __cplusplus
60
83
  }
@@ -26,228 +26,390 @@
26
26
  */
27
27
 
28
28
  #include <stdio.h>
29
+ #include <stdlib.h>
29
30
  #include <string.h>
30
31
  #include "picotest/picotest.h"
31
32
  #include "picohttpparser.h"
32
33
 
33
- static int bufis(const char* s, size_t l, const char* t)
34
+ static int bufis(const char *s, size_t l, const char *t)
34
35
  {
35
- return strlen(t) == l && memcmp(s, t, l) == 0;
36
+ return strlen(t) == l && memcmp(s, t, l) == 0;
36
37
  }
37
38
 
38
39
  static void test_request(void)
39
40
  {
40
- const char* method;
41
- size_t method_len;
42
- const char* path;
43
- size_t path_len;
44
- int minor_version;
45
- struct phr_header headers[4];
46
- size_t num_headers;
47
-
48
- #define PARSE(s, last_len, exp, comment) \
49
- do { \
50
- note(comment); \
51
- num_headers = sizeof(headers) / sizeof(headers[0]); \
52
- ok(phr_parse_request(s, sizeof(s) - 1, &method, &method_len, &path, \
53
- &path_len, &minor_version, headers, \
54
- &num_headers, last_len) \
55
- == (exp == 0 ? strlen(s) : exp)); \
56
- } while (0)
57
-
58
- PARSE("GET / HTTP/1.0\r\n\r\n", 0, 0, "simple");
59
- ok(num_headers == 0);
60
- ok(bufis(method, method_len, "GET"));
61
- ok(bufis(path, path_len, "/"));
62
- ok(minor_version == 0);
63
-
64
- PARSE("GET / HTTP/1.0\r\n\r", 0, -2, "partial");
65
-
66
- PARSE("GET /hoge HTTP/1.1\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0,
67
- "parse headers");
68
- ok(num_headers == 2);
69
- ok(bufis(method, method_len, "GET"));
70
- ok(bufis(path, path_len, "/hoge"));
71
- ok(minor_version == 1);
72
- ok(bufis(headers[0].name, headers[0].name_len, "Host"));
73
- ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
74
- ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
75
- ok(bufis(headers[1].value, headers[1].value_len, ""));
76
-
77
- PARSE("GET /hoge HTTP/1.1\r\nHost: example.com\r\nUser-Agent: \343\201\262\343/1.0\r\n\r\n", 0, 0,
78
- "multibyte included");
79
- ok(num_headers == 2);
80
- ok(bufis(method, method_len, "GET"));
81
- ok(bufis(path, path_len, "/hoge"));
82
- ok(minor_version == 1);
83
- ok(bufis(headers[0].name, headers[0].name_len, "Host"));
84
- ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
85
- ok(bufis(headers[1].name, headers[1].name_len, "User-Agent"));
86
- ok(bufis(headers[1].value, headers[1].value_len, "\343\201\262\343/1.0"));
87
-
88
- PARSE("GET / HTTP/1.0\r\nfoo: \r\nfoo: b\r\n \tc\r\n\r\n", 0, 0,
89
- "parse multiline");
90
- ok(num_headers == 3);
91
- ok(bufis(method, method_len, "GET"));
92
- ok(bufis(path, path_len, "/"));
93
- ok(minor_version == 0);
94
- ok(bufis(headers[0].name, headers[0].name_len, "foo"));
95
- ok(bufis(headers[0].value, headers[0].value_len, ""));
96
- ok(bufis(headers[1].name, headers[1].name_len, "foo"));
97
- ok(bufis(headers[1].value, headers[1].value_len, "b"));
98
- ok(headers[2].name == NULL);
99
- ok(bufis(headers[2].value, headers[2].value_len, " \tc"));
100
-
101
- PARSE("GET / HTTP/1.0\r\nfoo : ab\r\n\r\n", 0, 0,
102
- "parse header name with trailing space");
103
- ok(num_headers == 1);
104
- ok(bufis(method, method_len, "GET"));
105
- ok(bufis(path, path_len, "/"));
106
- ok(minor_version == 0);
107
- ok(bufis(headers[0].name, headers[0].name_len, "foo "));
108
- ok(bufis(headers[0].value, headers[0].value_len, "ab"));
109
-
110
- PARSE("GET", 0, -2, "incomplete 1");
111
- ok(method == NULL);
112
- PARSE("GET ", 0, -2, "incomplete 2");
113
- ok(bufis(method, method_len, "GET"));
114
- PARSE("GET /", 0, -2, "incomplete 3");
115
- ok(path == NULL);
116
- PARSE("GET / ", 0, -2, "incomplete 4");
117
- ok(bufis(path, path_len, "/"));
118
- PARSE("GET / H", 0, -2, "incomplete 5");
119
- PARSE("GET / HTTP/1.", 0, -2, "incomplete 6");
120
- PARSE("GET / HTTP/1.0", 0, -2, "incomplete 7");
121
- ok(minor_version == -1);
122
- PARSE("GET / HTTP/1.0\r", 0, -2, "incomplete 8");
123
- ok(minor_version == 0);
124
-
125
- PARSE("GET /hoge HTTP/1.0\r\n\r", strlen("GET /hoge HTTP/1.0\r\n\r") - 1,
126
- -2, "slowloris (incomplete)");
127
- PARSE("GET /hoge HTTP/1.0\r\n\r\n", strlen("GET /hoge HTTP/1.0\r\n\r\n") - 1,
128
- 0, "slowloris (complete)");
129
-
130
- PARSE("GET / HTTP/1.0\r\n:a\r\n\r\n", 0, -1, "empty header name");
131
- PARSE("GET / HTTP/1.0\r\n :a\r\n\r\n", 0, -1, "header name (space only)");
132
-
133
- PARSE("G\0T / HTTP/1.0\r\n\r\n", 0, -1, "NUL in method");
134
- PARSE("G\tT / HTTP/1.0\r\n\r\n", 0, -1, "tab in method");
135
- PARSE("GET /\x7fhello HTTP/1.0\r\n\r\n", 0, -1, "DEL in uri-path");
136
- PARSE("GET / HTTP/1.0\r\na\0b: c\r\n\r\n", 0, -1, "NUL in header name");
137
- PARSE("GET / HTTP/1.0\r\nab: c\0d\r\n\r\n", 0, -1, "NUL in header value");
138
- PARSE("GET / HTTP/1.0\r\na\033b: c\r\n\r\n", 0, -1, "CTL in header name");
139
- PARSE("GET / HTTP/1.0\r\nab: c\033\r\n\r\n", 0, -1, "CTL in header value");
140
- PARSE("GET /\xa0 HTTP/1.0\r\nh: c\xa2y\r\n\r\n", 0, 0, "accept MSB chars");
141
- ok(num_headers == 1);
142
- ok(bufis(method, method_len, "GET"));
143
- ok(bufis(path, path_len, "/\xa0"));
144
- ok(minor_version == 0);
145
- ok(bufis(headers[0].name, headers[0].name_len, "h"));
146
- ok(bufis(headers[0].value, headers[0].value_len, "c\xa2y"));
41
+ const char *method;
42
+ size_t method_len;
43
+ const char *path;
44
+ size_t path_len;
45
+ int minor_version;
46
+ struct phr_header headers[4];
47
+ size_t num_headers;
48
+
49
+ #define PARSE(s, last_len, exp, comment) \
50
+ do { \
51
+ note(comment); \
52
+ num_headers = sizeof(headers) / sizeof(headers[0]); \
53
+ ok(phr_parse_request(s, sizeof(s) - 1, &method, &method_len, &path, &path_len, &minor_version, headers, &num_headers, \
54
+ last_len) == (exp == 0 ? strlen(s) : exp)); \
55
+ } while (0)
56
+
57
+ PARSE("GET / HTTP/1.0\r\n\r\n", 0, 0, "simple");
58
+ ok(num_headers == 0);
59
+ ok(bufis(method, method_len, "GET"));
60
+ ok(bufis(path, path_len, "/"));
61
+ ok(minor_version == 0);
62
+
63
+ PARSE("GET / HTTP/1.0\r\n\r", 0, -2, "partial");
64
+
65
+ PARSE("GET /hoge HTTP/1.1\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0, "parse headers");
66
+ ok(num_headers == 2);
67
+ ok(bufis(method, method_len, "GET"));
68
+ ok(bufis(path, path_len, "/hoge"));
69
+ ok(minor_version == 1);
70
+ ok(bufis(headers[0].name, headers[0].name_len, "Host"));
71
+ ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
72
+ ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
73
+ ok(bufis(headers[1].value, headers[1].value_len, ""));
74
+
75
+ PARSE("GET /hoge HTTP/1.1\r\nHost: example.com\r\nUser-Agent: \343\201\262\343/1.0\r\n\r\n", 0, 0, "multibyte included");
76
+ ok(num_headers == 2);
77
+ ok(bufis(method, method_len, "GET"));
78
+ ok(bufis(path, path_len, "/hoge"));
79
+ ok(minor_version == 1);
80
+ ok(bufis(headers[0].name, headers[0].name_len, "Host"));
81
+ ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
82
+ ok(bufis(headers[1].name, headers[1].name_len, "User-Agent"));
83
+ ok(bufis(headers[1].value, headers[1].value_len, "\343\201\262\343/1.0"));
84
+
85
+ PARSE("GET / HTTP/1.0\r\nfoo: \r\nfoo: b\r\n \tc\r\n\r\n", 0, 0, "parse multiline");
86
+ ok(num_headers == 3);
87
+ ok(bufis(method, method_len, "GET"));
88
+ ok(bufis(path, path_len, "/"));
89
+ ok(minor_version == 0);
90
+ ok(bufis(headers[0].name, headers[0].name_len, "foo"));
91
+ ok(bufis(headers[0].value, headers[0].value_len, ""));
92
+ ok(bufis(headers[1].name, headers[1].name_len, "foo"));
93
+ ok(bufis(headers[1].value, headers[1].value_len, "b"));
94
+ ok(headers[2].name == NULL);
95
+ ok(bufis(headers[2].value, headers[2].value_len, " \tc"));
96
+
97
+ PARSE("GET / HTTP/1.0\r\nfoo : ab\r\n\r\n", 0, 0, "parse header name with trailing space");
98
+ ok(num_headers == 1);
99
+ ok(bufis(method, method_len, "GET"));
100
+ ok(bufis(path, path_len, "/"));
101
+ ok(minor_version == 0);
102
+ ok(bufis(headers[0].name, headers[0].name_len, "foo "));
103
+ ok(bufis(headers[0].value, headers[0].value_len, "ab"));
104
+
105
+ PARSE("GET", 0, -2, "incomplete 1");
106
+ ok(method == NULL);
107
+ PARSE("GET ", 0, -2, "incomplete 2");
108
+ ok(bufis(method, method_len, "GET"));
109
+ PARSE("GET /", 0, -2, "incomplete 3");
110
+ ok(path == NULL);
111
+ PARSE("GET / ", 0, -2, "incomplete 4");
112
+ ok(bufis(path, path_len, "/"));
113
+ PARSE("GET / H", 0, -2, "incomplete 5");
114
+ PARSE("GET / HTTP/1.", 0, -2, "incomplete 6");
115
+ PARSE("GET / HTTP/1.0", 0, -2, "incomplete 7");
116
+ ok(minor_version == -1);
117
+ PARSE("GET / HTTP/1.0\r", 0, -2, "incomplete 8");
118
+ ok(minor_version == 0);
119
+
120
+ PARSE("GET /hoge HTTP/1.0\r\n\r", strlen("GET /hoge HTTP/1.0\r\n\r") - 1, -2, "slowloris (incomplete)");
121
+ PARSE("GET /hoge HTTP/1.0\r\n\r\n", strlen("GET /hoge HTTP/1.0\r\n\r\n") - 1, 0, "slowloris (complete)");
122
+
123
+ PARSE("GET / HTTP/1.0\r\n:a\r\n\r\n", 0, -1, "empty header name");
124
+ PARSE("GET / HTTP/1.0\r\n :a\r\n\r\n", 0, -1, "header name (space only)");
125
+
126
+ PARSE("G\0T / HTTP/1.0\r\n\r\n", 0, -1, "NUL in method");
127
+ PARSE("G\tT / HTTP/1.0\r\n\r\n", 0, -1, "tab in method");
128
+ PARSE("GET /\x7fhello HTTP/1.0\r\n\r\n", 0, -1, "DEL in uri-path");
129
+ PARSE("GET / HTTP/1.0\r\na\0b: c\r\n\r\n", 0, -1, "NUL in header name");
130
+ PARSE("GET / HTTP/1.0\r\nab: c\0d\r\n\r\n", 0, -1, "NUL in header value");
131
+ PARSE("GET / HTTP/1.0\r\na\033b: c\r\n\r\n", 0, -1, "CTL in header name");
132
+ PARSE("GET / HTTP/1.0\r\nab: c\033\r\n\r\n", 0, -1, "CTL in header value");
133
+ PARSE("GET /\xa0 HTTP/1.0\r\nh: c\xa2y\r\n\r\n", 0, 0, "accept MSB chars");
134
+ ok(num_headers == 1);
135
+ ok(bufis(method, method_len, "GET"));
136
+ ok(bufis(path, path_len, "/\xa0"));
137
+ ok(minor_version == 0);
138
+ ok(bufis(headers[0].name, headers[0].name_len, "h"));
139
+ ok(bufis(headers[0].value, headers[0].value_len, "c\xa2y"));
147
140
 
148
141
  #undef PARSE
149
142
  }
150
143
 
151
144
  static void test_response(void)
152
145
  {
153
- int minor_version;
154
- int status;
155
- const char *msg;
156
- size_t msg_len;
157
- struct phr_header headers[4];
158
- size_t num_headers;
159
-
160
- #define PARSE(s, last_len, exp, comment) \
161
- do { \
162
- note(comment); \
163
- num_headers = sizeof(headers) / sizeof(headers[0]); \
164
- ok(phr_parse_response(s, strlen(s), &minor_version, &status, \
165
- &msg, &msg_len, headers, \
166
- &num_headers, last_len) \
167
- == (exp == 0 ? strlen(s) : exp)); \
168
- } while (0)
169
-
170
- PARSE("HTTP/1.0 200 OK\r\n\r\n", 0, 0, "simple");
171
- ok(num_headers == 0);
172
- ok(status == 200);
173
- ok(minor_version = 1);
174
- ok(bufis(msg, msg_len, "OK"));
175
-
176
- PARSE("HTTP/1.0 200 OK\r\n\r", 0, -2, "partial");
177
-
178
- PARSE("HTTP/1.1 200 OK\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0,
179
- "parse headers");
180
- ok(num_headers == 2);
181
- ok(minor_version == 1);
182
- ok(status == 200);
183
- ok(bufis(msg, msg_len, "OK"));
184
- ok(bufis(headers[0].name, headers[0].name_len, "Host"));
185
- ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
186
- ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
187
- ok(bufis(headers[1].value, headers[1].value_len, ""));
188
-
189
- PARSE("HTTP/1.0 200 OK\r\nfoo: \r\nfoo: b\r\n \tc\r\n\r\n", 0, 0,
190
- "parse multiline");
191
- ok(num_headers == 3);
192
- ok(minor_version == 0);
193
- ok(status == 200);
194
- ok(bufis(msg, msg_len, "OK"));
195
- ok(bufis(headers[0].name, headers[0].name_len, "foo"));
196
- ok(bufis(headers[0].value, headers[0].value_len, ""));
197
- ok(bufis(headers[1].name, headers[1].name_len, "foo"));
198
- ok(bufis(headers[1].value, headers[1].value_len, "b"));
199
- ok(headers[2].name == NULL);
200
- ok(bufis(headers[2].value, headers[2].value_len, " \tc"));
201
-
202
- PARSE("HTTP/1.0 500 Internal Server Error\r\n\r\n", 0, 0,
203
- "internal server error");
204
- ok(num_headers == 0);
205
- ok(minor_version == 0);
206
- ok(status == 500);
207
- ok(bufis(msg, msg_len, "Internal Server Error"));
208
- ok(msg_len == sizeof("Internal Server Error")-1);
209
-
210
- PARSE("H", 0, -2, "incomplete 1");
211
- PARSE("HTTP/1.", 0, -2, "incomplete 2");
212
- PARSE("HTTP/1.1", 0, -2, "incomplete 3");
213
- ok(minor_version == -1);
214
- PARSE("HTTP/1.1 ", 0, -2, "incomplete 4");
215
- ok(minor_version == 1);
216
- PARSE("HTTP/1.1 2", 0, -2, "incomplete 5");
217
- PARSE("HTTP/1.1 200", 0, -2, "incomplete 6");
218
- ok(status == 0);
219
- PARSE("HTTP/1.1 200 ", 0, -2, "incomplete 7");
220
- ok(status == 200);
221
- PARSE("HTTP/1.1 200 O", 0, -2, "incomplete 8");
222
- PARSE("HTTP/1.1 200 OK\r", 0, -2, "incomplete 9");
223
- ok(msg == NULL);
224
- PARSE("HTTP/1.1 200 OK\r\n", 0, -2, "incomplete 10");
225
- ok(bufis(msg, msg_len, "OK"));
226
- PARSE("HTTP/1.1 200 OK\n", 0, -2, "incomplete 11");
227
- ok(bufis(msg, msg_len, "OK"));
228
-
229
- PARSE("HTTP/1.1 200 OK\r\nA: 1\r", 0, -2, "incomplete 11");
230
- ok(num_headers == 0);
231
- PARSE("HTTP/1.1 200 OK\r\nA: 1\r\n", 0, -2, "incomplete 12");
232
- ok(num_headers == 1);
233
- ok(bufis(headers[0].name, headers[0].name_len, "A"));
234
- ok(bufis(headers[0].value, headers[0].value_len, "1"));
235
-
236
- PARSE("HTTP/1.0 200 OK\r\n\r", strlen("GET /hoge HTTP/1.0\r\n\r") - 1,
237
- -2, "slowloris (incomplete)");
238
- PARSE("HTTP/1.0 200 OK\r\n\r\n", strlen("HTTP/1.0 200 OK\r\n\r\n") - 1,
239
- 0, "slowloris (complete)");
240
-
241
- PARSE("HTTP/1. 200 OK\r\n\r\n", 0, -1, "invalid http version");
242
- PARSE("HTTP/1.2z 200 OK\r\n\r\n", 0, -1, "invalid http version 2");
243
- PARSE("HTTP/1.1 OK\r\n\r\n", 0, -1, "no status code");
244
-
146
+ int minor_version;
147
+ int status;
148
+ const char *msg;
149
+ size_t msg_len;
150
+ struct phr_header headers[4];
151
+ size_t num_headers;
152
+
153
+ #define PARSE(s, last_len, exp, comment) \
154
+ do { \
155
+ note(comment); \
156
+ num_headers = sizeof(headers) / sizeof(headers[0]); \
157
+ ok(phr_parse_response(s, strlen(s), &minor_version, &status, &msg, &msg_len, headers, &num_headers, last_len) == \
158
+ (exp == 0 ? strlen(s) : exp)); \
159
+ } while (0)
160
+
161
+ PARSE("HTTP/1.0 200 OK\r\n\r\n", 0, 0, "simple");
162
+ ok(num_headers == 0);
163
+ ok(status == 200);
164
+ ok(minor_version = 1);
165
+ ok(bufis(msg, msg_len, "OK"));
166
+
167
+ PARSE("HTTP/1.0 200 OK\r\n\r", 0, -2, "partial");
168
+
169
+ PARSE("HTTP/1.1 200 OK\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0, "parse headers");
170
+ ok(num_headers == 2);
171
+ ok(minor_version == 1);
172
+ ok(status == 200);
173
+ ok(bufis(msg, msg_len, "OK"));
174
+ ok(bufis(headers[0].name, headers[0].name_len, "Host"));
175
+ ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
176
+ ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
177
+ ok(bufis(headers[1].value, headers[1].value_len, ""));
178
+
179
+ PARSE("HTTP/1.0 200 OK\r\nfoo: \r\nfoo: b\r\n \tc\r\n\r\n", 0, 0, "parse multiline");
180
+ ok(num_headers == 3);
181
+ ok(minor_version == 0);
182
+ ok(status == 200);
183
+ ok(bufis(msg, msg_len, "OK"));
184
+ ok(bufis(headers[0].name, headers[0].name_len, "foo"));
185
+ ok(bufis(headers[0].value, headers[0].value_len, ""));
186
+ ok(bufis(headers[1].name, headers[1].name_len, "foo"));
187
+ ok(bufis(headers[1].value, headers[1].value_len, "b"));
188
+ ok(headers[2].name == NULL);
189
+ ok(bufis(headers[2].value, headers[2].value_len, " \tc"));
190
+
191
+ PARSE("HTTP/1.0 500 Internal Server Error\r\n\r\n", 0, 0, "internal server error");
192
+ ok(num_headers == 0);
193
+ ok(minor_version == 0);
194
+ ok(status == 500);
195
+ ok(bufis(msg, msg_len, "Internal Server Error"));
196
+ ok(msg_len == sizeof("Internal Server Error") - 1);
197
+
198
+ PARSE("H", 0, -2, "incomplete 1");
199
+ PARSE("HTTP/1.", 0, -2, "incomplete 2");
200
+ PARSE("HTTP/1.1", 0, -2, "incomplete 3");
201
+ ok(minor_version == -1);
202
+ PARSE("HTTP/1.1 ", 0, -2, "incomplete 4");
203
+ ok(minor_version == 1);
204
+ PARSE("HTTP/1.1 2", 0, -2, "incomplete 5");
205
+ PARSE("HTTP/1.1 200", 0, -2, "incomplete 6");
206
+ ok(status == 0);
207
+ PARSE("HTTP/1.1 200 ", 0, -2, "incomplete 7");
208
+ ok(status == 200);
209
+ PARSE("HTTP/1.1 200 O", 0, -2, "incomplete 8");
210
+ PARSE("HTTP/1.1 200 OK\r", 0, -2, "incomplete 9");
211
+ ok(msg == NULL);
212
+ PARSE("HTTP/1.1 200 OK\r\n", 0, -2, "incomplete 10");
213
+ ok(bufis(msg, msg_len, "OK"));
214
+ PARSE("HTTP/1.1 200 OK\n", 0, -2, "incomplete 11");
215
+ ok(bufis(msg, msg_len, "OK"));
216
+
217
+ PARSE("HTTP/1.1 200 OK\r\nA: 1\r", 0, -2, "incomplete 11");
218
+ ok(num_headers == 0);
219
+ PARSE("HTTP/1.1 200 OK\r\nA: 1\r\n", 0, -2, "incomplete 12");
220
+ ok(num_headers == 1);
221
+ ok(bufis(headers[0].name, headers[0].name_len, "A"));
222
+ ok(bufis(headers[0].value, headers[0].value_len, "1"));
223
+
224
+ PARSE("HTTP/1.0 200 OK\r\n\r", strlen("HTTP/1.0 200 OK\r\n\r") - 1, -2, "slowloris (incomplete)");
225
+ PARSE("HTTP/1.0 200 OK\r\n\r\n", strlen("HTTP/1.0 200 OK\r\n\r\n") - 1, 0, "slowloris (complete)");
226
+
227
+ PARSE("HTTP/1. 200 OK\r\n\r\n", 0, -1, "invalid http version");
228
+ PARSE("HTTP/1.2z 200 OK\r\n\r\n", 0, -1, "invalid http version 2");
229
+ PARSE("HTTP/1.1 OK\r\n\r\n", 0, -1, "no status code");
230
+
231
+ #undef PARSE
232
+ }
233
+
234
+ static void test_headers(void)
235
+ {
236
+ /* only test the interface; the core parser is tested by the tests above */
237
+
238
+ struct phr_header headers[4];
239
+ size_t num_headers;
240
+
241
+ #define PARSE(s, last_len, exp, comment) \
242
+ do { \
243
+ note(comment); \
244
+ num_headers = sizeof(headers) / sizeof(headers[0]); \
245
+ ok(phr_parse_headers(s, strlen(s), headers, &num_headers, last_len) == (exp == 0 ? strlen(s) : exp)); \
246
+ } while (0)
247
+
248
+ PARSE("Host: example.com\r\nCookie: \r\n\r\n", 0, 0, "simple");
249
+ ok(num_headers == 2);
250
+ ok(bufis(headers[0].name, headers[0].name_len, "Host"));
251
+ ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
252
+ ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
253
+ ok(bufis(headers[1].value, headers[1].value_len, ""));
254
+
255
+ PARSE("Host: example.com\r\nCookie: \r\n\r\n", 1, 0, "slowloris");
256
+ ok(num_headers == 2);
257
+ ok(bufis(headers[0].name, headers[0].name_len, "Host"));
258
+ ok(bufis(headers[0].value, headers[0].value_len, "example.com"));
259
+ ok(bufis(headers[1].name, headers[1].name_len, "Cookie"));
260
+ ok(bufis(headers[1].value, headers[1].value_len, ""));
261
+
262
+ PARSE("Host: example.com\r\nCookie: \r\n\r", 0, -2, "partial");
263
+
264
+ PARSE("Host: e\7fample.com\r\nCookie: \r\n\r", 0, -1, "error");
265
+
245
266
  #undef PARSE
246
267
  }
247
268
 
269
+ static void test_chunked_at_once(int line, int consume_trailer, const char *encoded, const char *decoded, ssize_t expected)
270
+ {
271
+ struct phr_chunked_decoder dec = {0};
272
+ char *buf;
273
+ size_t bufsz;
274
+ ssize_t ret;
275
+
276
+ dec.consume_trailer = consume_trailer;
277
+
278
+ note("testing at-once, source at line %d", line);
279
+
280
+ buf = strdup(encoded);
281
+ bufsz = strlen(buf);
282
+
283
+ ret = phr_decode_chunked(&dec, buf, &bufsz);
284
+
285
+ ok(ret == expected);
286
+ ok(bufsz == strlen(decoded));
287
+ ok(bufis(buf, bufsz, decoded));
288
+ if (expected >= 0) {
289
+ if (ret == expected)
290
+ ok(bufis(buf + bufsz, ret, encoded + strlen(encoded) - ret));
291
+ else
292
+ ok(0);
293
+ }
294
+
295
+ free(buf);
296
+ }
297
+
298
+ static void test_chunked_per_byte(int line, int consume_trailer, const char *encoded, const char *decoded, ssize_t expected)
299
+ {
300
+ struct phr_chunked_decoder dec = {0};
301
+ char *buf = malloc(strlen(encoded) + 1);
302
+ size_t bytes_to_consume = strlen(encoded) - (expected >= 0 ? expected : 0), bytes_ready = 0, bufsz, i;
303
+ ssize_t ret;
304
+
305
+ dec.consume_trailer = consume_trailer;
306
+
307
+ note("testing per-byte, source at line %d", line);
308
+
309
+ for (i = 0; i < bytes_to_consume - 1; ++i) {
310
+ buf[bytes_ready] = encoded[i];
311
+ bufsz = 1;
312
+ ret = phr_decode_chunked(&dec, buf + bytes_ready, &bufsz);
313
+ if (ret != -2) {
314
+ ok(0);
315
+ return;
316
+ }
317
+ bytes_ready += bufsz;
318
+ }
319
+ strcpy(buf + bytes_ready, encoded + bytes_to_consume - 1);
320
+ bufsz = strlen(buf + bytes_ready);
321
+ ret = phr_decode_chunked(&dec, buf + bytes_ready, &bufsz);
322
+ ok(ret == expected);
323
+ bytes_ready += bufsz;
324
+ ok(bytes_ready == strlen(decoded));
325
+ ok(bufis(buf, bytes_ready, decoded));
326
+ if (expected >= 0) {
327
+ if (ret == expected)
328
+ ok(bufis(buf + bytes_ready, expected, encoded + bytes_to_consume));
329
+ else
330
+ ok(0);
331
+ }
332
+
333
+ free(buf);
334
+ }
335
+
336
+ static void test_chunked_failure(int line, const char *encoded, ssize_t expected)
337
+ {
338
+ struct phr_chunked_decoder dec = {0};
339
+ char *buf = strdup(encoded);
340
+ size_t bufsz, i;
341
+ ssize_t ret;
342
+
343
+ note("testing failure at-once, source at line %d", line);
344
+ bufsz = strlen(buf);
345
+ ret = phr_decode_chunked(&dec, buf, &bufsz);
346
+ ok(ret == expected);
347
+
348
+ note("testing failure per-byte, source at line %d", line);
349
+ memset(&dec, 0, sizeof(dec));
350
+ for (i = 0; encoded[i] != '\0'; ++i) {
351
+ buf[0] = encoded[i];
352
+ bufsz = 1;
353
+ ret = phr_decode_chunked(&dec, buf, &bufsz);
354
+ if (ret == -1) {
355
+ ok(ret == expected);
356
+ return;
357
+ } else if (ret == -2) {
358
+ /* continue */
359
+ } else {
360
+ ok(0);
361
+ return;
362
+ }
363
+ }
364
+ ok(ret == expected);
365
+
366
+ free(buf);
367
+ }
368
+
369
+ static void (*chunked_test_runners[])(int, int, const char *, const char *, ssize_t) = {test_chunked_at_once, test_chunked_per_byte,
370
+ NULL};
371
+
372
+ static void test_chunked(void)
373
+ {
374
+ size_t i;
375
+
376
+ for (i = 0; chunked_test_runners[i] != NULL; ++i) {
377
+ chunked_test_runners[i](__LINE__, 0, "b\r\nhello world\r\n0\r\n", "hello world", 0);
378
+ chunked_test_runners[i](__LINE__, 0, "6\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", 0);
379
+ chunked_test_runners[i](__LINE__, 0, "6;comment=hi\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", 0);
380
+ chunked_test_runners[i](__LINE__, 0, "6\r\nhello \r\n5\r\nworld\r\n0\r\na: b\r\nc: d\r\n\r\n", "hello world",
381
+ sizeof("a: b\r\nc: d\r\n\r\n") - 1);
382
+ chunked_test_runners[i](__LINE__, 0, "b\r\nhello world\r\n0\r\n", "hello world", 0);
383
+ }
384
+
385
+ note("failures");
386
+ test_chunked_failure(__LINE__, "z\r\nabcdefg", -1);
387
+ if (sizeof(size_t) == 8) {
388
+ test_chunked_failure(__LINE__, "6\r\nhello \r\nffffffffffffffff\r\nabcdefg", -2);
389
+ test_chunked_failure(__LINE__, "6\r\nhello \r\nfffffffffffffffff\r\nabcdefg", -1);
390
+ }
391
+ }
392
+
393
+ static void test_chunked_consume_trailer(void)
394
+ {
395
+ size_t i;
396
+
397
+ for (i = 0; chunked_test_runners[i] != NULL; ++i) {
398
+ chunked_test_runners[i](__LINE__, 1, "b\r\nhello world\r\n0\r\n", "hello world", -2);
399
+ chunked_test_runners[i](__LINE__, 1, "6\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", -2);
400
+ chunked_test_runners[i](__LINE__, 1, "6;comment=hi\r\nhello \r\n5\r\nworld\r\n0\r\n", "hello world", -2);
401
+ chunked_test_runners[i](__LINE__, 1, "b\r\nhello world\r\n0\r\n\r\n", "hello world", 0);
402
+ chunked_test_runners[i](__LINE__, 1, "b\nhello world\n0\n\n", "hello world", 0);
403
+ chunked_test_runners[i](__LINE__, 1, "6\r\nhello \r\n5\r\nworld\r\n0\r\na: b\r\nc: d\r\n\r\n", "hello world", 0);
404
+ }
405
+ }
406
+
248
407
  int main(int argc, char **argv)
249
408
  {
250
- subtest("request", test_request);
251
- subtest("response", test_response);
252
- return done_testing();
409
+ subtest("request", test_request);
410
+ subtest("response", test_response);
411
+ subtest("headers", test_headers);
412
+ subtest("chunked", test_chunked);
413
+ subtest("chunked-consume-trailer", test_chunked_consume_trailer);
414
+ return done_testing();
253
415
  }