OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu> | |
3 * All rights reserved. | |
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 <windows.h> | |
31 #endif | |
32 | |
33 #ifdef HAVE_CONFIG_H | |
34 #include "config.h" | |
35 #endif | |
36 | |
37 #include <sys/types.h> | |
38 #include <sys/stat.h> | |
39 #ifdef HAVE_SYS_TIME_H | |
40 #include <sys/time.h> | |
41 #endif | |
42 #include <sys/queue.h> | |
43 #ifndef WIN32 | |
44 #include <sys/socket.h> | |
45 #include <signal.h> | |
46 #include <unistd.h> | |
47 #include <netdb.h> | |
48 #endif | |
49 #include <fcntl.h> | |
50 #include <stdlib.h> | |
51 #include <stdio.h> | |
52 #include <string.h> | |
53 #include <errno.h> | |
54 | |
55 #include "event.h" | |
56 #include "evhttp.h" | |
57 #include "log.h" | |
58 #include "http-internal.h" | |
59 | |
60 extern int pair[]; | |
61 extern int test_ok; | |
62 | |
63 static struct evhttp *http; | |
64 /* set if a test needs to call loopexit on a base */ | |
65 static struct event_base *base; | |
66 | |
67 void http_suite(void); | |
68 | |
69 void http_basic_cb(struct evhttp_request *req, void *arg); | |
70 static void http_chunked_cb(struct evhttp_request *req, void *arg); | |
71 void http_post_cb(struct evhttp_request *req, void *arg); | |
72 void http_dispatcher_cb(struct evhttp_request *req, void *arg); | |
73 static void http_large_delay_cb(struct evhttp_request *req, void *arg); | |
74 static void http_badreq_cb(struct evhttp_request *req, void *arg); | |
75 | |
76 static struct evhttp * | |
77 http_setup(short *pport, struct event_base *base) | |
78 { | |
79 int i; | |
80 struct evhttp *myhttp; | |
81 short port = -1; | |
82 | |
83 /* Try a few different ports */ | |
84 myhttp = evhttp_new(base); | |
85 for (i = 0; i < 50; ++i) { | |
86 if (evhttp_bind_socket(myhttp, "127.0.0.1", 8080 + i) != -1) { | |
87 port = 8080 + i; | |
88 break; | |
89 } | |
90 } | |
91 | |
92 if (port == -1) | |
93 event_errx(1, "Could not start web server"); | |
94 | |
95 /* Register a callback for certain types of requests */ | |
96 evhttp_set_cb(myhttp, "/test", http_basic_cb, NULL); | |
97 evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, NULL); | |
98 evhttp_set_cb(myhttp, "/postit", http_post_cb, NULL); | |
99 evhttp_set_cb(myhttp, "/largedelay", http_large_delay_cb, NULL); | |
100 evhttp_set_cb(myhttp, "/badrequest", http_badreq_cb, NULL); | |
101 evhttp_set_cb(myhttp, "/", http_dispatcher_cb, NULL); | |
102 | |
103 *pport = port; | |
104 return (myhttp); | |
105 } | |
106 | |
107 #ifndef NI_MAXSERV | |
108 #define NI_MAXSERV 1024 | |
109 #endif | |
110 | |
111 static int | |
112 http_connect(const char *address, u_short port) | |
113 { | |
114 /* Stupid code for connecting */ | |
115 #ifdef WIN32 | |
116 struct hostent *he; | |
117 struct sockaddr_in sin; | |
118 #else | |
119 struct addrinfo ai, *aitop; | |
120 char strport[NI_MAXSERV]; | |
121 #endif | |
122 struct sockaddr *sa; | |
123 int slen; | |
124 int fd; | |
125 | |
126 #ifdef WIN32 | |
127 if (!(he = gethostbyname(address))) { | |
128 event_warn("gethostbyname"); | |
129 } | |
130 memcpy(&sin.sin_addr, he->h_addr_list[0], he->h_length); | |
131 sin.sin_family = AF_INET; | |
132 sin.sin_port = htons(port); | |
133 slen = sizeof(struct sockaddr_in); | |
134 sa = (struct sockaddr*)&sin; | |
135 #else | |
136 memset(&ai, 0, sizeof (ai)); | |
137 ai.ai_family = AF_INET; | |
138 ai.ai_socktype = SOCK_STREAM; | |
139 snprintf(strport, sizeof (strport), "%d", port); | |
140 if (getaddrinfo(address, strport, &ai, &aitop) != 0) { | |
141 event_warn("getaddrinfo"); | |
142 return (-1); | |
143 } | |
144 sa = aitop->ai_addr; | |
145 slen = aitop->ai_addrlen; | |
146 #endif | |
147 | |
148 fd = socket(AF_INET, SOCK_STREAM, 0); | |
149 if (fd == -1) | |
150 event_err(1, "socket failed"); | |
151 | |
152 if (connect(fd, sa, slen) == -1) | |
153 event_err(1, "connect failed"); | |
154 | |
155 #ifndef WIN32 | |
156 freeaddrinfo(aitop); | |
157 #endif | |
158 | |
159 return (fd); | |
160 } | |
161 | |
162 static void | |
163 http_readcb(struct bufferevent *bev, void *arg) | |
164 { | |
165 const char *what = "This is funny"; | |
166 | |
167 event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input))); | |
168 | |
169 if (evbuffer_find(bev->input, | |
170 (const unsigned char*) what, strlen(what)) != NULL) { | |
171 struct evhttp_request *req = evhttp_request_new(NULL, NULL); | |
172 enum message_read_status done; | |
173 | |
174 req->kind = EVHTTP_RESPONSE; | |
175 done = evhttp_parse_firstline(req, bev->input); | |
176 if (done != ALL_DATA_READ) | |
177 goto out; | |
178 | |
179 done = evhttp_parse_headers(req, bev->input); | |
180 if (done != ALL_DATA_READ) | |
181 goto out; | |
182 | |
183 if (done == 1 && | |
184 evhttp_find_header(req->input_headers, | |
185 "Content-Type") != NULL) | |
186 test_ok++; | |
187 | |
188 out: | |
189 evhttp_request_free(req); | |
190 bufferevent_disable(bev, EV_READ); | |
191 if (base) | |
192 event_base_loopexit(base, NULL); | |
193 else | |
194 event_loopexit(NULL); | |
195 } | |
196 } | |
197 | |
198 static void | |
199 http_writecb(struct bufferevent *bev, void *arg) | |
200 { | |
201 if (EVBUFFER_LENGTH(bev->output) == 0) { | |
202 /* enable reading of the reply */ | |
203 bufferevent_enable(bev, EV_READ); | |
204 test_ok++; | |
205 } | |
206 } | |
207 | |
208 static void | |
209 http_errorcb(struct bufferevent *bev, short what, void *arg) | |
210 { | |
211 test_ok = -2; | |
212 event_loopexit(NULL); | |
213 } | |
214 | |
215 void | |
216 http_basic_cb(struct evhttp_request *req, void *arg) | |
217 { | |
218 struct evbuffer *evb = evbuffer_new(); | |
219 int empty = evhttp_find_header(req->input_headers, "Empty") != NULL; | |
220 event_debug(("%s: called\n", __func__)); | |
221 evbuffer_add_printf(evb, "This is funny"); | |
222 | |
223 /* For multi-line headers test */ | |
224 { | |
225 const char *multi = | |
226 evhttp_find_header(req->input_headers,"X-multi"); | |
227 if (multi) { | |
228 if (strcmp("END", multi + strlen(multi) - 3) == 0) | |
229 test_ok++; | |
230 if (evhttp_find_header(req->input_headers, "X-Last")) | |
231 test_ok++; | |
232 } | |
233 } | |
234 | |
235 /* injecting a bad content-length */ | |
236 if (evhttp_find_header(req->input_headers, "X-Negative")) | |
237 evhttp_add_header(req->output_headers, | |
238 "Content-Length", "-100"); | |
239 | |
240 /* allow sending of an empty reply */ | |
241 evhttp_send_reply(req, HTTP_OK, "Everything is fine", | |
242 !empty ? evb : NULL); | |
243 | |
244 evbuffer_free(evb); | |
245 } | |
246 | |
247 static char const* const CHUNKS[] = { | |
248 "This is funny", | |
249 "but not hilarious.", | |
250 "bwv 1052" | |
251 }; | |
252 | |
253 struct chunk_req_state { | |
254 struct evhttp_request *req; | |
255 int i; | |
256 }; | |
257 | |
258 static void | |
259 http_chunked_trickle_cb(int fd, short events, void *arg) | |
260 { | |
261 struct evbuffer *evb = evbuffer_new(); | |
262 struct chunk_req_state *state = arg; | |
263 struct timeval when = { 0, 0 }; | |
264 | |
265 evbuffer_add_printf(evb, "%s", CHUNKS[state->i]); | |
266 evhttp_send_reply_chunk(state->req, evb); | |
267 evbuffer_free(evb); | |
268 | |
269 if (++state->i < sizeof(CHUNKS)/sizeof(CHUNKS[0])) { | |
270 event_once(-1, EV_TIMEOUT, | |
271 http_chunked_trickle_cb, state, &when); | |
272 } else { | |
273 evhttp_send_reply_end(state->req); | |
274 free(state); | |
275 } | |
276 } | |
277 | |
278 static void | |
279 http_chunked_cb(struct evhttp_request *req, void *arg) | |
280 { | |
281 struct timeval when = { 0, 0 }; | |
282 struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state)); | |
283 event_debug(("%s: called\n", __func__)); | |
284 | |
285 memset(state, 0, sizeof(struct chunk_req_state)); | |
286 state->req = req; | |
287 | |
288 /* generate a chunked reply */ | |
289 evhttp_send_reply_start(req, HTTP_OK, "Everything is fine"); | |
290 | |
291 /* but trickle it across several iterations to ensure we're not | |
292 * assuming it comes all at once */ | |
293 event_once(-1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when); | |
294 } | |
295 | |
296 static void | |
297 http_complete_write(int fd, short what, void *arg) | |
298 { | |
299 struct bufferevent *bev = arg; | |
300 const char *http_request = "host\r\n" | |
301 "Connection: close\r\n" | |
302 "\r\n"; | |
303 bufferevent_write(bev, http_request, strlen(http_request)); | |
304 } | |
305 | |
306 static void | |
307 http_basic_test(void) | |
308 { | |
309 struct timeval tv; | |
310 struct bufferevent *bev; | |
311 int fd; | |
312 const char *http_request; | |
313 short port = -1; | |
314 | |
315 test_ok = 0; | |
316 fprintf(stdout, "Testing Basic HTTP Server: "); | |
317 | |
318 http = http_setup(&port, NULL); | |
319 | |
320 /* bind to a second socket */ | |
321 if (evhttp_bind_socket(http, "127.0.0.1", port + 1) == -1) { | |
322 fprintf(stdout, "FAILED (bind)\n"); | |
323 exit(1); | |
324 } | |
325 | |
326 fd = http_connect("127.0.0.1", port); | |
327 | |
328 /* Stupid thing to send a request */ | |
329 bev = bufferevent_new(fd, http_readcb, http_writecb, | |
330 http_errorcb, NULL); | |
331 | |
332 /* first half of the http request */ | |
333 http_request = | |
334 "GET /test HTTP/1.1\r\n" | |
335 "Host: some"; | |
336 | |
337 bufferevent_write(bev, http_request, strlen(http_request)); | |
338 timerclear(&tv); | |
339 tv.tv_usec = 10000; | |
340 event_once(-1, EV_TIMEOUT, http_complete_write, bev, &tv); | |
341 | |
342 event_dispatch(); | |
343 | |
344 if (test_ok != 3) { | |
345 fprintf(stdout, "FAILED\n"); | |
346 exit(1); | |
347 } | |
348 | |
349 /* connect to the second port */ | |
350 bufferevent_free(bev); | |
351 EVUTIL_CLOSESOCKET(fd); | |
352 | |
353 fd = http_connect("127.0.0.1", port + 1); | |
354 | |
355 /* Stupid thing to send a request */ | |
356 bev = bufferevent_new(fd, http_readcb, http_writecb, | |
357 http_errorcb, NULL); | |
358 | |
359 http_request = | |
360 "GET /test HTTP/1.1\r\n" | |
361 "Host: somehost\r\n" | |
362 "Connection: close\r\n" | |
363 "\r\n"; | |
364 | |
365 bufferevent_write(bev, http_request, strlen(http_request)); | |
366 | |
367 event_dispatch(); | |
368 | |
369 bufferevent_free(bev); | |
370 EVUTIL_CLOSESOCKET(fd); | |
371 | |
372 evhttp_free(http); | |
373 | |
374 if (test_ok != 5) { | |
375 fprintf(stdout, "FAILED\n"); | |
376 exit(1); | |
377 } | |
378 | |
379 fprintf(stdout, "OK\n"); | |
380 } | |
381 | |
382 static void | |
383 http_badreq_cb(struct evhttp_request *req, void *arg) | |
384 { | |
385 struct evbuffer *buf = evbuffer_new(); | |
386 | |
387 evhttp_add_header(req->output_headers, "Content-Type", "text/xml; charse
t=UTF-8"); | |
388 evbuffer_add_printf(buf, "Hello, %s!", "127.0.0.1"); | |
389 | |
390 evhttp_send_reply(req, HTTP_OK, "OK", buf); | |
391 evbuffer_free(buf); | |
392 } | |
393 | |
394 static void | |
395 http_badreq_errorcb(struct bufferevent *bev, short what, void *arg) | |
396 { | |
397 event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); | |
398 /* ignore */ | |
399 } | |
400 | |
401 static void | |
402 http_badreq_readcb(struct bufferevent *bev, void *arg) | |
403 { | |
404 const char *what = "Hello, 127.0.0.1"; | |
405 const char *bad_request = "400 Bad Request"; | |
406 | |
407 event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input))); | |
408 | |
409 if (evbuffer_find(bev->input, | |
410 (const unsigned char *) bad_request, strlen(bad_request)) != NUL
L) { | |
411 event_debug(("%s: bad request detected", __func__)); | |
412 test_ok = -10; | |
413 bufferevent_disable(bev, EV_READ); | |
414 event_loopexit(NULL); | |
415 return; | |
416 } | |
417 | |
418 if (evbuffer_find(bev->input, | |
419 (const unsigned char*) what, strlen(what)) != NULL) { | |
420 struct evhttp_request *req = evhttp_request_new(NULL, NULL); | |
421 enum message_read_status done; | |
422 | |
423 req->kind = EVHTTP_RESPONSE; | |
424 done = evhttp_parse_firstline(req, bev->input); | |
425 if (done != ALL_DATA_READ) | |
426 goto out; | |
427 | |
428 done = evhttp_parse_headers(req, bev->input); | |
429 if (done != ALL_DATA_READ) | |
430 goto out; | |
431 | |
432 if (done == 1 && | |
433 evhttp_find_header(req->input_headers, | |
434 "Content-Type") != NULL) | |
435 test_ok++; | |
436 | |
437 out: | |
438 evhttp_request_free(req); | |
439 evbuffer_drain(bev->input, EVBUFFER_LENGTH(bev->input)); | |
440 } | |
441 | |
442 shutdown(bev->ev_read.ev_fd, SHUT_WR); | |
443 } | |
444 | |
445 static void | |
446 http_badreq_successcb(int fd, short what, void *arg) | |
447 { | |
448 event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); | |
449 event_loopexit(NULL); | |
450 } | |
451 | |
452 static void | |
453 http_bad_request(void) | |
454 { | |
455 struct timeval tv; | |
456 struct bufferevent *bev; | |
457 int fd; | |
458 const char *http_request; | |
459 short port = -1; | |
460 | |
461 test_ok = 0; | |
462 fprintf(stdout, "Testing \"Bad Request\" on connection close: "); | |
463 | |
464 http = http_setup(&port, NULL); | |
465 | |
466 /* bind to a second socket */ | |
467 if (evhttp_bind_socket(http, "127.0.0.1", port + 1) == -1) { | |
468 fprintf(stdout, "FAILED (bind)\n"); | |
469 exit(1); | |
470 } | |
471 | |
472 /* NULL request test */ | |
473 fd = http_connect("127.0.0.1", port); | |
474 | |
475 /* Stupid thing to send a request */ | |
476 bev = bufferevent_new(fd, http_badreq_readcb, http_writecb, | |
477 http_badreq_errorcb, NULL); | |
478 bufferevent_enable(bev, EV_READ); | |
479 | |
480 /* real NULL request */ | |
481 http_request = ""; | |
482 | |
483 shutdown(fd, SHUT_WR); | |
484 timerclear(&tv); | |
485 tv.tv_usec = 10000; | |
486 event_once(-1, EV_TIMEOUT, http_badreq_successcb, bev, &tv); | |
487 | |
488 event_dispatch(); | |
489 | |
490 bufferevent_free(bev); | |
491 EVUTIL_CLOSESOCKET(fd); | |
492 | |
493 if (test_ok != 0) { | |
494 fprintf(stdout, "FAILED\n"); | |
495 exit(1); | |
496 } | |
497 | |
498 /* Second answer (BAD REQUEST) on connection close */ | |
499 | |
500 /* connect to the second port */ | |
501 fd = http_connect("127.0.0.1", port + 1); | |
502 | |
503 /* Stupid thing to send a request */ | |
504 bev = bufferevent_new(fd, http_badreq_readcb, http_writecb, | |
505 http_badreq_errorcb, NULL); | |
506 bufferevent_enable(bev, EV_READ); | |
507 | |
508 /* first half of the http request */ | |
509 http_request = | |
510 "GET /badrequest HTTP/1.0\r\n" \ | |
511 "Connection: Keep-Alive\r\n" \ | |
512 "\r\n"; | |
513 | |
514 bufferevent_write(bev, http_request, strlen(http_request)); | |
515 | |
516 timerclear(&tv); | |
517 tv.tv_usec = 10000; | |
518 event_once(-1, EV_TIMEOUT, http_badreq_successcb, bev, &tv); | |
519 | |
520 event_dispatch(); | |
521 | |
522 evhttp_free(http); | |
523 | |
524 if (test_ok != 2) { | |
525 fprintf(stdout, "FAILED\n"); | |
526 exit(1); | |
527 } | |
528 | |
529 fprintf(stdout, "OK\n"); | |
530 } | |
531 static struct evhttp_connection *delayed_client; | |
532 | |
533 static void | |
534 http_delay_reply(int fd, short what, void *arg) | |
535 { | |
536 struct evhttp_request *req = arg; | |
537 | |
538 evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL); | |
539 | |
540 ++test_ok; | |
541 } | |
542 | |
543 static void | |
544 http_large_delay_cb(struct evhttp_request *req, void *arg) | |
545 { | |
546 struct timeval tv; | |
547 timerclear(&tv); | |
548 tv.tv_sec = 3; | |
549 | |
550 event_once(-1, EV_TIMEOUT, http_delay_reply, req, &tv); | |
551 | |
552 /* here we close the client connection which will cause an EOF */ | |
553 evhttp_connection_fail(delayed_client, EVCON_HTTP_EOF); | |
554 } | |
555 | |
556 void http_request_done(struct evhttp_request *, void *); | |
557 void http_request_empty_done(struct evhttp_request *, void *); | |
558 | |
559 static void | |
560 http_connection_test(int persistent) | |
561 { | |
562 short port = -1; | |
563 struct evhttp_connection *evcon = NULL; | |
564 struct evhttp_request *req = NULL; | |
565 | |
566 test_ok = 0; | |
567 fprintf(stdout, "Testing Request Connection Pipeline %s: ", | |
568 persistent ? "(persistent)" : ""); | |
569 | |
570 http = http_setup(&port, NULL); | |
571 | |
572 evcon = evhttp_connection_new("127.0.0.1", port); | |
573 if (evcon == NULL) { | |
574 fprintf(stdout, "FAILED\n"); | |
575 exit(1); | |
576 } | |
577 | |
578 /* | |
579 * At this point, we want to schedule a request to the HTTP | |
580 * server using our make request method. | |
581 */ | |
582 | |
583 req = evhttp_request_new(http_request_done, NULL); | |
584 | |
585 /* Add the information that we care about */ | |
586 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
587 | |
588 /* We give ownership of the request to the connection */ | |
589 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { | |
590 fprintf(stdout, "FAILED\n"); | |
591 exit(1); | |
592 } | |
593 | |
594 event_dispatch(); | |
595 | |
596 if (test_ok != 1) { | |
597 fprintf(stdout, "FAILED\n"); | |
598 exit(1); | |
599 } | |
600 | |
601 /* try to make another request over the same connection */ | |
602 test_ok = 0; | |
603 | |
604 req = evhttp_request_new(http_request_done, NULL); | |
605 | |
606 /* Add the information that we care about */ | |
607 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
608 | |
609 /* | |
610 * if our connections are not supposed to be persistent; request | |
611 * a close from the server. | |
612 */ | |
613 if (!persistent) | |
614 evhttp_add_header(req->output_headers, "Connection", "close"); | |
615 | |
616 /* We give ownership of the request to the connection */ | |
617 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { | |
618 fprintf(stdout, "FAILED\n"); | |
619 exit(1); | |
620 } | |
621 | |
622 event_dispatch(); | |
623 | |
624 /* make another request: request empty reply */ | |
625 test_ok = 0; | |
626 | |
627 req = evhttp_request_new(http_request_empty_done, NULL); | |
628 | |
629 /* Add the information that we care about */ | |
630 evhttp_add_header(req->output_headers, "Empty", "itis"); | |
631 | |
632 /* We give ownership of the request to the connection */ | |
633 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { | |
634 fprintf(stdout, "FAILED\n"); | |
635 exit(1); | |
636 } | |
637 | |
638 event_dispatch(); | |
639 | |
640 if (test_ok != 1) { | |
641 fprintf(stdout, "FAILED\n"); | |
642 exit(1); | |
643 } | |
644 | |
645 evhttp_connection_free(evcon); | |
646 evhttp_free(http); | |
647 | |
648 fprintf(stdout, "OK\n"); | |
649 } | |
650 | |
651 void | |
652 http_request_done(struct evhttp_request *req, void *arg) | |
653 { | |
654 const char *what = "This is funny"; | |
655 | |
656 if (req->response_code != HTTP_OK) { | |
657 fprintf(stderr, "FAILED\n"); | |
658 exit(1); | |
659 } | |
660 | |
661 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { | |
662 fprintf(stderr, "FAILED\n"); | |
663 exit(1); | |
664 } | |
665 | |
666 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { | |
667 fprintf(stderr, "FAILED\n"); | |
668 exit(1); | |
669 } | |
670 | |
671 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { | |
672 fprintf(stderr, "FAILED\n"); | |
673 exit(1); | |
674 } | |
675 | |
676 test_ok = 1; | |
677 event_loopexit(NULL); | |
678 } | |
679 | |
680 /* test date header and content length */ | |
681 | |
682 void | |
683 http_request_empty_done(struct evhttp_request *req, void *arg) | |
684 { | |
685 if (req->response_code != HTTP_OK) { | |
686 fprintf(stderr, "FAILED\n"); | |
687 exit(1); | |
688 } | |
689 | |
690 if (evhttp_find_header(req->input_headers, "Date") == NULL) { | |
691 fprintf(stderr, "FAILED\n"); | |
692 exit(1); | |
693 } | |
694 | |
695 | |
696 if (evhttp_find_header(req->input_headers, "Content-Length") == NULL) { | |
697 fprintf(stderr, "FAILED\n"); | |
698 exit(1); | |
699 } | |
700 | |
701 if (strcmp(evhttp_find_header(req->input_headers, "Content-Length"), | |
702 "0")) { | |
703 fprintf(stderr, "FAILED\n"); | |
704 exit(1); | |
705 } | |
706 | |
707 if (EVBUFFER_LENGTH(req->input_buffer) != 0) { | |
708 fprintf(stderr, "FAILED\n"); | |
709 exit(1); | |
710 } | |
711 | |
712 test_ok = 1; | |
713 event_loopexit(NULL); | |
714 } | |
715 | |
716 /* | |
717 * HTTP DISPATCHER test | |
718 */ | |
719 | |
720 void | |
721 http_dispatcher_cb(struct evhttp_request *req, void *arg) | |
722 { | |
723 | |
724 struct evbuffer *evb = evbuffer_new(); | |
725 event_debug(("%s: called\n", __func__)); | |
726 evbuffer_add_printf(evb, "DISPATCHER_TEST"); | |
727 | |
728 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); | |
729 | |
730 evbuffer_free(evb); | |
731 } | |
732 | |
733 static void | |
734 http_dispatcher_test_done(struct evhttp_request *req, void *arg) | |
735 { | |
736 const char *what = "DISPATCHER_TEST"; | |
737 | |
738 if (req->response_code != HTTP_OK) { | |
739 fprintf(stderr, "FAILED\n"); | |
740 exit(1); | |
741 } | |
742 | |
743 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { | |
744 fprintf(stderr, "FAILED (content type)\n"); | |
745 exit(1); | |
746 } | |
747 | |
748 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { | |
749 fprintf(stderr, "FAILED (length %zu vs %zu)\n", | |
750 EVBUFFER_LENGTH(req->input_buffer), strlen(what)); | |
751 exit(1); | |
752 } | |
753 | |
754 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { | |
755 fprintf(stderr, "FAILED (data)\n"); | |
756 exit(1); | |
757 } | |
758 | |
759 test_ok = 1; | |
760 event_loopexit(NULL); | |
761 } | |
762 | |
763 static void | |
764 http_dispatcher_test(void) | |
765 { | |
766 short port = -1; | |
767 struct evhttp_connection *evcon = NULL; | |
768 struct evhttp_request *req = NULL; | |
769 | |
770 test_ok = 0; | |
771 fprintf(stdout, "Testing HTTP Dispatcher: "); | |
772 | |
773 http = http_setup(&port, NULL); | |
774 | |
775 evcon = evhttp_connection_new("127.0.0.1", port); | |
776 if (evcon == NULL) { | |
777 fprintf(stdout, "FAILED\n"); | |
778 exit(1); | |
779 } | |
780 | |
781 /* also bind to local host */ | |
782 evhttp_connection_set_local_address(evcon, "127.0.0.1"); | |
783 | |
784 /* | |
785 * At this point, we want to schedule an HTTP GET request | |
786 * server using our make request method. | |
787 */ | |
788 | |
789 req = evhttp_request_new(http_dispatcher_test_done, NULL); | |
790 if (req == NULL) { | |
791 fprintf(stdout, "FAILED\n"); | |
792 exit(1); | |
793 } | |
794 | |
795 /* Add the information that we care about */ | |
796 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
797 | |
798 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1)
{ | |
799 fprintf(stdout, "FAILED\n"); | |
800 exit(1); | |
801 } | |
802 | |
803 event_dispatch(); | |
804 | |
805 evhttp_connection_free(evcon); | |
806 evhttp_free(http); | |
807 | |
808 if (test_ok != 1) { | |
809 fprintf(stdout, "FAILED: %d\n", test_ok); | |
810 exit(1); | |
811 } | |
812 | |
813 fprintf(stdout, "OK\n"); | |
814 } | |
815 | |
816 /* | |
817 * HTTP POST test. | |
818 */ | |
819 | |
820 void http_postrequest_done(struct evhttp_request *, void *); | |
821 | |
822 #define POST_DATA "Okay. Not really printf" | |
823 | |
824 static void | |
825 http_post_test(void) | |
826 { | |
827 short port = -1; | |
828 struct evhttp_connection *evcon = NULL; | |
829 struct evhttp_request *req = NULL; | |
830 | |
831 test_ok = 0; | |
832 fprintf(stdout, "Testing HTTP POST Request: "); | |
833 | |
834 http = http_setup(&port, NULL); | |
835 | |
836 evcon = evhttp_connection_new("127.0.0.1", port); | |
837 if (evcon == NULL) { | |
838 fprintf(stdout, "FAILED\n"); | |
839 exit(1); | |
840 } | |
841 | |
842 /* | |
843 * At this point, we want to schedule an HTTP POST request | |
844 * server using our make request method. | |
845 */ | |
846 | |
847 req = evhttp_request_new(http_postrequest_done, NULL); | |
848 if (req == NULL) { | |
849 fprintf(stdout, "FAILED\n"); | |
850 exit(1); | |
851 } | |
852 | |
853 /* Add the information that we care about */ | |
854 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
855 evbuffer_add_printf(req->output_buffer, POST_DATA); | |
856 | |
857 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) { | |
858 fprintf(stdout, "FAILED\n"); | |
859 exit(1); | |
860 } | |
861 | |
862 event_dispatch(); | |
863 | |
864 evhttp_connection_free(evcon); | |
865 evhttp_free(http); | |
866 | |
867 if (test_ok != 1) { | |
868 fprintf(stdout, "FAILED: %d\n", test_ok); | |
869 exit(1); | |
870 } | |
871 | |
872 fprintf(stdout, "OK\n"); | |
873 } | |
874 | |
875 void | |
876 http_post_cb(struct evhttp_request *req, void *arg) | |
877 { | |
878 struct evbuffer *evb; | |
879 event_debug(("%s: called\n", __func__)); | |
880 | |
881 /* Yes, we are expecting a post request */ | |
882 if (req->type != EVHTTP_REQ_POST) { | |
883 fprintf(stdout, "FAILED (post type)\n"); | |
884 exit(1); | |
885 } | |
886 | |
887 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(POST_DATA)) { | |
888 fprintf(stdout, "FAILED (length: %zu vs %zu)\n", | |
889 EVBUFFER_LENGTH(req->input_buffer), strlen(POST_DATA)); | |
890 exit(1); | |
891 } | |
892 | |
893 if (memcmp(EVBUFFER_DATA(req->input_buffer), POST_DATA, | |
894 strlen(POST_DATA))) { | |
895 fprintf(stdout, "FAILED (data)\n"); | |
896 fprintf(stdout, "Got :%s\n", EVBUFFER_DATA(req->input_buffer)); | |
897 fprintf(stdout, "Want:%s\n", POST_DATA); | |
898 exit(1); | |
899 } | |
900 | |
901 evb = evbuffer_new(); | |
902 evbuffer_add_printf(evb, "This is funny"); | |
903 | |
904 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); | |
905 | |
906 evbuffer_free(evb); | |
907 } | |
908 | |
909 void | |
910 http_postrequest_done(struct evhttp_request *req, void *arg) | |
911 { | |
912 const char *what = "This is funny"; | |
913 | |
914 if (req == NULL) { | |
915 fprintf(stderr, "FAILED (timeout)\n"); | |
916 exit(1); | |
917 } | |
918 | |
919 if (req->response_code != HTTP_OK) { | |
920 | |
921 fprintf(stderr, "FAILED (response code)\n"); | |
922 exit(1); | |
923 } | |
924 | |
925 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { | |
926 fprintf(stderr, "FAILED (content type)\n"); | |
927 exit(1); | |
928 } | |
929 | |
930 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { | |
931 fprintf(stderr, "FAILED (length %zu vs %zu)\n", | |
932 EVBUFFER_LENGTH(req->input_buffer), strlen(what)); | |
933 exit(1); | |
934 } | |
935 | |
936 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { | |
937 fprintf(stderr, "FAILED (data)\n"); | |
938 exit(1); | |
939 } | |
940 | |
941 test_ok = 1; | |
942 event_loopexit(NULL); | |
943 } | |
944 | |
945 static void | |
946 http_failure_readcb(struct bufferevent *bev, void *arg) | |
947 { | |
948 const char *what = "400 Bad Request"; | |
949 if (evbuffer_find(bev->input, (const unsigned char*) what, strlen(what))
!= NULL) { | |
950 test_ok = 2; | |
951 bufferevent_disable(bev, EV_READ); | |
952 event_loopexit(NULL); | |
953 } | |
954 } | |
955 | |
956 /* | |
957 * Testing that the HTTP server can deal with a malformed request. | |
958 */ | |
959 static void | |
960 http_failure_test(void) | |
961 { | |
962 struct bufferevent *bev; | |
963 int fd; | |
964 const char *http_request; | |
965 short port = -1; | |
966 | |
967 test_ok = 0; | |
968 fprintf(stdout, "Testing Bad HTTP Request: "); | |
969 | |
970 http = http_setup(&port, NULL); | |
971 | |
972 fd = http_connect("127.0.0.1", port); | |
973 | |
974 /* Stupid thing to send a request */ | |
975 bev = bufferevent_new(fd, http_failure_readcb, http_writecb, | |
976 http_errorcb, NULL); | |
977 | |
978 http_request = "illegal request\r\n"; | |
979 | |
980 bufferevent_write(bev, http_request, strlen(http_request)); | |
981 | |
982 event_dispatch(); | |
983 | |
984 bufferevent_free(bev); | |
985 EVUTIL_CLOSESOCKET(fd); | |
986 | |
987 evhttp_free(http); | |
988 | |
989 if (test_ok != 2) { | |
990 fprintf(stdout, "FAILED\n"); | |
991 exit(1); | |
992 } | |
993 | |
994 fprintf(stdout, "OK\n"); | |
995 } | |
996 | |
997 static void | |
998 close_detect_done(struct evhttp_request *req, void *arg) | |
999 { | |
1000 struct timeval tv; | |
1001 if (req == NULL || req->response_code != HTTP_OK) { | |
1002 | |
1003 fprintf(stderr, "FAILED\n"); | |
1004 exit(1); | |
1005 } | |
1006 | |
1007 test_ok = 1; | |
1008 | |
1009 timerclear(&tv); | |
1010 tv.tv_sec = 3; /* longer than the http time out */ | |
1011 | |
1012 event_loopexit(&tv); | |
1013 } | |
1014 | |
1015 static void | |
1016 close_detect_launch(int fd, short what, void *arg) | |
1017 { | |
1018 struct evhttp_connection *evcon = arg; | |
1019 struct evhttp_request *req; | |
1020 | |
1021 req = evhttp_request_new(close_detect_done, NULL); | |
1022 | |
1023 /* Add the information that we care about */ | |
1024 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
1025 | |
1026 /* We give ownership of the request to the connection */ | |
1027 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { | |
1028 fprintf(stdout, "FAILED\n"); | |
1029 exit(1); | |
1030 } | |
1031 } | |
1032 | |
1033 static void | |
1034 close_detect_cb(struct evhttp_request *req, void *arg) | |
1035 { | |
1036 struct evhttp_connection *evcon = arg; | |
1037 struct timeval tv; | |
1038 | |
1039 if (req != NULL && req->response_code != HTTP_OK) { | |
1040 | |
1041 fprintf(stderr, "FAILED\n"); | |
1042 exit(1); | |
1043 } | |
1044 | |
1045 timerclear(&tv); | |
1046 tv.tv_sec = 3; /* longer than the http time out */ | |
1047 | |
1048 /* launch a new request on the persistent connection in 6 seconds */ | |
1049 event_once(-1, EV_TIMEOUT, close_detect_launch, evcon, &tv); | |
1050 } | |
1051 | |
1052 | |
1053 static void | |
1054 http_close_detection(int with_delay) | |
1055 { | |
1056 short port = -1; | |
1057 struct evhttp_connection *evcon = NULL; | |
1058 struct evhttp_request *req = NULL; | |
1059 | |
1060 test_ok = 0; | |
1061 fprintf(stdout, "Testing Connection Close Detection%s: ", | |
1062 with_delay ? " (with delay)" : ""); | |
1063 | |
1064 http = http_setup(&port, NULL); | |
1065 | |
1066 /* 2 second timeout */ | |
1067 evhttp_set_timeout(http, 2); | |
1068 | |
1069 evcon = evhttp_connection_new("127.0.0.1", port); | |
1070 if (evcon == NULL) { | |
1071 fprintf(stdout, "FAILED\n"); | |
1072 exit(1); | |
1073 } | |
1074 | |
1075 delayed_client = evcon; | |
1076 | |
1077 /* | |
1078 * At this point, we want to schedule a request to the HTTP | |
1079 * server using our make request method. | |
1080 */ | |
1081 | |
1082 req = evhttp_request_new(close_detect_cb, evcon); | |
1083 | |
1084 /* Add the information that we care about */ | |
1085 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
1086 | |
1087 /* We give ownership of the request to the connection */ | |
1088 if (evhttp_make_request(evcon, | |
1089 req, EVHTTP_REQ_GET, with_delay ? "/largedelay" : "/test") == -1) { | |
1090 fprintf(stdout, "FAILED\n"); | |
1091 exit(1); | |
1092 } | |
1093 | |
1094 event_dispatch(); | |
1095 | |
1096 if (test_ok != 1) { | |
1097 fprintf(stdout, "FAILED\n"); | |
1098 exit(1); | |
1099 } | |
1100 | |
1101 /* at this point, the http server should have no connection */ | |
1102 if (TAILQ_FIRST(&http->connections) != NULL) { | |
1103 fprintf(stdout, "FAILED (left connections)\n"); | |
1104 exit(1); | |
1105 } | |
1106 | |
1107 evhttp_connection_free(evcon); | |
1108 evhttp_free(http); | |
1109 | |
1110 fprintf(stdout, "OK\n"); | |
1111 } | |
1112 | |
1113 static void | |
1114 http_highport_test(void) | |
1115 { | |
1116 int i = -1; | |
1117 struct evhttp *myhttp = NULL; | |
1118 | |
1119 fprintf(stdout, "Testing HTTP Server with high port: "); | |
1120 | |
1121 /* Try a few different ports */ | |
1122 for (i = 0; i < 50; ++i) { | |
1123 myhttp = evhttp_start("127.0.0.1", 65535 - i); | |
1124 if (myhttp != NULL) { | |
1125 fprintf(stdout, "OK\n"); | |
1126 evhttp_free(myhttp); | |
1127 return; | |
1128 } | |
1129 } | |
1130 | |
1131 fprintf(stdout, "FAILED\n"); | |
1132 exit(1); | |
1133 } | |
1134 | |
1135 static void | |
1136 http_bad_header_test(void) | |
1137 { | |
1138 struct evkeyvalq headers; | |
1139 | |
1140 fprintf(stdout, "Testing HTTP Header filtering: "); | |
1141 | |
1142 TAILQ_INIT(&headers); | |
1143 | |
1144 if (evhttp_add_header(&headers, "One", "Two") != 0) | |
1145 goto fail; | |
1146 | |
1147 if (evhttp_add_header(&headers, "One\r", "Two") != -1) | |
1148 goto fail; | |
1149 if (evhttp_add_header(&headers, "One", "Two") != 0) | |
1150 goto fail; | |
1151 if (evhttp_add_header(&headers, "One", "Two\r\n Three") != 0) | |
1152 goto fail; | |
1153 if (evhttp_add_header(&headers, "One\r", "Two") != -1) | |
1154 goto fail; | |
1155 if (evhttp_add_header(&headers, "One\n", "Two") != -1) | |
1156 goto fail; | |
1157 if (evhttp_add_header(&headers, "One", "Two\r") != -1) | |
1158 goto fail; | |
1159 if (evhttp_add_header(&headers, "One", "Two\n") != -1) | |
1160 goto fail; | |
1161 | |
1162 evhttp_clear_headers(&headers); | |
1163 | |
1164 fprintf(stdout, "OK\n"); | |
1165 return; | |
1166 fail: | |
1167 fprintf(stdout, "FAILED\n"); | |
1168 exit(1); | |
1169 } | |
1170 | |
1171 static int validate_header( | |
1172 const struct evkeyvalq* headers, | |
1173 const char *key, const char *value) | |
1174 { | |
1175 const char *real_val = evhttp_find_header(headers, key); | |
1176 if (real_val == NULL) | |
1177 return (-1); | |
1178 if (strcmp(real_val, value) != 0) | |
1179 return (-1); | |
1180 return (0); | |
1181 } | |
1182 | |
1183 static void | |
1184 http_parse_query_test(void) | |
1185 { | |
1186 struct evkeyvalq headers; | |
1187 | |
1188 fprintf(stdout, "Testing HTTP query parsing: "); | |
1189 | |
1190 TAILQ_INIT(&headers); | |
1191 | |
1192 evhttp_parse_query("http://www.test.com/?q=test", &headers); | |
1193 if (validate_header(&headers, "q", "test") != 0) | |
1194 goto fail; | |
1195 evhttp_clear_headers(&headers); | |
1196 | |
1197 evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers); | |
1198 if (validate_header(&headers, "q", "test") != 0) | |
1199 goto fail; | |
1200 if (validate_header(&headers, "foo", "bar") != 0) | |
1201 goto fail; | |
1202 evhttp_clear_headers(&headers); | |
1203 | |
1204 evhttp_parse_query("http://www.test.com/?q=test+foo", &headers); | |
1205 if (validate_header(&headers, "q", "test foo") != 0) | |
1206 goto fail; | |
1207 evhttp_clear_headers(&headers); | |
1208 | |
1209 evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers); | |
1210 if (validate_header(&headers, "q", "test\nfoo") != 0) | |
1211 goto fail; | |
1212 evhttp_clear_headers(&headers); | |
1213 | |
1214 evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers); | |
1215 if (validate_header(&headers, "q", "test\rfoo") != 0) | |
1216 goto fail; | |
1217 evhttp_clear_headers(&headers); | |
1218 | |
1219 fprintf(stdout, "OK\n"); | |
1220 return; | |
1221 fail: | |
1222 fprintf(stdout, "FAILED\n"); | |
1223 exit(1); | |
1224 } | |
1225 | |
1226 static void | |
1227 http_base_test(void) | |
1228 { | |
1229 struct bufferevent *bev; | |
1230 int fd; | |
1231 const char *http_request; | |
1232 short port = -1; | |
1233 | |
1234 test_ok = 0; | |
1235 fprintf(stdout, "Testing HTTP Server Event Base: "); | |
1236 | |
1237 base = event_init(); | |
1238 | |
1239 /* | |
1240 * create another bogus base - which is being used by all subsequen | |
1241 * tests - yuck! | |
1242 */ | |
1243 event_init(); | |
1244 | |
1245 http = http_setup(&port, base); | |
1246 | |
1247 fd = http_connect("127.0.0.1", port); | |
1248 | |
1249 /* Stupid thing to send a request */ | |
1250 bev = bufferevent_new(fd, http_readcb, http_writecb, | |
1251 http_errorcb, NULL); | |
1252 bufferevent_base_set(base, bev); | |
1253 | |
1254 http_request = | |
1255 "GET /test HTTP/1.1\r\n" | |
1256 "Host: somehost\r\n" | |
1257 "Connection: close\r\n" | |
1258 "\r\n"; | |
1259 | |
1260 bufferevent_write(bev, http_request, strlen(http_request)); | |
1261 | |
1262 event_base_dispatch(base); | |
1263 | |
1264 bufferevent_free(bev); | |
1265 EVUTIL_CLOSESOCKET(fd); | |
1266 | |
1267 evhttp_free(http); | |
1268 | |
1269 event_base_free(base); | |
1270 base = NULL; | |
1271 | |
1272 if (test_ok != 2) { | |
1273 fprintf(stdout, "FAILED\n"); | |
1274 exit(1); | |
1275 } | |
1276 | |
1277 fprintf(stdout, "OK\n"); | |
1278 } | |
1279 | |
1280 /* | |
1281 * the server is going to reply with chunked data. | |
1282 */ | |
1283 | |
1284 static void | |
1285 http_chunked_readcb(struct bufferevent *bev, void *arg) | |
1286 { | |
1287 /* nothing here */ | |
1288 } | |
1289 | |
1290 static void | |
1291 http_chunked_errorcb(struct bufferevent *bev, short what, void *arg) | |
1292 { | |
1293 if (!test_ok) | |
1294 goto out; | |
1295 | |
1296 test_ok = -1; | |
1297 | |
1298 if ((what & EVBUFFER_EOF) != 0) { | |
1299 struct evhttp_request *req = evhttp_request_new(NULL, NULL); | |
1300 const char *header; | |
1301 enum message_read_status done; | |
1302 | |
1303 req->kind = EVHTTP_RESPONSE; | |
1304 done = evhttp_parse_firstline(req, EVBUFFER_INPUT(bev)); | |
1305 if (done != ALL_DATA_READ) | |
1306 goto out; | |
1307 | |
1308 done = evhttp_parse_headers(req, EVBUFFER_INPUT(bev)); | |
1309 if (done != ALL_DATA_READ) | |
1310 goto out; | |
1311 | |
1312 header = evhttp_find_header(req->input_headers, "Transfer-Encodi
ng"); | |
1313 if (header == NULL || strcmp(header, "chunked")) | |
1314 goto out; | |
1315 | |
1316 header = evhttp_find_header(req->input_headers, "Connection"); | |
1317 if (header == NULL || strcmp(header, "close")) | |
1318 goto out; | |
1319 | |
1320 header = evbuffer_readline(EVBUFFER_INPUT(bev)); | |
1321 if (header == NULL) | |
1322 goto out; | |
1323 /* 13 chars */ | |
1324 if (strcmp(header, "d")) | |
1325 goto out; | |
1326 free((char*)header); | |
1327 | |
1328 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), | |
1329 "This is funny", 13)) | |
1330 goto out; | |
1331 | |
1332 evbuffer_drain(EVBUFFER_INPUT(bev), 13 + 2); | |
1333 | |
1334 header = evbuffer_readline(EVBUFFER_INPUT(bev)); | |
1335 if (header == NULL) | |
1336 goto out; | |
1337 /* 18 chars */ | |
1338 if (strcmp(header, "12")) | |
1339 goto out; | |
1340 free((char *)header); | |
1341 | |
1342 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), | |
1343 "but not hilarious.", 18)) | |
1344 goto out; | |
1345 | |
1346 evbuffer_drain(EVBUFFER_INPUT(bev), 18 + 2); | |
1347 | |
1348 header = evbuffer_readline(EVBUFFER_INPUT(bev)); | |
1349 if (header == NULL) | |
1350 goto out; | |
1351 /* 8 chars */ | |
1352 if (strcmp(header, "8")) | |
1353 goto out; | |
1354 free((char *)header); | |
1355 | |
1356 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), | |
1357 "bwv 1052.", 8)) | |
1358 goto out; | |
1359 | |
1360 evbuffer_drain(EVBUFFER_INPUT(bev), 8 + 2); | |
1361 | |
1362 header = evbuffer_readline(EVBUFFER_INPUT(bev)); | |
1363 if (header == NULL) | |
1364 goto out; | |
1365 /* 0 chars */ | |
1366 if (strcmp(header, "0")) | |
1367 goto out; | |
1368 free((char *)header); | |
1369 | |
1370 test_ok = 2; | |
1371 } | |
1372 | |
1373 out: | |
1374 event_loopexit(NULL); | |
1375 } | |
1376 | |
1377 static void | |
1378 http_chunked_writecb(struct bufferevent *bev, void *arg) | |
1379 { | |
1380 if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(bev)) == 0) { | |
1381 /* enable reading of the reply */ | |
1382 bufferevent_enable(bev, EV_READ); | |
1383 test_ok++; | |
1384 } | |
1385 } | |
1386 | |
1387 static void | |
1388 http_chunked_request_done(struct evhttp_request *req, void *arg) | |
1389 { | |
1390 if (req->response_code != HTTP_OK) { | |
1391 fprintf(stderr, "FAILED\n"); | |
1392 exit(1); | |
1393 } | |
1394 | |
1395 if (evhttp_find_header(req->input_headers, | |
1396 "Transfer-Encoding") == NULL) { | |
1397 fprintf(stderr, "FAILED\n"); | |
1398 exit(1); | |
1399 } | |
1400 | |
1401 if (EVBUFFER_LENGTH(req->input_buffer) != 13 + 18 + 8) { | |
1402 fprintf(stderr, "FAILED\n"); | |
1403 exit(1); | |
1404 } | |
1405 | |
1406 if (strncmp((char *)EVBUFFER_DATA(req->input_buffer), | |
1407 "This is funnybut not hilarious.bwv 1052", | |
1408 13 + 18 + 8)) { | |
1409 fprintf(stderr, "FAILED\n"); | |
1410 exit(1); | |
1411 } | |
1412 | |
1413 test_ok = 1; | |
1414 event_loopexit(NULL); | |
1415 } | |
1416 | |
1417 static void | |
1418 http_chunked_test(void) | |
1419 { | |
1420 struct bufferevent *bev; | |
1421 int fd; | |
1422 const char *http_request; | |
1423 short port = -1; | |
1424 struct timeval tv_start, tv_end; | |
1425 struct evhttp_connection *evcon = NULL; | |
1426 struct evhttp_request *req = NULL; | |
1427 int i; | |
1428 | |
1429 test_ok = 0; | |
1430 fprintf(stdout, "Testing Chunked HTTP Reply: "); | |
1431 | |
1432 http = http_setup(&port, NULL); | |
1433 | |
1434 fd = http_connect("127.0.0.1", port); | |
1435 | |
1436 /* Stupid thing to send a request */ | |
1437 bev = bufferevent_new(fd, | |
1438 http_chunked_readcb, http_chunked_writecb, | |
1439 http_chunked_errorcb, NULL); | |
1440 | |
1441 http_request = | |
1442 "GET /chunked HTTP/1.1\r\n" | |
1443 "Host: somehost\r\n" | |
1444 "Connection: close\r\n" | |
1445 "\r\n"; | |
1446 | |
1447 bufferevent_write(bev, http_request, strlen(http_request)); | |
1448 | |
1449 evutil_gettimeofday(&tv_start, NULL); | |
1450 | |
1451 event_dispatch(); | |
1452 | |
1453 evutil_gettimeofday(&tv_end, NULL); | |
1454 evutil_timersub(&tv_end, &tv_start, &tv_end); | |
1455 | |
1456 if (tv_end.tv_sec >= 1) { | |
1457 fprintf(stdout, "FAILED (time)\n"); | |
1458 exit (1); | |
1459 } | |
1460 | |
1461 | |
1462 if (test_ok != 2) { | |
1463 fprintf(stdout, "FAILED\n"); | |
1464 exit(1); | |
1465 } | |
1466 | |
1467 /* now try again with the regular connection object */ | |
1468 evcon = evhttp_connection_new("127.0.0.1", port); | |
1469 if (evcon == NULL) { | |
1470 fprintf(stdout, "FAILED\n"); | |
1471 exit(1); | |
1472 } | |
1473 | |
1474 /* make two requests to check the keepalive behavior */ | |
1475 for (i = 0; i < 2; i++) { | |
1476 test_ok = 0; | |
1477 req = evhttp_request_new(http_chunked_request_done, NULL); | |
1478 | |
1479 /* Add the information that we care about */ | |
1480 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
1481 | |
1482 /* We give ownership of the request to the connection */ | |
1483 if (evhttp_make_request(evcon, req, | |
1484 EVHTTP_REQ_GET, "/chunked") == -1) { | |
1485 fprintf(stdout, "FAILED\n"); | |
1486 exit(1); | |
1487 } | |
1488 | |
1489 event_dispatch(); | |
1490 | |
1491 if (test_ok != 1) { | |
1492 fprintf(stdout, "FAILED\n"); | |
1493 exit(1); | |
1494 } | |
1495 } | |
1496 | |
1497 evhttp_connection_free(evcon); | |
1498 evhttp_free(http); | |
1499 | |
1500 fprintf(stdout, "OK\n"); | |
1501 } | |
1502 | |
1503 static void | |
1504 http_multi_line_header_test(void) | |
1505 { | |
1506 struct bufferevent *bev; | |
1507 int fd; | |
1508 const char *http_start_request; | |
1509 short port = -1; | |
1510 | |
1511 test_ok = 0; | |
1512 fprintf(stdout, "Testing HTTP Server with multi line: "); | |
1513 | |
1514 http = http_setup(&port, NULL); | |
1515 | |
1516 fd = http_connect("127.0.0.1", port); | |
1517 | |
1518 /* Stupid thing to send a request */ | |
1519 bev = bufferevent_new(fd, http_readcb, http_writecb, | |
1520 http_errorcb, NULL); | |
1521 | |
1522 http_start_request = | |
1523 "GET /test HTTP/1.1\r\n" | |
1524 "Host: somehost\r\n" | |
1525 "Connection: close\r\n" | |
1526 "X-Multi: aaaaaaaa\r\n" | |
1527 " a\r\n" | |
1528 "\tEND\r\n" | |
1529 "X-Last: last\r\n" | |
1530 "\r\n"; | |
1531 | |
1532 bufferevent_write(bev, http_start_request, strlen(http_start_request)); | |
1533 | |
1534 event_dispatch(); | |
1535 | |
1536 bufferevent_free(bev); | |
1537 EVUTIL_CLOSESOCKET(fd); | |
1538 | |
1539 evhttp_free(http); | |
1540 | |
1541 if (test_ok != 4) { | |
1542 fprintf(stdout, "FAILED\n"); | |
1543 exit(1); | |
1544 } | |
1545 | |
1546 fprintf(stdout, "OK\n"); | |
1547 } | |
1548 | |
1549 static void | |
1550 http_request_bad(struct evhttp_request *req, void *arg) | |
1551 { | |
1552 if (req != NULL) { | |
1553 fprintf(stderr, "FAILED\n"); | |
1554 exit(1); | |
1555 } | |
1556 | |
1557 test_ok = 1; | |
1558 event_loopexit(NULL); | |
1559 } | |
1560 | |
1561 static void | |
1562 http_negative_content_length_test(void) | |
1563 { | |
1564 short port = -1; | |
1565 struct evhttp_connection *evcon = NULL; | |
1566 struct evhttp_request *req = NULL; | |
1567 | |
1568 test_ok = 0; | |
1569 fprintf(stdout, "Testing HTTP Negative Content Length: "); | |
1570 | |
1571 http = http_setup(&port, NULL); | |
1572 | |
1573 evcon = evhttp_connection_new("127.0.0.1", port); | |
1574 if (evcon == NULL) { | |
1575 fprintf(stdout, "FAILED\n"); | |
1576 exit(1); | |
1577 } | |
1578 | |
1579 /* | |
1580 * At this point, we want to schedule a request to the HTTP | |
1581 * server using our make request method. | |
1582 */ | |
1583 | |
1584 req = evhttp_request_new(http_request_bad, NULL); | |
1585 | |
1586 /* Cause the response to have a negative content-length */ | |
1587 evhttp_add_header(req->output_headers, "X-Negative", "makeitso"); | |
1588 | |
1589 /* We give ownership of the request to the connection */ | |
1590 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { | |
1591 fprintf(stdout, "FAILED\n"); | |
1592 exit(1); | |
1593 } | |
1594 | |
1595 event_dispatch(); | |
1596 | |
1597 evhttp_free(http); | |
1598 | |
1599 if (test_ok != 1) { | |
1600 fprintf(stdout, "FAILED\n"); | |
1601 exit(1); | |
1602 } | |
1603 | |
1604 fprintf(stdout, "OK\n"); | |
1605 } | |
1606 | |
1607 /* | |
1608 * Testing client reset of server chunked connections | |
1609 */ | |
1610 | |
1611 struct terminate_state { | |
1612 struct evhttp_request *req; | |
1613 struct bufferevent *bev; | |
1614 int fd; | |
1615 } terminate_state; | |
1616 | |
1617 static void | |
1618 terminate_chunked_trickle_cb(int fd, short events, void *arg) | |
1619 { | |
1620 struct terminate_state *state = arg; | |
1621 struct evbuffer *evb = evbuffer_new(); | |
1622 struct timeval tv; | |
1623 | |
1624 if (evhttp_request_get_connection(state->req) == NULL) { | |
1625 test_ok = 1; | |
1626 evhttp_request_free(state->req); | |
1627 event_loopexit(NULL); | |
1628 return; | |
1629 } | |
1630 | |
1631 evbuffer_add_printf(evb, "%p", evb); | |
1632 evhttp_send_reply_chunk(state->req, evb); | |
1633 evbuffer_free(evb); | |
1634 | |
1635 tv.tv_sec = 0; | |
1636 tv.tv_usec = 3000; | |
1637 event_once(-1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); | |
1638 } | |
1639 | |
1640 static void | |
1641 terminate_chunked_cb(struct evhttp_request *req, void *arg) | |
1642 { | |
1643 struct terminate_state *state = arg; | |
1644 struct timeval tv; | |
1645 | |
1646 state->req = req; | |
1647 | |
1648 evhttp_send_reply_start(req, HTTP_OK, "OK"); | |
1649 | |
1650 tv.tv_sec = 0; | |
1651 tv.tv_usec = 3000; | |
1652 event_once(-1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); | |
1653 } | |
1654 | |
1655 static void | |
1656 terminate_chunked_client(int fd, short event, void *arg) | |
1657 { | |
1658 struct terminate_state *state = arg; | |
1659 bufferevent_free(state->bev); | |
1660 EVUTIL_CLOSESOCKET(state->fd); | |
1661 } | |
1662 | |
1663 static void | |
1664 terminate_readcb(struct bufferevent *bev, void *arg) | |
1665 { | |
1666 /* just drop the data */ | |
1667 evbuffer_drain(bev->output, -1); | |
1668 } | |
1669 | |
1670 | |
1671 static void | |
1672 http_terminate_chunked_test(void) | |
1673 { | |
1674 struct bufferevent *bev = NULL; | |
1675 struct timeval tv; | |
1676 const char *http_request; | |
1677 short port = -1; | |
1678 int fd = -1; | |
1679 | |
1680 test_ok = 0; | |
1681 fprintf(stdout, "Testing Terminated Chunked Connection: "); | |
1682 | |
1683 http = http_setup(&port, NULL); | |
1684 evhttp_del_cb(http, "/test"); | |
1685 evhttp_set_cb(http, "/test", terminate_chunked_cb, &terminate_state); | |
1686 | |
1687 fd = http_connect("127.0.0.1", port); | |
1688 | |
1689 /* Stupid thing to send a request */ | |
1690 bev = bufferevent_new(fd, terminate_readcb, http_writecb, | |
1691 http_errorcb, NULL); | |
1692 | |
1693 terminate_state.fd = fd; | |
1694 terminate_state.bev = bev; | |
1695 | |
1696 /* first half of the http request */ | |
1697 http_request = | |
1698 "GET /test HTTP/1.1\r\n" | |
1699 "Host: some\r\n\r\n"; | |
1700 | |
1701 bufferevent_write(bev, http_request, strlen(http_request)); | |
1702 evutil_timerclear(&tv); | |
1703 tv.tv_usec = 10000; | |
1704 event_once(-1, EV_TIMEOUT, terminate_chunked_client, &terminate_state, | |
1705 &tv); | |
1706 | |
1707 event_dispatch(); | |
1708 | |
1709 if (test_ok != 1) { | |
1710 fprintf(stdout, "FAILED\n"); | |
1711 exit(1); | |
1712 } | |
1713 | |
1714 fprintf(stdout, "OK\n"); | |
1715 | |
1716 if (fd >= 0) | |
1717 EVUTIL_CLOSESOCKET(fd); | |
1718 if (http) | |
1719 evhttp_free(http); | |
1720 } | |
1721 | |
1722 void | |
1723 http_suite(void) | |
1724 { | |
1725 http_base_test(); | |
1726 http_bad_header_test(); | |
1727 http_parse_query_test(); | |
1728 http_basic_test(); | |
1729 http_connection_test(0 /* not-persistent */); | |
1730 http_connection_test(1 /* persistent */); | |
1731 http_close_detection(0 /* without delay */); | |
1732 http_close_detection(1 /* with delay */); | |
1733 http_bad_request(); | |
1734 http_post_test(); | |
1735 http_failure_test(); | |
1736 http_highport_test(); | |
1737 http_dispatcher_test(); | |
1738 | |
1739 http_multi_line_header_test(); | |
1740 http_negative_content_length_test(); | |
1741 | |
1742 http_chunked_test(); | |
1743 http_terminate_chunked_test(); | |
1744 } | |
OLD | NEW |