xref: /netbsd-src/external/bsd/libevent/dist/test/regress_http.c (revision d25ffa98a4bfca1fe272f3c182496ec9934faac7)
1 /*	$NetBSD: regress_http.c,v 1.2 2010/11/11 14:08:45 pgoyette Exp $	*/
2 /*
3  * Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #ifdef WIN32
30 #include <winsock2.h>
31 #include <windows.h>
32 #endif
33 
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37 
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #ifdef HAVE_SYS_TIME_H
41 #include <sys/time.h>
42 #endif
43 #include <sys/queue.h>
44 #ifndef WIN32
45 #include <sys/socket.h>
46 #include <signal.h>
47 #include <unistd.h>
48 #include <netdb.h>
49 #endif
50 #include <fcntl.h>
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <errno.h>
55 
56 #include "event.h"
57 #include "evhttp.h"
58 #include "log.h"
59 #include "http-internal.h"
60 
61 extern int pair[];
62 extern int test_ok;
63 
64 static struct evhttp *http;
65 /* set if a test needs to call loopexit on a base */
66 static struct event_base *base;
67 
68 void http_suite(void);
69 
70 void http_basic_cb(struct evhttp_request *req, void *arg);
71 static void http_chunked_cb(struct evhttp_request *req, void *arg);
72 void http_post_cb(struct evhttp_request *req, void *arg);
73 void http_dispatcher_cb(struct evhttp_request *req, void *arg);
74 static void http_large_delay_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, "/", http_dispatcher_cb, NULL);
101 
102 	*pport = port;
103 	return (myhttp);
104 }
105 
106 #ifndef NI_MAXSERV
107 #define NI_MAXSERV 1024
108 #endif
109 
110 static int
111 http_connect(const char *address, u_short port)
112 {
113 	/* Stupid code for connecting */
114 #ifdef WIN32
115 	struct hostent *he;
116 	struct sockaddr_in sin;
117 #else
118 	struct addrinfo ai, *aitop;
119 	char strport[NI_MAXSERV];
120 #endif
121 	struct sockaddr *sa;
122 	int slen;
123 	int fd;
124 
125 #ifdef WIN32
126 	if (!(he = gethostbyname(address))) {
127 		event_warn("gethostbyname");
128 	}
129 	memcpy(&sin.sin_addr, he->h_addr_list[0], he->h_length);
130 	sin.sin_family = AF_INET;
131 	sin.sin_port = htons(port);
132 	slen = sizeof(struct sockaddr_in);
133 	sa = (struct sockaddr*)&sin;
134 #else
135 	memset(&ai, 0, sizeof (ai));
136 	ai.ai_family = AF_INET;
137 	ai.ai_socktype = SOCK_STREAM;
138 	snprintf(strport, sizeof (strport), "%d", port);
139 	if (getaddrinfo(address, strport, &ai, &aitop) != 0) {
140 		event_warn("getaddrinfo");
141 		return (-1);
142 	}
143 	sa = aitop->ai_addr;
144 	slen = aitop->ai_addrlen;
145 #endif
146 
147 	fd = socket(AF_INET, SOCK_STREAM, 0);
148 	if (fd == -1)
149 		event_err(1, "socket failed");
150 
151 	if (connect(fd, sa, slen) == -1)
152 		event_err(1, "connect failed");
153 
154 #ifndef WIN32
155 	freeaddrinfo(aitop);
156 #endif
157 
158 	return (fd);
159 }
160 
161 static void
162 http_readcb(struct bufferevent *bev, void *arg)
163 {
164 	const char *what = "This is funny";
165 
166  	event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input)));
167 
168 	if (evbuffer_find(bev->input,
169 		(const unsigned char*) what, strlen(what)) != NULL) {
170 		struct evhttp_request *req = evhttp_request_new(NULL, NULL);
171 		enum message_read_status done;
172 
173 		req->kind = EVHTTP_RESPONSE;
174 		done = evhttp_parse_firstline(req, bev->input);
175 		if (done != ALL_DATA_READ)
176 			goto out;
177 
178 		done = evhttp_parse_headers(req, bev->input);
179 		if (done != ALL_DATA_READ)
180 			goto out;
181 
182 		if (done == 1 &&
183 		    evhttp_find_header(req->input_headers,
184 			"Content-Type") != NULL)
185 			test_ok++;
186 
187 	out:
188 		evhttp_request_free(req);
189 		bufferevent_disable(bev, EV_READ);
190 		if (base)
191 			event_base_loopexit(base, NULL);
192 		else
193 			event_loopexit(NULL);
194 	}
195 }
196 
197 static void
198 http_writecb(struct bufferevent *bev, void *arg)
199 {
200 	if (EVBUFFER_LENGTH(bev->output) == 0) {
201 		/* enable reading of the reply */
202 		bufferevent_enable(bev, EV_READ);
203 		test_ok++;
204 	}
205 }
206 
207 static void
208 http_errorcb(struct bufferevent *bev, short what, void *arg)
209 {
210 	test_ok = -2;
211 	event_loopexit(NULL);
212 }
213 
214 void
215 http_basic_cb(struct evhttp_request *req, void *arg)
216 {
217 	struct evbuffer *evb = evbuffer_new();
218 	int empty = evhttp_find_header(req->input_headers, "Empty") != NULL;
219 	event_debug(("%s: called\n", __func__));
220 	evbuffer_add_printf(evb, "This is funny");
221 
222 	/* For multi-line headers test */
223 	{
224 		const char *multi =
225 		    evhttp_find_header(req->input_headers,"X-multi");
226 		if (multi) {
227 			if (strcmp("END", multi + strlen(multi) - 3) == 0)
228 				test_ok++;
229 			if (evhttp_find_header(req->input_headers, "X-Last"))
230 				test_ok++;
231 		}
232 	}
233 
234 	/* injecting a bad content-length */
235 	if (evhttp_find_header(req->input_headers, "X-Negative"))
236 		evhttp_add_header(req->output_headers,
237 		    "Content-Length", "-100");
238 
239 	/* allow sending of an empty reply */
240 	evhttp_send_reply(req, HTTP_OK, "Everything is fine",
241 	    !empty ? evb : NULL);
242 
243 	evbuffer_free(evb);
244 }
245 
246 static char const* const CHUNKS[] = {
247 	"This is funny",
248 	"but not hilarious.",
249 	"bwv 1052"
250 };
251 
252 struct chunk_req_state {
253 	struct evhttp_request *req;
254 	int i;
255 };
256 
257 static void
258 http_chunked_trickle_cb(int fd, short events, void *arg)
259 {
260 	struct evbuffer *evb = evbuffer_new();
261 	struct chunk_req_state *state = arg;
262 	struct timeval when = { 0, 0 };
263 
264 	evbuffer_add_printf(evb, "%s", CHUNKS[state->i]);
265 	evhttp_send_reply_chunk(state->req, evb);
266 	evbuffer_free(evb);
267 
268 	if (++state->i < sizeof(CHUNKS)/sizeof(CHUNKS[0])) {
269 		event_once(-1, EV_TIMEOUT,
270 		    http_chunked_trickle_cb, state, &when);
271 	} else {
272 		evhttp_send_reply_end(state->req);
273 		free(state);
274 	}
275 }
276 
277 static void
278 http_chunked_cb(struct evhttp_request *req, void *arg)
279 {
280 	struct timeval when = { 0, 0 };
281 	struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state));
282 	event_debug(("%s: called\n", __func__));
283 
284 	memset(state, 0, sizeof(struct chunk_req_state));
285 	state->req = req;
286 
287 	/* generate a chunked reply */
288 	evhttp_send_reply_start(req, HTTP_OK, "Everything is fine");
289 
290 	/* but trickle it across several iterations to ensure we're not
291 	 * assuming it comes all at once */
292 	event_once(-1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when);
293 }
294 
295 static void
296 http_complete_write(int fd, short what, void *arg)
297 {
298 	struct bufferevent *bev = arg;
299 	const char *http_request = "host\r\n"
300 	    "Connection: close\r\n"
301 	    "\r\n";
302 	bufferevent_write(bev, http_request, strlen(http_request));
303 }
304 
305 static void
306 http_basic_test(void)
307 {
308 	struct timeval tv;
309 	struct bufferevent *bev;
310 	int fd;
311 	const char *http_request;
312 	short port = -1;
313 
314 	test_ok = 0;
315 	fprintf(stdout, "Testing Basic HTTP Server: ");
316 
317 	http = http_setup(&port, NULL);
318 
319 	/* bind to a second socket */
320 	if (evhttp_bind_socket(http, "127.0.0.1", port + 1) == -1) {
321 		fprintf(stdout, "FAILED (bind)\n");
322 		exit(1);
323 	}
324 
325 	fd = http_connect("127.0.0.1", port);
326 
327 	/* Stupid thing to send a request */
328 	bev = bufferevent_new(fd, http_readcb, http_writecb,
329 	    http_errorcb, NULL);
330 
331 	/* first half of the http request */
332 	http_request =
333 	    "GET /test HTTP/1.1\r\n"
334 	    "Host: some";
335 
336 	bufferevent_write(bev, http_request, strlen(http_request));
337 	timerclear(&tv);
338 	tv.tv_usec = 10000;
339 	event_once(-1, EV_TIMEOUT, http_complete_write, bev, &tv);
340 
341 	event_dispatch();
342 
343 	if (test_ok != 3) {
344 		fprintf(stdout, "FAILED\n");
345 		exit(1);
346 	}
347 
348 	/* connect to the second port */
349 	bufferevent_free(bev);
350 	EVUTIL_CLOSESOCKET(fd);
351 
352 	fd = http_connect("127.0.0.1", port + 1);
353 
354 	/* Stupid thing to send a request */
355 	bev = bufferevent_new(fd, http_readcb, http_writecb,
356 	    http_errorcb, NULL);
357 
358 	http_request =
359 	    "GET /test HTTP/1.1\r\n"
360 	    "Host: somehost\r\n"
361 	    "Connection: close\r\n"
362 	    "\r\n";
363 
364 	bufferevent_write(bev, http_request, strlen(http_request));
365 
366 	event_dispatch();
367 
368 	bufferevent_free(bev);
369 	EVUTIL_CLOSESOCKET(fd);
370 
371 	evhttp_free(http);
372 
373 	if (test_ok != 5) {
374 		fprintf(stdout, "FAILED\n");
375 		exit(1);
376 	}
377 
378 	fprintf(stdout, "OK\n");
379 }
380 
381 static struct evhttp_connection *delayed_client;
382 
383 static void
384 http_delay_reply(int fd, short what, void *arg)
385 {
386 	struct evhttp_request *req = arg;
387 
388 	evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL);
389 
390 	++test_ok;
391 }
392 
393 static void
394 http_large_delay_cb(struct evhttp_request *req, void *arg)
395 {
396 	struct timeval tv;
397 	timerclear(&tv);
398 	tv.tv_sec = 3;
399 
400 	event_once(-1, EV_TIMEOUT, http_delay_reply, req, &tv);
401 
402 	/* here we close the client connection which will cause an EOF */
403 	evhttp_connection_fail(delayed_client, EVCON_HTTP_EOF);
404 }
405 
406 void http_request_done(struct evhttp_request *, void *);
407 void http_request_empty_done(struct evhttp_request *, void *);
408 
409 static void
410 http_connection_test(int persistent)
411 {
412 	short port = -1;
413 	struct evhttp_connection *evcon = NULL;
414 	struct evhttp_request *req = NULL;
415 
416 	test_ok = 0;
417 	fprintf(stdout, "Testing Request Connection Pipeline %s: ",
418 	    persistent ? "(persistent)" : "");
419 
420 	http = http_setup(&port, NULL);
421 
422 	evcon = evhttp_connection_new("127.0.0.1", port);
423 	if (evcon == NULL) {
424 		fprintf(stdout, "FAILED\n");
425 		exit(1);
426 	}
427 
428 	/*
429 	 * At this point, we want to schedule a request to the HTTP
430 	 * server using our make request method.
431 	 */
432 
433 	req = evhttp_request_new(http_request_done, NULL);
434 
435 	/* Add the information that we care about */
436 	evhttp_add_header(req->output_headers, "Host", "somehost");
437 
438 	/* We give ownership of the request to the connection */
439 	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
440 		fprintf(stdout, "FAILED\n");
441 		exit(1);
442 	}
443 
444 	event_dispatch();
445 
446 	if (test_ok != 1) {
447 		fprintf(stdout, "FAILED\n");
448 		exit(1);
449 	}
450 
451 	/* try to make another request over the same connection */
452 	test_ok = 0;
453 
454 	req = evhttp_request_new(http_request_done, NULL);
455 
456 	/* Add the information that we care about */
457 	evhttp_add_header(req->output_headers, "Host", "somehost");
458 
459 	/*
460 	 * if our connections are not supposed to be persistent; request
461 	 * a close from the server.
462 	 */
463 	if (!persistent)
464 		evhttp_add_header(req->output_headers, "Connection", "close");
465 
466 	/* We give ownership of the request to the connection */
467 	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
468 		fprintf(stdout, "FAILED\n");
469 		exit(1);
470 	}
471 
472 	event_dispatch();
473 
474 	/* make another request: request empty reply */
475 	test_ok = 0;
476 
477 	req = evhttp_request_new(http_request_empty_done, NULL);
478 
479 	/* Add the information that we care about */
480 	evhttp_add_header(req->output_headers, "Empty", "itis");
481 
482 	/* We give ownership of the request to the connection */
483 	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
484 		fprintf(stdout, "FAILED\n");
485 		exit(1);
486 	}
487 
488 	event_dispatch();
489 
490 	if (test_ok != 1) {
491 		fprintf(stdout, "FAILED\n");
492 		exit(1);
493 	}
494 
495 	evhttp_connection_free(evcon);
496 	evhttp_free(http);
497 
498 	fprintf(stdout, "OK\n");
499 }
500 
501 void
502 http_request_done(struct evhttp_request *req, void *arg)
503 {
504 	const char *what = "This is funny";
505 
506 	if (req->response_code != HTTP_OK) {
507 		fprintf(stderr, "FAILED\n");
508 		exit(1);
509 	}
510 
511 	if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
512 		fprintf(stderr, "FAILED\n");
513 		exit(1);
514 	}
515 
516 	if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) {
517 		fprintf(stderr, "FAILED\n");
518 		exit(1);
519 	}
520 
521 	if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) {
522 		fprintf(stderr, "FAILED\n");
523 		exit(1);
524 	}
525 
526 	test_ok = 1;
527 	event_loopexit(NULL);
528 }
529 
530 /* test date header and content length */
531 
532 void
533 http_request_empty_done(struct evhttp_request *req, void *arg)
534 {
535 	if (req->response_code != HTTP_OK) {
536 		fprintf(stderr, "FAILED\n");
537 		exit(1);
538 	}
539 
540 	if (evhttp_find_header(req->input_headers, "Date") == NULL) {
541 		fprintf(stderr, "FAILED\n");
542 		exit(1);
543 	}
544 
545 
546 	if (evhttp_find_header(req->input_headers, "Content-Length") == NULL) {
547 		fprintf(stderr, "FAILED\n");
548 		exit(1);
549 	}
550 
551 	if (strcmp(evhttp_find_header(req->input_headers, "Content-Length"),
552 		"0")) {
553 		fprintf(stderr, "FAILED\n");
554 		exit(1);
555 	}
556 
557 	if (EVBUFFER_LENGTH(req->input_buffer) != 0) {
558 		fprintf(stderr, "FAILED\n");
559 		exit(1);
560 	}
561 
562 	test_ok = 1;
563 	event_loopexit(NULL);
564 }
565 
566 /*
567  * HTTP DISPATCHER test
568  */
569 
570 void
571 http_dispatcher_cb(struct evhttp_request *req, void *arg)
572 {
573 
574 	struct evbuffer *evb = evbuffer_new();
575 	event_debug(("%s: called\n", __func__));
576 	evbuffer_add_printf(evb, "DISPATCHER_TEST");
577 
578 	evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
579 
580 	evbuffer_free(evb);
581 }
582 
583 static void
584 http_dispatcher_test_done(struct evhttp_request *req, void *arg)
585 {
586 	const char *what = "DISPATCHER_TEST";
587 
588 	if (req->response_code != HTTP_OK) {
589 		fprintf(stderr, "FAILED\n");
590 		exit(1);
591 	}
592 
593 	if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
594 		fprintf(stderr, "FAILED (content type)\n");
595 		exit(1);
596 	}
597 
598 	if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) {
599 		fprintf(stderr, "FAILED (length %zu vs %zu)\n",
600 		    EVBUFFER_LENGTH(req->input_buffer), strlen(what));
601 		exit(1);
602 	}
603 
604 	if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) {
605 		fprintf(stderr, "FAILED (data)\n");
606 		exit(1);
607 	}
608 
609 	test_ok = 1;
610 	event_loopexit(NULL);
611 }
612 
613 static void
614 http_dispatcher_test(void)
615 {
616 	short port = -1;
617 	struct evhttp_connection *evcon = NULL;
618 	struct evhttp_request *req = NULL;
619 
620 	test_ok = 0;
621 	fprintf(stdout, "Testing HTTP Dispatcher: ");
622 
623 	http = http_setup(&port, NULL);
624 
625 	evcon = evhttp_connection_new("127.0.0.1", port);
626 	if (evcon == NULL) {
627 		fprintf(stdout, "FAILED\n");
628 		exit(1);
629 	}
630 
631 	/* also bind to local host */
632 	evhttp_connection_set_local_address(evcon, "127.0.0.1");
633 
634 	/*
635 	 * At this point, we want to schedule an HTTP GET request
636 	 * server using our make request method.
637 	 */
638 
639 	req = evhttp_request_new(http_dispatcher_test_done, NULL);
640 	if (req == NULL) {
641 		fprintf(stdout, "FAILED\n");
642 		exit(1);
643 	}
644 
645 	/* Add the information that we care about */
646 	evhttp_add_header(req->output_headers, "Host", "somehost");
647 
648 	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) {
649 		fprintf(stdout, "FAILED\n");
650 		exit(1);
651 	}
652 
653 	event_dispatch();
654 
655 	evhttp_connection_free(evcon);
656 	evhttp_free(http);
657 
658 	if (test_ok != 1) {
659 		fprintf(stdout, "FAILED: %d\n", test_ok);
660 		exit(1);
661 	}
662 
663 	fprintf(stdout, "OK\n");
664 }
665 
666 /*
667  * HTTP POST test.
668  */
669 
670 void http_postrequest_done(struct evhttp_request *, void *);
671 
672 #define POST_DATA "Okay.  Not really printf"
673 
674 static void
675 http_post_test(void)
676 {
677 	short port = -1;
678 	struct evhttp_connection *evcon = NULL;
679 	struct evhttp_request *req = NULL;
680 
681 	test_ok = 0;
682 	fprintf(stdout, "Testing HTTP POST Request: ");
683 
684 	http = http_setup(&port, NULL);
685 
686 	evcon = evhttp_connection_new("127.0.0.1", port);
687 	if (evcon == NULL) {
688 		fprintf(stdout, "FAILED\n");
689 		exit(1);
690 	}
691 
692 	/*
693 	 * At this point, we want to schedule an HTTP POST request
694 	 * server using our make request method.
695 	 */
696 
697 	req = evhttp_request_new(http_postrequest_done, NULL);
698 	if (req == NULL) {
699 		fprintf(stdout, "FAILED\n");
700 		exit(1);
701 	}
702 
703 	/* Add the information that we care about */
704 	evhttp_add_header(req->output_headers, "Host", "somehost");
705 	evbuffer_add_printf(req->output_buffer, POST_DATA);
706 
707 	if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) {
708 		fprintf(stdout, "FAILED\n");
709 		exit(1);
710 	}
711 
712 	event_dispatch();
713 
714 	evhttp_connection_free(evcon);
715 	evhttp_free(http);
716 
717 	if (test_ok != 1) {
718 		fprintf(stdout, "FAILED: %d\n", test_ok);
719 		exit(1);
720 	}
721 
722 	fprintf(stdout, "OK\n");
723 }
724 
725 void
726 http_post_cb(struct evhttp_request *req, void *arg)
727 {
728 	struct evbuffer *evb;
729 	event_debug(("%s: called\n", __func__));
730 
731 	/* Yes, we are expecting a post request */
732 	if (req->type != EVHTTP_REQ_POST) {
733 		fprintf(stdout, "FAILED (post type)\n");
734 		exit(1);
735 	}
736 
737 	if (EVBUFFER_LENGTH(req->input_buffer) != strlen(POST_DATA)) {
738 		fprintf(stdout, "FAILED (length: %zu vs %zu)\n",
739 		    EVBUFFER_LENGTH(req->input_buffer), strlen(POST_DATA));
740 		exit(1);
741 	}
742 
743 	if (memcmp(EVBUFFER_DATA(req->input_buffer), POST_DATA,
744 		strlen(POST_DATA))) {
745 		fprintf(stdout, "FAILED (data)\n");
746 		fprintf(stdout, "Got :%s\n", EVBUFFER_DATA(req->input_buffer));
747 		fprintf(stdout, "Want:%s\n", POST_DATA);
748 		exit(1);
749 	}
750 
751 	evb = evbuffer_new();
752 	evbuffer_add_printf(evb, "This is funny");
753 
754 	evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
755 
756 	evbuffer_free(evb);
757 }
758 
759 void
760 http_postrequest_done(struct evhttp_request *req, void *arg)
761 {
762 	const char *what = "This is funny";
763 
764 	if (req == NULL) {
765 		fprintf(stderr, "FAILED (timeout)\n");
766 		exit(1);
767 	}
768 
769 	if (req->response_code != HTTP_OK) {
770 
771 		fprintf(stderr, "FAILED (response code)\n");
772 		exit(1);
773 	}
774 
775 	if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) {
776 		fprintf(stderr, "FAILED (content type)\n");
777 		exit(1);
778 	}
779 
780 	if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) {
781 		fprintf(stderr, "FAILED (length %zu vs %zu)\n",
782 		    EVBUFFER_LENGTH(req->input_buffer), strlen(what));
783 		exit(1);
784 	}
785 
786 	if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) {
787 		fprintf(stderr, "FAILED (data)\n");
788 		exit(1);
789 	}
790 
791 	test_ok = 1;
792 	event_loopexit(NULL);
793 }
794 
795 static void
796 http_failure_readcb(struct bufferevent *bev, void *arg)
797 {
798 	const char *what = "400 Bad Request";
799 	if (evbuffer_find(bev->input, (const unsigned char*) what, strlen(what)) != NULL) {
800 		test_ok = 2;
801 		bufferevent_disable(bev, EV_READ);
802 		event_loopexit(NULL);
803 	}
804 }
805 
806 /*
807  * Testing that the HTTP server can deal with a malformed request.
808  */
809 static void
810 http_failure_test(void)
811 {
812 	struct bufferevent *bev;
813 	int fd;
814 	const char *http_request;
815 	short port = -1;
816 
817 	test_ok = 0;
818 	fprintf(stdout, "Testing Bad HTTP Request: ");
819 
820 	http = http_setup(&port, NULL);
821 
822 	fd = http_connect("127.0.0.1", port);
823 
824 	/* Stupid thing to send a request */
825 	bev = bufferevent_new(fd, http_failure_readcb, http_writecb,
826 	    http_errorcb, NULL);
827 
828 	http_request = "illegal request\r\n";
829 
830 	bufferevent_write(bev, http_request, strlen(http_request));
831 
832 	event_dispatch();
833 
834 	bufferevent_free(bev);
835 	EVUTIL_CLOSESOCKET(fd);
836 
837 	evhttp_free(http);
838 
839 	if (test_ok != 2) {
840 		fprintf(stdout, "FAILED\n");
841 		exit(1);
842 	}
843 
844 	fprintf(stdout, "OK\n");
845 }
846 
847 static void
848 close_detect_done(struct evhttp_request *req, void *arg)
849 {
850 	struct timeval tv;
851 	if (req == NULL || req->response_code != HTTP_OK) {
852 
853 		fprintf(stderr, "FAILED\n");
854 		exit(1);
855 	}
856 
857 	test_ok = 1;
858 
859 	timerclear(&tv);
860 	tv.tv_sec = 5;   /* longer than the http time out */
861 
862 	event_loopexit(&tv);
863 }
864 
865 static void
866 close_detect_launch(int fd, short what, void *arg)
867 {
868 	struct evhttp_connection *evcon = arg;
869 	struct evhttp_request *req;
870 
871 	req = evhttp_request_new(close_detect_done, NULL);
872 
873 	/* Add the information that we care about */
874 	evhttp_add_header(req->output_headers, "Host", "somehost");
875 
876 	/* We give ownership of the request to the connection */
877 	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
878 		fprintf(stdout, "FAILED\n");
879 		exit(1);
880 	}
881 }
882 
883 static void
884 close_detect_cb(struct evhttp_request *req, void *arg)
885 {
886 	struct evhttp_connection *evcon = arg;
887 	struct timeval tv;
888 
889 	if (req != NULL && req->response_code != HTTP_OK) {
890 
891 		fprintf(stderr, "FAILED\n");
892 		exit(1);
893 	}
894 
895 	timerclear(&tv);
896 	tv.tv_sec = 5;   /* longer than the http time out */
897 
898 	/* launch a new request on the persistent connection in 6 seconds */
899 	event_once(-1, EV_TIMEOUT, close_detect_launch, evcon, &tv);
900 }
901 
902 
903 static void
904 http_close_detection(int with_delay)
905 {
906 	short port = -1;
907 	struct evhttp_connection *evcon = NULL;
908 	struct evhttp_request *req = NULL;
909 
910 	test_ok = 0;
911 	fprintf(stdout, "Testing Connection Close Detection%s: ",
912 		with_delay ? " (with delay)" : "");
913 
914 	http = http_setup(&port, NULL);
915 
916 	/* 2 second timeout */
917 	evhttp_set_timeout(http, 2);
918 
919 	evcon = evhttp_connection_new("127.0.0.1", port);
920 	if (evcon == NULL) {
921 		fprintf(stdout, "FAILED\n");
922 		exit(1);
923 	}
924 
925 	delayed_client = evcon;
926 
927 	/*
928 	 * At this point, we want to schedule a request to the HTTP
929 	 * server using our make request method.
930 	 */
931 
932 	req = evhttp_request_new(close_detect_cb, evcon);
933 
934 	/* Add the information that we care about */
935 	evhttp_add_header(req->output_headers, "Host", "somehost");
936 
937 	/* We give ownership of the request to the connection */
938 	if (evhttp_make_request(evcon,
939 	    req, EVHTTP_REQ_GET, with_delay ? "/largedelay" : "/test") == -1) {
940 		fprintf(stdout, "FAILED\n");
941 		exit(1);
942 	}
943 
944 	event_dispatch();
945 
946 	if (test_ok != 1) {
947 		fprintf(stdout, "FAILED\n");
948 		exit(1);
949 	}
950 
951 	/* at this point, the http server should have no connection */
952 	if (TAILQ_FIRST(&http->connections) != NULL) {
953 		fprintf(stdout, "FAILED (left connections)\n");
954 		exit(1);
955 	}
956 
957 	evhttp_connection_free(evcon);
958 	evhttp_free(http);
959 
960 	fprintf(stdout, "OK\n");
961 }
962 
963 static void
964 http_highport_test(void)
965 {
966 	int i = -1;
967 	struct evhttp *myhttp = NULL;
968 
969 	fprintf(stdout, "Testing HTTP Server with high port: ");
970 
971 	/* Try a few different ports */
972 	for (i = 0; i < 50; ++i) {
973 		myhttp = evhttp_start("127.0.0.1", 65535 - i);
974 		if (myhttp != NULL) {
975 			fprintf(stdout, "OK\n");
976 			evhttp_free(myhttp);
977 			return;
978 		}
979 	}
980 
981 	fprintf(stdout, "FAILED\n");
982 	exit(1);
983 }
984 
985 static void
986 http_bad_header_test(void)
987 {
988 	struct evkeyvalq headers;
989 
990 	fprintf(stdout, "Testing HTTP Header filtering: ");
991 
992 	TAILQ_INIT(&headers);
993 
994 	if (evhttp_add_header(&headers, "One", "Two") != 0)
995 		goto fail;
996 
997 	if (evhttp_add_header(&headers, "One\r", "Two") != -1)
998 		goto fail;
999 	if (evhttp_add_header(&headers, "One", "Two") != 0)
1000 		goto fail;
1001 	if (evhttp_add_header(&headers, "One", "Two\r\n Three") != 0)
1002 		goto fail;
1003 	if (evhttp_add_header(&headers, "One\r", "Two") != -1)
1004 		goto fail;
1005 	if (evhttp_add_header(&headers, "One\n", "Two") != -1)
1006 		goto fail;
1007 	if (evhttp_add_header(&headers, "One", "Two\r") != -1)
1008 		goto fail;
1009 	if (evhttp_add_header(&headers, "One", "Two\n") != -1)
1010 		goto fail;
1011 
1012 	evhttp_clear_headers(&headers);
1013 
1014 	fprintf(stdout, "OK\n");
1015 	return;
1016 fail:
1017 	fprintf(stdout, "FAILED\n");
1018 	exit(1);
1019 }
1020 
1021 static int validate_header(
1022 	const struct evkeyvalq* headers,
1023 	const char *key, const char *value)
1024 {
1025 	const char *real_val = evhttp_find_header(headers, key);
1026 	if (real_val == NULL)
1027 		return (-1);
1028 	if (strcmp(real_val, value) != 0)
1029 		return (-1);
1030 	return (0);
1031 }
1032 
1033 static void
1034 http_parse_query_test(void)
1035 {
1036 	struct evkeyvalq headers;
1037 
1038 	fprintf(stdout, "Testing HTTP query parsing: ");
1039 
1040 	TAILQ_INIT(&headers);
1041 
1042 	evhttp_parse_query("http://www.test.com/?q=test", &headers);
1043 	if (validate_header(&headers, "q", "test") != 0)
1044 		goto fail;
1045 	evhttp_clear_headers(&headers);
1046 
1047 	evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers);
1048 	if (validate_header(&headers, "q", "test") != 0)
1049 		goto fail;
1050 	if (validate_header(&headers, "foo", "bar") != 0)
1051 		goto fail;
1052 	evhttp_clear_headers(&headers);
1053 
1054 	evhttp_parse_query("http://www.test.com/?q=test+foo", &headers);
1055 	if (validate_header(&headers, "q", "test foo") != 0)
1056 		goto fail;
1057 	evhttp_clear_headers(&headers);
1058 
1059 	evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers);
1060 	if (validate_header(&headers, "q", "test\nfoo") != 0)
1061 		goto fail;
1062 	evhttp_clear_headers(&headers);
1063 
1064 	evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers);
1065 	if (validate_header(&headers, "q", "test\rfoo") != 0)
1066 		goto fail;
1067 	evhttp_clear_headers(&headers);
1068 
1069 	fprintf(stdout, "OK\n");
1070 	return;
1071 fail:
1072 	fprintf(stdout, "FAILED\n");
1073 	exit(1);
1074 }
1075 
1076 static void
1077 http_base_test(void)
1078 {
1079 	struct bufferevent *bev;
1080 	int fd;
1081 	const char *http_request;
1082 	short port = -1;
1083 
1084 	test_ok = 0;
1085 	fprintf(stdout, "Testing HTTP Server Event Base: ");
1086 
1087 	base = event_init();
1088 
1089 	/*
1090 	 * create another bogus base - which is being used by all subsequen
1091 	 * tests - yuck!
1092 	 */
1093 	event_init();
1094 
1095 	http = http_setup(&port, base);
1096 
1097 	fd = http_connect("127.0.0.1", port);
1098 
1099 	/* Stupid thing to send a request */
1100 	bev = bufferevent_new(fd, http_readcb, http_writecb,
1101 	    http_errorcb, NULL);
1102 	bufferevent_base_set(base, bev);
1103 
1104 	http_request =
1105 	    "GET /test HTTP/1.1\r\n"
1106 	    "Host: somehost\r\n"
1107 	    "Connection: close\r\n"
1108 	    "\r\n";
1109 
1110 	bufferevent_write(bev, http_request, strlen(http_request));
1111 
1112 	event_base_dispatch(base);
1113 
1114 	bufferevent_free(bev);
1115 	EVUTIL_CLOSESOCKET(fd);
1116 
1117 	evhttp_free(http);
1118 
1119 	event_base_free(base);
1120 	base = NULL;
1121 
1122 	if (test_ok != 2) {
1123 		fprintf(stdout, "FAILED\n");
1124 		exit(1);
1125 	}
1126 
1127 	fprintf(stdout, "OK\n");
1128 }
1129 
1130 /*
1131  * the server is going to reply with chunked data.
1132  */
1133 
1134 static void
1135 http_chunked_readcb(struct bufferevent *bev, void *arg)
1136 {
1137 	/* nothing here */
1138 }
1139 
1140 static void
1141 http_chunked_errorcb(struct bufferevent *bev, short what, void *arg)
1142 {
1143 	if (!test_ok)
1144 		goto out;
1145 
1146 	test_ok = -1;
1147 
1148 	if ((what & EVBUFFER_EOF) != 0) {
1149 		struct evhttp_request *req = evhttp_request_new(NULL, NULL);
1150 		const char *header;
1151 		enum message_read_status done;
1152 
1153 		req->kind = EVHTTP_RESPONSE;
1154 		done = evhttp_parse_firstline(req, EVBUFFER_INPUT(bev));
1155 		if (done != ALL_DATA_READ)
1156 			goto out;
1157 
1158 		done = evhttp_parse_headers(req, EVBUFFER_INPUT(bev));
1159 		if (done != ALL_DATA_READ)
1160 			goto out;
1161 
1162 		header = evhttp_find_header(req->input_headers, "Transfer-Encoding");
1163 		if (header == NULL || strcmp(header, "chunked"))
1164 			goto out;
1165 
1166 		header = evhttp_find_header(req->input_headers, "Connection");
1167 		if (header == NULL || strcmp(header, "close"))
1168 			goto out;
1169 
1170 		header = evbuffer_readline(EVBUFFER_INPUT(bev));
1171 		if (header == NULL)
1172 			goto out;
1173 		/* 13 chars */
1174 		if (strcmp(header, "d"))
1175 			goto out;
1176 		free((char*)header);
1177 
1178 		if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)),
1179 			"This is funny", 13))
1180 			goto out;
1181 
1182 		evbuffer_drain(EVBUFFER_INPUT(bev), 13 + 2);
1183 
1184 		header = evbuffer_readline(EVBUFFER_INPUT(bev));
1185 		if (header == NULL)
1186 			goto out;
1187 		/* 18 chars */
1188 		if (strcmp(header, "12"))
1189 			goto out;
1190 		free((char *)header);
1191 
1192 		if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)),
1193 			"but not hilarious.", 18))
1194 			goto out;
1195 
1196 		evbuffer_drain(EVBUFFER_INPUT(bev), 18 + 2);
1197 
1198 		header = evbuffer_readline(EVBUFFER_INPUT(bev));
1199 		if (header == NULL)
1200 			goto out;
1201 		/* 8 chars */
1202 		if (strcmp(header, "8"))
1203 			goto out;
1204 		free((char *)header);
1205 
1206 		if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)),
1207 			"bwv 1052.", 8))
1208 			goto out;
1209 
1210 		evbuffer_drain(EVBUFFER_INPUT(bev), 8 + 2);
1211 
1212 		header = evbuffer_readline(EVBUFFER_INPUT(bev));
1213 		if (header == NULL)
1214 			goto out;
1215 		/* 0 chars */
1216 		if (strcmp(header, "0"))
1217 			goto out;
1218 		free((char *)header);
1219 
1220 		test_ok = 2;
1221 	}
1222 
1223 out:
1224 	event_loopexit(NULL);
1225 }
1226 
1227 static void
1228 http_chunked_writecb(struct bufferevent *bev, void *arg)
1229 {
1230 	if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(bev)) == 0) {
1231 		/* enable reading of the reply */
1232 		bufferevent_enable(bev, EV_READ);
1233 		test_ok++;
1234 	}
1235 }
1236 
1237 static void
1238 http_chunked_request_done(struct evhttp_request *req, void *arg)
1239 {
1240 	if (req->response_code != HTTP_OK) {
1241 		fprintf(stderr, "FAILED\n");
1242 		exit(1);
1243 	}
1244 
1245 	if (evhttp_find_header(req->input_headers,
1246 		"Transfer-Encoding") == NULL) {
1247 		fprintf(stderr, "FAILED\n");
1248 		exit(1);
1249 	}
1250 
1251 	if (EVBUFFER_LENGTH(req->input_buffer) != 13 + 18 + 8) {
1252 		fprintf(stderr, "FAILED\n");
1253 		exit(1);
1254 	}
1255 
1256 	if (strncmp((char *)EVBUFFER_DATA(req->input_buffer),
1257 		"This is funnybut not hilarious.bwv 1052",
1258 		13 + 18 + 8)) {
1259 		fprintf(stderr, "FAILED\n");
1260 		exit(1);
1261 	}
1262 
1263 	test_ok = 1;
1264 	event_loopexit(NULL);
1265 }
1266 
1267 static void
1268 http_chunked_test(void)
1269 {
1270 	struct bufferevent *bev;
1271 	int fd;
1272 	const char *http_request;
1273 	short port = -1;
1274 	struct timeval tv_start, tv_end;
1275 	struct evhttp_connection *evcon = NULL;
1276 	struct evhttp_request *req = NULL;
1277 	int i;
1278 
1279 	test_ok = 0;
1280 	fprintf(stdout, "Testing Chunked HTTP Reply: ");
1281 
1282 	http = http_setup(&port, NULL);
1283 
1284 	fd = http_connect("127.0.0.1", port);
1285 
1286 	/* Stupid thing to send a request */
1287 	bev = bufferevent_new(fd,
1288 	    http_chunked_readcb, http_chunked_writecb,
1289 	    http_chunked_errorcb, NULL);
1290 
1291 	http_request =
1292 	    "GET /chunked HTTP/1.1\r\n"
1293 	    "Host: somehost\r\n"
1294 	    "Connection: close\r\n"
1295 	    "\r\n";
1296 
1297 	bufferevent_write(bev, http_request, strlen(http_request));
1298 
1299 	evutil_gettimeofday(&tv_start, NULL);
1300 
1301 	event_dispatch();
1302 
1303 	evutil_gettimeofday(&tv_end, NULL);
1304 	evutil_timersub(&tv_end, &tv_start, &tv_end);
1305 
1306 	if (tv_end.tv_sec >= 1) {
1307 		fprintf(stdout, "FAILED (time)\n");
1308 		exit (1);
1309 	}
1310 
1311 
1312 	if (test_ok != 2) {
1313 		fprintf(stdout, "FAILED\n");
1314 		exit(1);
1315 	}
1316 
1317 	/* now try again with the regular connection object */
1318 	evcon = evhttp_connection_new("127.0.0.1", port);
1319 	if (evcon == NULL) {
1320 		fprintf(stdout, "FAILED\n");
1321 		exit(1);
1322 	}
1323 
1324 	/* make two requests to check the keepalive behavior */
1325 	for (i = 0; i < 2; i++) {
1326 		test_ok = 0;
1327 		req = evhttp_request_new(http_chunked_request_done, NULL);
1328 
1329 		/* Add the information that we care about */
1330 		evhttp_add_header(req->output_headers, "Host", "somehost");
1331 
1332 		/* We give ownership of the request to the connection */
1333 		if (evhttp_make_request(evcon, req,
1334 			EVHTTP_REQ_GET, "/chunked") == -1) {
1335 			fprintf(stdout, "FAILED\n");
1336 			exit(1);
1337 		}
1338 
1339 		event_dispatch();
1340 
1341 		if (test_ok != 1) {
1342 			fprintf(stdout, "FAILED\n");
1343 			exit(1);
1344 		}
1345 	}
1346 
1347 	evhttp_connection_free(evcon);
1348 	evhttp_free(http);
1349 
1350 	fprintf(stdout, "OK\n");
1351 }
1352 
1353 static void
1354 http_multi_line_header_test(void)
1355 {
1356 	struct bufferevent *bev;
1357 	int fd;
1358 	const char *http_start_request;
1359 	short port = -1;
1360 
1361 	test_ok = 0;
1362 	fprintf(stdout, "Testing HTTP Server with multi line: ");
1363 
1364 	http = http_setup(&port, NULL);
1365 
1366 	fd = http_connect("127.0.0.1", port);
1367 
1368 	/* Stupid thing to send a request */
1369 	bev = bufferevent_new(fd, http_readcb, http_writecb,
1370 	    http_errorcb, NULL);
1371 
1372 	http_start_request =
1373 	    "GET /test HTTP/1.1\r\n"
1374 	    "Host: somehost\r\n"
1375 	    "Connection: close\r\n"
1376 	    "X-Multi:  aaaaaaaa\r\n"
1377 	    " a\r\n"
1378 	    "\tEND\r\n"
1379 	    "X-Last: last\r\n"
1380 	    "\r\n";
1381 
1382 	bufferevent_write(bev, http_start_request, strlen(http_start_request));
1383 
1384 	event_dispatch();
1385 
1386 	bufferevent_free(bev);
1387 	EVUTIL_CLOSESOCKET(fd);
1388 
1389 	evhttp_free(http);
1390 
1391 	if (test_ok != 4) {
1392 		fprintf(stdout, "FAILED\n");
1393 		exit(1);
1394 	}
1395 
1396 	fprintf(stdout, "OK\n");
1397 }
1398 
1399 static void
1400 http_request_bad(struct evhttp_request *req, void *arg)
1401 {
1402 	if (req != NULL) {
1403 		fprintf(stderr, "FAILED\n");
1404 		exit(1);
1405 	}
1406 
1407 	test_ok = 1;
1408 	event_loopexit(NULL);
1409 }
1410 
1411 static void
1412 http_negative_content_length_test(void)
1413 {
1414 	short port = -1;
1415 	struct evhttp_connection *evcon = NULL;
1416 	struct evhttp_request *req = NULL;
1417 
1418 	test_ok = 0;
1419 	fprintf(stdout, "Testing HTTP Negative Content Length: ");
1420 
1421 	http = http_setup(&port, NULL);
1422 
1423 	evcon = evhttp_connection_new("127.0.0.1", port);
1424 	if (evcon == NULL) {
1425 		fprintf(stdout, "FAILED\n");
1426 		exit(1);
1427 	}
1428 
1429 	/*
1430 	 * At this point, we want to schedule a request to the HTTP
1431 	 * server using our make request method.
1432 	 */
1433 
1434 	req = evhttp_request_new(http_request_bad, NULL);
1435 
1436 	/* Cause the response to have a negative content-length */
1437 	evhttp_add_header(req->output_headers, "X-Negative", "makeitso");
1438 
1439 	/* We give ownership of the request to the connection */
1440 	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
1441 		fprintf(stdout, "FAILED\n");
1442 		exit(1);
1443 	}
1444 
1445 	event_dispatch();
1446 
1447 	evhttp_free(http);
1448 
1449 	if (test_ok != 1) {
1450 		fprintf(stdout, "FAILED\n");
1451 		exit(1);
1452 	}
1453 
1454 	fprintf(stdout, "OK\n");
1455 }
1456 
1457 void
1458 http_suite(void)
1459 {
1460 	http_base_test();
1461 	http_bad_header_test();
1462 	http_parse_query_test();
1463 	http_basic_test();
1464 	http_connection_test(0 /* not-persistent */);
1465 	http_connection_test(1 /* persistent */);
1466 	http_close_detection(0 /* with delay */);
1467 	http_close_detection(1 /* with delay */);
1468 	http_post_test();
1469 	http_failure_test();
1470 	http_highport_test();
1471 	http_dispatcher_test();
1472 
1473 	http_multi_line_header_test();
1474 	http_negative_content_length_test();
1475 
1476 	http_chunked_test();
1477 }
1478