|
1 /* |
|
2 * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu> |
|
3 * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson |
|
4 * |
|
5 * Redistribution and use in source and binary forms, with or without |
|
6 * modification, are permitted provided that the following conditions |
|
7 * are met: |
|
8 * 1. Redistributions of source code must retain the above copyright |
|
9 * notice, this list of conditions and the following disclaimer. |
|
10 * 2. Redistributions in binary form must reproduce the above copyright |
|
11 * notice, this list of conditions and the following disclaimer in the |
|
12 * documentation and/or other materials provided with the distribution. |
|
13 * 3. The name of the author may not be used to endorse or promote products |
|
14 * derived from this software without specific prior written permission. |
|
15 * |
|
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
|
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
|
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
26 */ |
|
27 |
|
28 #ifdef WIN32 |
|
29 #include <winsock2.h> |
|
30 #include <ws2tcpip.h> |
|
31 #include <windows.h> |
|
32 #endif |
|
33 |
|
34 #include "event2/event-config.h" |
|
35 |
|
36 #include <sys/types.h> |
|
37 #include <sys/stat.h> |
|
38 #ifdef _EVENT_HAVE_SYS_TIME_H |
|
39 #include <sys/time.h> |
|
40 #endif |
|
41 #include <sys/queue.h> |
|
42 #ifndef WIN32 |
|
43 #include <sys/socket.h> |
|
44 #include <signal.h> |
|
45 #include <unistd.h> |
|
46 #include <netdb.h> |
|
47 #endif |
|
48 #include <fcntl.h> |
|
49 #include <stdlib.h> |
|
50 #include <stdio.h> |
|
51 #include <string.h> |
|
52 #include <errno.h> |
|
53 |
|
54 #include "event2/dns.h" |
|
55 |
|
56 #include "event2/event.h" |
|
57 #include "event2/http.h" |
|
58 #include "event2/buffer.h" |
|
59 #include "event2/bufferevent.h" |
|
60 #include "event2/util.h" |
|
61 #include "log-internal.h" |
|
62 #include "util-internal.h" |
|
63 #include "http-internal.h" |
|
64 #include "regress.h" |
|
65 #include "regress_testutils.h" |
|
66 |
|
67 static struct evhttp *http; |
|
68 /* set if a test needs to call loopexit on a base */ |
|
69 static struct event_base *exit_base; |
|
70 |
|
71 static char const BASIC_REQUEST_BODY[] = "This is funny"; |
|
72 |
|
73 static void http_basic_cb(struct evhttp_request *req, void *arg); |
|
74 static void http_chunked_cb(struct evhttp_request *req, void *arg); |
|
75 static void http_post_cb(struct evhttp_request *req, void *arg); |
|
76 static void http_put_cb(struct evhttp_request *req, void *arg); |
|
77 static void http_delete_cb(struct evhttp_request *req, void *arg); |
|
78 static void http_delay_cb(struct evhttp_request *req, void *arg); |
|
79 static void http_large_delay_cb(struct evhttp_request *req, void *arg); |
|
80 static void http_badreq_cb(struct evhttp_request *req, void *arg); |
|
81 static void http_dispatcher_cb(struct evhttp_request *req, void *arg); |
|
82 static int |
|
83 http_bind(struct evhttp *myhttp, ev_uint16_t *pport) |
|
84 { |
|
85 int port; |
|
86 struct evhttp_bound_socket *sock; |
|
87 |
|
88 sock = evhttp_bind_socket_with_handle(myhttp, "127.0.0.1", *pport); |
|
89 if (sock == NULL) |
|
90 event_errx(1, "Could not start web server"); |
|
91 |
|
92 port = regress_get_socket_port(evhttp_bound_socket_get_fd(sock)); |
|
93 if (port < 0) |
|
94 return -1; |
|
95 *pport = (ev_uint16_t) port; |
|
96 |
|
97 return 0; |
|
98 } |
|
99 |
|
100 static struct evhttp * |
|
101 http_setup(ev_uint16_t *pport, struct event_base *base) |
|
102 { |
|
103 struct evhttp *myhttp; |
|
104 |
|
105 /* Try a few different ports */ |
|
106 myhttp = evhttp_new(base); |
|
107 |
|
108 if (http_bind(myhttp, pport) < 0) |
|
109 return NULL; |
|
110 |
|
111 /* Register a callback for certain types of requests */ |
|
112 evhttp_set_cb(myhttp, "/test", http_basic_cb, base); |
|
113 evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, base); |
|
114 evhttp_set_cb(myhttp, "/streamed", http_chunked_cb, base); |
|
115 evhttp_set_cb(myhttp, "/postit", http_post_cb, base); |
|
116 evhttp_set_cb(myhttp, "/putit", http_put_cb, base); |
|
117 evhttp_set_cb(myhttp, "/deleteit", http_delete_cb, base); |
|
118 evhttp_set_cb(myhttp, "/delay", http_delay_cb, base); |
|
119 evhttp_set_cb(myhttp, "/largedelay", http_large_delay_cb, base); |
|
120 evhttp_set_cb(myhttp, "/badrequest", http_badreq_cb, base); |
|
121 evhttp_set_cb(myhttp, "/", http_dispatcher_cb, base); |
|
122 return (myhttp); |
|
123 } |
|
124 |
|
125 #ifndef NI_MAXSERV |
|
126 #define NI_MAXSERV 1024 |
|
127 #endif |
|
128 |
|
129 static evutil_socket_t |
|
130 http_connect(const char *address, u_short port) |
|
131 { |
|
132 /* Stupid code for connecting */ |
|
133 struct evutil_addrinfo ai, *aitop; |
|
134 char strport[NI_MAXSERV]; |
|
135 |
|
136 struct sockaddr *sa; |
|
137 int slen; |
|
138 evutil_socket_t fd; |
|
139 |
|
140 memset(&ai, 0, sizeof(ai)); |
|
141 ai.ai_family = AF_INET; |
|
142 ai.ai_socktype = SOCK_STREAM; |
|
143 evutil_snprintf(strport, sizeof(strport), "%d", port); |
|
144 if (evutil_getaddrinfo(address, strport, &ai, &aitop) != 0) { |
|
145 event_warn("getaddrinfo"); |
|
146 return (-1); |
|
147 } |
|
148 sa = aitop->ai_addr; |
|
149 slen = aitop->ai_addrlen; |
|
150 |
|
151 fd = socket(AF_INET, SOCK_STREAM, 0); |
|
152 if (fd == -1) |
|
153 event_err(1, "socket failed"); |
|
154 |
|
155 evutil_make_socket_nonblocking(fd); |
|
156 if (connect(fd, sa, slen) == -1) { |
|
157 #ifdef WIN32 |
|
158 int tmp_err = WSAGetLastError(); |
|
159 if (tmp_err != WSAEINPROGRESS && tmp_err != WSAEINVAL && |
|
160 tmp_err != WSAEWOULDBLOCK) |
|
161 event_err(1, "connect failed"); |
|
162 #else |
|
163 if (errno != EINPROGRESS) |
|
164 event_err(1, "connect failed"); |
|
165 #endif |
|
166 } |
|
167 |
|
168 evutil_freeaddrinfo(aitop); |
|
169 |
|
170 return (fd); |
|
171 } |
|
172 |
|
173 /* Helper: do a strcmp on the contents of buf and the string s. */ |
|
174 static int |
|
175 evbuffer_datacmp(struct evbuffer *buf, const char *s) |
|
176 { |
|
177 size_t b_sz = evbuffer_get_length(buf); |
|
178 size_t s_sz = strlen(s); |
|
179 unsigned char *d; |
|
180 int r; |
|
181 |
|
182 if (b_sz < s_sz) |
|
183 return -1; |
|
184 |
|
185 d = evbuffer_pullup(buf, s_sz); |
|
186 if ((r = memcmp(d, s, s_sz))) |
|
187 return r; |
|
188 |
|
189 if (b_sz > s_sz) |
|
190 return 1; |
|
191 else |
|
192 return 0; |
|
193 } |
|
194 |
|
195 /* Helper: Return true iff buf contains s */ |
|
196 static int |
|
197 evbuffer_contains(struct evbuffer *buf, const char *s) |
|
198 { |
|
199 struct evbuffer_ptr ptr; |
|
200 ptr = evbuffer_search(buf, s, strlen(s), NULL); |
|
201 return ptr.pos != -1; |
|
202 } |
|
203 |
|
204 static void |
|
205 http_readcb(struct bufferevent *bev, void *arg) |
|
206 { |
|
207 const char *what = BASIC_REQUEST_BODY; |
|
208 struct event_base *my_base = arg; |
|
209 |
|
210 if (evbuffer_contains(bufferevent_get_input(bev), what)) { |
|
211 struct evhttp_request *req = evhttp_request_new(NULL, NULL); |
|
212 enum message_read_status done; |
|
213 |
|
214 /* req->kind = EVHTTP_RESPONSE; */ |
|
215 done = evhttp_parse_firstline(req, bufferevent_get_input(bev)); |
|
216 if (done != ALL_DATA_READ) |
|
217 goto out; |
|
218 |
|
219 done = evhttp_parse_headers(req, bufferevent_get_input(bev)); |
|
220 if (done != ALL_DATA_READ) |
|
221 goto out; |
|
222 |
|
223 if (done == 1 && |
|
224 evhttp_find_header(evhttp_request_get_input_headers(req), |
|
225 "Content-Type") != NULL) |
|
226 test_ok++; |
|
227 |
|
228 out: |
|
229 evhttp_request_free(req); |
|
230 bufferevent_disable(bev, EV_READ); |
|
231 if (exit_base) |
|
232 event_base_loopexit(exit_base, NULL); |
|
233 else if (my_base) |
|
234 event_base_loopexit(my_base, NULL); |
|
235 else { |
|
236 fprintf(stderr, "No way to exit loop!\n"); |
|
237 exit(1); |
|
238 } |
|
239 } |
|
240 } |
|
241 |
|
242 static void |
|
243 http_writecb(struct bufferevent *bev, void *arg) |
|
244 { |
|
245 if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) { |
|
246 /* enable reading of the reply */ |
|
247 bufferevent_enable(bev, EV_READ); |
|
248 test_ok++; |
|
249 } |
|
250 } |
|
251 |
|
252 static void |
|
253 http_errorcb(struct bufferevent *bev, short what, void *arg) |
|
254 { |
|
255 test_ok = -2; |
|
256 event_base_loopexit(arg, NULL); |
|
257 } |
|
258 |
|
259 static void |
|
260 http_basic_cb(struct evhttp_request *req, void *arg) |
|
261 { |
|
262 struct evbuffer *evb = evbuffer_new(); |
|
263 int empty = evhttp_find_header(evhttp_request_get_input_headers(req), "Empty") != NULL; |
|
264 event_debug(("%s: called\n", __func__)); |
|
265 evbuffer_add_printf(evb, BASIC_REQUEST_BODY); |
|
266 |
|
267 /* For multi-line headers test */ |
|
268 { |
|
269 const char *multi = |
|
270 evhttp_find_header(evhttp_request_get_input_headers(req),"X-multi"); |
|
271 if (multi) { |
|
272 if (strcmp("END", multi + strlen(multi) - 3) == 0) |
|
273 test_ok++; |
|
274 if (evhttp_find_header(evhttp_request_get_input_headers(req), "X-Last")) |
|
275 test_ok++; |
|
276 } |
|
277 } |
|
278 |
|
279 /* injecting a bad content-length */ |
|
280 if (evhttp_find_header(evhttp_request_get_input_headers(req), "X-Negative")) |
|
281 evhttp_add_header(evhttp_request_get_output_headers(req), |
|
282 "Content-Length", "-100"); |
|
283 |
|
284 /* allow sending of an empty reply */ |
|
285 evhttp_send_reply(req, HTTP_OK, "Everything is fine", |
|
286 !empty ? evb : NULL); |
|
287 |
|
288 evbuffer_free(evb); |
|
289 } |
|
290 |
|
291 static char const* const CHUNKS[] = { |
|
292 "This is funny", |
|
293 "but not hilarious.", |
|
294 "bwv 1052" |
|
295 }; |
|
296 |
|
297 struct chunk_req_state { |
|
298 struct event_base *base; |
|
299 struct evhttp_request *req; |
|
300 int i; |
|
301 }; |
|
302 |
|
303 static void |
|
304 http_chunked_trickle_cb(evutil_socket_t fd, short events, void *arg) |
|
305 { |
|
306 struct evbuffer *evb = evbuffer_new(); |
|
307 struct chunk_req_state *state = arg; |
|
308 struct timeval when = { 0, 0 }; |
|
309 |
|
310 evbuffer_add_printf(evb, "%s", CHUNKS[state->i]); |
|
311 evhttp_send_reply_chunk(state->req, evb); |
|
312 evbuffer_free(evb); |
|
313 |
|
314 if (++state->i < (int) (sizeof(CHUNKS)/sizeof(CHUNKS[0]))) { |
|
315 event_base_once(state->base, -1, EV_TIMEOUT, |
|
316 http_chunked_trickle_cb, state, &when); |
|
317 } else { |
|
318 evhttp_send_reply_end(state->req); |
|
319 free(state); |
|
320 } |
|
321 } |
|
322 |
|
323 static void |
|
324 http_chunked_cb(struct evhttp_request *req, void *arg) |
|
325 { |
|
326 struct timeval when = { 0, 0 }; |
|
327 struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state)); |
|
328 event_debug(("%s: called\n", __func__)); |
|
329 |
|
330 memset(state, 0, sizeof(struct chunk_req_state)); |
|
331 state->req = req; |
|
332 state->base = arg; |
|
333 |
|
334 if (strcmp(evhttp_request_get_uri(req), "/streamed") == 0) { |
|
335 evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Length", "39"); |
|
336 } |
|
337 |
|
338 /* generate a chunked/streamed reply */ |
|
339 evhttp_send_reply_start(req, HTTP_OK, "Everything is fine"); |
|
340 |
|
341 /* but trickle it across several iterations to ensure we're not |
|
342 * assuming it comes all at once */ |
|
343 event_base_once(arg, -1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when); |
|
344 } |
|
345 |
|
346 static void |
|
347 http_complete_write(evutil_socket_t fd, short what, void *arg) |
|
348 { |
|
349 struct bufferevent *bev = arg; |
|
350 const char *http_request = "host\r\n" |
|
351 "Connection: close\r\n" |
|
352 "\r\n"; |
|
353 bufferevent_write(bev, http_request, strlen(http_request)); |
|
354 } |
|
355 |
|
356 static void |
|
357 http_basic_test(void *arg) |
|
358 { |
|
359 struct basic_test_data *data = arg; |
|
360 struct timeval tv; |
|
361 struct bufferevent *bev; |
|
362 evutil_socket_t fd; |
|
363 const char *http_request; |
|
364 ev_uint16_t port = 0, port2 = 0; |
|
365 |
|
366 test_ok = 0; |
|
367 |
|
368 http = http_setup(&port, data->base); |
|
369 |
|
370 /* bind to a second socket */ |
|
371 if (http_bind(http, &port2) == -1) { |
|
372 fprintf(stdout, "FAILED (bind)\n"); |
|
373 exit(1); |
|
374 } |
|
375 |
|
376 fd = http_connect("127.0.0.1", port); |
|
377 |
|
378 /* Stupid thing to send a request */ |
|
379 bev = bufferevent_socket_new(data->base, fd, 0); |
|
380 bufferevent_setcb(bev, http_readcb, http_writecb, |
|
381 http_errorcb, data->base); |
|
382 |
|
383 /* first half of the http request */ |
|
384 http_request = |
|
385 "GET /test HTTP/1.1\r\n" |
|
386 "Host: some"; |
|
387 |
|
388 bufferevent_write(bev, http_request, strlen(http_request)); |
|
389 evutil_timerclear(&tv); |
|
390 tv.tv_usec = 10000; |
|
391 event_base_once(data->base, |
|
392 -1, EV_TIMEOUT, http_complete_write, bev, &tv); |
|
393 |
|
394 event_base_dispatch(data->base); |
|
395 |
|
396 tt_assert(test_ok == 3); |
|
397 |
|
398 /* connect to the second port */ |
|
399 bufferevent_free(bev); |
|
400 evutil_closesocket(fd); |
|
401 |
|
402 fd = http_connect("127.0.0.1", port2); |
|
403 |
|
404 /* Stupid thing to send a request */ |
|
405 bev = bufferevent_socket_new(data->base, fd, 0); |
|
406 bufferevent_setcb(bev, http_readcb, http_writecb, |
|
407 http_errorcb, data->base); |
|
408 |
|
409 http_request = |
|
410 "GET /test HTTP/1.1\r\n" |
|
411 "Host: somehost\r\n" |
|
412 "Connection: close\r\n" |
|
413 "\r\n"; |
|
414 |
|
415 bufferevent_write(bev, http_request, strlen(http_request)); |
|
416 |
|
417 event_base_dispatch(data->base); |
|
418 |
|
419 tt_assert(test_ok == 5); |
|
420 |
|
421 /* Connect to the second port again. This time, send an absolute uri. */ |
|
422 bufferevent_free(bev); |
|
423 evutil_closesocket(fd); |
|
424 |
|
425 fd = http_connect("127.0.0.1", port2); |
|
426 |
|
427 /* Stupid thing to send a request */ |
|
428 bev = bufferevent_socket_new(data->base, fd, 0); |
|
429 bufferevent_setcb(bev, http_readcb, http_writecb, |
|
430 http_errorcb, data->base); |
|
431 |
|
432 http_request = |
|
433 "GET http://somehost.net/test HTTP/1.1\r\n" |
|
434 "Host: somehost\r\n" |
|
435 "Connection: close\r\n" |
|
436 "\r\n"; |
|
437 |
|
438 bufferevent_write(bev, http_request, strlen(http_request)); |
|
439 |
|
440 event_base_dispatch(data->base); |
|
441 |
|
442 tt_assert(test_ok == 7); |
|
443 |
|
444 evhttp_free(http); |
|
445 end: |
|
446 ; |
|
447 } |
|
448 |
|
449 static void |
|
450 http_delay_reply(evutil_socket_t fd, short what, void *arg) |
|
451 { |
|
452 struct evhttp_request *req = arg; |
|
453 |
|
454 evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL); |
|
455 |
|
456 ++test_ok; |
|
457 } |
|
458 |
|
459 static void |
|
460 http_delay_cb(struct evhttp_request *req, void *arg) |
|
461 { |
|
462 struct timeval tv; |
|
463 evutil_timerclear(&tv); |
|
464 tv.tv_sec = 0; |
|
465 tv.tv_usec = 200 * 1000; |
|
466 |
|
467 event_base_once(arg, -1, EV_TIMEOUT, http_delay_reply, req, &tv); |
|
468 } |
|
469 |
|
470 static void |
|
471 http_badreq_cb(struct evhttp_request *req, void *arg) |
|
472 { |
|
473 struct evbuffer *buf = evbuffer_new(); |
|
474 |
|
475 evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/xml; charset=UTF-8"); |
|
476 evbuffer_add_printf(buf, "Hello, %s!", "127.0.0.1"); |
|
477 |
|
478 evhttp_send_reply(req, HTTP_OK, "OK", buf); |
|
479 evbuffer_free(buf); |
|
480 } |
|
481 |
|
482 static void |
|
483 http_badreq_errorcb(struct bufferevent *bev, short what, void *arg) |
|
484 { |
|
485 event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); |
|
486 /* ignore */ |
|
487 } |
|
488 |
|
489 #ifndef SHUT_WR |
|
490 #ifdef WIN32 |
|
491 #define SHUT_WR SD_SEND |
|
492 #else |
|
493 #define SHUT_WR 1 |
|
494 #endif |
|
495 #endif |
|
496 |
|
497 static void |
|
498 http_badreq_readcb(struct bufferevent *bev, void *arg) |
|
499 { |
|
500 const char *what = "Hello, 127.0.0.1"; |
|
501 const char *bad_request = "400 Bad Request"; |
|
502 |
|
503 if (evbuffer_contains(bufferevent_get_input(bev), bad_request)) { |
|
504 TT_FAIL(("%s:bad request detected", __func__)); |
|
505 bufferevent_disable(bev, EV_READ); |
|
506 event_base_loopexit(arg, NULL); |
|
507 return; |
|
508 } |
|
509 |
|
510 if (evbuffer_contains(bufferevent_get_input(bev), what)) { |
|
511 struct evhttp_request *req = evhttp_request_new(NULL, NULL); |
|
512 enum message_read_status done; |
|
513 |
|
514 /* req->kind = EVHTTP_RESPONSE; */ |
|
515 done = evhttp_parse_firstline(req, bufferevent_get_input(bev)); |
|
516 if (done != ALL_DATA_READ) |
|
517 goto out; |
|
518 |
|
519 done = evhttp_parse_headers(req, bufferevent_get_input(bev)); |
|
520 if (done != ALL_DATA_READ) |
|
521 goto out; |
|
522 |
|
523 if (done == 1 && |
|
524 evhttp_find_header(evhttp_request_get_input_headers(req), |
|
525 "Content-Type") != NULL) |
|
526 test_ok++; |
|
527 |
|
528 out: |
|
529 evhttp_request_free(req); |
|
530 evbuffer_drain(bufferevent_get_input(bev), evbuffer_get_length(bufferevent_get_input(bev))); |
|
531 } |
|
532 |
|
533 shutdown(bufferevent_getfd(bev), SHUT_WR); |
|
534 } |
|
535 |
|
536 static void |
|
537 http_badreq_successcb(evutil_socket_t fd, short what, void *arg) |
|
538 { |
|
539 event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); |
|
540 event_base_loopexit(exit_base, NULL); |
|
541 } |
|
542 |
|
543 static void |
|
544 http_bad_request_test(void *arg) |
|
545 { |
|
546 struct basic_test_data *data = arg; |
|
547 struct timeval tv; |
|
548 struct bufferevent *bev = NULL; |
|
549 evutil_socket_t fd; |
|
550 const char *http_request; |
|
551 ev_uint16_t port=0, port2=0; |
|
552 |
|
553 test_ok = 0; |
|
554 exit_base = data->base; |
|
555 |
|
556 http = http_setup(&port, data->base); |
|
557 |
|
558 /* bind to a second socket */ |
|
559 if (http_bind(http, &port2) == -1) |
|
560 TT_DIE(("Bind socket failed")); |
|
561 |
|
562 /* NULL request test */ |
|
563 fd = http_connect("127.0.0.1", port); |
|
564 |
|
565 /* Stupid thing to send a request */ |
|
566 bev = bufferevent_socket_new(data->base, fd, 0); |
|
567 bufferevent_setcb(bev, http_badreq_readcb, http_writecb, |
|
568 http_badreq_errorcb, data->base); |
|
569 bufferevent_enable(bev, EV_READ); |
|
570 |
|
571 /* real NULL request */ |
|
572 http_request = ""; |
|
573 |
|
574 bufferevent_write(bev, http_request, strlen(http_request)); |
|
575 |
|
576 shutdown(fd, SHUT_WR); |
|
577 timerclear(&tv); |
|
578 tv.tv_usec = 10000; |
|
579 event_base_once(data->base, -1, EV_TIMEOUT, http_badreq_successcb, bev, &tv); |
|
580 |
|
581 event_base_dispatch(data->base); |
|
582 |
|
583 bufferevent_free(bev); |
|
584 evutil_closesocket(fd); |
|
585 |
|
586 if (test_ok != 0) { |
|
587 fprintf(stdout, "FAILED\n"); |
|
588 exit(1); |
|
589 } |
|
590 |
|
591 /* Second answer (BAD REQUEST) on connection close */ |
|
592 |
|
593 /* connect to the second port */ |
|
594 fd = http_connect("127.0.0.1", port2); |
|
595 |
|
596 /* Stupid thing to send a request */ |
|
597 bev = bufferevent_socket_new(data->base, fd, 0); |
|
598 bufferevent_setcb(bev, http_badreq_readcb, http_writecb, |
|
599 http_badreq_errorcb, data->base); |
|
600 bufferevent_enable(bev, EV_READ); |
|
601 |
|
602 /* first half of the http request */ |
|
603 http_request = |
|
604 "GET /badrequest HTTP/1.0\r\n" \ |
|
605 "Connection: Keep-Alive\r\n" \ |
|
606 "\r\n"; |
|
607 |
|
608 bufferevent_write(bev, http_request, strlen(http_request)); |
|
609 |
|
610 timerclear(&tv); |
|
611 tv.tv_usec = 10000; |
|
612 event_base_once(data->base, -1, EV_TIMEOUT, http_badreq_successcb, bev, &tv); |
|
613 |
|
614 event_base_dispatch(data->base); |
|
615 |
|
616 tt_int_op(test_ok, ==, 2); |
|
617 |
|
618 end: |
|
619 evhttp_free(http); |
|
620 if (bev) |
|
621 bufferevent_free(bev); |
|
622 } |
|
623 |
|
624 static struct evhttp_connection *delayed_client; |
|
625 |
|
626 static void |
|
627 http_large_delay_cb(struct evhttp_request *req, void *arg) |
|
628 { |
|
629 struct timeval tv; |
|
630 evutil_timerclear(&tv); |
|
631 tv.tv_sec = 3; |
|
632 |
|
633 event_base_once(arg, -1, EV_TIMEOUT, http_delay_reply, req, &tv); |
|
634 evhttp_connection_fail(delayed_client, EVCON_HTTP_EOF); |
|
635 } |
|
636 |
|
637 /* |
|
638 * HTTP DELETE test, just piggyback on the basic test |
|
639 */ |
|
640 |
|
641 static void |
|
642 http_delete_cb(struct evhttp_request *req, void *arg) |
|
643 { |
|
644 struct evbuffer *evb = evbuffer_new(); |
|
645 int empty = evhttp_find_header(evhttp_request_get_input_headers(req), "Empty") != NULL; |
|
646 |
|
647 /* Expecting a DELETE request */ |
|
648 if (evhttp_request_get_command(req) != EVHTTP_REQ_DELETE) { |
|
649 fprintf(stdout, "FAILED (delete type)\n"); |
|
650 exit(1); |
|
651 } |
|
652 |
|
653 event_debug(("%s: called\n", __func__)); |
|
654 evbuffer_add_printf(evb, BASIC_REQUEST_BODY); |
|
655 |
|
656 /* allow sending of an empty reply */ |
|
657 evhttp_send_reply(req, HTTP_OK, "Everything is fine", |
|
658 !empty ? evb : NULL); |
|
659 |
|
660 evbuffer_free(evb); |
|
661 } |
|
662 |
|
663 static void |
|
664 http_delete_test(void *arg) |
|
665 { |
|
666 struct basic_test_data *data = arg; |
|
667 struct bufferevent *bev; |
|
668 evutil_socket_t fd; |
|
669 const char *http_request; |
|
670 ev_uint16_t port = 0; |
|
671 |
|
672 test_ok = 0; |
|
673 |
|
674 http = http_setup(&port, data->base); |
|
675 |
|
676 fd = http_connect("127.0.0.1", port); |
|
677 |
|
678 /* Stupid thing to send a request */ |
|
679 bev = bufferevent_socket_new(data->base, fd, 0); |
|
680 bufferevent_setcb(bev, http_readcb, http_writecb, |
|
681 http_errorcb, data->base); |
|
682 |
|
683 http_request = |
|
684 "DELETE /deleteit HTTP/1.1\r\n" |
|
685 "Host: somehost\r\n" |
|
686 "Connection: close\r\n" |
|
687 "\r\n"; |
|
688 |
|
689 bufferevent_write(bev, http_request, strlen(http_request)); |
|
690 |
|
691 event_base_dispatch(data->base); |
|
692 |
|
693 bufferevent_free(bev); |
|
694 evutil_closesocket(fd); |
|
695 |
|
696 evhttp_free(http); |
|
697 |
|
698 tt_int_op(test_ok, ==, 2); |
|
699 end: |
|
700 ; |
|
701 } |
|
702 |
|
703 static void |
|
704 http_allowed_methods_eventcb(struct bufferevent *bev, short what, void *arg) |
|
705 { |
|
706 char **output = arg; |
|
707 if ((what & (BEV_EVENT_ERROR|BEV_EVENT_EOF))) { |
|
708 char buf[4096]; |
|
709 int n; |
|
710 n = evbuffer_remove(bufferevent_get_input(bev), buf, |
|
711 sizeof(buf)-1); |
|
712 if (n >= 0) { |
|
713 buf[n]='\0'; |
|
714 if (*output) |
|
715 free(*output); |
|
716 *output = strdup(buf); |
|
717 } |
|
718 event_base_loopexit(exit_base, NULL); |
|
719 } |
|
720 } |
|
721 |
|
722 static void |
|
723 http_allowed_methods_test(void *arg) |
|
724 { |
|
725 struct basic_test_data *data = arg; |
|
726 struct bufferevent *bev1, *bev2, *bev3; |
|
727 evutil_socket_t fd1, fd2, fd3; |
|
728 const char *http_request; |
|
729 char *result1=NULL, *result2=NULL, *result3=NULL; |
|
730 ev_uint16_t port = 0; |
|
731 |
|
732 exit_base = data->base; |
|
733 test_ok = 0; |
|
734 |
|
735 http = http_setup(&port, data->base); |
|
736 |
|
737 fd1 = http_connect("127.0.0.1", port); |
|
738 |
|
739 /* GET is out; PATCH is in. */ |
|
740 evhttp_set_allowed_methods(http, EVHTTP_REQ_PATCH); |
|
741 |
|
742 /* Stupid thing to send a request */ |
|
743 bev1 = bufferevent_socket_new(data->base, fd1, 0); |
|
744 bufferevent_enable(bev1, EV_READ|EV_WRITE); |
|
745 bufferevent_setcb(bev1, NULL, NULL, |
|
746 http_allowed_methods_eventcb, &result1); |
|
747 |
|
748 http_request = |
|
749 "GET /index.html HTTP/1.1\r\n" |
|
750 "Host: somehost\r\n" |
|
751 "Connection: close\r\n" |
|
752 "\r\n"; |
|
753 |
|
754 bufferevent_write(bev1, http_request, strlen(http_request)); |
|
755 |
|
756 event_base_dispatch(data->base); |
|
757 |
|
758 fd2 = http_connect("127.0.0.1", port); |
|
759 |
|
760 bev2 = bufferevent_socket_new(data->base, fd2, 0); |
|
761 bufferevent_enable(bev2, EV_READ|EV_WRITE); |
|
762 bufferevent_setcb(bev2, NULL, NULL, |
|
763 http_allowed_methods_eventcb, &result2); |
|
764 |
|
765 http_request = |
|
766 "PATCH /test HTTP/1.1\r\n" |
|
767 "Host: somehost\r\n" |
|
768 "Connection: close\r\n" |
|
769 "\r\n"; |
|
770 |
|
771 bufferevent_write(bev2, http_request, strlen(http_request)); |
|
772 |
|
773 event_base_dispatch(data->base); |
|
774 |
|
775 fd3 = http_connect("127.0.0.1", port); |
|
776 |
|
777 bev3 = bufferevent_socket_new(data->base, fd3, 0); |
|
778 bufferevent_enable(bev3, EV_READ|EV_WRITE); |
|
779 bufferevent_setcb(bev3, NULL, NULL, |
|
780 http_allowed_methods_eventcb, &result3); |
|
781 |
|
782 http_request = |
|
783 "FLOOP /test HTTP/1.1\r\n" |
|
784 "Host: somehost\r\n" |
|
785 "Connection: close\r\n" |
|
786 "\r\n"; |
|
787 |
|
788 bufferevent_write(bev3, http_request, strlen(http_request)); |
|
789 |
|
790 event_base_dispatch(data->base); |
|
791 |
|
792 bufferevent_free(bev1); |
|
793 bufferevent_free(bev2); |
|
794 bufferevent_free(bev3); |
|
795 evutil_closesocket(fd1); |
|
796 evutil_closesocket(fd2); |
|
797 evutil_closesocket(fd3); |
|
798 |
|
799 evhttp_free(http); |
|
800 |
|
801 /* Method known but disallowed */ |
|
802 tt_assert(result1); |
|
803 tt_assert(!strncmp(result1, "HTTP/1.1 501 ", strlen("HTTP/1.1 501 "))); |
|
804 |
|
805 /* Method known and allowed */ |
|
806 tt_assert(result2); |
|
807 tt_assert(!strncmp(result2, "HTTP/1.1 200 ", strlen("HTTP/1.1 200 "))); |
|
808 |
|
809 /* Method unknown */ |
|
810 tt_assert(result3); |
|
811 tt_assert(!strncmp(result3, "HTTP/1.1 501 ", strlen("HTTP/1.1 501 "))); |
|
812 |
|
813 end: |
|
814 if (result1) |
|
815 free(result1); |
|
816 if (result2) |
|
817 free(result2); |
|
818 if (result3) |
|
819 free(result3); |
|
820 } |
|
821 |
|
822 static void http_request_done(struct evhttp_request *, void *); |
|
823 static void http_request_empty_done(struct evhttp_request *, void *); |
|
824 |
|
825 static void |
|
826 _http_connection_test(struct basic_test_data *data, int persistent) |
|
827 { |
|
828 ev_uint16_t port = 0; |
|
829 struct evhttp_connection *evcon = NULL; |
|
830 struct evhttp_request *req = NULL; |
|
831 |
|
832 test_ok = 0; |
|
833 |
|
834 http = http_setup(&port, data->base); |
|
835 |
|
836 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
|
837 tt_assert(evcon); |
|
838 |
|
839 tt_assert(evhttp_connection_get_base(evcon) == data->base); |
|
840 |
|
841 exit_base = data->base; |
|
842 /* |
|
843 * At this point, we want to schedule a request to the HTTP |
|
844 * server using our make request method. |
|
845 */ |
|
846 |
|
847 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
|
848 |
|
849 /* Add the information that we care about */ |
|
850 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
851 |
|
852 /* We give ownership of the request to the connection */ |
|
853 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
|
854 fprintf(stdout, "FAILED\n"); |
|
855 exit(1); |
|
856 } |
|
857 |
|
858 event_base_dispatch(data->base); |
|
859 |
|
860 tt_assert(test_ok); |
|
861 |
|
862 /* try to make another request over the same connection */ |
|
863 test_ok = 0; |
|
864 |
|
865 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
|
866 |
|
867 /* Add the information that we care about */ |
|
868 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
869 |
|
870 /* |
|
871 * if our connections are not supposed to be persistent; request |
|
872 * a close from the server. |
|
873 */ |
|
874 if (!persistent) |
|
875 evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close"); |
|
876 |
|
877 /* We give ownership of the request to the connection */ |
|
878 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
|
879 tt_abort_msg("couldn't make request"); |
|
880 } |
|
881 |
|
882 event_base_dispatch(data->base); |
|
883 |
|
884 /* make another request: request empty reply */ |
|
885 test_ok = 0; |
|
886 |
|
887 req = evhttp_request_new(http_request_empty_done, data->base); |
|
888 |
|
889 /* Add the information that we care about */ |
|
890 evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis"); |
|
891 |
|
892 /* We give ownership of the request to the connection */ |
|
893 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
|
894 tt_abort_msg("Couldn't make request"); |
|
895 } |
|
896 |
|
897 event_base_dispatch(data->base); |
|
898 |
|
899 end: |
|
900 if (evcon) |
|
901 evhttp_connection_free(evcon); |
|
902 if (http) |
|
903 evhttp_free(http); |
|
904 } |
|
905 |
|
906 static void |
|
907 http_connection_test(void *arg) |
|
908 { |
|
909 _http_connection_test(arg, 0); |
|
910 } |
|
911 static void |
|
912 http_persist_connection_test(void *arg) |
|
913 { |
|
914 _http_connection_test(arg, 1); |
|
915 } |
|
916 |
|
917 static struct regress_dns_server_table search_table[] = { |
|
918 { "localhost", "A", "127.0.0.1", 0 }, |
|
919 { NULL, NULL, NULL, 0 } |
|
920 }; |
|
921 |
|
922 static void |
|
923 http_connection_async_test(void *arg) |
|
924 { |
|
925 struct basic_test_data *data = arg; |
|
926 ev_uint16_t port = 0; |
|
927 struct evhttp_connection *evcon = NULL; |
|
928 struct evhttp_request *req = NULL; |
|
929 struct evdns_base *dns_base = NULL; |
|
930 ev_uint16_t portnum = 0; |
|
931 char address[64]; |
|
932 |
|
933 exit_base = data->base; |
|
934 tt_assert(regress_dnsserver(data->base, &portnum, search_table)); |
|
935 |
|
936 dns_base = evdns_base_new(data->base, 0/* init name servers */); |
|
937 tt_assert(dns_base); |
|
938 |
|
939 /* Add ourself as the only nameserver, and make sure we really are |
|
940 * the only nameserver. */ |
|
941 evutil_snprintf(address, sizeof(address), "127.0.0.1:%d", portnum); |
|
942 evdns_base_nameserver_ip_add(dns_base, address); |
|
943 |
|
944 test_ok = 0; |
|
945 |
|
946 http = http_setup(&port, data->base); |
|
947 |
|
948 evcon = evhttp_connection_base_new(data->base, dns_base, "127.0.0.1", port); |
|
949 tt_assert(evcon); |
|
950 |
|
951 /* |
|
952 * At this point, we want to schedule a request to the HTTP |
|
953 * server using our make request method. |
|
954 */ |
|
955 |
|
956 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
|
957 |
|
958 /* Add the information that we care about */ |
|
959 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
960 |
|
961 /* We give ownership of the request to the connection */ |
|
962 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
|
963 fprintf(stdout, "FAILED\n"); |
|
964 exit(1); |
|
965 } |
|
966 |
|
967 event_base_dispatch(data->base); |
|
968 |
|
969 tt_assert(test_ok); |
|
970 |
|
971 /* try to make another request over the same connection */ |
|
972 test_ok = 0; |
|
973 |
|
974 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
|
975 |
|
976 /* Add the information that we care about */ |
|
977 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
978 |
|
979 /* |
|
980 * if our connections are not supposed to be persistent; request |
|
981 * a close from the server. |
|
982 */ |
|
983 evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close"); |
|
984 |
|
985 /* We give ownership of the request to the connection */ |
|
986 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
|
987 tt_abort_msg("couldn't make request"); |
|
988 } |
|
989 |
|
990 event_base_dispatch(data->base); |
|
991 |
|
992 /* make another request: request empty reply */ |
|
993 test_ok = 0; |
|
994 |
|
995 req = evhttp_request_new(http_request_empty_done, data->base); |
|
996 |
|
997 /* Add the information that we care about */ |
|
998 evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis"); |
|
999 |
|
1000 /* We give ownership of the request to the connection */ |
|
1001 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
|
1002 tt_abort_msg("Couldn't make request"); |
|
1003 } |
|
1004 |
|
1005 event_base_dispatch(data->base); |
|
1006 |
|
1007 end: |
|
1008 if (evcon) |
|
1009 evhttp_connection_free(evcon); |
|
1010 if (http) |
|
1011 evhttp_free(http); |
|
1012 if (dns_base) |
|
1013 evdns_base_free(dns_base, 0); |
|
1014 regress_clean_dnsserver(); |
|
1015 } |
|
1016 |
|
1017 static void |
|
1018 http_request_never_call(struct evhttp_request *req, void *arg) |
|
1019 { |
|
1020 fprintf(stdout, "FAILED\n"); |
|
1021 exit(1); |
|
1022 } |
|
1023 |
|
1024 static void |
|
1025 http_do_cancel(evutil_socket_t fd, short what, void *arg) |
|
1026 { |
|
1027 struct evhttp_request *req = arg; |
|
1028 struct timeval tv; |
|
1029 struct event_base *base; |
|
1030 evutil_timerclear(&tv); |
|
1031 tv.tv_sec = 0; |
|
1032 tv.tv_usec = 500 * 1000; |
|
1033 |
|
1034 base = evhttp_connection_get_base(evhttp_request_get_connection(req)); |
|
1035 evhttp_cancel_request(req); |
|
1036 |
|
1037 event_base_loopexit(base, &tv); |
|
1038 |
|
1039 ++test_ok; |
|
1040 } |
|
1041 |
|
1042 static void |
|
1043 http_cancel_test(void *arg) |
|
1044 { |
|
1045 struct basic_test_data *data = arg; |
|
1046 ev_uint16_t port = 0; |
|
1047 struct evhttp_connection *evcon = NULL; |
|
1048 struct evhttp_request *req = NULL; |
|
1049 struct timeval tv; |
|
1050 |
|
1051 exit_base = data->base; |
|
1052 |
|
1053 test_ok = 0; |
|
1054 |
|
1055 http = http_setup(&port, data->base); |
|
1056 |
|
1057 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
|
1058 tt_assert(evcon); |
|
1059 |
|
1060 /* |
|
1061 * At this point, we want to schedule a request to the HTTP |
|
1062 * server using our make request method. |
|
1063 */ |
|
1064 |
|
1065 req = evhttp_request_new(http_request_never_call, NULL); |
|
1066 |
|
1067 /* Add the information that we care about */ |
|
1068 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
1069 |
|
1070 /* We give ownership of the request to the connection */ |
|
1071 tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/delay"), |
|
1072 !=, -1); |
|
1073 |
|
1074 evutil_timerclear(&tv); |
|
1075 tv.tv_sec = 0; |
|
1076 tv.tv_usec = 100 * 1000; |
|
1077 |
|
1078 event_base_once(data->base, -1, EV_TIMEOUT, http_do_cancel, req, &tv); |
|
1079 |
|
1080 event_base_dispatch(data->base); |
|
1081 |
|
1082 tt_int_op(test_ok, ==, 2); |
|
1083 |
|
1084 /* try to make another request over the same connection */ |
|
1085 test_ok = 0; |
|
1086 |
|
1087 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
|
1088 |
|
1089 /* Add the information that we care about */ |
|
1090 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
1091 |
|
1092 /* We give ownership of the request to the connection */ |
|
1093 tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test"), |
|
1094 !=, -1); |
|
1095 |
|
1096 event_base_dispatch(data->base); |
|
1097 |
|
1098 /* make another request: request empty reply */ |
|
1099 test_ok = 0; |
|
1100 |
|
1101 req = evhttp_request_new(http_request_empty_done, data->base); |
|
1102 |
|
1103 /* Add the information that we care about */ |
|
1104 evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis"); |
|
1105 |
|
1106 /* We give ownership of the request to the connection */ |
|
1107 tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test"), |
|
1108 !=, -1); |
|
1109 |
|
1110 event_base_dispatch(data->base); |
|
1111 |
|
1112 end: |
|
1113 if (evcon) |
|
1114 evhttp_connection_free(evcon); |
|
1115 if (http) |
|
1116 evhttp_free(http); |
|
1117 } |
|
1118 |
|
1119 static void |
|
1120 http_request_done(struct evhttp_request *req, void *arg) |
|
1121 { |
|
1122 const char *what = arg; |
|
1123 |
|
1124 if (evhttp_request_get_response_code(req) != HTTP_OK) { |
|
1125 fprintf(stderr, "FAILED\n"); |
|
1126 exit(1); |
|
1127 } |
|
1128 |
|
1129 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) { |
|
1130 fprintf(stderr, "FAILED\n"); |
|
1131 exit(1); |
|
1132 } |
|
1133 |
|
1134 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) { |
|
1135 fprintf(stderr, "FAILED\n"); |
|
1136 exit(1); |
|
1137 } |
|
1138 |
|
1139 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) { |
|
1140 fprintf(stderr, "FAILED\n"); |
|
1141 exit(1); |
|
1142 } |
|
1143 |
|
1144 test_ok = 1; |
|
1145 EVUTIL_ASSERT(exit_base); |
|
1146 event_base_loopexit(exit_base, NULL); |
|
1147 } |
|
1148 |
|
1149 static void |
|
1150 http_request_expect_error(struct evhttp_request *req, void *arg) |
|
1151 { |
|
1152 if (evhttp_request_get_response_code(req) == HTTP_OK) { |
|
1153 fprintf(stderr, "FAILED\n"); |
|
1154 exit(1); |
|
1155 } |
|
1156 |
|
1157 test_ok = 1; |
|
1158 EVUTIL_ASSERT(arg); |
|
1159 event_base_loopexit(arg, NULL); |
|
1160 } |
|
1161 |
|
1162 /* test virtual hosts */ |
|
1163 static void |
|
1164 http_virtual_host_test(void *arg) |
|
1165 { |
|
1166 struct basic_test_data *data = arg; |
|
1167 ev_uint16_t port = 0; |
|
1168 struct evhttp_connection *evcon = NULL; |
|
1169 struct evhttp_request *req = NULL; |
|
1170 struct evhttp *second = NULL, *third = NULL; |
|
1171 evutil_socket_t fd; |
|
1172 struct bufferevent *bev; |
|
1173 const char *http_request; |
|
1174 |
|
1175 exit_base = data->base; |
|
1176 |
|
1177 http = http_setup(&port, data->base); |
|
1178 |
|
1179 /* virtual host */ |
|
1180 second = evhttp_new(NULL); |
|
1181 evhttp_set_cb(second, "/funnybunny", http_basic_cb, NULL); |
|
1182 third = evhttp_new(NULL); |
|
1183 evhttp_set_cb(third, "/blackcoffee", http_basic_cb, NULL); |
|
1184 |
|
1185 if (evhttp_add_virtual_host(http, "foo.com", second) == -1) { |
|
1186 tt_abort_msg("Couldn't add vhost"); |
|
1187 } |
|
1188 |
|
1189 if (evhttp_add_virtual_host(http, "bar.*.foo.com", third) == -1) { |
|
1190 tt_abort_msg("Couldn't add wildcarded vhost"); |
|
1191 } |
|
1192 |
|
1193 /* add some aliases to the vhosts */ |
|
1194 tt_assert(evhttp_add_server_alias(second, "manolito.info") == 0); |
|
1195 tt_assert(evhttp_add_server_alias(third, "bonkers.org") == 0); |
|
1196 |
|
1197 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
|
1198 tt_assert(evcon); |
|
1199 |
|
1200 /* make a request with a different host and expect an error */ |
|
1201 req = evhttp_request_new(http_request_expect_error, data->base); |
|
1202 |
|
1203 /* Add the information that we care about */ |
|
1204 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
1205 |
|
1206 /* We give ownership of the request to the connection */ |
|
1207 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
|
1208 "/funnybunny") == -1) { |
|
1209 tt_abort_msg("Couldn't make request"); |
|
1210 } |
|
1211 |
|
1212 event_base_dispatch(data->base); |
|
1213 |
|
1214 tt_assert(test_ok == 1); |
|
1215 |
|
1216 test_ok = 0; |
|
1217 |
|
1218 /* make a request with the right host and expect a response */ |
|
1219 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
|
1220 |
|
1221 /* Add the information that we care about */ |
|
1222 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "foo.com"); |
|
1223 |
|
1224 /* We give ownership of the request to the connection */ |
|
1225 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
|
1226 "/funnybunny") == -1) { |
|
1227 fprintf(stdout, "FAILED\n"); |
|
1228 exit(1); |
|
1229 } |
|
1230 |
|
1231 event_base_dispatch(data->base); |
|
1232 |
|
1233 tt_assert(test_ok == 1); |
|
1234 |
|
1235 test_ok = 0; |
|
1236 |
|
1237 /* make a request with the right host and expect a response */ |
|
1238 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
|
1239 |
|
1240 /* Add the information that we care about */ |
|
1241 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "bar.magic.foo.com"); |
|
1242 |
|
1243 /* We give ownership of the request to the connection */ |
|
1244 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
|
1245 "/blackcoffee") == -1) { |
|
1246 tt_abort_msg("Couldn't make request"); |
|
1247 } |
|
1248 |
|
1249 event_base_dispatch(data->base); |
|
1250 |
|
1251 tt_assert(test_ok == 1) |
|
1252 |
|
1253 test_ok = 0; |
|
1254 |
|
1255 /* make a request with the right host and expect a response */ |
|
1256 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
|
1257 |
|
1258 /* Add the information that we care about */ |
|
1259 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "manolito.info"); |
|
1260 |
|
1261 /* We give ownership of the request to the connection */ |
|
1262 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
|
1263 "/funnybunny") == -1) { |
|
1264 tt_abort_msg("Couldn't make request"); |
|
1265 } |
|
1266 |
|
1267 event_base_dispatch(data->base); |
|
1268 |
|
1269 tt_assert(test_ok == 1) |
|
1270 |
|
1271 test_ok = 0; |
|
1272 |
|
1273 /* make a request with the right host and expect a response */ |
|
1274 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
|
1275 |
|
1276 /* Add the Host header. This time with the optional port. */ |
|
1277 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "bonkers.org:8000"); |
|
1278 |
|
1279 /* We give ownership of the request to the connection */ |
|
1280 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
|
1281 "/blackcoffee") == -1) { |
|
1282 tt_abort_msg("Couldn't make request"); |
|
1283 } |
|
1284 |
|
1285 event_base_dispatch(data->base); |
|
1286 |
|
1287 tt_assert(test_ok == 1) |
|
1288 |
|
1289 test_ok = 0; |
|
1290 |
|
1291 /* Now make a raw request with an absolute URI. */ |
|
1292 fd = http_connect("127.0.0.1", port); |
|
1293 |
|
1294 /* Stupid thing to send a request */ |
|
1295 bev = bufferevent_socket_new(data->base, fd, 0); |
|
1296 bufferevent_setcb(bev, http_readcb, http_writecb, |
|
1297 http_errorcb, NULL); |
|
1298 |
|
1299 /* The host in the URI should override the Host: header */ |
|
1300 http_request = |
|
1301 "GET http://manolito.info/funnybunny HTTP/1.1\r\n" |
|
1302 "Host: somehost\r\n" |
|
1303 "Connection: close\r\n" |
|
1304 "\r\n"; |
|
1305 |
|
1306 bufferevent_write(bev, http_request, strlen(http_request)); |
|
1307 |
|
1308 event_base_dispatch(data->base); |
|
1309 |
|
1310 tt_int_op(test_ok, ==, 2); |
|
1311 |
|
1312 bufferevent_free(bev); |
|
1313 evutil_closesocket(fd); |
|
1314 |
|
1315 end: |
|
1316 if (evcon) |
|
1317 evhttp_connection_free(evcon); |
|
1318 if (http) |
|
1319 evhttp_free(http); |
|
1320 } |
|
1321 |
|
1322 |
|
1323 /* test date header and content length */ |
|
1324 |
|
1325 static void |
|
1326 http_request_empty_done(struct evhttp_request *req, void *arg) |
|
1327 { |
|
1328 if (evhttp_request_get_response_code(req) != HTTP_OK) { |
|
1329 fprintf(stderr, "FAILED\n"); |
|
1330 exit(1); |
|
1331 } |
|
1332 |
|
1333 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Date") == NULL) { |
|
1334 fprintf(stderr, "FAILED\n"); |
|
1335 exit(1); |
|
1336 } |
|
1337 |
|
1338 |
|
1339 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Length") == NULL) { |
|
1340 fprintf(stderr, "FAILED\n"); |
|
1341 exit(1); |
|
1342 } |
|
1343 |
|
1344 if (strcmp(evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Length"), |
|
1345 "0")) { |
|
1346 fprintf(stderr, "FAILED\n"); |
|
1347 exit(1); |
|
1348 } |
|
1349 |
|
1350 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 0) { |
|
1351 fprintf(stderr, "FAILED\n"); |
|
1352 exit(1); |
|
1353 } |
|
1354 |
|
1355 test_ok = 1; |
|
1356 EVUTIL_ASSERT(arg); |
|
1357 event_base_loopexit(arg, NULL); |
|
1358 } |
|
1359 |
|
1360 /* |
|
1361 * HTTP DISPATCHER test |
|
1362 */ |
|
1363 |
|
1364 void |
|
1365 http_dispatcher_cb(struct evhttp_request *req, void *arg) |
|
1366 { |
|
1367 |
|
1368 struct evbuffer *evb = evbuffer_new(); |
|
1369 event_debug(("%s: called\n", __func__)); |
|
1370 evbuffer_add_printf(evb, "DISPATCHER_TEST"); |
|
1371 |
|
1372 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); |
|
1373 |
|
1374 evbuffer_free(evb); |
|
1375 } |
|
1376 |
|
1377 static void |
|
1378 http_dispatcher_test_done(struct evhttp_request *req, void *arg) |
|
1379 { |
|
1380 struct event_base *base = arg; |
|
1381 const char *what = "DISPATCHER_TEST"; |
|
1382 |
|
1383 if (evhttp_request_get_response_code(req) != HTTP_OK) { |
|
1384 fprintf(stderr, "FAILED\n"); |
|
1385 exit(1); |
|
1386 } |
|
1387 |
|
1388 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) { |
|
1389 fprintf(stderr, "FAILED (content type)\n"); |
|
1390 exit(1); |
|
1391 } |
|
1392 |
|
1393 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) { |
|
1394 fprintf(stderr, "FAILED (length %lu vs %lu)\n", |
|
1395 (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what)); |
|
1396 exit(1); |
|
1397 } |
|
1398 |
|
1399 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) { |
|
1400 fprintf(stderr, "FAILED (data)\n"); |
|
1401 exit(1); |
|
1402 } |
|
1403 |
|
1404 test_ok = 1; |
|
1405 event_base_loopexit(base, NULL); |
|
1406 } |
|
1407 |
|
1408 static void |
|
1409 http_dispatcher_test(void *arg) |
|
1410 { |
|
1411 struct basic_test_data *data = arg; |
|
1412 ev_uint16_t port = 0; |
|
1413 struct evhttp_connection *evcon = NULL; |
|
1414 struct evhttp_request *req = NULL; |
|
1415 |
|
1416 test_ok = 0; |
|
1417 |
|
1418 http = http_setup(&port, data->base); |
|
1419 |
|
1420 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
|
1421 tt_assert(evcon); |
|
1422 |
|
1423 /* also bind to local host */ |
|
1424 evhttp_connection_set_local_address(evcon, "127.0.0.1"); |
|
1425 |
|
1426 /* |
|
1427 * At this point, we want to schedule an HTTP GET request |
|
1428 * server using our make request method. |
|
1429 */ |
|
1430 |
|
1431 req = evhttp_request_new(http_dispatcher_test_done, data->base); |
|
1432 tt_assert(req); |
|
1433 |
|
1434 /* Add the information that we care about */ |
|
1435 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
1436 |
|
1437 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) { |
|
1438 tt_abort_msg("Couldn't make request"); |
|
1439 } |
|
1440 |
|
1441 event_base_dispatch(data->base); |
|
1442 |
|
1443 end: |
|
1444 if (evcon) |
|
1445 evhttp_connection_free(evcon); |
|
1446 if (http) |
|
1447 evhttp_free(http); |
|
1448 } |
|
1449 |
|
1450 /* |
|
1451 * HTTP POST test. |
|
1452 */ |
|
1453 |
|
1454 void http_postrequest_done(struct evhttp_request *, void *); |
|
1455 |
|
1456 #define POST_DATA "Okay. Not really printf" |
|
1457 |
|
1458 static void |
|
1459 http_post_test(void *arg) |
|
1460 { |
|
1461 struct basic_test_data *data = arg; |
|
1462 ev_uint16_t port = 0; |
|
1463 struct evhttp_connection *evcon = NULL; |
|
1464 struct evhttp_request *req = NULL; |
|
1465 |
|
1466 test_ok = 0; |
|
1467 |
|
1468 http = http_setup(&port, data->base); |
|
1469 |
|
1470 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
|
1471 tt_assert(evcon); |
|
1472 |
|
1473 /* |
|
1474 * At this point, we want to schedule an HTTP POST request |
|
1475 * server using our make request method. |
|
1476 */ |
|
1477 |
|
1478 req = evhttp_request_new(http_postrequest_done, data->base); |
|
1479 tt_assert(req); |
|
1480 |
|
1481 /* Add the information that we care about */ |
|
1482 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
1483 evbuffer_add_printf(evhttp_request_get_output_buffer(req), POST_DATA); |
|
1484 |
|
1485 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) { |
|
1486 tt_abort_msg("Couldn't make request"); |
|
1487 } |
|
1488 |
|
1489 event_base_dispatch(data->base); |
|
1490 |
|
1491 tt_int_op(test_ok, ==, 1); |
|
1492 |
|
1493 test_ok = 0; |
|
1494 |
|
1495 req = evhttp_request_new(http_postrequest_done, data->base); |
|
1496 tt_assert(req); |
|
1497 |
|
1498 /* Now try with 100-continue. */ |
|
1499 |
|
1500 /* Add the information that we care about */ |
|
1501 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
1502 evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "100-continue"); |
|
1503 evbuffer_add_printf(evhttp_request_get_output_buffer(req), POST_DATA); |
|
1504 |
|
1505 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) { |
|
1506 tt_abort_msg("Couldn't make request"); |
|
1507 } |
|
1508 |
|
1509 event_base_dispatch(data->base); |
|
1510 |
|
1511 tt_int_op(test_ok, ==, 1); |
|
1512 |
|
1513 evhttp_connection_free(evcon); |
|
1514 evhttp_free(http); |
|
1515 |
|
1516 end: |
|
1517 ; |
|
1518 } |
|
1519 |
|
1520 void |
|
1521 http_post_cb(struct evhttp_request *req, void *arg) |
|
1522 { |
|
1523 struct evbuffer *evb; |
|
1524 event_debug(("%s: called\n", __func__)); |
|
1525 |
|
1526 /* Yes, we are expecting a post request */ |
|
1527 if (evhttp_request_get_command(req) != EVHTTP_REQ_POST) { |
|
1528 fprintf(stdout, "FAILED (post type)\n"); |
|
1529 exit(1); |
|
1530 } |
|
1531 |
|
1532 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(POST_DATA)) { |
|
1533 fprintf(stdout, "FAILED (length: %lu vs %lu)\n", |
|
1534 (unsigned long) evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long) strlen(POST_DATA)); |
|
1535 exit(1); |
|
1536 } |
|
1537 |
|
1538 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), POST_DATA) != 0) { |
|
1539 fprintf(stdout, "FAILED (data)\n"); |
|
1540 fprintf(stdout, "Got :%s\n", evbuffer_pullup(evhttp_request_get_input_buffer(req),-1)); |
|
1541 fprintf(stdout, "Want:%s\n", POST_DATA); |
|
1542 exit(1); |
|
1543 } |
|
1544 |
|
1545 evb = evbuffer_new(); |
|
1546 evbuffer_add_printf(evb, BASIC_REQUEST_BODY); |
|
1547 |
|
1548 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); |
|
1549 |
|
1550 evbuffer_free(evb); |
|
1551 } |
|
1552 |
|
1553 void |
|
1554 http_postrequest_done(struct evhttp_request *req, void *arg) |
|
1555 { |
|
1556 const char *what = BASIC_REQUEST_BODY; |
|
1557 struct event_base *base = arg; |
|
1558 |
|
1559 if (req == NULL) { |
|
1560 fprintf(stderr, "FAILED (timeout)\n"); |
|
1561 exit(1); |
|
1562 } |
|
1563 |
|
1564 if (evhttp_request_get_response_code(req) != HTTP_OK) { |
|
1565 |
|
1566 fprintf(stderr, "FAILED (response code)\n"); |
|
1567 exit(1); |
|
1568 } |
|
1569 |
|
1570 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) { |
|
1571 fprintf(stderr, "FAILED (content type)\n"); |
|
1572 exit(1); |
|
1573 } |
|
1574 |
|
1575 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) { |
|
1576 fprintf(stderr, "FAILED (length %lu vs %lu)\n", |
|
1577 (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what)); |
|
1578 exit(1); |
|
1579 } |
|
1580 |
|
1581 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) { |
|
1582 fprintf(stderr, "FAILED (data)\n"); |
|
1583 exit(1); |
|
1584 } |
|
1585 |
|
1586 test_ok = 1; |
|
1587 event_base_loopexit(base, NULL); |
|
1588 } |
|
1589 |
|
1590 /* |
|
1591 * HTTP PUT test, basically just like POST, but ... |
|
1592 */ |
|
1593 |
|
1594 void http_putrequest_done(struct evhttp_request *, void *); |
|
1595 |
|
1596 #define PUT_DATA "Hi, I'm some PUT data" |
|
1597 |
|
1598 static void |
|
1599 http_put_test(void *arg) |
|
1600 { |
|
1601 struct basic_test_data *data = arg; |
|
1602 ev_uint16_t port = 0; |
|
1603 struct evhttp_connection *evcon = NULL; |
|
1604 struct evhttp_request *req = NULL; |
|
1605 |
|
1606 test_ok = 0; |
|
1607 |
|
1608 http = http_setup(&port, data->base); |
|
1609 |
|
1610 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
|
1611 tt_assert(evcon); |
|
1612 |
|
1613 /* |
|
1614 * Schedule the HTTP PUT request |
|
1615 */ |
|
1616 |
|
1617 req = evhttp_request_new(http_putrequest_done, data->base); |
|
1618 tt_assert(req); |
|
1619 |
|
1620 /* Add the information that we care about */ |
|
1621 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "someotherhost"); |
|
1622 evbuffer_add_printf(evhttp_request_get_output_buffer(req), PUT_DATA); |
|
1623 |
|
1624 if (evhttp_make_request(evcon, req, EVHTTP_REQ_PUT, "/putit") == -1) { |
|
1625 tt_abort_msg("Couldn't make request"); |
|
1626 } |
|
1627 |
|
1628 event_base_dispatch(data->base); |
|
1629 |
|
1630 evhttp_connection_free(evcon); |
|
1631 evhttp_free(http); |
|
1632 |
|
1633 tt_int_op(test_ok, ==, 1); |
|
1634 end: |
|
1635 ; |
|
1636 } |
|
1637 |
|
1638 void |
|
1639 http_put_cb(struct evhttp_request *req, void *arg) |
|
1640 { |
|
1641 struct evbuffer *evb; |
|
1642 event_debug(("%s: called\n", __func__)); |
|
1643 |
|
1644 /* Expecting a PUT request */ |
|
1645 if (evhttp_request_get_command(req) != EVHTTP_REQ_PUT) { |
|
1646 fprintf(stdout, "FAILED (put type)\n"); |
|
1647 exit(1); |
|
1648 } |
|
1649 |
|
1650 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(PUT_DATA)) { |
|
1651 fprintf(stdout, "FAILED (length: %lu vs %lu)\n", |
|
1652 (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(PUT_DATA)); |
|
1653 exit(1); |
|
1654 } |
|
1655 |
|
1656 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), PUT_DATA) != 0) { |
|
1657 fprintf(stdout, "FAILED (data)\n"); |
|
1658 fprintf(stdout, "Got :%s\n", evbuffer_pullup(evhttp_request_get_input_buffer(req),-1)); |
|
1659 fprintf(stdout, "Want:%s\n", PUT_DATA); |
|
1660 exit(1); |
|
1661 } |
|
1662 |
|
1663 evb = evbuffer_new(); |
|
1664 evbuffer_add_printf(evb, "That ain't funny"); |
|
1665 |
|
1666 evhttp_send_reply(req, HTTP_OK, "Everything is great", evb); |
|
1667 |
|
1668 evbuffer_free(evb); |
|
1669 } |
|
1670 |
|
1671 void |
|
1672 http_putrequest_done(struct evhttp_request *req, void *arg) |
|
1673 { |
|
1674 struct event_base *base = arg; |
|
1675 const char *what = "That ain't funny"; |
|
1676 |
|
1677 if (req == NULL) { |
|
1678 fprintf(stderr, "FAILED (timeout)\n"); |
|
1679 exit(1); |
|
1680 } |
|
1681 |
|
1682 if (evhttp_request_get_response_code(req) != HTTP_OK) { |
|
1683 |
|
1684 fprintf(stderr, "FAILED (response code)\n"); |
|
1685 exit(1); |
|
1686 } |
|
1687 |
|
1688 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) { |
|
1689 fprintf(stderr, "FAILED (content type)\n"); |
|
1690 exit(1); |
|
1691 } |
|
1692 |
|
1693 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) { |
|
1694 fprintf(stderr, "FAILED (length %lu vs %lu)\n", |
|
1695 (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what)); |
|
1696 exit(1); |
|
1697 } |
|
1698 |
|
1699 |
|
1700 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) { |
|
1701 fprintf(stderr, "FAILED (data)\n"); |
|
1702 exit(1); |
|
1703 } |
|
1704 |
|
1705 test_ok = 1; |
|
1706 event_base_loopexit(base, NULL); |
|
1707 } |
|
1708 |
|
1709 static void |
|
1710 http_failure_readcb(struct bufferevent *bev, void *arg) |
|
1711 { |
|
1712 const char *what = "400 Bad Request"; |
|
1713 if (evbuffer_contains(bufferevent_get_input(bev), what)) { |
|
1714 test_ok = 2; |
|
1715 bufferevent_disable(bev, EV_READ); |
|
1716 event_base_loopexit(arg, NULL); |
|
1717 } |
|
1718 } |
|
1719 |
|
1720 /* |
|
1721 * Testing that the HTTP server can deal with a malformed request. |
|
1722 */ |
|
1723 static void |
|
1724 http_failure_test(void *arg) |
|
1725 { |
|
1726 struct basic_test_data *data = arg; |
|
1727 struct bufferevent *bev; |
|
1728 evutil_socket_t fd; |
|
1729 const char *http_request; |
|
1730 ev_uint16_t port = 0; |
|
1731 |
|
1732 test_ok = 0; |
|
1733 |
|
1734 http = http_setup(&port, data->base); |
|
1735 |
|
1736 fd = http_connect("127.0.0.1", port); |
|
1737 |
|
1738 /* Stupid thing to send a request */ |
|
1739 bev = bufferevent_socket_new(data->base, fd, 0); |
|
1740 bufferevent_setcb(bev, http_failure_readcb, http_writecb, |
|
1741 http_errorcb, data->base); |
|
1742 |
|
1743 http_request = "illegal request\r\n"; |
|
1744 |
|
1745 bufferevent_write(bev, http_request, strlen(http_request)); |
|
1746 |
|
1747 event_base_dispatch(data->base); |
|
1748 |
|
1749 bufferevent_free(bev); |
|
1750 evutil_closesocket(fd); |
|
1751 |
|
1752 evhttp_free(http); |
|
1753 |
|
1754 tt_int_op(test_ok, ==, 2); |
|
1755 end: |
|
1756 ; |
|
1757 } |
|
1758 |
|
1759 static void |
|
1760 close_detect_done(struct evhttp_request *req, void *arg) |
|
1761 { |
|
1762 struct timeval tv; |
|
1763 tt_assert(req); |
|
1764 tt_assert(evhttp_request_get_response_code(req) == HTTP_OK); |
|
1765 |
|
1766 test_ok = 1; |
|
1767 |
|
1768 end: |
|
1769 evutil_timerclear(&tv); |
|
1770 tv.tv_sec = 3; |
|
1771 event_base_loopexit(arg, &tv); |
|
1772 } |
|
1773 |
|
1774 static void |
|
1775 close_detect_launch(evutil_socket_t fd, short what, void *arg) |
|
1776 { |
|
1777 struct evhttp_connection *evcon = arg; |
|
1778 struct event_base *base = evhttp_connection_get_base(evcon); |
|
1779 struct evhttp_request *req; |
|
1780 |
|
1781 req = evhttp_request_new(close_detect_done, base); |
|
1782 |
|
1783 /* Add the information that we care about */ |
|
1784 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
1785 |
|
1786 /* We give ownership of the request to the connection */ |
|
1787 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
|
1788 tt_fail_msg("Couldn't make request"); |
|
1789 } |
|
1790 } |
|
1791 |
|
1792 static void |
|
1793 close_detect_cb(struct evhttp_request *req, void *arg) |
|
1794 { |
|
1795 struct evhttp_connection *evcon = arg; |
|
1796 struct event_base *base = evhttp_connection_get_base(evcon); |
|
1797 struct timeval tv; |
|
1798 |
|
1799 if (req != NULL && evhttp_request_get_response_code(req) != HTTP_OK) { |
|
1800 tt_abort_msg("Failed"); |
|
1801 } |
|
1802 |
|
1803 evutil_timerclear(&tv); |
|
1804 tv.tv_sec = 3; /* longer than the http time out */ |
|
1805 |
|
1806 /* launch a new request on the persistent connection in 3 seconds */ |
|
1807 event_base_once(base, -1, EV_TIMEOUT, close_detect_launch, evcon, &tv); |
|
1808 end: |
|
1809 ; |
|
1810 } |
|
1811 |
|
1812 |
|
1813 static void |
|
1814 _http_close_detection(struct basic_test_data *data, int with_delay) |
|
1815 { |
|
1816 ev_uint16_t port = 0; |
|
1817 struct evhttp_connection *evcon = NULL; |
|
1818 struct evhttp_request *req = NULL; |
|
1819 |
|
1820 test_ok = 0; |
|
1821 http = http_setup(&port, data->base); |
|
1822 |
|
1823 /* 2 second timeout */ |
|
1824 evhttp_set_timeout(http, 1); |
|
1825 |
|
1826 evcon = evhttp_connection_base_new(data->base, NULL, |
|
1827 "127.0.0.1", port); |
|
1828 tt_assert(evcon); |
|
1829 delayed_client = evcon; |
|
1830 |
|
1831 /* |
|
1832 * At this point, we want to schedule a request to the HTTP |
|
1833 * server using our make request method. |
|
1834 */ |
|
1835 |
|
1836 req = evhttp_request_new(close_detect_cb, evcon); |
|
1837 |
|
1838 /* Add the information that we care about */ |
|
1839 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
1840 |
|
1841 /* We give ownership of the request to the connection */ |
|
1842 if (evhttp_make_request(evcon, |
|
1843 req, EVHTTP_REQ_GET, with_delay ? "/largedelay" : "/test") == -1) { |
|
1844 tt_abort_msg("couldn't make request"); |
|
1845 } |
|
1846 |
|
1847 event_base_dispatch(data->base); |
|
1848 |
|
1849 /* at this point, the http server should have no connection */ |
|
1850 tt_assert(TAILQ_FIRST(&http->connections) == NULL); |
|
1851 |
|
1852 end: |
|
1853 if (evcon) |
|
1854 evhttp_connection_free(evcon); |
|
1855 if (http) |
|
1856 evhttp_free(http); |
|
1857 } |
|
1858 static void |
|
1859 http_close_detection_test(void *arg) |
|
1860 { |
|
1861 _http_close_detection(arg, 0); |
|
1862 } |
|
1863 static void |
|
1864 http_close_detection_delay_test(void *arg) |
|
1865 { |
|
1866 _http_close_detection(arg, 1); |
|
1867 } |
|
1868 |
|
1869 static void |
|
1870 http_highport_test(void *arg) |
|
1871 { |
|
1872 struct basic_test_data *data = arg; |
|
1873 int i = -1; |
|
1874 struct evhttp *myhttp = NULL; |
|
1875 |
|
1876 /* Try a few different ports */ |
|
1877 for (i = 0; i < 50; ++i) { |
|
1878 myhttp = evhttp_new(data->base); |
|
1879 if (evhttp_bind_socket(myhttp, "127.0.0.1", 65535 - i) == 0) { |
|
1880 test_ok = 1; |
|
1881 evhttp_free(myhttp); |
|
1882 return; |
|
1883 } |
|
1884 evhttp_free(myhttp); |
|
1885 } |
|
1886 |
|
1887 tt_fail_msg("Couldn't get a high port"); |
|
1888 } |
|
1889 |
|
1890 static void |
|
1891 http_bad_header_test(void *ptr) |
|
1892 { |
|
1893 struct evkeyvalq headers; |
|
1894 |
|
1895 TAILQ_INIT(&headers); |
|
1896 |
|
1897 tt_want(evhttp_add_header(&headers, "One", "Two") == 0); |
|
1898 tt_want(evhttp_add_header(&headers, "One", "Two\r\n Three") == 0); |
|
1899 tt_want(evhttp_add_header(&headers, "One\r", "Two") == -1); |
|
1900 tt_want(evhttp_add_header(&headers, "One\n", "Two") == -1); |
|
1901 tt_want(evhttp_add_header(&headers, "One", "Two\r") == -1); |
|
1902 tt_want(evhttp_add_header(&headers, "One", "Two\n") == -1); |
|
1903 |
|
1904 evhttp_clear_headers(&headers); |
|
1905 } |
|
1906 |
|
1907 static int validate_header( |
|
1908 const struct evkeyvalq* headers, |
|
1909 const char *key, const char *value) |
|
1910 { |
|
1911 const char *real_val = evhttp_find_header(headers, key); |
|
1912 tt_assert(real_val != NULL); |
|
1913 tt_want(strcmp(real_val, value) == 0); |
|
1914 end: |
|
1915 return (0); |
|
1916 } |
|
1917 |
|
1918 static void |
|
1919 http_parse_query_test(void *ptr) |
|
1920 { |
|
1921 struct evkeyvalq headers; |
|
1922 int r; |
|
1923 |
|
1924 TAILQ_INIT(&headers); |
|
1925 |
|
1926 r = evhttp_parse_query("http://www.test.com/?q=test", &headers); |
|
1927 tt_want(validate_header(&headers, "q", "test") == 0); |
|
1928 tt_int_op(r, ==, 0); |
|
1929 evhttp_clear_headers(&headers); |
|
1930 |
|
1931 r = evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers); |
|
1932 tt_want(validate_header(&headers, "q", "test") == 0); |
|
1933 tt_want(validate_header(&headers, "foo", "bar") == 0); |
|
1934 tt_int_op(r, ==, 0); |
|
1935 evhttp_clear_headers(&headers); |
|
1936 |
|
1937 r = evhttp_parse_query("http://www.test.com/?q=test+foo", &headers); |
|
1938 tt_want(validate_header(&headers, "q", "test foo") == 0); |
|
1939 tt_int_op(r, ==, 0); |
|
1940 evhttp_clear_headers(&headers); |
|
1941 |
|
1942 r = evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers); |
|
1943 tt_want(validate_header(&headers, "q", "test\nfoo") == 0); |
|
1944 tt_int_op(r, ==, 0); |
|
1945 evhttp_clear_headers(&headers); |
|
1946 |
|
1947 r = evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers); |
|
1948 tt_want(validate_header(&headers, "q", "test\rfoo") == 0); |
|
1949 tt_int_op(r, ==, 0); |
|
1950 evhttp_clear_headers(&headers); |
|
1951 |
|
1952 r = evhttp_parse_query("http://www.test.com/?q=test&&q2", &headers); |
|
1953 tt_int_op(r, ==, -1); |
|
1954 evhttp_clear_headers(&headers); |
|
1955 |
|
1956 r = evhttp_parse_query("http://www.test.com/?q=test+this", &headers); |
|
1957 tt_want(validate_header(&headers, "q", "test this") == 0); |
|
1958 tt_int_op(r, ==, 0); |
|
1959 evhttp_clear_headers(&headers); |
|
1960 |
|
1961 r = evhttp_parse_query("http://www.test.com/?q=test&q2=foo", &headers); |
|
1962 tt_int_op(r, ==, 0); |
|
1963 tt_want(validate_header(&headers, "q", "test") == 0); |
|
1964 tt_want(validate_header(&headers, "q2", "foo") == 0); |
|
1965 evhttp_clear_headers(&headers); |
|
1966 |
|
1967 r = evhttp_parse_query("http://www.test.com/?q&q2=foo", &headers); |
|
1968 tt_int_op(r, ==, -1); |
|
1969 evhttp_clear_headers(&headers); |
|
1970 |
|
1971 r = evhttp_parse_query("http://www.test.com/?q=foo&q2", &headers); |
|
1972 tt_int_op(r, ==, -1); |
|
1973 evhttp_clear_headers(&headers); |
|
1974 |
|
1975 r = evhttp_parse_query("http://www.test.com/?q=foo&q2&q3=x", &headers); |
|
1976 tt_int_op(r, ==, -1); |
|
1977 evhttp_clear_headers(&headers); |
|
1978 |
|
1979 r = evhttp_parse_query("http://www.test.com/?q=&q2=&q3=", &headers); |
|
1980 tt_int_op(r, ==, 0); |
|
1981 tt_want(validate_header(&headers, "q", "") == 0); |
|
1982 tt_want(validate_header(&headers, "q2", "") == 0); |
|
1983 tt_want(validate_header(&headers, "q3", "") == 0); |
|
1984 evhttp_clear_headers(&headers); |
|
1985 |
|
1986 end: |
|
1987 evhttp_clear_headers(&headers); |
|
1988 } |
|
1989 |
|
1990 static void |
|
1991 http_parse_uri_test(void *ptr) |
|
1992 { |
|
1993 const int nonconform = (ptr != NULL); |
|
1994 const unsigned parse_flags = |
|
1995 nonconform ? EVHTTP_URI_NONCONFORMANT : 0; |
|
1996 struct evhttp_uri *uri = NULL; |
|
1997 char url_tmp[4096]; |
|
1998 #define URI_PARSE(uri) \ |
|
1999 evhttp_uri_parse_with_flags((uri), parse_flags) |
|
2000 |
|
2001 #define TT_URI(want) do { \ |
|
2002 char *ret = evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)); \ |
|
2003 tt_want(ret != NULL); \ |
|
2004 tt_want(ret == url_tmp); \ |
|
2005 if (strcmp(ret,want) != 0) \ |
|
2006 TT_FAIL(("\"%s\" != \"%s\"",ret,want)); \ |
|
2007 } while(0) |
|
2008 |
|
2009 tt_want(evhttp_uri_join(NULL, 0, 0) == NULL); |
|
2010 tt_want(evhttp_uri_join(NULL, url_tmp, 0) == NULL); |
|
2011 tt_want(evhttp_uri_join(NULL, url_tmp, sizeof(url_tmp)) == NULL); |
|
2012 |
|
2013 /* bad URIs: parsing */ |
|
2014 #define BAD(s) do { \ |
|
2015 if (URI_PARSE(s) != NULL) \ |
|
2016 TT_FAIL(("Expected error parsing \"%s\"",s)); \ |
|
2017 } while(0) |
|
2018 /* Nonconformant URIs we can parse: parsing */ |
|
2019 #define NCF(s) do { \ |
|
2020 uri = URI_PARSE(s); \ |
|
2021 if (uri != NULL && !nonconform) { \ |
|
2022 TT_FAIL(("Expected error parsing \"%s\"",s)); \ |
|
2023 } else if (uri == NULL && nonconform) { \ |
|
2024 TT_FAIL(("Couldn't parse nonconformant URI \"%s\"", \ |
|
2025 s)); \ |
|
2026 } \ |
|
2027 if (uri) { \ |
|
2028 tt_want(evhttp_uri_join(uri, url_tmp, \ |
|
2029 sizeof(url_tmp))); \ |
|
2030 evhttp_uri_free(uri); \ |
|
2031 } \ |
|
2032 } while(0) |
|
2033 |
|
2034 NCF("http://www.test.com/ why hello"); |
|
2035 NCF("http://www.test.com/why-hello\x01"); |
|
2036 NCF("http://www.test.com/why-hello?\x01"); |
|
2037 NCF("http://www.test.com/why-hello#\x01"); |
|
2038 BAD("http://www.\x01.test.com/why-hello"); |
|
2039 BAD("http://www.%7test.com/why-hello"); |
|
2040 NCF("http://www.test.com/why-hell%7o"); |
|
2041 BAD("h%3ttp://www.test.com/why-hello"); |
|
2042 NCF("http://www.test.com/why-hello%7"); |
|
2043 NCF("http://www.test.com/why-hell%7o"); |
|
2044 NCF("http://www.test.com/foo?ba%r"); |
|
2045 NCF("http://www.test.com/foo#ba%r"); |
|
2046 BAD("99:99/foo"); |
|
2047 BAD("http://www.test.com:999x/"); |
|
2048 BAD("http://www.test.com:x/"); |
|
2049 BAD("http://[hello-there]/"); |
|
2050 BAD("http://[::1]]/"); |
|
2051 BAD("http://[::1/"); |
|
2052 BAD("http://[foob/"); |
|
2053 BAD("http://[/"); |
|
2054 BAD("http://[ffff:ffff:ffff:ffff:Ffff:ffff:ffff:" |
|
2055 "ffff:ffff:ffff:ffff:ffff:ffff:ffff]/"); |
|
2056 BAD("http://[vX.foo]/"); |
|
2057 BAD("http://[vX.foo]/"); |
|
2058 BAD("http://[v.foo]/"); |
|
2059 BAD("http://[v5.fo%o]/"); |
|
2060 BAD("http://[v5X]/"); |
|
2061 BAD("http://[v5]/"); |
|
2062 BAD("http://[]/"); |
|
2063 BAD("http://f\x01red@www.example.com/"); |
|
2064 BAD("http://f%0red@www.example.com/"); |
|
2065 BAD("http://www.example.com:9999999999999999999999999999999999999/"); |
|
2066 BAD("http://www.example.com:hihi/"); |
|
2067 BAD("://www.example.com/"); |
|
2068 |
|
2069 /* bad URIs: joining */ |
|
2070 uri = evhttp_uri_new(); |
|
2071 tt_want(0==evhttp_uri_set_host(uri, "www.example.com")); |
|
2072 tt_want(evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)) != NULL); |
|
2073 /* not enough space: */ |
|
2074 tt_want(evhttp_uri_join(uri, url_tmp, 3) == NULL); |
|
2075 /* host is set, but path doesn't start with "/": */ |
|
2076 tt_want(0==evhttp_uri_set_path(uri, "hi_mom")); |
|
2077 tt_want(evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)) == NULL); |
|
2078 tt_want(evhttp_uri_join(uri, NULL, sizeof(url_tmp))==NULL); |
|
2079 tt_want(evhttp_uri_join(uri, url_tmp, 0)==NULL); |
|
2080 evhttp_uri_free(uri); |
|
2081 uri = URI_PARSE("mailto:foo@bar"); |
|
2082 tt_want(uri != NULL); |
|
2083 tt_want(evhttp_uri_get_host(uri) == NULL); |
|
2084 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2085 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2086 tt_want(!strcmp(evhttp_uri_get_scheme(uri), "mailto")); |
|
2087 tt_want(!strcmp(evhttp_uri_get_path(uri), "foo@bar")); |
|
2088 tt_want(evhttp_uri_get_query(uri) == NULL); |
|
2089 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2090 TT_URI("mailto:foo@bar"); |
|
2091 evhttp_uri_free(uri); |
|
2092 |
|
2093 uri = evhttp_uri_new(); |
|
2094 /* Bad URI usage: setting invalid values */ |
|
2095 tt_want(-1 == evhttp_uri_set_scheme(uri,"")); |
|
2096 tt_want(-1 == evhttp_uri_set_scheme(uri,"33")); |
|
2097 tt_want(-1 == evhttp_uri_set_scheme(uri,"hi!")); |
|
2098 tt_want(-1 == evhttp_uri_set_userinfo(uri,"hello@")); |
|
2099 tt_want(-1 == evhttp_uri_set_host(uri,"[1.2.3.4]")); |
|
2100 tt_want(-1 == evhttp_uri_set_host(uri,"[")); |
|
2101 tt_want(-1 == evhttp_uri_set_host(uri,"www.[foo].com")); |
|
2102 tt_want(-1 == evhttp_uri_set_port(uri,-3)); |
|
2103 tt_want(-1 == evhttp_uri_set_path(uri,"hello?world")); |
|
2104 tt_want(-1 == evhttp_uri_set_query(uri,"hello#world")); |
|
2105 tt_want(-1 == evhttp_uri_set_fragment(uri,"hello#world")); |
|
2106 /* Valid URI usage: setting valid values */ |
|
2107 tt_want(0 == evhttp_uri_set_scheme(uri,"http")); |
|
2108 tt_want(0 == evhttp_uri_set_scheme(uri,NULL)); |
|
2109 tt_want(0 == evhttp_uri_set_userinfo(uri,"username:pass")); |
|
2110 tt_want(0 == evhttp_uri_set_userinfo(uri,NULL)); |
|
2111 tt_want(0 == evhttp_uri_set_host(uri,"www.example.com")); |
|
2112 tt_want(0 == evhttp_uri_set_host(uri,"1.2.3.4")); |
|
2113 tt_want(0 == evhttp_uri_set_host(uri,"[1:2:3:4::]")); |
|
2114 tt_want(0 == evhttp_uri_set_host(uri,"[v7.wobblewobble]")); |
|
2115 tt_want(0 == evhttp_uri_set_host(uri,NULL)); |
|
2116 tt_want(0 == evhttp_uri_set_host(uri,"")); |
|
2117 tt_want(0 == evhttp_uri_set_port(uri, -1)); |
|
2118 tt_want(0 == evhttp_uri_set_port(uri, 80)); |
|
2119 tt_want(0 == evhttp_uri_set_port(uri, 65535)); |
|
2120 tt_want(0 == evhttp_uri_set_path(uri, "")); |
|
2121 tt_want(0 == evhttp_uri_set_path(uri, "/documents/public/index.html")); |
|
2122 tt_want(0 == evhttp_uri_set_path(uri, NULL)); |
|
2123 tt_want(0 == evhttp_uri_set_query(uri, "key=val&key2=val2")); |
|
2124 tt_want(0 == evhttp_uri_set_query(uri, "keyvalblarg")); |
|
2125 tt_want(0 == evhttp_uri_set_query(uri, "")); |
|
2126 tt_want(0 == evhttp_uri_set_query(uri, NULL)); |
|
2127 tt_want(0 == evhttp_uri_set_fragment(uri, "")); |
|
2128 tt_want(0 == evhttp_uri_set_fragment(uri, "here?i?am")); |
|
2129 tt_want(0 == evhttp_uri_set_fragment(uri, NULL)); |
|
2130 evhttp_uri_free(uri); |
|
2131 |
|
2132 /* Valid parsing */ |
|
2133 uri = URI_PARSE("http://www.test.com/?q=t%33est"); |
|
2134 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
|
2135 tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); |
|
2136 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
|
2137 tt_want(strcmp(evhttp_uri_get_query(uri), "q=t%33est") == 0); |
|
2138 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2139 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2140 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2141 TT_URI("http://www.test.com/?q=t%33est"); |
|
2142 evhttp_uri_free(uri); |
|
2143 |
|
2144 uri = URI_PARSE("http://%77ww.test.com"); |
|
2145 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
|
2146 tt_want(strcmp(evhttp_uri_get_host(uri), "%77ww.test.com") == 0); |
|
2147 tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); |
|
2148 tt_want(evhttp_uri_get_query(uri) == NULL); |
|
2149 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2150 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2151 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2152 TT_URI("http://%77ww.test.com"); |
|
2153 evhttp_uri_free(uri); |
|
2154 |
|
2155 uri = URI_PARSE("http://www.test.com?q=test"); |
|
2156 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
|
2157 tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); |
|
2158 tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); |
|
2159 tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); |
|
2160 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2161 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2162 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2163 TT_URI("http://www.test.com?q=test"); |
|
2164 evhttp_uri_free(uri); |
|
2165 |
|
2166 uri = URI_PARSE("http://www.test.com#fragment"); |
|
2167 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
|
2168 tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); |
|
2169 tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); |
|
2170 tt_want(evhttp_uri_get_query(uri) == NULL); |
|
2171 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2172 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2173 tt_want_str_op(evhttp_uri_get_fragment(uri), ==, "fragment"); |
|
2174 TT_URI("http://www.test.com#fragment"); |
|
2175 evhttp_uri_free(uri); |
|
2176 |
|
2177 uri = URI_PARSE("http://8000/"); |
|
2178 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
|
2179 tt_want(strcmp(evhttp_uri_get_host(uri), "8000") == 0); |
|
2180 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
|
2181 tt_want(evhttp_uri_get_query(uri) == NULL); |
|
2182 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2183 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2184 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2185 TT_URI("http://8000/"); |
|
2186 evhttp_uri_free(uri); |
|
2187 |
|
2188 uri = URI_PARSE("http://:8000/"); |
|
2189 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
|
2190 tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0); |
|
2191 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
|
2192 tt_want(evhttp_uri_get_query(uri) == NULL); |
|
2193 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2194 tt_want(evhttp_uri_get_port(uri) == 8000); |
|
2195 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2196 TT_URI("http://:8000/"); |
|
2197 evhttp_uri_free(uri); |
|
2198 |
|
2199 uri = URI_PARSE("http://www.test.com:/"); /* empty port */ |
|
2200 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
|
2201 tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); |
|
2202 tt_want_str_op(evhttp_uri_get_path(uri), ==, "/"); |
|
2203 tt_want(evhttp_uri_get_query(uri) == NULL); |
|
2204 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2205 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2206 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2207 TT_URI("http://www.test.com/"); |
|
2208 evhttp_uri_free(uri); |
|
2209 |
|
2210 uri = URI_PARSE("http://www.test.com:"); /* empty port 2 */ |
|
2211 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
|
2212 tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); |
|
2213 tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); |
|
2214 tt_want(evhttp_uri_get_query(uri) == NULL); |
|
2215 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2216 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2217 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2218 TT_URI("http://www.test.com"); |
|
2219 evhttp_uri_free(uri); |
|
2220 |
|
2221 uri = URI_PARSE("ftp://www.test.com/?q=test"); |
|
2222 tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0); |
|
2223 tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); |
|
2224 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
|
2225 tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); |
|
2226 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2227 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2228 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2229 TT_URI("ftp://www.test.com/?q=test"); |
|
2230 evhttp_uri_free(uri); |
|
2231 |
|
2232 uri = URI_PARSE("ftp://[::1]:999/?q=test"); |
|
2233 tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0); |
|
2234 tt_want(strcmp(evhttp_uri_get_host(uri), "[::1]") == 0); |
|
2235 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
|
2236 tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); |
|
2237 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2238 tt_want(evhttp_uri_get_port(uri) == 999); |
|
2239 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2240 TT_URI("ftp://[::1]:999/?q=test"); |
|
2241 evhttp_uri_free(uri); |
|
2242 |
|
2243 uri = URI_PARSE("ftp://[ff00::127.0.0.1]/?q=test"); |
|
2244 tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0); |
|
2245 tt_want(strcmp(evhttp_uri_get_host(uri), "[ff00::127.0.0.1]") == 0); |
|
2246 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
|
2247 tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); |
|
2248 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2249 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2250 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2251 TT_URI("ftp://[ff00::127.0.0.1]/?q=test"); |
|
2252 evhttp_uri_free(uri); |
|
2253 |
|
2254 uri = URI_PARSE("ftp://[v99.not_(any:time)_soon]/?q=test"); |
|
2255 tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0); |
|
2256 tt_want(strcmp(evhttp_uri_get_host(uri), "[v99.not_(any:time)_soon]") == 0); |
|
2257 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
|
2258 tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); |
|
2259 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2260 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2261 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2262 TT_URI("ftp://[v99.not_(any:time)_soon]/?q=test"); |
|
2263 evhttp_uri_free(uri); |
|
2264 |
|
2265 uri = URI_PARSE("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment"); |
|
2266 tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0); |
|
2267 tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user:pass") == 0); |
|
2268 tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0); |
|
2269 tt_want(evhttp_uri_get_port(uri) == 42); |
|
2270 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
|
2271 tt_want(strcmp(evhttp_uri_get_query(uri), "q=test&s=some+thing") == 0); |
|
2272 tt_want(strcmp(evhttp_uri_get_fragment(uri), "fragment") == 0); |
|
2273 TT_URI("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment"); |
|
2274 evhttp_uri_free(uri); |
|
2275 |
|
2276 uri = URI_PARSE("scheme://user@foo.com/#fragment"); |
|
2277 tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0); |
|
2278 tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user") == 0); |
|
2279 tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0); |
|
2280 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2281 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
|
2282 tt_want(evhttp_uri_get_query(uri) == NULL); |
|
2283 tt_want(strcmp(evhttp_uri_get_fragment(uri), "fragment") == 0); |
|
2284 TT_URI("scheme://user@foo.com/#fragment"); |
|
2285 evhttp_uri_free(uri); |
|
2286 |
|
2287 uri = URI_PARSE("scheme://%75ser@foo.com/#frag@ment"); |
|
2288 tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0); |
|
2289 tt_want(strcmp(evhttp_uri_get_userinfo(uri), "%75ser") == 0); |
|
2290 tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0); |
|
2291 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2292 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
|
2293 tt_want(evhttp_uri_get_query(uri) == NULL); |
|
2294 tt_want(strcmp(evhttp_uri_get_fragment(uri), "frag@ment") == 0); |
|
2295 TT_URI("scheme://%75ser@foo.com/#frag@ment"); |
|
2296 evhttp_uri_free(uri); |
|
2297 |
|
2298 uri = URI_PARSE("file:///some/path/to/the/file"); |
|
2299 tt_want(strcmp(evhttp_uri_get_scheme(uri), "file") == 0); |
|
2300 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2301 tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0); |
|
2302 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2303 tt_want(strcmp(evhttp_uri_get_path(uri), "/some/path/to/the/file") == 0); |
|
2304 tt_want(evhttp_uri_get_query(uri) == NULL); |
|
2305 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2306 TT_URI("file:///some/path/to/the/file"); |
|
2307 evhttp_uri_free(uri); |
|
2308 |
|
2309 uri = URI_PARSE("///some/path/to/the-file"); |
|
2310 tt_want(uri != NULL); |
|
2311 tt_want(evhttp_uri_get_scheme(uri) == NULL); |
|
2312 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2313 tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0); |
|
2314 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2315 tt_want(strcmp(evhttp_uri_get_path(uri), "/some/path/to/the-file") == 0); |
|
2316 tt_want(evhttp_uri_get_query(uri) == NULL); |
|
2317 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2318 TT_URI("///some/path/to/the-file"); |
|
2319 evhttp_uri_free(uri); |
|
2320 |
|
2321 uri = URI_PARSE("/s:ome/path/to/the-file?q=99#fred"); |
|
2322 tt_want(uri != NULL); |
|
2323 tt_want(evhttp_uri_get_scheme(uri) == NULL); |
|
2324 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2325 tt_want(evhttp_uri_get_host(uri) == NULL); |
|
2326 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2327 tt_want(strcmp(evhttp_uri_get_path(uri), "/s:ome/path/to/the-file") == 0); |
|
2328 tt_want(strcmp(evhttp_uri_get_query(uri), "q=99") == 0); |
|
2329 tt_want(strcmp(evhttp_uri_get_fragment(uri), "fred") == 0); |
|
2330 TT_URI("/s:ome/path/to/the-file?q=99#fred"); |
|
2331 evhttp_uri_free(uri); |
|
2332 |
|
2333 uri = URI_PARSE("relative/path/with/co:lon"); |
|
2334 tt_want(uri != NULL); |
|
2335 tt_want(evhttp_uri_get_scheme(uri) == NULL); |
|
2336 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2337 tt_want(evhttp_uri_get_host(uri) == NULL); |
|
2338 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2339 tt_want(strcmp(evhttp_uri_get_path(uri), "relative/path/with/co:lon") == 0); |
|
2340 tt_want(evhttp_uri_get_query(uri) == NULL); |
|
2341 tt_want(evhttp_uri_get_fragment(uri) == NULL); |
|
2342 TT_URI("relative/path/with/co:lon"); |
|
2343 evhttp_uri_free(uri); |
|
2344 |
|
2345 uri = URI_PARSE("bob?q=99&q2=q?33#fr?ed"); |
|
2346 tt_want(uri != NULL); |
|
2347 tt_want(evhttp_uri_get_scheme(uri) == NULL); |
|
2348 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2349 tt_want(evhttp_uri_get_host(uri) == NULL); |
|
2350 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2351 tt_want(strcmp(evhttp_uri_get_path(uri), "bob") == 0); |
|
2352 tt_want(strcmp(evhttp_uri_get_query(uri), "q=99&q2=q?33") == 0); |
|
2353 tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0); |
|
2354 TT_URI("bob?q=99&q2=q?33#fr?ed"); |
|
2355 evhttp_uri_free(uri); |
|
2356 |
|
2357 uri = URI_PARSE("#fr?ed"); |
|
2358 tt_want(uri != NULL); |
|
2359 tt_want(evhttp_uri_get_scheme(uri) == NULL); |
|
2360 tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
|
2361 tt_want(evhttp_uri_get_host(uri) == NULL); |
|
2362 tt_want(evhttp_uri_get_port(uri) == -1); |
|
2363 tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); |
|
2364 tt_want(evhttp_uri_get_query(uri) == NULL); |
|
2365 tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0); |
|
2366 TT_URI("#fr?ed"); |
|
2367 evhttp_uri_free(uri); |
|
2368 #undef URI_PARSE |
|
2369 #undef TT_URI |
|
2370 #undef BAD |
|
2371 } |
|
2372 |
|
2373 static void |
|
2374 http_uriencode_test(void *ptr) |
|
2375 { |
|
2376 char *s=NULL, *s2=NULL; |
|
2377 size_t sz; |
|
2378 |
|
2379 #define ENC(from,want,plus) do { \ |
|
2380 s = evhttp_uriencode((from), -1, (plus)); \ |
|
2381 tt_assert(s); \ |
|
2382 tt_str_op(s,==,(want)); \ |
|
2383 sz = -1; \ |
|
2384 s2 = evhttp_uridecode((s), (plus), &sz); \ |
|
2385 tt_assert(s2); \ |
|
2386 tt_str_op(s2,==,(from)); \ |
|
2387 tt_int_op(sz,==,strlen(from)); \ |
|
2388 free(s); \ |
|
2389 free(s2); \ |
|
2390 s = s2 = NULL; \ |
|
2391 } while (0) |
|
2392 |
|
2393 #define DEC(from,want,dp) do { \ |
|
2394 s = evhttp_uridecode((from),(dp),&sz); \ |
|
2395 tt_assert(s); \ |
|
2396 tt_str_op(s,==,(want)); \ |
|
2397 tt_int_op(sz,==,strlen(want)); \ |
|
2398 free(s); \ |
|
2399 s = NULL; \ |
|
2400 } while (0) |
|
2401 |
|
2402 #define OLD_DEC(from,want) do { \ |
|
2403 s = evhttp_decode_uri((from)); \ |
|
2404 tt_assert(s); \ |
|
2405 tt_str_op(s,==,(want)); \ |
|
2406 free(s); \ |
|
2407 s = NULL; \ |
|
2408 } while (0) |
|
2409 |
|
2410 |
|
2411 ENC("Hello", "Hello",0); |
|
2412 ENC("99", "99",0); |
|
2413 ENC("", "",0); |
|
2414 ENC( |
|
2415 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_", |
|
2416 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_",0); |
|
2417 ENC(" ", "%20",0); |
|
2418 ENC(" ", "+",1); |
|
2419 ENC("\xff\xf0\xe0", "%FF%F0%E0",0); |
|
2420 ENC("\x01\x19", "%01%19",1); |
|
2421 ENC("http://www.ietf.org/rfc/rfc3986.txt", |
|
2422 "http%3A%2F%2Fwww.ietf.org%2Frfc%2Frfc3986.txt",1); |
|
2423 |
|
2424 ENC("1+2=3", "1%2B2%3D3",1); |
|
2425 ENC("1+2=3", "1%2B2%3D3",0); |
|
2426 |
|
2427 /* Now try encoding with internal NULs. */ |
|
2428 s = evhttp_uriencode("hello\0world", 11, 0); |
|
2429 tt_assert(s); |
|
2430 tt_str_op(s,==,"hello%00world"); |
|
2431 free(s); |
|
2432 s = NULL; |
|
2433 |
|
2434 /* Now try out some decoding cases that we don't generate with |
|
2435 * encode_uri: Make sure that malformed stuff doesn't crash... */ |
|
2436 DEC("%%xhello th+ere \xff", |
|
2437 "%%xhello th+ere \xff", 0); |
|
2438 /* Make sure plus decoding works */ |
|
2439 DEC("plus+should%20work+", "plus should work ",1); |
|
2440 /* Try some lowercase hex */ |
|
2441 DEC("%f0%a0%b0", "\xf0\xa0\xb0",1); |
|
2442 |
|
2443 /* Try an internal NUL. */ |
|
2444 sz = 0; |
|
2445 s = evhttp_uridecode("%00%00x%00%00", 1, &sz); |
|
2446 tt_int_op(sz,==,5); |
|
2447 tt_assert(!memcmp(s, "\0\0x\0\0", 5)); |
|
2448 free(s); |
|
2449 s = NULL; |
|
2450 |
|
2451 /* Try with size == NULL */ |
|
2452 sz = 0; |
|
2453 s = evhttp_uridecode("%00%00x%00%00", 1, NULL); |
|
2454 tt_assert(!memcmp(s, "\0\0x\0\0", 5)); |
|
2455 free(s); |
|
2456 s = NULL; |
|
2457 |
|
2458 /* Test out the crazy old behavior of the deprecated |
|
2459 * evhttp_decode_uri */ |
|
2460 OLD_DEC("http://example.com/normal+path/?key=val+with+spaces", |
|
2461 "http://example.com/normal+path/?key=val with spaces"); |
|
2462 |
|
2463 end: |
|
2464 if (s) |
|
2465 free(s); |
|
2466 if (s2) |
|
2467 free(s2); |
|
2468 #undef ENC |
|
2469 #undef DEC |
|
2470 #undef OLD_DEC |
|
2471 } |
|
2472 |
|
2473 static void |
|
2474 http_base_test(void *ptr) |
|
2475 { |
|
2476 struct event_base *base = NULL; |
|
2477 struct bufferevent *bev; |
|
2478 evutil_socket_t fd; |
|
2479 const char *http_request; |
|
2480 ev_uint16_t port = 0; |
|
2481 |
|
2482 test_ok = 0; |
|
2483 base = event_base_new(); |
|
2484 http = http_setup(&port, base); |
|
2485 |
|
2486 fd = http_connect("127.0.0.1", port); |
|
2487 |
|
2488 /* Stupid thing to send a request */ |
|
2489 bev = bufferevent_socket_new(base, fd, 0); |
|
2490 bufferevent_setcb(bev, http_readcb, http_writecb, |
|
2491 http_errorcb, base); |
|
2492 bufferevent_base_set(base, bev); |
|
2493 |
|
2494 http_request = |
|
2495 "GET /test HTTP/1.1\r\n" |
|
2496 "Host: somehost\r\n" |
|
2497 "Connection: close\r\n" |
|
2498 "\r\n"; |
|
2499 |
|
2500 bufferevent_write(bev, http_request, strlen(http_request)); |
|
2501 |
|
2502 event_base_dispatch(base); |
|
2503 |
|
2504 bufferevent_free(bev); |
|
2505 evutil_closesocket(fd); |
|
2506 |
|
2507 evhttp_free(http); |
|
2508 |
|
2509 tt_int_op(test_ok, ==, 2); |
|
2510 |
|
2511 end: |
|
2512 if (base) |
|
2513 event_base_free(base); |
|
2514 } |
|
2515 |
|
2516 /* |
|
2517 * the server is just going to close the connection if it times out during |
|
2518 * reading the headers. |
|
2519 */ |
|
2520 |
|
2521 static void |
|
2522 http_incomplete_readcb(struct bufferevent *bev, void *arg) |
|
2523 { |
|
2524 test_ok = -1; |
|
2525 event_base_loopexit(exit_base,NULL); |
|
2526 } |
|
2527 |
|
2528 static void |
|
2529 http_incomplete_errorcb(struct bufferevent *bev, short what, void *arg) |
|
2530 { |
|
2531 if (what == (BEV_EVENT_READING|BEV_EVENT_EOF)) |
|
2532 test_ok++; |
|
2533 else |
|
2534 test_ok = -2; |
|
2535 event_base_loopexit(exit_base,NULL); |
|
2536 } |
|
2537 |
|
2538 static void |
|
2539 http_incomplete_writecb(struct bufferevent *bev, void *arg) |
|
2540 { |
|
2541 if (arg != NULL) { |
|
2542 evutil_socket_t fd = *(evutil_socket_t *)arg; |
|
2543 /* terminate the write side to simulate EOF */ |
|
2544 shutdown(fd, SHUT_WR); |
|
2545 } |
|
2546 if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) { |
|
2547 /* enable reading of the reply */ |
|
2548 bufferevent_enable(bev, EV_READ); |
|
2549 test_ok++; |
|
2550 } |
|
2551 } |
|
2552 |
|
2553 static void |
|
2554 _http_incomplete_test(struct basic_test_data *data, int use_timeout) |
|
2555 { |
|
2556 struct bufferevent *bev; |
|
2557 evutil_socket_t fd; |
|
2558 const char *http_request; |
|
2559 ev_uint16_t port = 0; |
|
2560 struct timeval tv_start, tv_end; |
|
2561 |
|
2562 exit_base = data->base; |
|
2563 |
|
2564 test_ok = 0; |
|
2565 |
|
2566 http = http_setup(&port, data->base); |
|
2567 evhttp_set_timeout(http, 1); |
|
2568 |
|
2569 fd = http_connect("127.0.0.1", port); |
|
2570 |
|
2571 /* Stupid thing to send a request */ |
|
2572 bev = bufferevent_socket_new(data->base, fd, 0); |
|
2573 bufferevent_setcb(bev, |
|
2574 http_incomplete_readcb, http_incomplete_writecb, |
|
2575 http_incomplete_errorcb, use_timeout ? NULL : &fd); |
|
2576 |
|
2577 http_request = |
|
2578 "GET /test HTTP/1.1\r\n" |
|
2579 "Host: somehost\r\n"; |
|
2580 |
|
2581 bufferevent_write(bev, http_request, strlen(http_request)); |
|
2582 |
|
2583 evutil_gettimeofday(&tv_start, NULL); |
|
2584 |
|
2585 event_base_dispatch(data->base); |
|
2586 |
|
2587 evutil_gettimeofday(&tv_end, NULL); |
|
2588 evutil_timersub(&tv_end, &tv_start, &tv_end); |
|
2589 |
|
2590 bufferevent_free(bev); |
|
2591 if (use_timeout) { |
|
2592 evutil_closesocket(fd); |
|
2593 } |
|
2594 |
|
2595 evhttp_free(http); |
|
2596 |
|
2597 if (use_timeout && tv_end.tv_sec >= 3) { |
|
2598 tt_abort_msg("time"); |
|
2599 } else if (!use_timeout && tv_end.tv_sec >= 1) { |
|
2600 /* we should be done immediately */ |
|
2601 tt_abort_msg("time"); |
|
2602 } |
|
2603 |
|
2604 tt_int_op(test_ok, ==, 2); |
|
2605 end: |
|
2606 ; |
|
2607 } |
|
2608 static void |
|
2609 http_incomplete_test(void *arg) |
|
2610 { |
|
2611 _http_incomplete_test(arg, 0); |
|
2612 } |
|
2613 static void |
|
2614 http_incomplete_timeout_test(void *arg) |
|
2615 { |
|
2616 _http_incomplete_test(arg, 1); |
|
2617 } |
|
2618 |
|
2619 /* |
|
2620 * the server is going to reply with chunked data. |
|
2621 */ |
|
2622 |
|
2623 static void |
|
2624 http_chunked_readcb(struct bufferevent *bev, void *arg) |
|
2625 { |
|
2626 /* nothing here */ |
|
2627 } |
|
2628 |
|
2629 static void |
|
2630 http_chunked_errorcb(struct bufferevent *bev, short what, void *arg) |
|
2631 { |
|
2632 if (!test_ok) |
|
2633 goto out; |
|
2634 |
|
2635 test_ok = -1; |
|
2636 |
|
2637 if ((what & BEV_EVENT_EOF) != 0) { |
|
2638 struct evhttp_request *req = evhttp_request_new(NULL, NULL); |
|
2639 const char *header; |
|
2640 enum message_read_status done; |
|
2641 |
|
2642 /* req->kind = EVHTTP_RESPONSE; */ |
|
2643 done = evhttp_parse_firstline(req, bufferevent_get_input(bev)); |
|
2644 if (done != ALL_DATA_READ) |
|
2645 goto out; |
|
2646 |
|
2647 done = evhttp_parse_headers(req, bufferevent_get_input(bev)); |
|
2648 if (done != ALL_DATA_READ) |
|
2649 goto out; |
|
2650 |
|
2651 header = evhttp_find_header(evhttp_request_get_input_headers(req), "Transfer-Encoding"); |
|
2652 if (header == NULL || strcmp(header, "chunked")) |
|
2653 goto out; |
|
2654 |
|
2655 header = evhttp_find_header(evhttp_request_get_input_headers(req), "Connection"); |
|
2656 if (header == NULL || strcmp(header, "close")) |
|
2657 goto out; |
|
2658 |
|
2659 header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF); |
|
2660 if (header == NULL) |
|
2661 goto out; |
|
2662 /* 13 chars */ |
|
2663 if (strcmp(header, "d")) { |
|
2664 free((void*)header); |
|
2665 goto out; |
|
2666 } |
|
2667 free((void*)header); |
|
2668 |
|
2669 if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 13), |
|
2670 "This is funny", 13)) |
|
2671 goto out; |
|
2672 |
|
2673 evbuffer_drain(bufferevent_get_input(bev), 13 + 2); |
|
2674 |
|
2675 header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF); |
|
2676 if (header == NULL) |
|
2677 goto out; |
|
2678 /* 18 chars */ |
|
2679 if (strcmp(header, "12")) |
|
2680 goto out; |
|
2681 free((char *)header); |
|
2682 |
|
2683 if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 18), |
|
2684 "but not hilarious.", 18)) |
|
2685 goto out; |
|
2686 |
|
2687 evbuffer_drain(bufferevent_get_input(bev), 18 + 2); |
|
2688 |
|
2689 header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF); |
|
2690 if (header == NULL) |
|
2691 goto out; |
|
2692 /* 8 chars */ |
|
2693 if (strcmp(header, "8")) { |
|
2694 free((void*)header); |
|
2695 goto out; |
|
2696 } |
|
2697 free((char *)header); |
|
2698 |
|
2699 if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 8), |
|
2700 "bwv 1052.", 8)) |
|
2701 goto out; |
|
2702 |
|
2703 evbuffer_drain(bufferevent_get_input(bev), 8 + 2); |
|
2704 |
|
2705 header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF); |
|
2706 if (header == NULL) |
|
2707 goto out; |
|
2708 /* 0 chars */ |
|
2709 if (strcmp(header, "0")) { |
|
2710 free((void*)header); |
|
2711 goto out; |
|
2712 } |
|
2713 free((void *)header); |
|
2714 |
|
2715 test_ok = 2; |
|
2716 |
|
2717 evhttp_request_free(req); |
|
2718 } |
|
2719 |
|
2720 out: |
|
2721 event_base_loopexit(arg, NULL); |
|
2722 } |
|
2723 |
|
2724 static void |
|
2725 http_chunked_writecb(struct bufferevent *bev, void *arg) |
|
2726 { |
|
2727 if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) { |
|
2728 /* enable reading of the reply */ |
|
2729 bufferevent_enable(bev, EV_READ); |
|
2730 test_ok++; |
|
2731 } |
|
2732 } |
|
2733 |
|
2734 static void |
|
2735 http_chunked_request_done(struct evhttp_request *req, void *arg) |
|
2736 { |
|
2737 if (evhttp_request_get_response_code(req) != HTTP_OK) { |
|
2738 fprintf(stderr, "FAILED\n"); |
|
2739 exit(1); |
|
2740 } |
|
2741 |
|
2742 if (evhttp_find_header(evhttp_request_get_input_headers(req), |
|
2743 "Transfer-Encoding") == NULL) { |
|
2744 fprintf(stderr, "FAILED\n"); |
|
2745 exit(1); |
|
2746 } |
|
2747 |
|
2748 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 13 + 18 + 8) { |
|
2749 fprintf(stderr, "FAILED\n"); |
|
2750 exit(1); |
|
2751 } |
|
2752 |
|
2753 if (strncmp((char *)evbuffer_pullup(evhttp_request_get_input_buffer(req), 13 + 18 + 8), |
|
2754 "This is funnybut not hilarious.bwv 1052", |
|
2755 13 + 18 + 8)) { |
|
2756 fprintf(stderr, "FAILED\n"); |
|
2757 exit(1); |
|
2758 } |
|
2759 |
|
2760 test_ok = 1; |
|
2761 event_base_loopexit(arg, NULL); |
|
2762 } |
|
2763 |
|
2764 static void |
|
2765 http_chunk_out_test(void *arg) |
|
2766 { |
|
2767 struct basic_test_data *data = arg; |
|
2768 struct bufferevent *bev; |
|
2769 evutil_socket_t fd; |
|
2770 const char *http_request; |
|
2771 ev_uint16_t port = 0; |
|
2772 struct timeval tv_start, tv_end; |
|
2773 struct evhttp_connection *evcon = NULL; |
|
2774 struct evhttp_request *req = NULL; |
|
2775 int i; |
|
2776 |
|
2777 exit_base = data->base; |
|
2778 test_ok = 0; |
|
2779 |
|
2780 http = http_setup(&port, data->base); |
|
2781 |
|
2782 fd = http_connect("127.0.0.1", port); |
|
2783 |
|
2784 /* Stupid thing to send a request */ |
|
2785 bev = bufferevent_socket_new(data->base, fd, 0); |
|
2786 bufferevent_setcb(bev, |
|
2787 http_chunked_readcb, http_chunked_writecb, |
|
2788 http_chunked_errorcb, data->base); |
|
2789 |
|
2790 http_request = |
|
2791 "GET /chunked HTTP/1.1\r\n" |
|
2792 "Host: somehost\r\n" |
|
2793 "Connection: close\r\n" |
|
2794 "\r\n"; |
|
2795 |
|
2796 bufferevent_write(bev, http_request, strlen(http_request)); |
|
2797 |
|
2798 evutil_gettimeofday(&tv_start, NULL); |
|
2799 |
|
2800 event_base_dispatch(data->base); |
|
2801 |
|
2802 bufferevent_free(bev); |
|
2803 |
|
2804 evutil_gettimeofday(&tv_end, NULL); |
|
2805 evutil_timersub(&tv_end, &tv_start, &tv_end); |
|
2806 |
|
2807 tt_int_op(tv_end.tv_sec, <, 1); |
|
2808 |
|
2809 tt_int_op(test_ok, ==, 2); |
|
2810 |
|
2811 /* now try again with the regular connection object */ |
|
2812 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
|
2813 tt_assert(evcon); |
|
2814 |
|
2815 /* make two requests to check the keepalive behavior */ |
|
2816 for (i = 0; i < 2; i++) { |
|
2817 test_ok = 0; |
|
2818 req = evhttp_request_new(http_chunked_request_done,data->base); |
|
2819 |
|
2820 /* Add the information that we care about */ |
|
2821 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
2822 |
|
2823 /* We give ownership of the request to the connection */ |
|
2824 if (evhttp_make_request(evcon, req, |
|
2825 EVHTTP_REQ_GET, "/chunked") == -1) { |
|
2826 tt_abort_msg("Couldn't make request"); |
|
2827 } |
|
2828 |
|
2829 event_base_dispatch(data->base); |
|
2830 |
|
2831 tt_assert(test_ok == 1); |
|
2832 } |
|
2833 |
|
2834 end: |
|
2835 if (evcon) |
|
2836 evhttp_connection_free(evcon); |
|
2837 if (http) |
|
2838 evhttp_free(http); |
|
2839 } |
|
2840 |
|
2841 static void |
|
2842 http_stream_out_test(void *arg) |
|
2843 { |
|
2844 struct basic_test_data *data = arg; |
|
2845 ev_uint16_t port = 0; |
|
2846 struct evhttp_connection *evcon = NULL; |
|
2847 struct evhttp_request *req = NULL; |
|
2848 |
|
2849 test_ok = 0; |
|
2850 exit_base = data->base; |
|
2851 |
|
2852 http = http_setup(&port, data->base); |
|
2853 |
|
2854 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
|
2855 tt_assert(evcon); |
|
2856 |
|
2857 /* |
|
2858 * At this point, we want to schedule a request to the HTTP |
|
2859 * server using our make request method. |
|
2860 */ |
|
2861 |
|
2862 req = evhttp_request_new(http_request_done, |
|
2863 (void *)"This is funnybut not hilarious.bwv 1052"); |
|
2864 |
|
2865 /* Add the information that we care about */ |
|
2866 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
2867 |
|
2868 /* We give ownership of the request to the connection */ |
|
2869 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/streamed") |
|
2870 == -1) { |
|
2871 tt_abort_msg("Couldn't make request"); |
|
2872 } |
|
2873 |
|
2874 event_base_dispatch(data->base); |
|
2875 |
|
2876 end: |
|
2877 if (evcon) |
|
2878 evhttp_connection_free(evcon); |
|
2879 if (http) |
|
2880 evhttp_free(http); |
|
2881 } |
|
2882 |
|
2883 static void |
|
2884 http_stream_in_chunk(struct evhttp_request *req, void *arg) |
|
2885 { |
|
2886 struct evbuffer *reply = arg; |
|
2887 |
|
2888 if (evhttp_request_get_response_code(req) != HTTP_OK) { |
|
2889 fprintf(stderr, "FAILED\n"); |
|
2890 exit(1); |
|
2891 } |
|
2892 |
|
2893 evbuffer_add_buffer(reply, evhttp_request_get_input_buffer(req)); |
|
2894 } |
|
2895 |
|
2896 static void |
|
2897 http_stream_in_done(struct evhttp_request *req, void *arg) |
|
2898 { |
|
2899 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 0) { |
|
2900 fprintf(stderr, "FAILED\n"); |
|
2901 exit(1); |
|
2902 } |
|
2903 |
|
2904 event_base_loopexit(exit_base, NULL); |
|
2905 } |
|
2906 |
|
2907 /** |
|
2908 * Makes a request and reads the response in chunks. |
|
2909 */ |
|
2910 static void |
|
2911 _http_stream_in_test(struct basic_test_data *data, char const *url, |
|
2912 size_t expected_len, char const *expected) |
|
2913 { |
|
2914 struct evhttp_connection *evcon; |
|
2915 struct evbuffer *reply = evbuffer_new(); |
|
2916 struct evhttp_request *req = NULL; |
|
2917 ev_uint16_t port = 0; |
|
2918 |
|
2919 exit_base = data->base; |
|
2920 http = http_setup(&port, data->base); |
|
2921 |
|
2922 evcon = evhttp_connection_base_new(data->base, NULL,"127.0.0.1", port); |
|
2923 tt_assert(evcon); |
|
2924 |
|
2925 req = evhttp_request_new(http_stream_in_done, reply); |
|
2926 evhttp_request_set_chunked_cb(req, http_stream_in_chunk); |
|
2927 |
|
2928 /* We give ownership of the request to the connection */ |
|
2929 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, url) == -1) { |
|
2930 tt_abort_msg("Couldn't make request"); |
|
2931 } |
|
2932 |
|
2933 event_base_dispatch(data->base); |
|
2934 |
|
2935 if (evbuffer_get_length(reply) != expected_len) { |
|
2936 TT_DIE(("reply length %lu; expected %lu; FAILED (%s)\n", |
|
2937 (unsigned long)evbuffer_get_length(reply), |
|
2938 (unsigned long)expected_len, |
|
2939 (char*)evbuffer_pullup(reply, -1))); |
|
2940 } |
|
2941 |
|
2942 if (memcmp(evbuffer_pullup(reply, -1), expected, expected_len) != 0) { |
|
2943 tt_abort_msg("Memory mismatch"); |
|
2944 } |
|
2945 |
|
2946 test_ok = 1; |
|
2947 end: |
|
2948 if (reply) |
|
2949 evbuffer_free(reply); |
|
2950 if (evcon) |
|
2951 evhttp_connection_free(evcon); |
|
2952 if (http) |
|
2953 evhttp_free(http); |
|
2954 } |
|
2955 |
|
2956 static void |
|
2957 http_stream_in_test(void *arg) |
|
2958 { |
|
2959 _http_stream_in_test(arg, "/chunked", 13 + 18 + 8, |
|
2960 "This is funnybut not hilarious.bwv 1052"); |
|
2961 |
|
2962 _http_stream_in_test(arg, "/test", strlen(BASIC_REQUEST_BODY), |
|
2963 BASIC_REQUEST_BODY); |
|
2964 } |
|
2965 |
|
2966 static void |
|
2967 http_stream_in_cancel_chunk(struct evhttp_request *req, void *arg) |
|
2968 { |
|
2969 tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_OK); |
|
2970 |
|
2971 end: |
|
2972 evhttp_cancel_request(req); |
|
2973 event_base_loopexit(arg, NULL); |
|
2974 } |
|
2975 |
|
2976 static void |
|
2977 http_stream_in_cancel_done(struct evhttp_request *req, void *arg) |
|
2978 { |
|
2979 /* should never be called */ |
|
2980 tt_fail_msg("In cancel done"); |
|
2981 } |
|
2982 |
|
2983 static void |
|
2984 http_stream_in_cancel_test(void *arg) |
|
2985 { |
|
2986 struct basic_test_data *data = arg; |
|
2987 struct evhttp_connection *evcon; |
|
2988 struct evhttp_request *req = NULL; |
|
2989 ev_uint16_t port = 0; |
|
2990 |
|
2991 http = http_setup(&port, data->base); |
|
2992 |
|
2993 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
|
2994 tt_assert(evcon); |
|
2995 |
|
2996 req = evhttp_request_new(http_stream_in_cancel_done, data->base); |
|
2997 evhttp_request_set_chunked_cb(req, http_stream_in_cancel_chunk); |
|
2998 |
|
2999 /* We give ownership of the request to the connection */ |
|
3000 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/chunked") == -1) { |
|
3001 tt_abort_msg("Couldn't make request"); |
|
3002 } |
|
3003 |
|
3004 event_base_dispatch(data->base); |
|
3005 |
|
3006 test_ok = 1; |
|
3007 end: |
|
3008 evhttp_connection_free(evcon); |
|
3009 evhttp_free(http); |
|
3010 |
|
3011 } |
|
3012 |
|
3013 static void |
|
3014 http_connection_fail_done(struct evhttp_request *req, void *arg) |
|
3015 { |
|
3016 /* An ENETUNREACH error results in an unrecoverable |
|
3017 * evhttp_connection error (see evhttp_connection_fail()). The |
|
3018 * connection will be reset, and the user will be notified with a NULL |
|
3019 * req parameter. */ |
|
3020 tt_assert(!req); |
|
3021 |
|
3022 test_ok = 1; |
|
3023 |
|
3024 end: |
|
3025 event_base_loopexit(arg, NULL); |
|
3026 } |
|
3027 |
|
3028 /* Test unrecoverable evhttp_connection errors by generating an ENETUNREACH |
|
3029 * error on connection. */ |
|
3030 static void |
|
3031 http_connection_fail_test(void *arg) |
|
3032 { |
|
3033 struct basic_test_data *data = arg; |
|
3034 ev_uint16_t port = 0; |
|
3035 struct evhttp_connection *evcon = NULL; |
|
3036 struct evhttp_request *req = NULL; |
|
3037 |
|
3038 exit_base = data->base; |
|
3039 test_ok = 0; |
|
3040 |
|
3041 /* auto detect a port */ |
|
3042 http = http_setup(&port, data->base); |
|
3043 evhttp_free(http); |
|
3044 http = NULL; |
|
3045 |
|
3046 /* Pick an unroutable address. This administratively scoped multicast |
|
3047 * address should do when working with TCP. */ |
|
3048 evcon = evhttp_connection_base_new(data->base, NULL, "239.10.20.30", 80); |
|
3049 tt_assert(evcon); |
|
3050 |
|
3051 /* |
|
3052 * At this point, we want to schedule an HTTP GET request |
|
3053 * server using our make request method. |
|
3054 */ |
|
3055 |
|
3056 req = evhttp_request_new(http_connection_fail_done, data->base); |
|
3057 tt_assert(req); |
|
3058 |
|
3059 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/") == -1) { |
|
3060 tt_abort_msg("Couldn't make request"); |
|
3061 } |
|
3062 |
|
3063 event_base_dispatch(data->base); |
|
3064 |
|
3065 tt_int_op(test_ok, ==, 1); |
|
3066 |
|
3067 end: |
|
3068 if (evcon) |
|
3069 evhttp_connection_free(evcon); |
|
3070 } |
|
3071 |
|
3072 static void |
|
3073 http_connection_retry_done(struct evhttp_request *req, void *arg) |
|
3074 { |
|
3075 tt_assert(req); |
|
3076 tt_int_op(evhttp_request_get_response_code(req), !=, HTTP_OK); |
|
3077 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") != NULL) { |
|
3078 tt_abort_msg("(content type)\n"); |
|
3079 } |
|
3080 |
|
3081 tt_uint_op(evbuffer_get_length(evhttp_request_get_input_buffer(req)), ==, 0); |
|
3082 |
|
3083 test_ok = 1; |
|
3084 end: |
|
3085 event_base_loopexit(arg,NULL); |
|
3086 } |
|
3087 |
|
3088 static struct event_base *http_make_web_server_base=NULL; |
|
3089 static void |
|
3090 http_make_web_server(evutil_socket_t fd, short what, void *arg) |
|
3091 { |
|
3092 ev_uint16_t port = *(ev_uint16_t*)arg; |
|
3093 http = http_setup(&port, http_make_web_server_base); |
|
3094 } |
|
3095 |
|
3096 static void |
|
3097 http_connection_retry_test(void *arg) |
|
3098 { |
|
3099 struct basic_test_data *data = arg; |
|
3100 ev_uint16_t port = 0; |
|
3101 struct evhttp_connection *evcon = NULL; |
|
3102 struct evhttp_request *req = NULL; |
|
3103 struct timeval tv, tv_start, tv_end; |
|
3104 |
|
3105 exit_base = data->base; |
|
3106 test_ok = 0; |
|
3107 |
|
3108 /* auto detect a port */ |
|
3109 http = http_setup(&port, data->base); |
|
3110 evhttp_free(http); |
|
3111 http = NULL; |
|
3112 |
|
3113 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
|
3114 tt_assert(evcon); |
|
3115 |
|
3116 evhttp_connection_set_timeout(evcon, 1); |
|
3117 /* also bind to local host */ |
|
3118 evhttp_connection_set_local_address(evcon, "127.0.0.1"); |
|
3119 |
|
3120 /* |
|
3121 * At this point, we want to schedule an HTTP GET request |
|
3122 * server using our make request method. |
|
3123 */ |
|
3124 |
|
3125 req = evhttp_request_new(http_connection_retry_done, data->base); |
|
3126 tt_assert(req); |
|
3127 |
|
3128 /* Add the information that we care about */ |
|
3129 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
3130 |
|
3131 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
|
3132 "/?arg=val") == -1) { |
|
3133 tt_abort_msg("Couldn't make request"); |
|
3134 } |
|
3135 |
|
3136 evutil_gettimeofday(&tv_start, NULL); |
|
3137 event_base_dispatch(data->base); |
|
3138 evutil_gettimeofday(&tv_end, NULL); |
|
3139 evutil_timersub(&tv_end, &tv_start, &tv_end); |
|
3140 tt_int_op(tv_end.tv_sec, <, 1); |
|
3141 |
|
3142 tt_int_op(test_ok, ==, 1); |
|
3143 |
|
3144 /* |
|
3145 * now test the same but with retries |
|
3146 */ |
|
3147 test_ok = 0; |
|
3148 |
|
3149 evhttp_connection_set_timeout(evcon, 1); |
|
3150 evhttp_connection_set_retries(evcon, 1); |
|
3151 |
|
3152 req = evhttp_request_new(http_connection_retry_done, data->base); |
|
3153 tt_assert(req); |
|
3154 |
|
3155 /* Add the information that we care about */ |
|
3156 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
3157 |
|
3158 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
|
3159 "/?arg=val") == -1) { |
|
3160 tt_abort_msg("Couldn't make request"); |
|
3161 } |
|
3162 |
|
3163 evutil_gettimeofday(&tv_start, NULL); |
|
3164 event_base_dispatch(data->base); |
|
3165 evutil_gettimeofday(&tv_end, NULL); |
|
3166 evutil_timersub(&tv_end, &tv_start, &tv_end); |
|
3167 tt_int_op(tv_end.tv_sec, >, 1); |
|
3168 tt_int_op(tv_end.tv_sec, <, 6); |
|
3169 |
|
3170 tt_assert(test_ok == 1); |
|
3171 |
|
3172 /* |
|
3173 * now test the same but with retries and give it a web server |
|
3174 * at the end |
|
3175 */ |
|
3176 test_ok = 0; |
|
3177 |
|
3178 evhttp_connection_set_timeout(evcon, 1); |
|
3179 evhttp_connection_set_retries(evcon, 3); |
|
3180 |
|
3181 req = evhttp_request_new(http_dispatcher_test_done, data->base); |
|
3182 tt_assert(req); |
|
3183 |
|
3184 /* Add the information that we care about */ |
|
3185 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
3186 |
|
3187 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
|
3188 "/?arg=val") == -1) { |
|
3189 tt_abort_msg("Couldn't make request"); |
|
3190 } |
|
3191 |
|
3192 /* start up a web server one second after the connection tried |
|
3193 * to send a request |
|
3194 */ |
|
3195 evutil_timerclear(&tv); |
|
3196 tv.tv_sec = 1; |
|
3197 http_make_web_server_base = data->base; |
|
3198 event_base_once(data->base, -1, EV_TIMEOUT, http_make_web_server, &port, &tv); |
|
3199 |
|
3200 evutil_gettimeofday(&tv_start, NULL); |
|
3201 event_base_dispatch(data->base); |
|
3202 evutil_gettimeofday(&tv_end, NULL); |
|
3203 |
|
3204 evutil_timersub(&tv_end, &tv_start, &tv_end); |
|
3205 |
|
3206 tt_int_op(tv_end.tv_sec, >, 1); |
|
3207 tt_int_op(tv_end.tv_sec, <, 6); |
|
3208 |
|
3209 tt_int_op(test_ok, ==, 1); |
|
3210 |
|
3211 end: |
|
3212 if (evcon) |
|
3213 evhttp_connection_free(evcon); |
|
3214 if (http) |
|
3215 evhttp_free(http); |
|
3216 } |
|
3217 |
|
3218 static void |
|
3219 http_primitives(void *ptr) |
|
3220 { |
|
3221 char *escaped = NULL; |
|
3222 struct evhttp *http = NULL; |
|
3223 |
|
3224 escaped = evhttp_htmlescape("<script>"); |
|
3225 tt_assert(escaped); |
|
3226 tt_str_op(escaped, ==, "<script>"); |
|
3227 free(escaped); |
|
3228 |
|
3229 escaped = evhttp_htmlescape("\"\'&"); |
|
3230 tt_assert(escaped); |
|
3231 tt_str_op(escaped, ==, ""'&"); |
|
3232 |
|
3233 http = evhttp_new(NULL); |
|
3234 tt_assert(http); |
|
3235 tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, NULL), ==, 0); |
|
3236 tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, NULL), ==, -1); |
|
3237 tt_int_op(evhttp_del_cb(http, "/test"), ==, 0); |
|
3238 tt_int_op(evhttp_del_cb(http, "/test"), ==, -1); |
|
3239 tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, NULL), ==, 0); |
|
3240 |
|
3241 end: |
|
3242 if (escaped) |
|
3243 free(escaped); |
|
3244 if (http) |
|
3245 evhttp_free(http); |
|
3246 } |
|
3247 |
|
3248 static void |
|
3249 http_multi_line_header_test(void *arg) |
|
3250 { |
|
3251 struct basic_test_data *data = arg; |
|
3252 struct bufferevent *bev= NULL; |
|
3253 evutil_socket_t fd = -1; |
|
3254 const char *http_start_request; |
|
3255 ev_uint16_t port = 0; |
|
3256 |
|
3257 test_ok = 0; |
|
3258 |
|
3259 http = http_setup(&port, data->base); |
|
3260 |
|
3261 fd = http_connect("127.0.0.1", port); |
|
3262 |
|
3263 /* Stupid thing to send a request */ |
|
3264 bev = bufferevent_socket_new(data->base, fd, 0); |
|
3265 bufferevent_setcb(bev, http_readcb, http_writecb, |
|
3266 http_errorcb, data->base); |
|
3267 |
|
3268 http_start_request = |
|
3269 "GET /test HTTP/1.1\r\n" |
|
3270 "Host: somehost\r\n" |
|
3271 "Connection: close\r\n" |
|
3272 "X-Multi: aaaaaaaa\r\n" |
|
3273 " a\r\n" |
|
3274 "\tEND\r\n" |
|
3275 "X-Last: last\r\n" |
|
3276 "\r\n"; |
|
3277 |
|
3278 bufferevent_write(bev, http_start_request, strlen(http_start_request)); |
|
3279 |
|
3280 event_base_dispatch(data->base); |
|
3281 |
|
3282 tt_int_op(test_ok, ==, 4); |
|
3283 end: |
|
3284 if (bev) |
|
3285 bufferevent_free(bev); |
|
3286 if (fd >= 0) |
|
3287 evutil_closesocket(fd); |
|
3288 if (http) |
|
3289 evhttp_free(http); |
|
3290 } |
|
3291 |
|
3292 static void |
|
3293 http_request_bad(struct evhttp_request *req, void *arg) |
|
3294 { |
|
3295 if (req != NULL) { |
|
3296 fprintf(stderr, "FAILED\n"); |
|
3297 exit(1); |
|
3298 } |
|
3299 |
|
3300 test_ok = 1; |
|
3301 event_base_loopexit(arg, NULL); |
|
3302 } |
|
3303 |
|
3304 static void |
|
3305 http_negative_content_length_test(void *arg) |
|
3306 { |
|
3307 struct basic_test_data *data = arg; |
|
3308 ev_uint16_t port = 0; |
|
3309 struct evhttp_connection *evcon = NULL; |
|
3310 struct evhttp_request *req = NULL; |
|
3311 |
|
3312 test_ok = 0; |
|
3313 |
|
3314 http = http_setup(&port, data->base); |
|
3315 |
|
3316 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
|
3317 tt_assert(evcon); |
|
3318 |
|
3319 /* |
|
3320 * At this point, we want to schedule a request to the HTTP |
|
3321 * server using our make request method. |
|
3322 */ |
|
3323 |
|
3324 req = evhttp_request_new(http_request_bad, data->base); |
|
3325 |
|
3326 /* Cause the response to have a negative content-length */ |
|
3327 evhttp_add_header(evhttp_request_get_output_headers(req), "X-Negative", "makeitso"); |
|
3328 |
|
3329 /* We give ownership of the request to the connection */ |
|
3330 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
|
3331 tt_abort_msg("Couldn't make request"); |
|
3332 } |
|
3333 |
|
3334 event_base_dispatch(data->base); |
|
3335 |
|
3336 end: |
|
3337 if (evcon) |
|
3338 evhttp_connection_free(evcon); |
|
3339 if (http) |
|
3340 evhttp_free(http); |
|
3341 } |
|
3342 |
|
3343 |
|
3344 static void |
|
3345 http_data_length_constraints_test_done(struct evhttp_request *req, void *arg) |
|
3346 { |
|
3347 tt_assert(req); |
|
3348 tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_BADREQUEST); |
|
3349 end: |
|
3350 event_base_loopexit(arg, NULL); |
|
3351 } |
|
3352 |
|
3353 static void |
|
3354 http_large_entity_test_done(struct evhttp_request *req, void *arg) |
|
3355 { |
|
3356 tt_assert(req); |
|
3357 tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_ENTITYTOOLARGE); |
|
3358 end: |
|
3359 event_base_loopexit(arg, NULL); |
|
3360 } |
|
3361 |
|
3362 static void |
|
3363 http_data_length_constraints_test(void *arg) |
|
3364 { |
|
3365 struct basic_test_data *data = arg; |
|
3366 ev_uint16_t port = 0; |
|
3367 struct evhttp_connection *evcon = NULL; |
|
3368 struct evhttp_request *req = NULL; |
|
3369 char long_str[8192]; |
|
3370 |
|
3371 test_ok = 0; |
|
3372 |
|
3373 http = http_setup(&port, data->base); |
|
3374 |
|
3375 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
|
3376 tt_assert(evcon); |
|
3377 |
|
3378 /* also bind to local host */ |
|
3379 evhttp_connection_set_local_address(evcon, "127.0.0.1"); |
|
3380 |
|
3381 /* |
|
3382 * At this point, we want to schedule an HTTP GET request |
|
3383 * server using our make request method. |
|
3384 */ |
|
3385 |
|
3386 req = evhttp_request_new(http_data_length_constraints_test_done, data->base); |
|
3387 tt_assert(req); |
|
3388 |
|
3389 memset(long_str, 'a', 8192); |
|
3390 long_str[8191] = '\0'; |
|
3391 /* Add the information that we care about */ |
|
3392 evhttp_set_max_headers_size(http, 8191); |
|
3393 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
3394 evhttp_add_header(evhttp_request_get_output_headers(req), "Longheader", long_str); |
|
3395 |
|
3396 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) { |
|
3397 tt_abort_msg("Couldn't make request"); |
|
3398 } |
|
3399 event_base_dispatch(data->base); |
|
3400 |
|
3401 req = evhttp_request_new(http_data_length_constraints_test_done, data->base); |
|
3402 tt_assert(req); |
|
3403 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
3404 |
|
3405 /* GET /?arg=verylongvalue HTTP/1.1 */ |
|
3406 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, long_str) == -1) { |
|
3407 tt_abort_msg("Couldn't make request"); |
|
3408 } |
|
3409 event_base_dispatch(data->base); |
|
3410 |
|
3411 evhttp_set_max_body_size(http, 8190); |
|
3412 req = evhttp_request_new(http_data_length_constraints_test_done, data->base); |
|
3413 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
3414 evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str); |
|
3415 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) { |
|
3416 tt_abort_msg("Couldn't make request"); |
|
3417 } |
|
3418 event_base_dispatch(data->base); |
|
3419 |
|
3420 req = evhttp_request_new(http_large_entity_test_done, data->base); |
|
3421 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
|
3422 evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "100-continue"); |
|
3423 evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str); |
|
3424 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) { |
|
3425 tt_abort_msg("Couldn't make request"); |
|
3426 } |
|
3427 event_base_dispatch(data->base); |
|
3428 |
|
3429 test_ok = 1; |
|
3430 end: |
|
3431 if (evcon) |
|
3432 evhttp_connection_free(evcon); |
|
3433 if (http) |
|
3434 evhttp_free(http); |
|
3435 } |
|
3436 |
|
3437 /* |
|
3438 * Testing client reset of server chunked connections |
|
3439 */ |
|
3440 |
|
3441 struct terminate_state { |
|
3442 struct event_base *base; |
|
3443 struct evhttp_request *req; |
|
3444 struct bufferevent *bev; |
|
3445 evutil_socket_t fd; |
|
3446 int gotclosecb: 1; |
|
3447 }; |
|
3448 |
|
3449 static void |
|
3450 terminate_chunked_trickle_cb(evutil_socket_t fd, short events, void *arg) |
|
3451 { |
|
3452 struct terminate_state *state = arg; |
|
3453 struct evbuffer *evb; |
|
3454 struct timeval tv; |
|
3455 |
|
3456 if (evhttp_request_get_connection(state->req) == NULL) { |
|
3457 test_ok = 1; |
|
3458 evhttp_request_free(state->req); |
|
3459 event_base_loopexit(state->base,NULL); |
|
3460 return; |
|
3461 } |
|
3462 |
|
3463 evb = evbuffer_new(); |
|
3464 evbuffer_add_printf(evb, "%p", evb); |
|
3465 evhttp_send_reply_chunk(state->req, evb); |
|
3466 evbuffer_free(evb); |
|
3467 |
|
3468 tv.tv_sec = 0; |
|
3469 tv.tv_usec = 3000; |
|
3470 EVUTIL_ASSERT(state); |
|
3471 EVUTIL_ASSERT(state->base); |
|
3472 event_base_once(state->base, -1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); |
|
3473 } |
|
3474 |
|
3475 static void |
|
3476 terminate_chunked_close_cb(struct evhttp_connection *evcon, void *arg) |
|
3477 { |
|
3478 struct terminate_state *state = arg; |
|
3479 state->gotclosecb = 1; |
|
3480 } |
|
3481 |
|
3482 static void |
|
3483 terminate_chunked_cb(struct evhttp_request *req, void *arg) |
|
3484 { |
|
3485 struct terminate_state *state = arg; |
|
3486 struct timeval tv; |
|
3487 |
|
3488 /* we want to know if this connection closes on us */ |
|
3489 evhttp_connection_set_closecb( |
|
3490 evhttp_request_get_connection(req), |
|
3491 terminate_chunked_close_cb, arg); |
|
3492 |
|
3493 state->req = req; |
|
3494 |
|
3495 evhttp_send_reply_start(req, HTTP_OK, "OK"); |
|
3496 |
|
3497 tv.tv_sec = 0; |
|
3498 tv.tv_usec = 3000; |
|
3499 event_base_once(state->base, -1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); |
|
3500 } |
|
3501 |
|
3502 static void |
|
3503 terminate_chunked_client(evutil_socket_t fd, short event, void *arg) |
|
3504 { |
|
3505 struct terminate_state *state = arg; |
|
3506 bufferevent_free(state->bev); |
|
3507 evutil_closesocket(state->fd); |
|
3508 } |
|
3509 |
|
3510 static void |
|
3511 terminate_readcb(struct bufferevent *bev, void *arg) |
|
3512 { |
|
3513 /* just drop the data */ |
|
3514 evbuffer_drain(bufferevent_get_input(bev), -1); |
|
3515 } |
|
3516 |
|
3517 |
|
3518 static void |
|
3519 http_terminate_chunked_test(void *arg) |
|
3520 { |
|
3521 struct basic_test_data *data = arg; |
|
3522 struct bufferevent *bev = NULL; |
|
3523 struct timeval tv; |
|
3524 const char *http_request; |
|
3525 ev_uint16_t port = 0; |
|
3526 evutil_socket_t fd = -1; |
|
3527 struct terminate_state terminate_state; |
|
3528 |
|
3529 test_ok = 0; |
|
3530 |
|
3531 http = http_setup(&port, data->base); |
|
3532 evhttp_del_cb(http, "/test"); |
|
3533 tt_assert(evhttp_set_cb(http, "/test", |
|
3534 terminate_chunked_cb, &terminate_state) == 0); |
|
3535 |
|
3536 fd = http_connect("127.0.0.1", port); |
|
3537 |
|
3538 /* Stupid thing to send a request */ |
|
3539 bev = bufferevent_socket_new(data->base, fd, 0); |
|
3540 bufferevent_setcb(bev, terminate_readcb, http_writecb, |
|
3541 http_errorcb, data->base); |
|
3542 |
|
3543 memset(&terminate_state, 0, sizeof(terminate_state)); |
|
3544 terminate_state.base = data->base; |
|
3545 terminate_state.fd = fd; |
|
3546 terminate_state.bev = bev; |
|
3547 terminate_state.gotclosecb = 0; |
|
3548 |
|
3549 /* first half of the http request */ |
|
3550 http_request = |
|
3551 "GET /test HTTP/1.1\r\n" |
|
3552 "Host: some\r\n\r\n"; |
|
3553 |
|
3554 bufferevent_write(bev, http_request, strlen(http_request)); |
|
3555 evutil_timerclear(&tv); |
|
3556 tv.tv_usec = 10000; |
|
3557 event_base_once(data->base, -1, EV_TIMEOUT, terminate_chunked_client, &terminate_state, |
|
3558 &tv); |
|
3559 |
|
3560 event_base_dispatch(data->base); |
|
3561 |
|
3562 if (terminate_state.gotclosecb == 0) |
|
3563 test_ok = 0; |
|
3564 |
|
3565 end: |
|
3566 if (fd >= 0) |
|
3567 evutil_closesocket(fd); |
|
3568 if (http) |
|
3569 evhttp_free(http); |
|
3570 } |
|
3571 |
|
3572 #define HTTP_LEGACY(name) \ |
|
3573 { #name, run_legacy_test_fn, TT_ISOLATED|TT_LEGACY, &legacy_setup, \ |
|
3574 http_##name##_test } |
|
3575 |
|
3576 #define HTTP(name) \ |
|
3577 { #name, http_##name##_test, TT_ISOLATED, &basic_setup, NULL } |
|
3578 |
|
3579 struct testcase_t http_testcases[] = { |
|
3580 { "primitives", http_primitives, 0, NULL, NULL }, |
|
3581 { "base", http_base_test, TT_FORK, NULL, NULL }, |
|
3582 { "bad_headers", http_bad_header_test, 0, NULL, NULL }, |
|
3583 { "parse_query", http_parse_query_test, 0, NULL, NULL }, |
|
3584 { "parse_uri", http_parse_uri_test, 0, NULL, NULL }, |
|
3585 { "parse_uri_nc", http_parse_uri_test, 0, &basic_setup, (void*)"nc" }, |
|
3586 { "uriencode", http_uriencode_test, 0, NULL, NULL }, |
|
3587 HTTP(basic), |
|
3588 HTTP(cancel), |
|
3589 HTTP(virtual_host), |
|
3590 HTTP(post), |
|
3591 HTTP(put), |
|
3592 HTTP(delete), |
|
3593 HTTP(allowed_methods), |
|
3594 HTTP(failure), |
|
3595 HTTP(connection), |
|
3596 HTTP(persist_connection), |
|
3597 HTTP(connection_async), |
|
3598 HTTP(close_detection), |
|
3599 HTTP(close_detection_delay), |
|
3600 HTTP(bad_request), |
|
3601 HTTP(incomplete), |
|
3602 HTTP(incomplete_timeout), |
|
3603 HTTP(terminate_chunked), |
|
3604 |
|
3605 HTTP(highport), |
|
3606 HTTP(dispatcher), |
|
3607 HTTP(multi_line_header), |
|
3608 HTTP(negative_content_length), |
|
3609 HTTP(chunk_out), |
|
3610 HTTP(stream_out), |
|
3611 |
|
3612 HTTP(stream_in), |
|
3613 HTTP(stream_in_cancel), |
|
3614 |
|
3615 HTTP(connection_fail), |
|
3616 HTTP(connection_retry), |
|
3617 HTTP(data_length_constraints), |
|
3618 |
|
3619 END_OF_TESTCASES |
|
3620 }; |
|
3621 |