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