Merge branch 'fetcher-response-code'
[strongswan.git] / src / libstrongswan / tests / suites / test_fetch_http.c
1 /*
2 * Copyright (C) 2014 Martin Willi
3 * Copyright (C) 2014 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "test_suite.h"
17
18 #include <unistd.h>
19 #include <time.h>
20
21 #define HTTP_SUCCESS(status) ((status) >= 200 && (status) < 300)
22
23 /**
24 * HTTP test definition
25 */
26 typedef struct {
27 /* HTTP Method */
28 char *meth;
29 /* HTTP 1.x minor version */
30 int minor;
31 /* host to connect to */
32 char *host;
33 /* HTTP service port */
34 int port;
35 /* path on host to fetch from */
36 char *path;
37 /* request Content-Type, if any */
38 char *type;
39 /* request data, if any */
40 void *req;
41 /* length of request data */
42 int req_len;
43 /* response data, if any */
44 void *res;
45 /* length of response data */
46 int res_len;
47 /* status code, defaults to 200 */
48 u_int code;
49 } test_service_t;
50
51 static char large[] = {
52 0x88,0x3e,0xa3,0xe3,0x95,0x67,0x53,0x93,0xc8,0xce,0x5c,0xcd,0x8c,0x03,0x0c,0xa8,
53 0x94,0xaf,0x49,0xf6,0xc6,0x50,0xad,0xb8,0xea,0xb8,0x85,0x8a,0xde,0x92,0xe1,0xbc,
54 0xf3,0x15,0xbb,0x5b,0xb8,0x35,0xd8,0x17,0xad,0xcf,0x6b,0x07,0x63,0x61,0x2e,0x2f,
55 0xa5,0xc9,0x1d,0xa7,0xac,0xaa,0x4d,0xde,0x71,0x65,0x95,0x87,0x66,0x50,0xa2,0xa6,
56 0x28,0xef,0x49,0x5c,0x53,0xa3,0x87,0xad,0x42,0xc3,0x41,0xd8,0xfa,0x92,0xd8,0x32,
57 0xce,0x7c,0xf2,0x72,0x2f,0x51,0x27,0x71,0xe3,0x78,0x59,0xf9,0x46,0x23,0xf3,0xa7,
58 0x38,0x12,0x05,0xbb,0x1a,0xb0,0xe0,0x12,0xae,0x97,0xa1,0x0f,0xd4,0x34,0xe0,0x15,
59 0xb4,0xa3,0x15,0x08,0xbe,0xff,0x4d,0x31,0x81,0x39,0x62,0x29,0xf0,0x90,0x79,0x02,
60 0x4d,0x0c,0xf4,0x9e,0xe5,0xd4,0xdc,0xca,0xea,0xb8,0x85,0x8a,0xde,0x92,0xe1,0xbc,
61 0xf3,0x15,0xbb,0x5b,0xb8,0x35,0xd8,0x17,0xad,0xcf,0x6b,0x07,0x63,0x61,0x2e,0x2f,
62 0xa5,0xc9,0x1d,0xa7,0xac,0xaa,0x4d,0xde,0x71,0x65,0x95,0x87,0x66,0x50,0xa2,0xa6,
63 0x28,0xef,0x49,0x5c,0x53,0xa3,0x87,0xad,0x42,0xc3,0x41,0xd8,0xfa,0x92,0xd8,0x32,
64 0xce,0x7c,0xf2,0x72,0x2f,0x51,0x27,0x71,0xe3,0x78,0x59,0xf9,0x46,0x23,0xf3,0xa7,
65 0x38,0x12,0x05,0xbb,0x1a,0xb0,0xe0,0x12,0xae,0x97,0xa1,0x0f,0xd4,0x34,0xe0,0x15,
66 0xf3,0x15,0xbb,0x5b,0xb8,0x35,0xd8,0x17,0xad,0xcf,0x6b,0x07,0x63,0x61,0x2e,0x2f,
67 0xa5,0xc9,0x1d,0xa7,0xac,0xaa,0x4d,0xde,0x71,0x65,0x95,0x87,0x66,0x50,0xa2,0xa6,
68 0x28,0xef,0x49,0x5c,0x53,0xa3,0x87,0xad,0x42,0xc3,0x41,0xd8,0xfa,0x92,0xd8,0x32,
69 0xce,0x7c,0xf2,0x72,0x2f,0x51,0x27,0x71,0xe3,0x78,0x59,0xf9,0x46,0x23,0xf3,0xa7,
70 0x38,0x12,0x05,0xbb,0x1a,0xb0,0xe0,0x12,0xae,0x97,0xa1,0x0f,0xd4,0x34,0xe0,0x15,
71 0xb4,0xa3,0x15,0x08,0xbe,0xff,0x4d,0x31,0x81,0x39,0x62,0x29,0xf0,0x90,0x79,0x02,
72 0x4d,0x0c,0xf4,0x9e,0xe5,0xd4,0xdc,0xca,0xea,0xb8,0x85,0x8a,0xde,0x92,0xe1,0xbc,
73 0xf3,0x15,0xbb,0x5b,0xb8,0x35,0xd8,0x17,0xad,0xcf,0x6b,0x07,0x63,0x61,0x2e,0x2f,
74 0xa5,0xc9,0x1d,0xa7,0xac,0xaa,0x4d,0xde,0x71,0x65,0x95,0x87,0x66,0x50,0xa2,0xa6,
75 0x28,0xef,0x49,0x5c,0x53,0xa3,0x87,0xad,0x42,0xc3,0x41,0xd8,0xfa,0x92,0xd8,0x32,
76 0xce,0x7c,0xf2,0x72,0x2f,0x51,0x27,0x71,0xe3,0x78,0x59,0xf9,0x46,0x23,0xf3,0xa7,
77 0x38,0x12,0x05,0xbb,0x1a,0xb0,0xe0,0x12,0xae,0x97,0xa1,0x0f,0xd4,0x34,0xe0,0x15,
78 0xb4,0xa3,0x15,0x08,0xbe,0xff,0x4d,0x31,0x81,0x39,0x62,0x29,0xf0,0x90,0x79,0x02,
79 0x4d,0x0c,0xf4,0x9e,0xe5,0xd4,0xdc,0xca,0xea,0xb8,0x85,0x8a,0xde,0x92,0xe1,0xbc,
80 };
81
82 static bool servicing(void *data, stream_t *stream)
83 {
84 test_service_t *test = (test_service_t*)data;
85 char buf[1024], hdr[256], *start, *end = NULL, *body = NULL, *type = NULL;
86 struct tm tm;
87 time_t t;
88 ssize_t len, tot = 0;
89 int nr = 0;
90
91 start = buf;
92
93 /* parse method and headers */
94 while (end != start)
95 {
96 len = stream->read(stream, buf + tot, sizeof(buf) - tot, TRUE);
97 ck_assert(len > 0);
98 tot += len;
99
100 while (TRUE)
101 {
102 end = memchr(start, '\n', tot);
103 if (!end)
104 {
105 break;
106 }
107 *end = '\0';
108 ck_assert(end > buf);
109 ck_assert(*(--end) == '\r');
110 *end = '\0';
111 if (end == start)
112 {
113 body = end + strlen("\r\n");
114 break;
115 }
116 switch (nr++)
117 {
118 case 0:
119 snprintf(hdr, sizeof(hdr), "%s %s HTTP/1.%u",
120 test->meth, test->path, test->minor);
121 ck_assert_str_eq(hdr, start);
122 break;
123 default:
124 if (strcasepfx(start, "Content-Length: "))
125 {
126 ck_assert_int_eq(
127 atoi(start + strlen("Content-Length: ")),
128 test->req_len);
129 }
130 if (strcasepfx(start, "Content-Type: "))
131 {
132 type = start + strlen("Content-Type: ");
133 }
134 break;
135 }
136 start = end + strlen("\r\n");
137 }
138 }
139
140 if (test->type)
141 {
142 ck_assert(type);
143 ck_assert_str_eq(type, test->type);
144 }
145
146 /* request body */
147 if (test->req_len)
148 {
149 ck_assert(stream->read_all(stream, buf + tot,
150 test->req_len - (tot - (body - buf))));
151 ck_assert(memeq(body, test->req, test->req_len));
152 }
153
154 if (!test->code)
155 {
156 test->code = 200;
157 }
158
159 /* response headers */
160 snprintf(buf, sizeof(buf), "HTTP/1.%u %u OK\r\n", test->minor, test->code);
161 ck_assert(stream->write_all(stream, buf, strlen(buf)));
162
163 /* if the response code indicates an error the following write operations
164 * might fail because the client already terminated the TCP connection */
165 #define may_fail(test, op) ck_assert(op || !HTTP_SUCCESS(test->code))
166
167 t = time(NULL);
168 gmtime_r(&t, &tm);
169 strftime(buf, sizeof(buf), "%a, %d %b %Y %T %z", &tm);
170 may_fail(test, stream->write_all(stream, buf, strlen(buf)));
171 snprintf(buf, sizeof(buf), "Server: strongSwan unit test\r\n");
172 may_fail(test, stream->write_all(stream, buf, strlen(buf)));
173
174 /* rest of response headers */
175 snprintf(buf, sizeof(buf), "Content-Type: text/plain\r\n");
176 may_fail(test, stream->write_all(stream, buf, strlen(buf)));
177 snprintf(buf, sizeof(buf), "Content-Length: %u\r\n", test->res_len);
178 may_fail(test, stream->write_all(stream, buf, strlen(buf)));
179 snprintf(buf, sizeof(buf), "Connection: close\r\n");
180 may_fail(test, stream->write_all(stream, buf, strlen(buf)));
181 snprintf(buf, sizeof(buf), "\r\n");
182 may_fail(test, stream->write_all(stream, buf, strlen(buf)));
183
184 /* response body */
185 may_fail(test, stream->write_all(stream, test->res, test->res_len));
186 return FALSE;
187 }
188
189 static test_service_t gtests[] = {
190 { "GET", 1, "127.0.0.1", 6543, "/a/test/?b=c", NULL,
191 NULL, 0, "\x12\x34", 2, 0 },
192 { "GET", 0, "localhost", 6543, "/", NULL,
193 NULL, 0, NULL, 0, 0 },
194 { "GET", 0, "127.0.0.1", 6543, "/largefile", NULL,
195 NULL, 0, large, sizeof(large), 0 },
196 { "GET", 1, "[::1]", 6543, "/ipv6-url", NULL,
197 NULL, 0, "\x00\r\n\r\x00testdatablabla", 20, 0 },
198 };
199
200 START_TEST(test_get)
201 {
202 stream_service_t *service;
203 status_t status;
204 chunk_t data, expected;
205 char uri[256];
206
207 lib->processor->set_threads(lib->processor, 8);
208
209 snprintf(uri, sizeof(uri), "tcp://%s:%u", gtests[_i].host, gtests[_i].port);
210 service = lib->streams->create_service(lib->streams, uri, 1);
211 ck_assert(service != NULL);
212 service->on_accept(service, servicing, &gtests[_i], JOB_PRIO_HIGH, 0);
213
214 snprintf(uri, sizeof(uri), "http://%s:%u%s",
215 gtests[_i].host, gtests[_i].port, gtests[_i].path);
216 status = lib->fetcher->fetch(lib->fetcher, uri, &data,
217 !gtests[_i].minor ? FETCH_HTTP_VERSION_1_0 : FETCH_END,
218 FETCH_END);
219 ck_assert_int_eq(status, SUCCESS);
220 expected = chunk_create(gtests[_i].res, gtests[_i].res_len);
221 ck_assert_msg(chunk_compare(expected, data) == 0,
222 "exp %B\ngot %B\n", &expected, &data);
223 free(data.ptr);
224
225 service->destroy(service);
226 }
227 END_TEST
228
229
230 static test_service_t ptests[] = {
231 { "POST", 1, "127.0.0.1", 6543, "/a/test/?b=c", "application/binary",
232 "\x23\x45", 2, "\x12\x34", 2, 0 },
233 { "POST", 0, "localhost", 6543, "/largefile", "application/x-large",
234 large, sizeof(large), large, sizeof(large), 0 },
235 { "POST", 1, "[::1]", 6543, "/ipv6-url", "text/plain",
236 "\x00\r\n\r\x00testdatablabla", 20, "\x00\r\n\r\x00testdatablabla", 20, 0 },
237 };
238
239 START_TEST(test_post)
240 {
241 stream_service_t *service;
242 status_t status;
243 chunk_t data, expected;
244 char uri[256];
245
246 lib->processor->set_threads(lib->processor, 8);
247
248 snprintf(uri, sizeof(uri), "tcp://%s:%u", ptests[_i].host, ptests[_i].port);
249 service = lib->streams->create_service(lib->streams, uri, 1);
250 ck_assert(service != NULL);
251 service->on_accept(service, servicing, &ptests[_i], JOB_PRIO_HIGH, 0);
252
253 snprintf(uri, sizeof(uri), "http://%s:%u%s",
254 ptests[_i].host, ptests[_i].port, ptests[_i].path);
255 status = lib->fetcher->fetch(lib->fetcher, uri, &data,
256 FETCH_REQUEST_TYPE, ptests[_i].type,
257 FETCH_REQUEST_DATA,
258 chunk_create(ptests[_i].req, ptests[_i].req_len),
259 !ptests[_i].minor ? FETCH_HTTP_VERSION_1_0 : FETCH_END,
260 FETCH_END);
261 ck_assert_int_eq(status, SUCCESS);
262 expected = chunk_create(ptests[_i].res, ptests[_i].res_len);
263 ck_assert_msg(chunk_compare(expected, data) == 0,
264 "exp %B\ngot %B\n", &expected, &data);
265 free(data.ptr);
266
267 service->destroy(service);
268 }
269 END_TEST
270
271
272 static test_service_t rtests[] = {
273 { "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 200 },
274 { "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 204 },
275 { "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 400 },
276 { "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 404 },
277 { "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 500 },
278 };
279
280 START_TEST(test_response_code)
281 {
282 stream_service_t *service;
283 status_t status;
284 chunk_t data;
285 char uri[256];
286 u_int code;
287
288 lib->processor->set_threads(lib->processor, 8);
289
290 snprintf(uri, sizeof(uri), "tcp://%s:%u", rtests[_i].host, rtests[_i].port);
291 service = lib->streams->create_service(lib->streams, uri, 1);
292 ck_assert(service != NULL);
293 service->on_accept(service, servicing, &rtests[_i], JOB_PRIO_HIGH, 0);
294
295 snprintf(uri, sizeof(uri), "http://%s:%u%s",
296 rtests[_i].host, rtests[_i].port, rtests[_i].path);
297 status = lib->fetcher->fetch(lib->fetcher, uri, &data,
298 FETCH_RESPONSE_CODE, &code, FETCH_END);
299 ck_assert_int_eq(status, HTTP_SUCCESS(rtests[_i].code) ? SUCCESS : FAILED);
300 ck_assert_int_eq(code, rtests[_i].code);
301 free(data.ptr);
302
303 service->destroy(service);
304 }
305 END_TEST
306
307 Suite *fetch_http_suite_create()
308 {
309 Suite *s;
310 TCase *tc;
311
312 s = suite_create("http fetcher");
313
314 tc = tcase_create("GET");
315 tcase_add_loop_test(tc, test_get, 0, countof(gtests));
316 suite_add_tcase(s, tc);
317
318 tc = tcase_create("POST");
319 tcase_add_loop_test(tc, test_post, 0, countof(ptests));
320 suite_add_tcase(s, tc);
321
322 tc = tcase_create("response code");
323 tcase_add_loop_test(tc, test_response_code, 0, countof(rtests));
324 suite_add_tcase(s, tc);
325
326 return s;
327 }