1275970Scy/*
2275970Scy * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu>
3275970Scy * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
4275970Scy *
5275970Scy * Redistribution and use in source and binary forms, with or without
6275970Scy * modification, are permitted provided that the following conditions
7275970Scy * are met:
8275970Scy * 1. Redistributions of source code must retain the above copyright
9275970Scy *    notice, this list of conditions and the following disclaimer.
10275970Scy * 2. Redistributions in binary form must reproduce the above copyright
11275970Scy *    notice, this list of conditions and the following disclaimer in the
12275970Scy *    documentation and/or other materials provided with the distribution.
13275970Scy * 3. The name of the author may not be used to endorse or promote products
14275970Scy *    derived from this software without specific prior written permission.
15275970Scy *
16275970Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17275970Scy * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18275970Scy * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19275970Scy * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20275970Scy * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21275970Scy * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22275970Scy * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23275970Scy * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24275970Scy * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25275970Scy * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26275970Scy */
27275970Scy#include "util-internal.h"
28275970Scy
29275970Scy#ifdef _WIN32
30275970Scy#include <winsock2.h>
31275970Scy#include <ws2tcpip.h>
32275970Scy#include <windows.h>
33275970Scy#endif
34275970Scy
35275970Scy#include "event2/event-config.h"
36275970Scy
37275970Scy#include <sys/types.h>
38275970Scy#include <sys/stat.h>
39275970Scy#ifdef EVENT__HAVE_SYS_TIME_H
40275970Scy#include <sys/time.h>
41275970Scy#endif
42275970Scy#include <sys/queue.h>
43275970Scy#ifndef _WIN32
44275970Scy#include <sys/socket.h>
45275970Scy#include <signal.h>
46275970Scy#include <unistd.h>
47275970Scy#include <netdb.h>
48275970Scy#endif
49275970Scy#include <fcntl.h>
50275970Scy#include <stdlib.h>
51275970Scy#include <stdio.h>
52275970Scy#include <string.h>
53275970Scy#include <errno.h>
54275970Scy
55275970Scy#include "event2/dns.h"
56275970Scy
57275970Scy#include "event2/event.h"
58275970Scy#include "event2/http.h"
59275970Scy#include "event2/buffer.h"
60275970Scy#include "event2/bufferevent.h"
61275970Scy#include "event2/util.h"
62275970Scy#include "log-internal.h"
63275970Scy#include "http-internal.h"
64275970Scy#include "regress.h"
65275970Scy#include "regress_testutils.h"
66275970Scy
67275970Scystatic struct evhttp *http;
68275970Scy/* set if a test needs to call loopexit on a base */
69275970Scystatic struct event_base *exit_base;
70275970Scy
71275970Scystatic char const BASIC_REQUEST_BODY[] = "This is funny";
72275970Scy
73275970Scy#define IMPL_HTTP_REQUEST_ERROR_CB(name, expecting_error)                    \
74275970Scy	static void                                                              \
75275970Scy	http_request_error_cb_with_##name##_(enum evhttp_request_error error,    \
76275970Scy	                                     void *arg)                          \
77275970Scy	{                                                                        \
78275970Scy		if (error != expecting_error) { 									 \
79275970Scy			fprintf(stderr, "FAILED\n"); 									 \
80275970Scy			exit(1); 														 \
81275970Scy		} 																	 \
82275970Scy		test_ok = 1; 														 \
83275970Scy	}
84275970ScyIMPL_HTTP_REQUEST_ERROR_CB(cancel, EVREQ_HTTP_REQUEST_CANCEL)
85275970Scy
86275970Scystatic void http_basic_cb(struct evhttp_request *req, void *arg);
87275970Scystatic void http_chunked_cb(struct evhttp_request *req, void *arg);
88275970Scystatic void http_post_cb(struct evhttp_request *req, void *arg);
89275970Scystatic void http_put_cb(struct evhttp_request *req, void *arg);
90275970Scystatic void http_delete_cb(struct evhttp_request *req, void *arg);
91275970Scystatic void http_delay_cb(struct evhttp_request *req, void *arg);
92275970Scystatic void http_large_delay_cb(struct evhttp_request *req, void *arg);
93275970Scystatic void http_badreq_cb(struct evhttp_request *req, void *arg);
94275970Scystatic void http_dispatcher_cb(struct evhttp_request *req, void *arg);
95275970Scystatic void http_on_complete_cb(struct evhttp_request *req, void *arg);
96275970Scy
97275970Scystatic int
98275970Scyhttp_bind(struct evhttp *myhttp, ev_uint16_t *pport, int ipv6)
99275970Scy{
100275970Scy	int port;
101275970Scy	struct evhttp_bound_socket *sock;
102275970Scy
103275970Scy	if (ipv6)
104275970Scy		sock = evhttp_bind_socket_with_handle(myhttp, "::1", *pport);
105275970Scy	else
106275970Scy		sock = evhttp_bind_socket_with_handle(myhttp, "127.0.0.1", *pport);
107275970Scy
108282408Scy	if (sock == NULL) {
109282408Scy		if (ipv6)
110282408Scy			return -1;
111282408Scy		else
112282408Scy			event_errx(1, "Could not start web server");
113282408Scy	}
114275970Scy
115275970Scy	port = regress_get_socket_port(evhttp_bound_socket_get_fd(sock));
116275970Scy	if (port < 0)
117275970Scy		return -1;
118275970Scy	*pport = (ev_uint16_t) port;
119275970Scy
120275970Scy	return 0;
121275970Scy}
122275970Scy
123275970Scystatic struct evhttp *
124275970Scyhttp_setup(ev_uint16_t *pport, struct event_base *base, int ipv6)
125275970Scy{
126275970Scy	struct evhttp *myhttp;
127275970Scy
128275970Scy	/* Try a few different ports */
129275970Scy	myhttp = evhttp_new(base);
130275970Scy
131275970Scy	if (http_bind(myhttp, pport, ipv6) < 0)
132275970Scy		return NULL;
133275970Scy
134275970Scy	/* Register a callback for certain types of requests */
135275970Scy	evhttp_set_cb(myhttp, "/test", http_basic_cb, base);
136275970Scy	evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, base);
137275970Scy	evhttp_set_cb(myhttp, "/streamed", http_chunked_cb, base);
138275970Scy	evhttp_set_cb(myhttp, "/postit", http_post_cb, base);
139275970Scy	evhttp_set_cb(myhttp, "/putit", http_put_cb, base);
140275970Scy	evhttp_set_cb(myhttp, "/deleteit", http_delete_cb, base);
141275970Scy	evhttp_set_cb(myhttp, "/delay", http_delay_cb, base);
142275970Scy	evhttp_set_cb(myhttp, "/largedelay", http_large_delay_cb, base);
143275970Scy	evhttp_set_cb(myhttp, "/badrequest", http_badreq_cb, base);
144275970Scy	evhttp_set_cb(myhttp, "/oncomplete", http_on_complete_cb, base);
145275970Scy	evhttp_set_cb(myhttp, "/", http_dispatcher_cb, base);
146275970Scy	return (myhttp);
147275970Scy}
148275970Scy
149275970Scy#ifndef NI_MAXSERV
150275970Scy#define NI_MAXSERV 1024
151275970Scy#endif
152275970Scy
153275970Scystatic evutil_socket_t
154275970Scyhttp_connect(const char *address, u_short port)
155275970Scy{
156275970Scy	/* Stupid code for connecting */
157275970Scy	struct evutil_addrinfo ai, *aitop;
158275970Scy	char strport[NI_MAXSERV];
159275970Scy
160275970Scy	struct sockaddr *sa;
161275970Scy	int slen;
162275970Scy	evutil_socket_t fd;
163275970Scy
164275970Scy	memset(&ai, 0, sizeof(ai));
165275970Scy	ai.ai_family = AF_INET;
166275970Scy	ai.ai_socktype = SOCK_STREAM;
167275970Scy	evutil_snprintf(strport, sizeof(strport), "%d", port);
168275970Scy	if (evutil_getaddrinfo(address, strport, &ai, &aitop) != 0) {
169275970Scy		event_warn("getaddrinfo");
170275970Scy		return (-1);
171275970Scy	}
172275970Scy	sa = aitop->ai_addr;
173275970Scy	slen = aitop->ai_addrlen;
174275970Scy
175275970Scy	fd = socket(AF_INET, SOCK_STREAM, 0);
176275970Scy	if (fd == -1)
177275970Scy		event_err(1, "socket failed");
178275970Scy
179275970Scy	evutil_make_socket_nonblocking(fd);
180275970Scy	if (connect(fd, sa, slen) == -1) {
181275970Scy#ifdef _WIN32
182275970Scy		int tmp_err = WSAGetLastError();
183275970Scy		if (tmp_err != WSAEINPROGRESS && tmp_err != WSAEINVAL &&
184275970Scy		    tmp_err != WSAEWOULDBLOCK)
185275970Scy			event_err(1, "connect failed");
186275970Scy#else
187275970Scy		if (errno != EINPROGRESS)
188275970Scy			event_err(1, "connect failed");
189275970Scy#endif
190275970Scy	}
191275970Scy
192275970Scy	evutil_freeaddrinfo(aitop);
193275970Scy
194275970Scy	return (fd);
195275970Scy}
196275970Scy
197275970Scy/* Helper: do a strcmp on the contents of buf and the string s. */
198275970Scystatic int
199275970Scyevbuffer_datacmp(struct evbuffer *buf, const char *s)
200275970Scy{
201275970Scy	size_t b_sz = evbuffer_get_length(buf);
202275970Scy	size_t s_sz = strlen(s);
203275970Scy	unsigned char *d;
204275970Scy	int r;
205275970Scy
206275970Scy	if (b_sz < s_sz)
207275970Scy		return -1;
208275970Scy
209275970Scy	d = evbuffer_pullup(buf, s_sz);
210275970Scy	if ((r = memcmp(d, s, s_sz)))
211275970Scy		return r;
212275970Scy
213275970Scy	if (b_sz > s_sz)
214275970Scy		return 1;
215275970Scy	else
216275970Scy		return 0;
217275970Scy}
218275970Scy
219275970Scy/* Helper: Return true iff buf contains s */
220275970Scystatic int
221275970Scyevbuffer_contains(struct evbuffer *buf, const char *s)
222275970Scy{
223275970Scy	struct evbuffer_ptr ptr;
224275970Scy	ptr = evbuffer_search(buf, s, strlen(s), NULL);
225275970Scy	return ptr.pos != -1;
226275970Scy}
227275970Scy
228275970Scystatic void
229275970Scyhttp_readcb(struct bufferevent *bev, void *arg)
230275970Scy{
231275970Scy	const char *what = BASIC_REQUEST_BODY;
232275970Scy	struct event_base *my_base = arg;
233275970Scy
234275970Scy	if (evbuffer_contains(bufferevent_get_input(bev), what)) {
235275970Scy		struct evhttp_request *req = evhttp_request_new(NULL, NULL);
236275970Scy		enum message_read_status done;
237275970Scy
238275970Scy		/* req->kind = EVHTTP_RESPONSE; */
239275970Scy		done = evhttp_parse_firstline_(req, bufferevent_get_input(bev));
240275970Scy		if (done != ALL_DATA_READ)
241275970Scy			goto out;
242275970Scy
243275970Scy		done = evhttp_parse_headers_(req, bufferevent_get_input(bev));
244275970Scy		if (done != ALL_DATA_READ)
245275970Scy			goto out;
246275970Scy
247275970Scy		if (done == 1 &&
248275970Scy		    evhttp_find_header(evhttp_request_get_input_headers(req),
249275970Scy			"Content-Type") != NULL)
250275970Scy			test_ok++;
251275970Scy
252275970Scy	 out:
253275970Scy		evhttp_request_free(req);
254275970Scy		bufferevent_disable(bev, EV_READ);
255275970Scy		if (exit_base)
256275970Scy			event_base_loopexit(exit_base, NULL);
257275970Scy		else if (my_base)
258275970Scy			event_base_loopexit(my_base, NULL);
259275970Scy		else {
260275970Scy			fprintf(stderr, "No way to exit loop!\n");
261275970Scy			exit(1);
262275970Scy		}
263275970Scy	}
264275970Scy}
265275970Scy
266275970Scystatic void
267275970Scyhttp_writecb(struct bufferevent *bev, void *arg)
268275970Scy{
269275970Scy	if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) {
270275970Scy		/* enable reading of the reply */
271275970Scy		bufferevent_enable(bev, EV_READ);
272275970Scy		test_ok++;
273275970Scy	}
274275970Scy}
275275970Scy
276275970Scystatic void
277275970Scyhttp_errorcb(struct bufferevent *bev, short what, void *arg)
278275970Scy{
279275970Scy	test_ok = -2;
280275970Scy	event_base_loopexit(arg, NULL);
281275970Scy}
282275970Scy
283275970Scystatic int found_multi = 0;
284275970Scystatic int found_multi2 = 0;
285275970Scy
286275970Scystatic void
287275970Scyhttp_basic_cb(struct evhttp_request *req, void *arg)
288275970Scy{
289275970Scy	struct evbuffer *evb = evbuffer_new();
290275970Scy	struct evhttp_connection *evcon;
291275970Scy	int empty = evhttp_find_header(evhttp_request_get_input_headers(req), "Empty") != NULL;
292275970Scy	event_debug(("%s: called\n", __func__));
293275970Scy	evbuffer_add_printf(evb, BASIC_REQUEST_BODY);
294275970Scy
295275970Scy	evcon = evhttp_request_get_connection(req);
296275970Scy	tt_assert(evhttp_connection_get_server(evcon) == http);
297275970Scy
298275970Scy	/* For multi-line headers test */
299275970Scy	{
300275970Scy		const char *multi =
301275970Scy		    evhttp_find_header(evhttp_request_get_input_headers(req),"X-Multi");
302275970Scy		if (multi) {
303275970Scy			found_multi = !strcmp(multi,"aaaaaaaa a END");
304275970Scy			if (strcmp("END", multi + strlen(multi) - 3) == 0)
305275970Scy				test_ok++;
306275970Scy			if (evhttp_find_header(evhttp_request_get_input_headers(req), "X-Last"))
307275970Scy				test_ok++;
308275970Scy		}
309275970Scy	}
310275970Scy	{
311275970Scy		const char *multi2 =
312275970Scy		    evhttp_find_header(evhttp_request_get_input_headers(req),"X-Multi-Extra-WS");
313275970Scy		if (multi2) {
314275970Scy			found_multi2 = !strcmp(multi2,"libevent 2.1");
315275970Scy		}
316275970Scy	}
317275970Scy
318275970Scy
319275970Scy	/* injecting a bad content-length */
320275970Scy	if (evhttp_find_header(evhttp_request_get_input_headers(req), "X-Negative"))
321275970Scy		evhttp_add_header(evhttp_request_get_output_headers(req),
322275970Scy		    "Content-Length", "-100");
323275970Scy
324275970Scy	/* allow sending of an empty reply */
325275970Scy	evhttp_send_reply(req, HTTP_OK, "Everything is fine",
326275970Scy	    !empty ? evb : NULL);
327275970Scy
328275970Scyend:
329275970Scy	evbuffer_free(evb);
330275970Scy}
331275970Scy
332275970Scystatic char const* const CHUNKS[] = {
333275970Scy	"This is funny",
334275970Scy	"but not hilarious.",
335275970Scy	"bwv 1052"
336275970Scy};
337275970Scy
338275970Scystruct chunk_req_state {
339275970Scy	struct event_base *base;
340275970Scy	struct evhttp_request *req;
341275970Scy	int i;
342275970Scy};
343275970Scy
344275970Scystatic void
345275970Scyhttp_chunked_trickle_cb(evutil_socket_t fd, short events, void *arg)
346275970Scy{
347275970Scy	struct evbuffer *evb = evbuffer_new();
348275970Scy	struct chunk_req_state *state = arg;
349275970Scy	struct timeval when = { 0, 0 };
350275970Scy
351275970Scy	evbuffer_add_printf(evb, "%s", CHUNKS[state->i]);
352275970Scy	evhttp_send_reply_chunk(state->req, evb);
353275970Scy	evbuffer_free(evb);
354275970Scy
355275970Scy	if (++state->i < (int) (sizeof(CHUNKS)/sizeof(CHUNKS[0]))) {
356275970Scy		event_base_once(state->base, -1, EV_TIMEOUT,
357275970Scy		    http_chunked_trickle_cb, state, &when);
358275970Scy	} else {
359275970Scy		evhttp_send_reply_end(state->req);
360275970Scy		free(state);
361275970Scy	}
362275970Scy}
363275970Scy
364275970Scystatic void
365275970Scyhttp_chunked_cb(struct evhttp_request *req, void *arg)
366275970Scy{
367275970Scy	struct timeval when = { 0, 0 };
368275970Scy	struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state));
369275970Scy	event_debug(("%s: called\n", __func__));
370289764Sglebius	if (state == NULL) {
371289764Sglebius		fprintf(stderr, "Unable to allocate memory in http_chunked_cb()\n");
372289764Sglebius		exit(1);
373289764Sglebius	}
374275970Scy
375275970Scy	memset(state, 0, sizeof(struct chunk_req_state));
376275970Scy	state->req = req;
377275970Scy	state->base = arg;
378275970Scy
379275970Scy	if (strcmp(evhttp_request_get_uri(req), "/streamed") == 0) {
380275970Scy		evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Length", "39");
381275970Scy	}
382275970Scy
383275970Scy	/* generate a chunked/streamed reply */
384275970Scy	evhttp_send_reply_start(req, HTTP_OK, "Everything is fine");
385275970Scy
386275970Scy	/* but trickle it across several iterations to ensure we're not
387275970Scy	 * assuming it comes all at once */
388275970Scy	event_base_once(arg, -1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when);
389275970Scy}
390275970Scy
391275970Scystatic void
392275970Scyhttp_complete_write(evutil_socket_t fd, short what, void *arg)
393275970Scy{
394275970Scy	struct bufferevent *bev = arg;
395275970Scy	const char *http_request = "host\r\n"
396275970Scy	    "Connection: close\r\n"
397275970Scy	    "\r\n";
398275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
399275970Scy}
400275970Scy
401275970Scystatic void
402275970Scyhttp_basic_test(void *arg)
403275970Scy{
404275970Scy	struct basic_test_data *data = arg;
405275970Scy	struct timeval tv;
406282408Scy	struct bufferevent *bev = NULL;
407275970Scy	evutil_socket_t fd;
408275970Scy	const char *http_request;
409275970Scy	ev_uint16_t port = 0, port2 = 0;
410275970Scy
411275970Scy	test_ok = 0;
412275970Scy
413275970Scy	http = http_setup(&port, data->base, 0);
414275970Scy
415275970Scy	/* bind to a second socket */
416275970Scy	if (http_bind(http, &port2, 0) == -1) {
417275970Scy		fprintf(stdout, "FAILED (bind)\n");
418275970Scy		exit(1);
419275970Scy	}
420275970Scy
421275970Scy	fd = http_connect("127.0.0.1", port);
422275970Scy
423275970Scy	/* Stupid thing to send a request */
424275970Scy	bev = bufferevent_socket_new(data->base, fd, 0);
425275970Scy	bufferevent_setcb(bev, http_readcb, http_writecb,
426275970Scy	    http_errorcb, data->base);
427275970Scy
428275970Scy	/* first half of the http request */
429275970Scy	http_request =
430275970Scy	    "GET /test HTTP/1.1\r\n"
431275970Scy	    "Host: some";
432275970Scy
433275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
434275970Scy	evutil_timerclear(&tv);
435275970Scy	tv.tv_usec = 10000;
436275970Scy	event_base_once(data->base,
437275970Scy	    -1, EV_TIMEOUT, http_complete_write, bev, &tv);
438275970Scy
439275970Scy	event_base_dispatch(data->base);
440275970Scy
441275970Scy	tt_assert(test_ok == 3);
442275970Scy
443275970Scy	/* connect to the second port */
444275970Scy	bufferevent_free(bev);
445275970Scy	evutil_closesocket(fd);
446275970Scy
447275970Scy	fd = http_connect("127.0.0.1", port2);
448275970Scy
449275970Scy	/* Stupid thing to send a request */
450275970Scy	bev = bufferevent_socket_new(data->base, fd, 0);
451275970Scy	bufferevent_setcb(bev, http_readcb, http_writecb,
452275970Scy	    http_errorcb, data->base);
453275970Scy
454275970Scy	http_request =
455275970Scy	    "GET /test HTTP/1.1\r\n"
456275970Scy	    "Host: somehost\r\n"
457275970Scy	    "Connection: close\r\n"
458275970Scy	    "\r\n";
459275970Scy
460275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
461275970Scy
462275970Scy	event_base_dispatch(data->base);
463275970Scy
464275970Scy	tt_assert(test_ok == 5);
465275970Scy
466275970Scy	/* Connect to the second port again. This time, send an absolute uri. */
467275970Scy	bufferevent_free(bev);
468275970Scy	evutil_closesocket(fd);
469275970Scy
470275970Scy	fd = http_connect("127.0.0.1", port2);
471275970Scy
472275970Scy	/* Stupid thing to send a request */
473275970Scy	bev = bufferevent_socket_new(data->base, fd, 0);
474275970Scy	bufferevent_setcb(bev, http_readcb, http_writecb,
475275970Scy	    http_errorcb, data->base);
476275970Scy
477275970Scy	http_request =
478275970Scy	    "GET http://somehost.net/test HTTP/1.1\r\n"
479275970Scy	    "Host: somehost\r\n"
480275970Scy	    "Connection: close\r\n"
481275970Scy	    "\r\n";
482275970Scy
483275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
484275970Scy
485275970Scy	event_base_dispatch(data->base);
486275970Scy
487275970Scy	tt_assert(test_ok == 7);
488275970Scy
489275970Scy	evhttp_free(http);
490275970Scy end:
491282408Scy	if (bev)
492282408Scy		bufferevent_free(bev);
493275970Scy}
494275970Scy
495275970Scy
496275970Scystatic void
497275970Scyhttp_delay_reply(evutil_socket_t fd, short what, void *arg)
498275970Scy{
499275970Scy	struct evhttp_request *req = arg;
500275970Scy
501275970Scy	evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL);
502275970Scy
503275970Scy	++test_ok;
504275970Scy}
505275970Scy
506275970Scystatic void
507275970Scyhttp_delay_cb(struct evhttp_request *req, void *arg)
508275970Scy{
509275970Scy	struct timeval tv;
510275970Scy	evutil_timerclear(&tv);
511275970Scy	tv.tv_sec = 0;
512275970Scy	tv.tv_usec = 200 * 1000;
513275970Scy
514275970Scy	event_base_once(arg, -1, EV_TIMEOUT, http_delay_reply, req, &tv);
515275970Scy}
516275970Scy
517275970Scystatic void
518275970Scyhttp_badreq_cb(struct evhttp_request *req, void *arg)
519275970Scy{
520275970Scy	struct evbuffer *buf = evbuffer_new();
521275970Scy
522275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/xml; charset=UTF-8");
523275970Scy	evbuffer_add_printf(buf, "Hello, %s!", "127.0.0.1");
524275970Scy
525275970Scy	evhttp_send_reply(req, HTTP_OK, "OK", buf);
526275970Scy	evbuffer_free(buf);
527275970Scy}
528275970Scy
529275970Scystatic void
530275970Scyhttp_badreq_errorcb(struct bufferevent *bev, short what, void *arg)
531275970Scy{
532275970Scy	event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg));
533275970Scy	/* ignore */
534275970Scy}
535275970Scy
536275970Scy#ifndef SHUT_WR
537275970Scy#ifdef _WIN32
538275970Scy#define SHUT_WR SD_SEND
539275970Scy#else
540275970Scy#define SHUT_WR 1
541275970Scy#endif
542275970Scy#endif
543275970Scy
544275970Scystatic void
545275970Scyhttp_badreq_readcb(struct bufferevent *bev, void *arg)
546275970Scy{
547275970Scy	const char *what = "Hello, 127.0.0.1";
548275970Scy	const char *bad_request = "400 Bad Request";
549275970Scy
550275970Scy	if (evbuffer_contains(bufferevent_get_input(bev), bad_request)) {
551275970Scy		TT_FAIL(("%s:bad request detected", __func__));
552275970Scy		bufferevent_disable(bev, EV_READ);
553275970Scy		event_base_loopexit(arg, NULL);
554275970Scy		return;
555275970Scy	}
556275970Scy
557275970Scy	if (evbuffer_contains(bufferevent_get_input(bev), what)) {
558275970Scy		struct evhttp_request *req = evhttp_request_new(NULL, NULL);
559275970Scy		enum message_read_status done;
560275970Scy
561275970Scy		/* req->kind = EVHTTP_RESPONSE; */
562275970Scy		done = evhttp_parse_firstline_(req, bufferevent_get_input(bev));
563275970Scy		if (done != ALL_DATA_READ)
564275970Scy			goto out;
565275970Scy
566275970Scy		done = evhttp_parse_headers_(req, bufferevent_get_input(bev));
567275970Scy		if (done != ALL_DATA_READ)
568275970Scy			goto out;
569275970Scy
570275970Scy		if (done == 1 &&
571275970Scy		    evhttp_find_header(evhttp_request_get_input_headers(req),
572275970Scy			"Content-Type") != NULL)
573275970Scy			test_ok++;
574275970Scy
575275970Scy	out:
576275970Scy		evhttp_request_free(req);
577275970Scy		evbuffer_drain(bufferevent_get_input(bev), evbuffer_get_length(bufferevent_get_input(bev)));
578275970Scy	}
579275970Scy
580275970Scy	shutdown(bufferevent_getfd(bev), SHUT_WR);
581275970Scy}
582275970Scy
583275970Scystatic void
584275970Scyhttp_badreq_successcb(evutil_socket_t fd, short what, void *arg)
585275970Scy{
586275970Scy	event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg));
587275970Scy	event_base_loopexit(exit_base, NULL);
588275970Scy}
589275970Scy
590275970Scystatic void
591275970Scyhttp_bad_request_test(void *arg)
592275970Scy{
593275970Scy	struct basic_test_data *data = arg;
594275970Scy	struct timeval tv;
595275970Scy	struct bufferevent *bev = NULL;
596275970Scy	evutil_socket_t fd = -1;
597275970Scy	const char *http_request;
598275970Scy	ev_uint16_t port=0, port2=0;
599275970Scy
600275970Scy	test_ok = 0;
601275970Scy	exit_base = data->base;
602275970Scy
603275970Scy	http = http_setup(&port, data->base, 0);
604275970Scy
605275970Scy	/* bind to a second socket */
606275970Scy	if (http_bind(http, &port2, 0) == -1)
607275970Scy		TT_DIE(("Bind socket failed"));
608275970Scy
609275970Scy	/* NULL request test */
610275970Scy	fd = http_connect("127.0.0.1", port);
611275970Scy	tt_int_op(fd, >=, 0);
612275970Scy
613275970Scy	/* Stupid thing to send a request */
614275970Scy	bev = bufferevent_socket_new(data->base, fd, 0);
615275970Scy	bufferevent_setcb(bev, http_badreq_readcb, http_writecb,
616275970Scy	    http_badreq_errorcb, data->base);
617275970Scy	bufferevent_enable(bev, EV_READ);
618275970Scy
619275970Scy	/* real NULL request */
620275970Scy	http_request = "";
621275970Scy
622275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
623275970Scy
624275970Scy	shutdown(fd, SHUT_WR);
625275970Scy	timerclear(&tv);
626275970Scy	tv.tv_usec = 10000;
627275970Scy	event_base_once(data->base, -1, EV_TIMEOUT, http_badreq_successcb, bev, &tv);
628275970Scy
629275970Scy	event_base_dispatch(data->base);
630275970Scy
631275970Scy	bufferevent_free(bev);
632275970Scy	evutil_closesocket(fd);
633275970Scy
634275970Scy	if (test_ok != 0) {
635275970Scy		fprintf(stdout, "FAILED\n");
636275970Scy		exit(1);
637275970Scy	}
638275970Scy
639275970Scy	/* Second answer (BAD REQUEST) on connection close */
640275970Scy
641275970Scy	/* connect to the second port */
642275970Scy	fd = http_connect("127.0.0.1", port2);
643275970Scy
644275970Scy	/* Stupid thing to send a request */
645275970Scy	bev = bufferevent_socket_new(data->base, fd, 0);
646275970Scy	bufferevent_setcb(bev, http_badreq_readcb, http_writecb,
647275970Scy	    http_badreq_errorcb, data->base);
648275970Scy	bufferevent_enable(bev, EV_READ);
649275970Scy
650275970Scy	/* first half of the http request */
651275970Scy	http_request =
652275970Scy		"GET /badrequest HTTP/1.0\r\n"	\
653275970Scy		"Connection: Keep-Alive\r\n"	\
654275970Scy		"\r\n";
655275970Scy
656275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
657275970Scy
658275970Scy	timerclear(&tv);
659275970Scy	tv.tv_usec = 10000;
660275970Scy	event_base_once(data->base, -1, EV_TIMEOUT, http_badreq_successcb, bev, &tv);
661275970Scy
662275970Scy	event_base_dispatch(data->base);
663275970Scy
664275970Scy	tt_int_op(test_ok, ==, 2);
665275970Scy
666275970Scyend:
667275970Scy	evhttp_free(http);
668275970Scy	if (bev)
669275970Scy		bufferevent_free(bev);
670275970Scy	if (fd >= 0)
671275970Scy		evutil_closesocket(fd);
672275970Scy}
673275970Scy
674275970Scystatic struct evhttp_connection *delayed_client;
675275970Scy
676275970Scystatic void
677275970Scyhttp_large_delay_cb(struct evhttp_request *req, void *arg)
678275970Scy{
679275970Scy	struct timeval tv;
680275970Scy	evutil_timerclear(&tv);
681275970Scy	tv.tv_usec = 500000;
682275970Scy
683275970Scy	event_base_once(arg, -1, EV_TIMEOUT, http_delay_reply, req, &tv);
684275970Scy	evhttp_connection_fail_(delayed_client, EVREQ_HTTP_EOF);
685275970Scy}
686275970Scy
687275970Scy/*
688275970Scy * HTTP DELETE test,  just piggyback on the basic test
689275970Scy */
690275970Scy
691275970Scystatic void
692275970Scyhttp_delete_cb(struct evhttp_request *req, void *arg)
693275970Scy{
694275970Scy	struct evbuffer *evb = evbuffer_new();
695275970Scy	int empty = evhttp_find_header(evhttp_request_get_input_headers(req), "Empty") != NULL;
696275970Scy
697275970Scy	/* Expecting a DELETE request */
698275970Scy	if (evhttp_request_get_command(req) != EVHTTP_REQ_DELETE) {
699275970Scy		fprintf(stdout, "FAILED (delete type)\n");
700275970Scy		exit(1);
701275970Scy	}
702275970Scy
703275970Scy	event_debug(("%s: called\n", __func__));
704275970Scy	evbuffer_add_printf(evb, BASIC_REQUEST_BODY);
705275970Scy
706275970Scy	/* allow sending of an empty reply */
707275970Scy	evhttp_send_reply(req, HTTP_OK, "Everything is fine",
708275970Scy	    !empty ? evb : NULL);
709275970Scy
710275970Scy	evbuffer_free(evb);
711275970Scy}
712275970Scy
713275970Scystatic void
714275970Scyhttp_delete_test(void *arg)
715275970Scy{
716275970Scy	struct basic_test_data *data = arg;
717275970Scy	struct bufferevent *bev;
718275970Scy	evutil_socket_t fd = -1;
719275970Scy	const char *http_request;
720275970Scy	ev_uint16_t port = 0;
721275970Scy
722275970Scy	test_ok = 0;
723275970Scy
724275970Scy	http = http_setup(&port, data->base, 0);
725275970Scy
726282408Scy	tt_assert(http);
727275970Scy	fd = http_connect("127.0.0.1", port);
728275970Scy	tt_int_op(fd, >=, 0);
729275970Scy
730275970Scy	/* Stupid thing to send a request */
731275970Scy	bev = bufferevent_socket_new(data->base, fd, 0);
732275970Scy	bufferevent_setcb(bev, http_readcb, http_writecb,
733275970Scy	    http_errorcb, data->base);
734275970Scy
735275970Scy	http_request =
736275970Scy	    "DELETE /deleteit HTTP/1.1\r\n"
737275970Scy	    "Host: somehost\r\n"
738275970Scy	    "Connection: close\r\n"
739275970Scy	    "\r\n";
740275970Scy
741275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
742275970Scy
743275970Scy	event_base_dispatch(data->base);
744275970Scy
745275970Scy	bufferevent_free(bev);
746275970Scy	evutil_closesocket(fd);
747282408Scy	fd = -1;
748275970Scy
749275970Scy	evhttp_free(http);
750275970Scy
751275970Scy	tt_int_op(test_ok, ==, 2);
752275970Scy end:
753275970Scy	if (fd >= 0)
754275970Scy		evutil_closesocket(fd);
755275970Scy}
756275970Scy
757275970Scystatic void
758275970Scyhttp_sent_cb(struct evhttp_request *req, void *arg)
759275970Scy{
760275970Scy	ev_uintptr_t val = (ev_uintptr_t)arg;
761275970Scy	struct evbuffer *b;
762275970Scy
763275970Scy	if (val != 0xDEADBEEF) {
764275970Scy		fprintf(stdout, "FAILED on_complete_cb argument\n");
765275970Scy		exit(1);
766275970Scy	}
767275970Scy
768275970Scy	b = evhttp_request_get_output_buffer(req);
769275970Scy	if (evbuffer_get_length(b) != 0) {
770275970Scy		fprintf(stdout, "FAILED on_complete_cb output buffer not written\n");
771275970Scy		exit(1);
772275970Scy	}
773275970Scy
774275970Scy	event_debug(("%s: called\n", __func__));
775275970Scy
776275970Scy	++test_ok;
777275970Scy}
778275970Scy
779275970Scystatic void
780275970Scyhttp_on_complete_cb(struct evhttp_request *req, void *arg)
781275970Scy{
782275970Scy	struct evbuffer *evb = evbuffer_new();
783275970Scy
784275970Scy	evhttp_request_set_on_complete_cb(req, http_sent_cb, (void *)0xDEADBEEF);
785275970Scy
786275970Scy	event_debug(("%s: called\n", __func__));
787275970Scy	evbuffer_add_printf(evb, BASIC_REQUEST_BODY);
788275970Scy
789275970Scy	/* allow sending of an empty reply */
790275970Scy	evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
791275970Scy
792275970Scy	evbuffer_free(evb);
793275970Scy
794275970Scy	++test_ok;
795275970Scy}
796275970Scy
797275970Scystatic void
798275970Scyhttp_on_complete_test(void *arg)
799275970Scy{
800275970Scy	struct basic_test_data *data = arg;
801275970Scy	struct bufferevent *bev;
802275970Scy	evutil_socket_t fd = -1;
803275970Scy	const char *http_request;
804275970Scy	ev_uint16_t port = 0;
805275970Scy
806275970Scy	test_ok = 0;
807275970Scy
808275970Scy	http = http_setup(&port, data->base, 0);
809275970Scy
810275970Scy	fd = http_connect("127.0.0.1", port);
811275970Scy	tt_int_op(fd, >=, 0);
812275970Scy
813275970Scy	/* Stupid thing to send a request */
814275970Scy	bev = bufferevent_socket_new(data->base, fd, 0);
815275970Scy	bufferevent_setcb(bev, http_readcb, http_writecb,
816275970Scy	    http_errorcb, data->base);
817275970Scy
818275970Scy	http_request =
819275970Scy	    "GET /oncomplete HTTP/1.1\r\n"
820275970Scy	    "Host: somehost\r\n"
821275970Scy	    "Connection: close\r\n"
822275970Scy	    "\r\n";
823275970Scy
824275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
825275970Scy
826275970Scy	event_base_dispatch(data->base);
827275970Scy
828275970Scy	bufferevent_free(bev);
829275970Scy
830275970Scy	evhttp_free(http);
831275970Scy
832275970Scy	tt_int_op(test_ok, ==, 4);
833275970Scy end:
834275970Scy	if (fd >= 0)
835275970Scy		evutil_closesocket(fd);
836275970Scy}
837275970Scy
838275970Scystatic void
839275970Scyhttp_allowed_methods_eventcb(struct bufferevent *bev, short what, void *arg)
840275970Scy{
841275970Scy	char **output = arg;
842275970Scy	if ((what & (BEV_EVENT_ERROR|BEV_EVENT_EOF))) {
843275970Scy		char buf[4096];
844275970Scy		int n;
845275970Scy		n = evbuffer_remove(bufferevent_get_input(bev), buf,
846275970Scy		    sizeof(buf)-1);
847275970Scy		if (n >= 0) {
848275970Scy			buf[n]='\0';
849275970Scy			if (*output)
850275970Scy				free(*output);
851275970Scy			*output = strdup(buf);
852275970Scy		}
853275970Scy		event_base_loopexit(exit_base, NULL);
854275970Scy	}
855275970Scy}
856275970Scy
857275970Scystatic void
858275970Scyhttp_allowed_methods_test(void *arg)
859275970Scy{
860275970Scy	struct basic_test_data *data = arg;
861275970Scy	struct bufferevent *bev1, *bev2, *bev3;
862275970Scy	evutil_socket_t fd1=-1, fd2=-1, fd3=-1;
863275970Scy	const char *http_request;
864275970Scy	char *result1=NULL, *result2=NULL, *result3=NULL;
865275970Scy	ev_uint16_t port = 0;
866275970Scy
867275970Scy	exit_base = data->base;
868275970Scy	test_ok = 0;
869275970Scy
870275970Scy	http = http_setup(&port, data->base, 0);
871275970Scy
872275970Scy	fd1 = http_connect("127.0.0.1", port);
873275970Scy	tt_int_op(fd1, >=, 0);
874275970Scy
875275970Scy	/* GET is out; PATCH is in. */
876275970Scy	evhttp_set_allowed_methods(http, EVHTTP_REQ_PATCH);
877275970Scy
878275970Scy	/* Stupid thing to send a request */
879275970Scy	bev1 = bufferevent_socket_new(data->base, fd1, 0);
880275970Scy	bufferevent_enable(bev1, EV_READ|EV_WRITE);
881275970Scy	bufferevent_setcb(bev1, NULL, NULL,
882275970Scy	    http_allowed_methods_eventcb, &result1);
883275970Scy
884275970Scy	http_request =
885275970Scy	    "GET /index.html HTTP/1.1\r\n"
886275970Scy	    "Host: somehost\r\n"
887275970Scy	    "Connection: close\r\n"
888275970Scy	    "\r\n";
889275970Scy
890275970Scy	bufferevent_write(bev1, http_request, strlen(http_request));
891275970Scy
892275970Scy	event_base_dispatch(data->base);
893275970Scy
894275970Scy	fd2 = http_connect("127.0.0.1", port);
895275970Scy	tt_int_op(fd2, >=, 0);
896275970Scy
897275970Scy	bev2 = bufferevent_socket_new(data->base, fd2, 0);
898275970Scy	bufferevent_enable(bev2, EV_READ|EV_WRITE);
899275970Scy	bufferevent_setcb(bev2, NULL, NULL,
900275970Scy	    http_allowed_methods_eventcb, &result2);
901275970Scy
902275970Scy	http_request =
903275970Scy	    "PATCH /test HTTP/1.1\r\n"
904275970Scy	    "Host: somehost\r\n"
905275970Scy	    "Connection: close\r\n"
906275970Scy	    "\r\n";
907275970Scy
908275970Scy	bufferevent_write(bev2, http_request, strlen(http_request));
909275970Scy
910275970Scy	event_base_dispatch(data->base);
911275970Scy
912275970Scy	fd3 = http_connect("127.0.0.1", port);
913275970Scy	tt_int_op(fd3, >=, 0);
914275970Scy
915275970Scy	bev3 = bufferevent_socket_new(data->base, fd3, 0);
916275970Scy	bufferevent_enable(bev3, EV_READ|EV_WRITE);
917275970Scy	bufferevent_setcb(bev3, NULL, NULL,
918275970Scy	    http_allowed_methods_eventcb, &result3);
919275970Scy
920275970Scy	http_request =
921275970Scy	    "FLOOP /test HTTP/1.1\r\n"
922275970Scy	    "Host: somehost\r\n"
923275970Scy	    "Connection: close\r\n"
924275970Scy	    "\r\n";
925275970Scy
926275970Scy	bufferevent_write(bev3, http_request, strlen(http_request));
927275970Scy
928275970Scy	event_base_dispatch(data->base);
929275970Scy
930275970Scy	bufferevent_free(bev1);
931275970Scy	bufferevent_free(bev2);
932275970Scy	bufferevent_free(bev3);
933275970Scy
934275970Scy	evhttp_free(http);
935275970Scy
936275970Scy	/* Method known but disallowed */
937275970Scy	tt_assert(result1);
938275970Scy	tt_assert(!strncmp(result1, "HTTP/1.1 501 ", strlen("HTTP/1.1 501 ")));
939275970Scy
940275970Scy	/* Method known and allowed */
941275970Scy	tt_assert(result2);
942275970Scy	tt_assert(!strncmp(result2, "HTTP/1.1 200 ", strlen("HTTP/1.1 200 ")));
943275970Scy
944275970Scy	/* Method unknown */
945275970Scy	tt_assert(result3);
946275970Scy	tt_assert(!strncmp(result3, "HTTP/1.1 501 ", strlen("HTTP/1.1 501 ")));
947275970Scy
948275970Scy end:
949275970Scy	if (result1)
950275970Scy		free(result1);
951275970Scy	if (result2)
952275970Scy		free(result2);
953275970Scy	if (result3)
954275970Scy		free(result3);
955275970Scy	if (fd1 >= 0)
956275970Scy		evutil_closesocket(fd1);
957275970Scy	if (fd2 >= 0)
958275970Scy		evutil_closesocket(fd2);
959275970Scy	if (fd3 >= 0)
960275970Scy		evutil_closesocket(fd3);
961275970Scy}
962275970Scy
963275970Scystatic void http_request_done(struct evhttp_request *, void *);
964275970Scystatic void http_request_empty_done(struct evhttp_request *, void *);
965275970Scy
966275970Scystatic void
967282408Scyhttp_connection_test_(struct basic_test_data *data, int persistent,
968282408Scy	const char *address, struct evdns_base *dnsbase, int ipv6, int family)
969275970Scy{
970275970Scy	ev_uint16_t port = 0;
971275970Scy	struct evhttp_connection *evcon = NULL;
972275970Scy	struct evhttp_request *req = NULL;
973275970Scy
974275970Scy	test_ok = 0;
975275970Scy
976275970Scy	http = http_setup(&port, data->base, ipv6);
977282408Scy	if (!http && ipv6) {
978282408Scy		tt_skip();
979282408Scy	}
980282408Scy	tt_assert(http);
981275970Scy
982275970Scy	evcon = evhttp_connection_base_new(data->base, dnsbase, address, port);
983275970Scy	tt_assert(evcon);
984282408Scy	evhttp_connection_set_family(evcon, family);
985275970Scy
986275970Scy	tt_assert(evhttp_connection_get_base(evcon) == data->base);
987275970Scy
988275970Scy	exit_base = data->base;
989275970Scy
990275970Scy	tt_assert(evhttp_connection_get_server(evcon) == NULL);
991275970Scy
992275970Scy	/*
993275970Scy	 * At this point, we want to schedule a request to the HTTP
994275970Scy	 * server using our make request method.
995275970Scy	 */
996275970Scy	req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
997275970Scy
998275970Scy	/* Add the information that we care about */
999275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
1000275970Scy
1001275970Scy	/* We give ownership of the request to the connection */
1002275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
1003275970Scy		fprintf(stdout, "FAILED\n");
1004275970Scy		exit(1);
1005275970Scy	}
1006275970Scy
1007275970Scy	event_base_dispatch(data->base);
1008275970Scy
1009275970Scy	tt_assert(test_ok);
1010275970Scy
1011275970Scy	/* try to make another request over the same connection */
1012275970Scy	test_ok = 0;
1013275970Scy
1014275970Scy	req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
1015275970Scy
1016275970Scy	/* Add the information that we care about */
1017275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
1018275970Scy
1019275970Scy	/*
1020275970Scy	 * if our connections are not supposed to be persistent; request
1021275970Scy	 * a close from the server.
1022275970Scy	 */
1023275970Scy	if (!persistent)
1024275970Scy		evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close");
1025275970Scy
1026275970Scy	/* We give ownership of the request to the connection */
1027275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
1028275970Scy		tt_abort_msg("couldn't make request");
1029275970Scy	}
1030275970Scy
1031275970Scy	event_base_dispatch(data->base);
1032275970Scy
1033275970Scy	/* make another request: request empty reply */
1034275970Scy	test_ok = 0;
1035275970Scy
1036275970Scy	req = evhttp_request_new(http_request_empty_done, data->base);
1037275970Scy
1038275970Scy	/* Add the information that we care about */
1039275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis");
1040275970Scy
1041275970Scy	/* We give ownership of the request to the connection */
1042275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
1043275970Scy		tt_abort_msg("Couldn't make request");
1044275970Scy	}
1045275970Scy
1046275970Scy	event_base_dispatch(data->base);
1047275970Scy
1048275970Scy end:
1049275970Scy	if (evcon)
1050275970Scy		evhttp_connection_free(evcon);
1051275970Scy	if (http)
1052275970Scy		evhttp_free(http);
1053275970Scy}
1054275970Scy
1055275970Scystatic void
1056275970Scyhttp_connection_test(void *arg)
1057275970Scy{
1058282408Scy	http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_UNSPEC);
1059275970Scy}
1060275970Scystatic void
1061275970Scyhttp_persist_connection_test(void *arg)
1062275970Scy{
1063282408Scy	http_connection_test_(arg, 1, "127.0.0.1", NULL, 0, AF_UNSPEC);
1064275970Scy}
1065275970Scy
1066275970Scystatic struct regress_dns_server_table search_table[] = {
1067275970Scy	{ "localhost", "A", "127.0.0.1", 0 },
1068275970Scy	{ NULL, NULL, NULL, 0 }
1069275970Scy};
1070275970Scy
1071275970Scystatic void
1072275970Scyhttp_connection_async_test(void *arg)
1073275970Scy{
1074275970Scy	struct basic_test_data *data = arg;
1075275970Scy	ev_uint16_t port = 0;
1076275970Scy	struct evhttp_connection *evcon = NULL;
1077275970Scy	struct evhttp_request *req = NULL;
1078275970Scy	struct evdns_base *dns_base = NULL;
1079275970Scy	ev_uint16_t portnum = 0;
1080275970Scy	char address[64];
1081275970Scy
1082275970Scy	exit_base = data->base;
1083275970Scy	tt_assert(regress_dnsserver(data->base, &portnum, search_table));
1084275970Scy
1085275970Scy	dns_base = evdns_base_new(data->base, 0/* init name servers */);
1086275970Scy	tt_assert(dns_base);
1087275970Scy
1088275970Scy	/* Add ourself as the only nameserver, and make sure we really are
1089275970Scy	 * the only nameserver. */
1090275970Scy	evutil_snprintf(address, sizeof(address), "127.0.0.1:%d", portnum);
1091275970Scy	evdns_base_nameserver_ip_add(dns_base, address);
1092275970Scy
1093275970Scy	test_ok = 0;
1094275970Scy
1095275970Scy	http = http_setup(&port, data->base, 0);
1096275970Scy
1097275970Scy	evcon = evhttp_connection_base_new(data->base, dns_base, "127.0.0.1", port);
1098275970Scy	tt_assert(evcon);
1099275970Scy
1100275970Scy	/*
1101275970Scy	 * At this point, we want to schedule a request to the HTTP
1102275970Scy	 * server using our make request method.
1103275970Scy	 */
1104275970Scy
1105275970Scy	req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
1106275970Scy
1107275970Scy	/* Add the information that we care about */
1108275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
1109275970Scy
1110275970Scy	/* We give ownership of the request to the connection */
1111275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
1112275970Scy		fprintf(stdout, "FAILED\n");
1113275970Scy		exit(1);
1114275970Scy	}
1115275970Scy
1116275970Scy	event_base_dispatch(data->base);
1117275970Scy
1118275970Scy	tt_assert(test_ok);
1119275970Scy
1120275970Scy	/* try to make another request over the same connection */
1121275970Scy	test_ok = 0;
1122275970Scy
1123275970Scy	req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
1124275970Scy
1125275970Scy	/* Add the information that we care about */
1126275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
1127275970Scy
1128275970Scy	/*
1129275970Scy	 * if our connections are not supposed to be persistent; request
1130275970Scy	 * a close from the server.
1131275970Scy	 */
1132275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close");
1133275970Scy
1134275970Scy	/* We give ownership of the request to the connection */
1135275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
1136275970Scy		tt_abort_msg("couldn't make request");
1137275970Scy	}
1138275970Scy
1139275970Scy	event_base_dispatch(data->base);
1140275970Scy
1141275970Scy	/* make another request: request empty reply */
1142275970Scy	test_ok = 0;
1143275970Scy
1144275970Scy	req = evhttp_request_new(http_request_empty_done, data->base);
1145275970Scy
1146275970Scy	/* Add the information that we care about */
1147275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis");
1148275970Scy
1149275970Scy	/* We give ownership of the request to the connection */
1150275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
1151275970Scy		tt_abort_msg("Couldn't make request");
1152275970Scy	}
1153275970Scy
1154275970Scy	event_base_dispatch(data->base);
1155275970Scy
1156275970Scy end:
1157275970Scy	if (evcon)
1158275970Scy		evhttp_connection_free(evcon);
1159275970Scy	if (http)
1160275970Scy		evhttp_free(http);
1161275970Scy	if (dns_base)
1162275970Scy		evdns_base_free(dns_base, 0);
1163275970Scy	regress_clean_dnsserver();
1164275970Scy}
1165275970Scy
1166275970Scystatic void
1167282408Scyhttp_autofree_connection_test(void *arg)
1168282408Scy{
1169282408Scy	struct basic_test_data *data = arg;
1170282408Scy	ev_uint16_t port = 0;
1171282408Scy	struct evhttp_connection *evcon = NULL;
1172282408Scy	struct evhttp_request *req[2] = { NULL };
1173282408Scy
1174282408Scy	test_ok = 0;
1175282408Scy	http = http_setup(&port, data->base, 0);
1176282408Scy
1177282408Scy	evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
1178282408Scy	tt_assert(evcon);
1179282408Scy
1180282408Scy	/*
1181282408Scy	 * At this point, we want to schedule two request to the HTTP
1182282408Scy	 * server using our make request method.
1183282408Scy	 */
1184282408Scy	req[0] = evhttp_request_new(http_request_empty_done, data->base);
1185282408Scy	req[1] = evhttp_request_new(http_request_empty_done, data->base);
1186282408Scy
1187282408Scy	/* Add the information that we care about */
1188282408Scy	evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Host", "somehost");
1189282408Scy	evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Connection", "close");
1190282408Scy	evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Empty", "itis");
1191282408Scy	evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Host", "somehost");
1192282408Scy	evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Connection", "close");
1193282408Scy	evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Empty", "itis");
1194282408Scy
1195282408Scy	/* We give ownership of the request to the connection */
1196282408Scy	if (evhttp_make_request(evcon, req[0], EVHTTP_REQ_GET, "/test") == -1) {
1197282408Scy		tt_abort_msg("couldn't make request");
1198282408Scy	}
1199282408Scy	if (evhttp_make_request(evcon, req[1], EVHTTP_REQ_GET, "/test") == -1) {
1200282408Scy		tt_abort_msg("couldn't make request");
1201282408Scy	}
1202282408Scy
1203282408Scy	/*
1204282408Scy	 * Tell libevent to free the connection when the request completes
1205282408Scy	 *	We then set the evcon pointer to NULL since we don't want to free it
1206282408Scy	 *	when this function ends.
1207282408Scy	 */
1208282408Scy	evhttp_connection_free_on_completion(evcon);
1209282408Scy	evcon = NULL;
1210282408Scy
1211282408Scy	event_base_dispatch(data->base);
1212282408Scy
1213282408Scy	/* at this point, the http server should have no connection */
1214282408Scy	tt_assert(TAILQ_FIRST(&http->connections) == NULL);
1215282408Scy
1216282408Scy end:
1217282408Scy	if (evcon)
1218282408Scy		evhttp_connection_free(evcon);
1219282408Scy	if (http)
1220282408Scy		evhttp_free(http);
1221282408Scy}
1222282408Scy
1223282408Scystatic void
1224275970Scyhttp_request_never_call(struct evhttp_request *req, void *arg)
1225275970Scy{
1226275970Scy	fprintf(stdout, "FAILED\n");
1227275970Scy	exit(1);
1228275970Scy}
1229275970Scy
1230275970Scystatic void
1231275970Scyhttp_do_cancel(evutil_socket_t fd, short what, void *arg)
1232275970Scy{
1233275970Scy	struct evhttp_request *req = arg;
1234275970Scy	struct timeval tv;
1235275970Scy	struct event_base *base;
1236275970Scy	evutil_timerclear(&tv);
1237275970Scy	tv.tv_sec = 0;
1238275970Scy	tv.tv_usec = 500 * 1000;
1239275970Scy
1240275970Scy	base = evhttp_connection_get_base(evhttp_request_get_connection(req));
1241275970Scy	evhttp_cancel_request(req);
1242275970Scy
1243275970Scy	event_base_loopexit(base, &tv);
1244275970Scy
1245275970Scy	++test_ok;
1246275970Scy}
1247275970Scy
1248275970Scystatic void
1249275970Scyhttp_cancel_test(void *arg)
1250275970Scy{
1251275970Scy	struct basic_test_data *data = arg;
1252275970Scy	ev_uint16_t port = 0;
1253275970Scy	struct evhttp_connection *evcon = NULL;
1254275970Scy	struct evhttp_request *req = NULL;
1255275970Scy	struct timeval tv;
1256275970Scy
1257275970Scy	exit_base = data->base;
1258275970Scy
1259275970Scy	test_ok = 0;
1260275970Scy
1261275970Scy	http = http_setup(&port, data->base, 0);
1262275970Scy
1263275970Scy	evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
1264275970Scy	tt_assert(evcon);
1265275970Scy
1266275970Scy	/*
1267275970Scy	 * At this point, we want to schedule a request to the HTTP
1268275970Scy	 * server using our make request method.
1269275970Scy	 */
1270275970Scy
1271275970Scy	req = evhttp_request_new(http_request_never_call, NULL);
1272275970Scy	evhttp_request_set_error_cb(req, http_request_error_cb_with_cancel_);
1273275970Scy
1274275970Scy	/* Add the information that we care about */
1275275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
1276275970Scy
1277275970Scy	/* We give ownership of the request to the connection */
1278275970Scy	tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/delay"),
1279275970Scy		  !=, -1);
1280275970Scy
1281275970Scy	evutil_timerclear(&tv);
1282275970Scy	tv.tv_sec = 0;
1283275970Scy	tv.tv_usec = 100 * 1000;
1284275970Scy
1285275970Scy	event_base_once(data->base, -1, EV_TIMEOUT, http_do_cancel, req, &tv);
1286275970Scy
1287275970Scy	event_base_dispatch(data->base);
1288275970Scy
1289275970Scy	tt_int_op(test_ok, ==, 3);
1290275970Scy
1291275970Scy	/* try to make another request over the same connection */
1292275970Scy	test_ok = 0;
1293275970Scy
1294275970Scy	req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
1295275970Scy
1296275970Scy	/* Add the information that we care about */
1297275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
1298275970Scy
1299275970Scy	/* We give ownership of the request to the connection */
1300275970Scy	tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test"),
1301275970Scy		  !=, -1);
1302275970Scy
1303275970Scy	event_base_dispatch(data->base);
1304275970Scy
1305275970Scy	/* make another request: request empty reply */
1306275970Scy	test_ok = 0;
1307275970Scy
1308275970Scy	req = evhttp_request_new(http_request_empty_done, data->base);
1309275970Scy
1310275970Scy	/* Add the information that we care about */
1311275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis");
1312275970Scy
1313275970Scy	/* We give ownership of the request to the connection */
1314275970Scy	tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test"),
1315275970Scy		  !=, -1);
1316275970Scy
1317275970Scy	event_base_dispatch(data->base);
1318275970Scy
1319275970Scy end:
1320275970Scy	if (evcon)
1321275970Scy		evhttp_connection_free(evcon);
1322275970Scy	if (http)
1323275970Scy		evhttp_free(http);
1324275970Scy}
1325275970Scy
1326275970Scystatic void
1327275970Scyhttp_request_done(struct evhttp_request *req, void *arg)
1328275970Scy{
1329275970Scy	const char *what = arg;
1330275970Scy
1331275970Scy	if (evhttp_request_get_response_code(req) != HTTP_OK) {
1332275970Scy		fprintf(stderr, "FAILED\n");
1333275970Scy		exit(1);
1334275970Scy	}
1335275970Scy
1336275970Scy	if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) {
1337275970Scy		fprintf(stderr, "FAILED\n");
1338275970Scy		exit(1);
1339275970Scy	}
1340275970Scy
1341275970Scy	if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) {
1342275970Scy		fprintf(stderr, "FAILED\n");
1343275970Scy		exit(1);
1344275970Scy	}
1345275970Scy
1346275970Scy	if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) {
1347275970Scy		fprintf(stderr, "FAILED\n");
1348275970Scy		exit(1);
1349275970Scy	}
1350275970Scy
1351275970Scy	test_ok = 1;
1352275970Scy	EVUTIL_ASSERT(exit_base);
1353275970Scy	event_base_loopexit(exit_base, NULL);
1354275970Scy}
1355275970Scy
1356275970Scystatic void
1357275970Scyhttp_request_expect_error(struct evhttp_request *req, void *arg)
1358275970Scy{
1359275970Scy	if (evhttp_request_get_response_code(req) == HTTP_OK) {
1360275970Scy		fprintf(stderr, "FAILED\n");
1361275970Scy		exit(1);
1362275970Scy	}
1363275970Scy
1364275970Scy	test_ok = 1;
1365275970Scy	EVUTIL_ASSERT(arg);
1366275970Scy	event_base_loopexit(arg, NULL);
1367275970Scy}
1368275970Scy
1369275970Scy/* test virtual hosts */
1370275970Scystatic void
1371275970Scyhttp_virtual_host_test(void *arg)
1372275970Scy{
1373275970Scy	struct basic_test_data *data = arg;
1374275970Scy	ev_uint16_t port = 0;
1375275970Scy	struct evhttp_connection *evcon = NULL;
1376275970Scy	struct evhttp_request *req = NULL;
1377275970Scy	struct evhttp *second = NULL, *third = NULL;
1378275970Scy	evutil_socket_t fd;
1379275970Scy	struct bufferevent *bev;
1380275970Scy	const char *http_request;
1381275970Scy
1382275970Scy	exit_base = data->base;
1383275970Scy
1384275970Scy	http = http_setup(&port, data->base, 0);
1385275970Scy
1386275970Scy	/* virtual host */
1387275970Scy	second = evhttp_new(NULL);
1388275970Scy	evhttp_set_cb(second, "/funnybunny", http_basic_cb, NULL);
1389275970Scy	third = evhttp_new(NULL);
1390275970Scy	evhttp_set_cb(third, "/blackcoffee", http_basic_cb, NULL);
1391275970Scy
1392275970Scy	if (evhttp_add_virtual_host(http, "foo.com", second) == -1) {
1393275970Scy		tt_abort_msg("Couldn't add vhost");
1394275970Scy	}
1395275970Scy
1396275970Scy	if (evhttp_add_virtual_host(http, "bar.*.foo.com", third) == -1) {
1397275970Scy		tt_abort_msg("Couldn't add wildcarded vhost");
1398275970Scy	}
1399275970Scy
1400275970Scy	/* add some aliases to the vhosts */
1401275970Scy	tt_assert(evhttp_add_server_alias(second, "manolito.info") == 0);
1402275970Scy	tt_assert(evhttp_add_server_alias(third, "bonkers.org") == 0);
1403275970Scy
1404275970Scy	evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
1405275970Scy	tt_assert(evcon);
1406275970Scy
1407275970Scy	/* make a request with a different host and expect an error */
1408275970Scy	req = evhttp_request_new(http_request_expect_error, data->base);
1409275970Scy
1410275970Scy	/* Add the information that we care about */
1411275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
1412275970Scy
1413275970Scy	/* We give ownership of the request to the connection */
1414275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
1415275970Scy		"/funnybunny") == -1) {
1416275970Scy		tt_abort_msg("Couldn't make request");
1417275970Scy	}
1418275970Scy
1419275970Scy	event_base_dispatch(data->base);
1420275970Scy
1421275970Scy	tt_assert(test_ok == 1);
1422275970Scy
1423275970Scy	test_ok = 0;
1424275970Scy
1425275970Scy	/* make a request with the right host and expect a response */
1426275970Scy	req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
1427275970Scy
1428275970Scy	/* Add the information that we care about */
1429275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "foo.com");
1430275970Scy
1431275970Scy	/* We give ownership of the request to the connection */
1432275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
1433275970Scy		"/funnybunny") == -1) {
1434275970Scy		fprintf(stdout, "FAILED\n");
1435275970Scy		exit(1);
1436275970Scy	}
1437275970Scy
1438275970Scy	event_base_dispatch(data->base);
1439275970Scy
1440275970Scy	tt_assert(test_ok == 1);
1441275970Scy
1442275970Scy	test_ok = 0;
1443275970Scy
1444275970Scy	/* make a request with the right host and expect a response */
1445275970Scy	req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
1446275970Scy
1447275970Scy	/* Add the information that we care about */
1448275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "bar.magic.foo.com");
1449275970Scy
1450275970Scy	/* We give ownership of the request to the connection */
1451275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
1452275970Scy		"/blackcoffee") == -1) {
1453275970Scy		tt_abort_msg("Couldn't make request");
1454275970Scy	}
1455275970Scy
1456275970Scy	event_base_dispatch(data->base);
1457275970Scy
1458275970Scy	tt_assert(test_ok == 1)
1459275970Scy
1460275970Scy	test_ok = 0;
1461275970Scy
1462275970Scy	/* make a request with the right host and expect a response */
1463275970Scy	req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
1464275970Scy
1465275970Scy	/* Add the information that we care about */
1466275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "manolito.info");
1467275970Scy
1468275970Scy	/* We give ownership of the request to the connection */
1469275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
1470275970Scy		"/funnybunny") == -1) {
1471275970Scy		tt_abort_msg("Couldn't make request");
1472275970Scy	}
1473275970Scy
1474275970Scy	event_base_dispatch(data->base);
1475275970Scy
1476275970Scy	tt_assert(test_ok == 1)
1477275970Scy
1478275970Scy	test_ok = 0;
1479275970Scy
1480275970Scy	/* make a request with the right host and expect a response */
1481275970Scy	req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
1482275970Scy
1483275970Scy	/* Add the Host header. This time with the optional port. */
1484275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "bonkers.org:8000");
1485275970Scy
1486275970Scy	/* We give ownership of the request to the connection */
1487275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
1488275970Scy		"/blackcoffee") == -1) {
1489275970Scy		tt_abort_msg("Couldn't make request");
1490275970Scy	}
1491275970Scy
1492275970Scy	event_base_dispatch(data->base);
1493275970Scy
1494275970Scy	tt_assert(test_ok == 1)
1495275970Scy
1496275970Scy	test_ok = 0;
1497275970Scy
1498275970Scy	/* Now make a raw request with an absolute URI. */
1499275970Scy	fd = http_connect("127.0.0.1", port);
1500275970Scy
1501275970Scy	/* Stupid thing to send a request */
1502275970Scy	bev = bufferevent_socket_new(data->base, fd, 0);
1503275970Scy	bufferevent_setcb(bev, http_readcb, http_writecb,
1504275970Scy	    http_errorcb, NULL);
1505275970Scy
1506275970Scy	/* The host in the URI should override the Host: header */
1507275970Scy	http_request =
1508275970Scy	    "GET http://manolito.info/funnybunny HTTP/1.1\r\n"
1509275970Scy	    "Host: somehost\r\n"
1510275970Scy	    "Connection: close\r\n"
1511275970Scy	    "\r\n";
1512275970Scy
1513275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
1514275970Scy
1515275970Scy	event_base_dispatch(data->base);
1516275970Scy
1517275970Scy	tt_int_op(test_ok, ==, 2);
1518275970Scy
1519275970Scy	bufferevent_free(bev);
1520275970Scy	evutil_closesocket(fd);
1521275970Scy
1522275970Scy end:
1523275970Scy	if (evcon)
1524275970Scy		evhttp_connection_free(evcon);
1525275970Scy	if (http)
1526275970Scy		evhttp_free(http);
1527275970Scy}
1528275970Scy
1529275970Scy
1530275970Scy/* test date header and content length */
1531275970Scy
1532275970Scystatic void
1533275970Scyhttp_request_empty_done(struct evhttp_request *req, void *arg)
1534275970Scy{
1535275970Scy	if (evhttp_request_get_response_code(req) != HTTP_OK) {
1536275970Scy		fprintf(stderr, "FAILED\n");
1537275970Scy		exit(1);
1538275970Scy	}
1539275970Scy
1540275970Scy	if (evhttp_find_header(evhttp_request_get_input_headers(req), "Date") == NULL) {
1541275970Scy		fprintf(stderr, "FAILED\n");
1542275970Scy		exit(1);
1543275970Scy	}
1544275970Scy
1545275970Scy
1546275970Scy	if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Length") == NULL) {
1547275970Scy		fprintf(stderr, "FAILED\n");
1548275970Scy		exit(1);
1549275970Scy	}
1550275970Scy
1551275970Scy	if (strcmp(evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Length"),
1552275970Scy		"0")) {
1553275970Scy		fprintf(stderr, "FAILED\n");
1554275970Scy		exit(1);
1555275970Scy	}
1556275970Scy
1557275970Scy	if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 0) {
1558275970Scy		fprintf(stderr, "FAILED\n");
1559275970Scy		exit(1);
1560275970Scy	}
1561275970Scy
1562275970Scy	test_ok = 1;
1563275970Scy	EVUTIL_ASSERT(arg);
1564275970Scy	event_base_loopexit(arg, NULL);
1565275970Scy}
1566275970Scy
1567275970Scy/*
1568275970Scy * HTTP DISPATCHER test
1569275970Scy */
1570275970Scy
1571275970Scyvoid
1572275970Scyhttp_dispatcher_cb(struct evhttp_request *req, void *arg)
1573275970Scy{
1574275970Scy
1575275970Scy	struct evbuffer *evb = evbuffer_new();
1576275970Scy	event_debug(("%s: called\n", __func__));
1577275970Scy	evbuffer_add_printf(evb, "DISPATCHER_TEST");
1578275970Scy
1579275970Scy	evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
1580275970Scy
1581275970Scy	evbuffer_free(evb);
1582275970Scy}
1583275970Scy
1584275970Scystatic void
1585275970Scyhttp_dispatcher_test_done(struct evhttp_request *req, void *arg)
1586275970Scy{
1587275970Scy	struct event_base *base = arg;
1588275970Scy	const char *what = "DISPATCHER_TEST";
1589275970Scy
1590275970Scy	if (evhttp_request_get_response_code(req) != HTTP_OK) {
1591275970Scy		fprintf(stderr, "FAILED\n");
1592275970Scy		exit(1);
1593275970Scy	}
1594275970Scy
1595275970Scy	if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) {
1596275970Scy		fprintf(stderr, "FAILED (content type)\n");
1597275970Scy		exit(1);
1598275970Scy	}
1599275970Scy
1600275970Scy	if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) {
1601275970Scy		fprintf(stderr, "FAILED (length %lu vs %lu)\n",
1602275970Scy		    (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what));
1603275970Scy		exit(1);
1604275970Scy	}
1605275970Scy
1606275970Scy	if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) {
1607275970Scy		fprintf(stderr, "FAILED (data)\n");
1608275970Scy		exit(1);
1609275970Scy	}
1610275970Scy
1611275970Scy	test_ok = 1;
1612275970Scy	event_base_loopexit(base, NULL);
1613275970Scy}
1614275970Scy
1615275970Scystatic void
1616275970Scyhttp_dispatcher_test(void *arg)
1617275970Scy{
1618275970Scy	struct basic_test_data *data = arg;
1619275970Scy	ev_uint16_t port = 0;
1620275970Scy	struct evhttp_connection *evcon = NULL;
1621275970Scy	struct evhttp_request *req = NULL;
1622275970Scy
1623275970Scy	test_ok = 0;
1624275970Scy
1625275970Scy	http = http_setup(&port, data->base, 0);
1626275970Scy
1627275970Scy	evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
1628275970Scy	tt_assert(evcon);
1629275970Scy
1630275970Scy	/* also bind to local host */
1631275970Scy	evhttp_connection_set_local_address(evcon, "127.0.0.1");
1632275970Scy
1633275970Scy	/*
1634275970Scy	 * At this point, we want to schedule an HTTP GET request
1635275970Scy	 * server using our make request method.
1636275970Scy	 */
1637275970Scy
1638275970Scy	req = evhttp_request_new(http_dispatcher_test_done, data->base);
1639275970Scy	tt_assert(req);
1640275970Scy
1641275970Scy	/* Add the information that we care about */
1642275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
1643275970Scy
1644275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) {
1645275970Scy		tt_abort_msg("Couldn't make request");
1646275970Scy	}
1647275970Scy
1648275970Scy	event_base_dispatch(data->base);
1649275970Scy
1650275970Scy end:
1651275970Scy	if (evcon)
1652275970Scy		evhttp_connection_free(evcon);
1653275970Scy	if (http)
1654275970Scy		evhttp_free(http);
1655275970Scy}
1656275970Scy
1657275970Scy/*
1658275970Scy * HTTP POST test.
1659275970Scy */
1660275970Scy
1661275970Scyvoid http_postrequest_done(struct evhttp_request *, void *);
1662275970Scy
1663275970Scy#define POST_DATA "Okay.  Not really printf"
1664275970Scy
1665275970Scystatic void
1666275970Scyhttp_post_test(void *arg)
1667275970Scy{
1668275970Scy	struct basic_test_data *data = arg;
1669275970Scy	ev_uint16_t port = 0;
1670275970Scy	struct evhttp_connection *evcon = NULL;
1671275970Scy	struct evhttp_request *req = NULL;
1672275970Scy
1673275970Scy	test_ok = 0;
1674275970Scy
1675275970Scy	http = http_setup(&port, data->base, 0);
1676275970Scy
1677275970Scy	evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
1678275970Scy	tt_assert(evcon);
1679275970Scy
1680275970Scy	/*
1681275970Scy	 * At this point, we want to schedule an HTTP POST request
1682275970Scy	 * server using our make request method.
1683275970Scy	 */
1684275970Scy
1685275970Scy	req = evhttp_request_new(http_postrequest_done, data->base);
1686275970Scy	tt_assert(req);
1687275970Scy
1688275970Scy	/* Add the information that we care about */
1689275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
1690275970Scy	evbuffer_add_printf(evhttp_request_get_output_buffer(req), POST_DATA);
1691275970Scy
1692275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) {
1693275970Scy		tt_abort_msg("Couldn't make request");
1694275970Scy	}
1695275970Scy
1696275970Scy	event_base_dispatch(data->base);
1697275970Scy
1698275970Scy	tt_int_op(test_ok, ==, 1);
1699275970Scy
1700275970Scy	test_ok = 0;
1701275970Scy
1702275970Scy	req = evhttp_request_new(http_postrequest_done, data->base);
1703275970Scy	tt_assert(req);
1704275970Scy
1705275970Scy	/* Now try with 100-continue. */
1706275970Scy
1707275970Scy	/* Add the information that we care about */
1708275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
1709275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "100-continue");
1710275970Scy	evbuffer_add_printf(evhttp_request_get_output_buffer(req), POST_DATA);
1711275970Scy
1712275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) {
1713275970Scy		tt_abort_msg("Couldn't make request");
1714275970Scy	}
1715275970Scy
1716275970Scy	event_base_dispatch(data->base);
1717275970Scy
1718275970Scy	tt_int_op(test_ok, ==, 1);
1719275970Scy
1720275970Scy	evhttp_connection_free(evcon);
1721275970Scy	evhttp_free(http);
1722275970Scy
1723275970Scy end:
1724275970Scy	;
1725275970Scy}
1726275970Scy
1727275970Scyvoid
1728275970Scyhttp_post_cb(struct evhttp_request *req, void *arg)
1729275970Scy{
1730275970Scy	struct evbuffer *evb;
1731275970Scy	event_debug(("%s: called\n", __func__));
1732275970Scy
1733275970Scy	/* Yes, we are expecting a post request */
1734275970Scy	if (evhttp_request_get_command(req) != EVHTTP_REQ_POST) {
1735275970Scy		fprintf(stdout, "FAILED (post type)\n");
1736275970Scy		exit(1);
1737275970Scy	}
1738275970Scy
1739275970Scy	if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(POST_DATA)) {
1740275970Scy		fprintf(stdout, "FAILED (length: %lu vs %lu)\n",
1741275970Scy		    (unsigned long) evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long) strlen(POST_DATA));
1742275970Scy		exit(1);
1743275970Scy	}
1744275970Scy
1745275970Scy	if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), POST_DATA) != 0) {
1746275970Scy		fprintf(stdout, "FAILED (data)\n");
1747275970Scy		fprintf(stdout, "Got :%s\n", evbuffer_pullup(evhttp_request_get_input_buffer(req),-1));
1748275970Scy		fprintf(stdout, "Want:%s\n", POST_DATA);
1749275970Scy		exit(1);
1750275970Scy	}
1751275970Scy
1752275970Scy	evb = evbuffer_new();
1753275970Scy	evbuffer_add_printf(evb, BASIC_REQUEST_BODY);
1754275970Scy
1755275970Scy	evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
1756275970Scy
1757275970Scy	evbuffer_free(evb);
1758275970Scy}
1759275970Scy
1760275970Scyvoid
1761275970Scyhttp_postrequest_done(struct evhttp_request *req, void *arg)
1762275970Scy{
1763275970Scy	const char *what = BASIC_REQUEST_BODY;
1764275970Scy	struct event_base *base = arg;
1765275970Scy
1766275970Scy	if (req == NULL) {
1767275970Scy		fprintf(stderr, "FAILED (timeout)\n");
1768275970Scy		exit(1);
1769275970Scy	}
1770275970Scy
1771275970Scy	if (evhttp_request_get_response_code(req) != HTTP_OK) {
1772275970Scy
1773275970Scy		fprintf(stderr, "FAILED (response code)\n");
1774275970Scy		exit(1);
1775275970Scy	}
1776275970Scy
1777275970Scy	if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) {
1778275970Scy		fprintf(stderr, "FAILED (content type)\n");
1779275970Scy		exit(1);
1780275970Scy	}
1781275970Scy
1782275970Scy	if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) {
1783275970Scy		fprintf(stderr, "FAILED (length %lu vs %lu)\n",
1784275970Scy		    (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what));
1785275970Scy		exit(1);
1786275970Scy	}
1787275970Scy
1788275970Scy	if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) {
1789275970Scy		fprintf(stderr, "FAILED (data)\n");
1790275970Scy		exit(1);
1791275970Scy	}
1792275970Scy
1793275970Scy	test_ok = 1;
1794275970Scy	event_base_loopexit(base, NULL);
1795275970Scy}
1796275970Scy
1797275970Scy/*
1798275970Scy * HTTP PUT test, basically just like POST, but ...
1799275970Scy */
1800275970Scy
1801275970Scyvoid http_putrequest_done(struct evhttp_request *, void *);
1802275970Scy
1803275970Scy#define PUT_DATA "Hi, I'm some PUT data"
1804275970Scy
1805275970Scystatic void
1806275970Scyhttp_put_test(void *arg)
1807275970Scy{
1808275970Scy	struct basic_test_data *data = arg;
1809275970Scy	ev_uint16_t port = 0;
1810275970Scy	struct evhttp_connection *evcon = NULL;
1811275970Scy	struct evhttp_request *req = NULL;
1812275970Scy
1813275970Scy	test_ok = 0;
1814275970Scy
1815275970Scy	http = http_setup(&port, data->base, 0);
1816275970Scy
1817275970Scy	evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
1818275970Scy	tt_assert(evcon);
1819275970Scy
1820275970Scy	/*
1821275970Scy	 * Schedule the HTTP PUT request
1822275970Scy	 */
1823275970Scy
1824275970Scy	req = evhttp_request_new(http_putrequest_done, data->base);
1825275970Scy	tt_assert(req);
1826275970Scy
1827275970Scy	/* Add the information that we care about */
1828275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "someotherhost");
1829275970Scy	evbuffer_add_printf(evhttp_request_get_output_buffer(req), PUT_DATA);
1830275970Scy
1831275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_PUT, "/putit") == -1) {
1832275970Scy		tt_abort_msg("Couldn't make request");
1833275970Scy	}
1834275970Scy
1835275970Scy	event_base_dispatch(data->base);
1836275970Scy
1837275970Scy	evhttp_connection_free(evcon);
1838275970Scy	evhttp_free(http);
1839275970Scy
1840275970Scy	tt_int_op(test_ok, ==, 1);
1841275970Scy end:
1842275970Scy	;
1843275970Scy}
1844275970Scy
1845275970Scyvoid
1846275970Scyhttp_put_cb(struct evhttp_request *req, void *arg)
1847275970Scy{
1848275970Scy	struct evbuffer *evb;
1849275970Scy	event_debug(("%s: called\n", __func__));
1850275970Scy
1851275970Scy	/* Expecting a PUT request */
1852275970Scy	if (evhttp_request_get_command(req) != EVHTTP_REQ_PUT) {
1853275970Scy		fprintf(stdout, "FAILED (put type)\n");
1854275970Scy		exit(1);
1855275970Scy	}
1856275970Scy
1857275970Scy	if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(PUT_DATA)) {
1858275970Scy		fprintf(stdout, "FAILED (length: %lu vs %lu)\n",
1859275970Scy		    (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(PUT_DATA));
1860275970Scy		exit(1);
1861275970Scy	}
1862275970Scy
1863275970Scy	if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), PUT_DATA) != 0) {
1864275970Scy		fprintf(stdout, "FAILED (data)\n");
1865275970Scy		fprintf(stdout, "Got :%s\n", evbuffer_pullup(evhttp_request_get_input_buffer(req),-1));
1866275970Scy		fprintf(stdout, "Want:%s\n", PUT_DATA);
1867275970Scy		exit(1);
1868275970Scy	}
1869275970Scy
1870275970Scy	evb = evbuffer_new();
1871275970Scy	evbuffer_add_printf(evb, "That ain't funny");
1872275970Scy
1873275970Scy	evhttp_send_reply(req, HTTP_OK, "Everything is great", evb);
1874275970Scy
1875275970Scy	evbuffer_free(evb);
1876275970Scy}
1877275970Scy
1878275970Scyvoid
1879275970Scyhttp_putrequest_done(struct evhttp_request *req, void *arg)
1880275970Scy{
1881275970Scy	struct event_base *base = arg;
1882275970Scy	const char *what = "That ain't funny";
1883275970Scy
1884275970Scy	if (req == NULL) {
1885275970Scy		fprintf(stderr, "FAILED (timeout)\n");
1886275970Scy		exit(1);
1887275970Scy	}
1888275970Scy
1889275970Scy	if (evhttp_request_get_response_code(req) != HTTP_OK) {
1890275970Scy
1891275970Scy		fprintf(stderr, "FAILED (response code)\n");
1892275970Scy		exit(1);
1893275970Scy	}
1894275970Scy
1895275970Scy	if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) {
1896275970Scy		fprintf(stderr, "FAILED (content type)\n");
1897275970Scy		exit(1);
1898275970Scy	}
1899275970Scy
1900275970Scy	if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) {
1901275970Scy		fprintf(stderr, "FAILED (length %lu vs %lu)\n",
1902275970Scy		    (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what));
1903275970Scy		exit(1);
1904275970Scy	}
1905275970Scy
1906275970Scy
1907275970Scy	if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) {
1908275970Scy		fprintf(stderr, "FAILED (data)\n");
1909275970Scy		exit(1);
1910275970Scy	}
1911275970Scy
1912275970Scy	test_ok = 1;
1913275970Scy	event_base_loopexit(base, NULL);
1914275970Scy}
1915275970Scy
1916275970Scystatic void
1917275970Scyhttp_failure_readcb(struct bufferevent *bev, void *arg)
1918275970Scy{
1919275970Scy	const char *what = "400 Bad Request";
1920275970Scy	if (evbuffer_contains(bufferevent_get_input(bev), what)) {
1921275970Scy		test_ok = 2;
1922275970Scy		bufferevent_disable(bev, EV_READ);
1923275970Scy		event_base_loopexit(arg, NULL);
1924275970Scy	}
1925275970Scy}
1926275970Scy
1927275970Scy/*
1928275970Scy * Testing that the HTTP server can deal with a malformed request.
1929275970Scy */
1930275970Scystatic void
1931275970Scyhttp_failure_test(void *arg)
1932275970Scy{
1933275970Scy	struct basic_test_data *data = arg;
1934275970Scy	struct bufferevent *bev;
1935275970Scy	evutil_socket_t fd = -1;
1936275970Scy	const char *http_request;
1937275970Scy	ev_uint16_t port = 0;
1938275970Scy
1939275970Scy	test_ok = 0;
1940275970Scy
1941275970Scy	http = http_setup(&port, data->base, 0);
1942275970Scy
1943275970Scy	fd = http_connect("127.0.0.1", port);
1944275970Scy	tt_int_op(fd, >=, 0);
1945275970Scy
1946275970Scy	/* Stupid thing to send a request */
1947275970Scy	bev = bufferevent_socket_new(data->base, fd, 0);
1948275970Scy	bufferevent_setcb(bev, http_failure_readcb, http_writecb,
1949275970Scy	    http_errorcb, data->base);
1950275970Scy
1951275970Scy	http_request = "illegal request\r\n";
1952275970Scy
1953275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
1954275970Scy
1955275970Scy	event_base_dispatch(data->base);
1956275970Scy
1957275970Scy	bufferevent_free(bev);
1958275970Scy
1959275970Scy	evhttp_free(http);
1960275970Scy
1961275970Scy	tt_int_op(test_ok, ==, 2);
1962275970Scy end:
1963275970Scy	if (fd >= 0)
1964275970Scy		evutil_closesocket(fd);
1965275970Scy}
1966275970Scy
1967275970Scystatic void
1968275970Scyclose_detect_done(struct evhttp_request *req, void *arg)
1969275970Scy{
1970275970Scy	struct timeval tv;
1971275970Scy	tt_assert(req);
1972275970Scy	tt_assert(evhttp_request_get_response_code(req) == HTTP_OK);
1973275970Scy
1974275970Scy	test_ok = 1;
1975275970Scy
1976275970Scy end:
1977275970Scy	evutil_timerclear(&tv);
1978275970Scy	tv.tv_usec = 150000;
1979275970Scy	event_base_loopexit(arg, &tv);
1980275970Scy}
1981275970Scy
1982275970Scystatic void
1983275970Scyclose_detect_launch(evutil_socket_t fd, short what, void *arg)
1984275970Scy{
1985275970Scy	struct evhttp_connection *evcon = arg;
1986275970Scy	struct event_base *base = evhttp_connection_get_base(evcon);
1987275970Scy	struct evhttp_request *req;
1988275970Scy
1989275970Scy	req = evhttp_request_new(close_detect_done, base);
1990275970Scy
1991275970Scy	/* Add the information that we care about */
1992275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
1993275970Scy
1994275970Scy	/* We give ownership of the request to the connection */
1995275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
1996275970Scy		tt_fail_msg("Couldn't make request");
1997275970Scy	}
1998275970Scy}
1999275970Scy
2000275970Scystatic void
2001275970Scyclose_detect_cb(struct evhttp_request *req, void *arg)
2002275970Scy{
2003275970Scy	struct evhttp_connection *evcon = arg;
2004275970Scy	struct event_base *base = evhttp_connection_get_base(evcon);
2005275970Scy	struct timeval tv;
2006275970Scy
2007275970Scy	if (req != NULL && evhttp_request_get_response_code(req) != HTTP_OK) {
2008275970Scy		tt_abort_msg("Failed");
2009275970Scy	}
2010275970Scy
2011275970Scy	evutil_timerclear(&tv);
2012275970Scy	tv.tv_sec = 0;   /* longer than the http time out */
2013275970Scy	tv.tv_usec = 600000;   /* longer than the http time out */
2014275970Scy
2015275970Scy	/* launch a new request on the persistent connection in .3 seconds */
2016275970Scy	event_base_once(base, -1, EV_TIMEOUT, close_detect_launch, evcon, &tv);
2017275970Scy end:
2018275970Scy	;
2019275970Scy}
2020275970Scy
2021275970Scy
2022275970Scystatic void
2023275970Scyhttp_close_detection_(struct basic_test_data *data, int with_delay)
2024275970Scy{
2025275970Scy	ev_uint16_t port = 0;
2026275970Scy	struct evhttp_connection *evcon = NULL;
2027275970Scy	struct evhttp_request *req = NULL;
2028275970Scy	const struct timeval sec_tenth = { 0, 100000 };
2029275970Scy
2030275970Scy	test_ok = 0;
2031275970Scy	http = http_setup(&port, data->base, 0);
2032275970Scy
2033275970Scy	/* .1 second timeout */
2034275970Scy	evhttp_set_timeout_tv(http, &sec_tenth);
2035275970Scy
2036275970Scy	evcon = evhttp_connection_base_new(data->base, NULL,
2037275970Scy	    "127.0.0.1", port);
2038275970Scy	tt_assert(evcon);
2039275970Scy	evhttp_connection_set_timeout_tv(evcon, &sec_tenth);
2040275970Scy
2041275970Scy
2042275970Scy	tt_assert(evcon);
2043275970Scy	delayed_client = evcon;
2044275970Scy
2045275970Scy	/*
2046275970Scy	 * At this point, we want to schedule a request to the HTTP
2047275970Scy	 * server using our make request method.
2048275970Scy	 */
2049275970Scy
2050275970Scy	req = evhttp_request_new(close_detect_cb, evcon);
2051275970Scy
2052275970Scy	/* Add the information that we care about */
2053275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
2054275970Scy
2055275970Scy	/* We give ownership of the request to the connection */
2056275970Scy	if (evhttp_make_request(evcon,
2057275970Scy	    req, EVHTTP_REQ_GET, with_delay ? "/largedelay" : "/test") == -1) {
2058275970Scy		tt_abort_msg("couldn't make request");
2059275970Scy	}
2060275970Scy
2061275970Scy	event_base_dispatch(data->base);
2062275970Scy
2063275970Scy	/* at this point, the http server should have no connection */
2064275970Scy	tt_assert(TAILQ_FIRST(&http->connections) == NULL);
2065275970Scy
2066275970Scy end:
2067275970Scy	if (evcon)
2068275970Scy		evhttp_connection_free(evcon);
2069275970Scy	if (http)
2070275970Scy		evhttp_free(http);
2071275970Scy}
2072275970Scystatic void
2073275970Scyhttp_close_detection_test(void *arg)
2074275970Scy{
2075275970Scy	http_close_detection_(arg, 0);
2076275970Scy}
2077275970Scystatic void
2078275970Scyhttp_close_detection_delay_test(void *arg)
2079275970Scy{
2080275970Scy	http_close_detection_(arg, 1);
2081275970Scy}
2082275970Scy
2083275970Scystatic void
2084275970Scyhttp_highport_test(void *arg)
2085275970Scy{
2086275970Scy	struct basic_test_data *data = arg;
2087275970Scy	int i = -1;
2088275970Scy	struct evhttp *myhttp = NULL;
2089275970Scy
2090275970Scy	/* Try a few different ports */
2091275970Scy	for (i = 0; i < 50; ++i) {
2092275970Scy		myhttp = evhttp_new(data->base);
2093275970Scy		if (evhttp_bind_socket(myhttp, "127.0.0.1", 65535 - i) == 0) {
2094275970Scy			test_ok = 1;
2095275970Scy			evhttp_free(myhttp);
2096275970Scy			return;
2097275970Scy		}
2098275970Scy		evhttp_free(myhttp);
2099275970Scy	}
2100275970Scy
2101275970Scy	tt_fail_msg("Couldn't get a high port");
2102275970Scy}
2103275970Scy
2104275970Scystatic void
2105275970Scyhttp_bad_header_test(void *ptr)
2106275970Scy{
2107275970Scy	struct evkeyvalq headers;
2108275970Scy
2109275970Scy	TAILQ_INIT(&headers);
2110275970Scy
2111275970Scy	tt_want(evhttp_add_header(&headers, "One", "Two") == 0);
2112275970Scy	tt_want(evhttp_add_header(&headers, "One", "Two\r\n Three") == 0);
2113275970Scy	tt_want(evhttp_add_header(&headers, "One\r", "Two") == -1);
2114275970Scy	tt_want(evhttp_add_header(&headers, "One\n", "Two") == -1);
2115275970Scy	tt_want(evhttp_add_header(&headers, "One", "Two\r") == -1);
2116275970Scy	tt_want(evhttp_add_header(&headers, "One", "Two\n") == -1);
2117275970Scy
2118275970Scy	evhttp_clear_headers(&headers);
2119275970Scy}
2120275970Scy
2121275970Scystatic int validate_header(
2122275970Scy	const struct evkeyvalq* headers,
2123275970Scy	const char *key, const char *value)
2124275970Scy{
2125275970Scy	const char *real_val = evhttp_find_header(headers, key);
2126275970Scy	tt_assert(real_val != NULL);
2127275970Scy	tt_want(strcmp(real_val, value) == 0);
2128275970Scyend:
2129275970Scy	return (0);
2130275970Scy}
2131275970Scy
2132275970Scystatic void
2133275970Scyhttp_parse_query_test(void *ptr)
2134275970Scy{
2135275970Scy	struct evkeyvalq headers;
2136275970Scy	int r;
2137275970Scy
2138275970Scy	TAILQ_INIT(&headers);
2139275970Scy
2140275970Scy	r = evhttp_parse_query("http://www.test.com/?q=test", &headers);
2141275970Scy	tt_want(validate_header(&headers, "q", "test") == 0);
2142275970Scy	tt_int_op(r, ==, 0);
2143275970Scy	evhttp_clear_headers(&headers);
2144275970Scy
2145275970Scy	r = evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers);
2146275970Scy	tt_want(validate_header(&headers, "q", "test") == 0);
2147275970Scy	tt_want(validate_header(&headers, "foo", "bar") == 0);
2148275970Scy	tt_int_op(r, ==, 0);
2149275970Scy	evhttp_clear_headers(&headers);
2150275970Scy
2151275970Scy	r = evhttp_parse_query("http://www.test.com/?q=test+foo", &headers);
2152275970Scy	tt_want(validate_header(&headers, "q", "test foo") == 0);
2153275970Scy	tt_int_op(r, ==, 0);
2154275970Scy	evhttp_clear_headers(&headers);
2155275970Scy
2156275970Scy	r = evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers);
2157275970Scy	tt_want(validate_header(&headers, "q", "test\nfoo") == 0);
2158275970Scy	tt_int_op(r, ==, 0);
2159275970Scy	evhttp_clear_headers(&headers);
2160275970Scy
2161275970Scy	r = evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers);
2162275970Scy	tt_want(validate_header(&headers, "q", "test\rfoo") == 0);
2163275970Scy	tt_int_op(r, ==, 0);
2164275970Scy	evhttp_clear_headers(&headers);
2165275970Scy
2166275970Scy	r = evhttp_parse_query("http://www.test.com/?q=test&&q2", &headers);
2167275970Scy	tt_int_op(r, ==, -1);
2168275970Scy	evhttp_clear_headers(&headers);
2169275970Scy
2170275970Scy	r = evhttp_parse_query("http://www.test.com/?q=test+this", &headers);
2171275970Scy	tt_want(validate_header(&headers, "q", "test this") == 0);
2172275970Scy	tt_int_op(r, ==, 0);
2173275970Scy	evhttp_clear_headers(&headers);
2174275970Scy
2175275970Scy	r = evhttp_parse_query("http://www.test.com/?q=test&q2=foo", &headers);
2176275970Scy	tt_int_op(r, ==, 0);
2177275970Scy	tt_want(validate_header(&headers, "q", "test") == 0);
2178275970Scy	tt_want(validate_header(&headers, "q2", "foo") == 0);
2179275970Scy	evhttp_clear_headers(&headers);
2180275970Scy
2181275970Scy	r = evhttp_parse_query("http://www.test.com/?q&q2=foo", &headers);
2182275970Scy	tt_int_op(r, ==, -1);
2183275970Scy	evhttp_clear_headers(&headers);
2184275970Scy
2185275970Scy	r = evhttp_parse_query("http://www.test.com/?q=foo&q2", &headers);
2186275970Scy	tt_int_op(r, ==, -1);
2187275970Scy	evhttp_clear_headers(&headers);
2188275970Scy
2189275970Scy	r = evhttp_parse_query("http://www.test.com/?q=foo&q2&q3=x", &headers);
2190275970Scy	tt_int_op(r, ==, -1);
2191275970Scy	evhttp_clear_headers(&headers);
2192275970Scy
2193275970Scy	r = evhttp_parse_query("http://www.test.com/?q=&q2=&q3=", &headers);
2194275970Scy	tt_int_op(r, ==, 0);
2195275970Scy	tt_want(validate_header(&headers, "q", "") == 0);
2196275970Scy	tt_want(validate_header(&headers, "q2", "") == 0);
2197275970Scy	tt_want(validate_header(&headers, "q3", "") == 0);
2198275970Scy	evhttp_clear_headers(&headers);
2199275970Scy
2200275970Scyend:
2201275970Scy	evhttp_clear_headers(&headers);
2202275970Scy}
2203275970Scy
2204275970Scystatic void
2205275970Scyhttp_parse_uri_test(void *ptr)
2206275970Scy{
2207275970Scy	const int nonconform = (ptr != NULL);
2208275970Scy	const unsigned parse_flags =
2209275970Scy	    nonconform ? EVHTTP_URI_NONCONFORMANT : 0;
2210275970Scy	struct evhttp_uri *uri = NULL;
2211275970Scy	char url_tmp[4096];
2212275970Scy#define URI_PARSE(uri) \
2213275970Scy	evhttp_uri_parse_with_flags((uri), parse_flags)
2214275970Scy
2215275970Scy#define TT_URI(want) do { 						\
2216275970Scy	char *ret = evhttp_uri_join(uri, url_tmp, sizeof(url_tmp));	\
2217275970Scy	tt_want(ret != NULL);						\
2218275970Scy	tt_want(ret == url_tmp);					\
2219275970Scy	if (strcmp(ret,want) != 0)					\
2220275970Scy		TT_FAIL(("\"%s\" != \"%s\"",ret,want));			\
2221275970Scy	} while(0)
2222275970Scy
2223275970Scy	tt_want(evhttp_uri_join(NULL, 0, 0) == NULL);
2224275970Scy	tt_want(evhttp_uri_join(NULL, url_tmp, 0) == NULL);
2225275970Scy	tt_want(evhttp_uri_join(NULL, url_tmp, sizeof(url_tmp)) == NULL);
2226275970Scy
2227275970Scy	/* bad URIs: parsing */
2228275970Scy#define BAD(s) do {							\
2229275970Scy		if (URI_PARSE(s) != NULL)				\
2230275970Scy			TT_FAIL(("Expected error parsing \"%s\"",s));	\
2231275970Scy	} while(0)
2232275970Scy	/* Nonconformant URIs we can parse: parsing */
2233275970Scy#define NCF(s) do {							\
2234275970Scy		uri = URI_PARSE(s);					\
2235275970Scy		if (uri != NULL && !nonconform) {			\
2236275970Scy			TT_FAIL(("Expected error parsing \"%s\"",s));	\
2237275970Scy		} else if (uri == NULL && nonconform) {			\
2238275970Scy			TT_FAIL(("Couldn't parse nonconformant URI \"%s\"", \
2239275970Scy				s));					\
2240275970Scy		}							\
2241275970Scy		if (uri) {						\
2242275970Scy			tt_want(evhttp_uri_join(uri, url_tmp,		\
2243275970Scy				sizeof(url_tmp)));			\
2244275970Scy			evhttp_uri_free(uri);				\
2245275970Scy		}							\
2246275970Scy	} while(0)
2247275970Scy
2248275970Scy	NCF("http://www.test.com/ why hello");
2249275970Scy	NCF("http://www.test.com/why-hello\x01");
2250275970Scy	NCF("http://www.test.com/why-hello?\x01");
2251275970Scy	NCF("http://www.test.com/why-hello#\x01");
2252275970Scy	BAD("http://www.\x01.test.com/why-hello");
2253275970Scy	BAD("http://www.%7test.com/why-hello");
2254275970Scy	NCF("http://www.test.com/why-hell%7o");
2255275970Scy	BAD("h%3ttp://www.test.com/why-hello");
2256275970Scy	NCF("http://www.test.com/why-hello%7");
2257275970Scy	NCF("http://www.test.com/why-hell%7o");
2258275970Scy	NCF("http://www.test.com/foo?ba%r");
2259275970Scy	NCF("http://www.test.com/foo#ba%r");
2260275970Scy	BAD("99:99/foo");
2261275970Scy	BAD("http://www.test.com:999x/");
2262275970Scy	BAD("http://www.test.com:x/");
2263275970Scy	BAD("http://[hello-there]/");
2264275970Scy	BAD("http://[::1]]/");
2265275970Scy	BAD("http://[::1/");
2266275970Scy	BAD("http://[foob/");
2267275970Scy	BAD("http://[/");
2268275970Scy	BAD("http://[ffff:ffff:ffff:ffff:Ffff:ffff:ffff:"
2269275970Scy	            "ffff:ffff:ffff:ffff:ffff:ffff:ffff]/");
2270275970Scy	BAD("http://[vX.foo]/");
2271275970Scy	BAD("http://[vX.foo]/");
2272275970Scy	BAD("http://[v.foo]/");
2273275970Scy	BAD("http://[v5.fo%o]/");
2274275970Scy	BAD("http://[v5X]/");
2275275970Scy	BAD("http://[v5]/");
2276275970Scy	BAD("http://[]/");
2277275970Scy	BAD("http://f\x01red@www.example.com/");
2278275970Scy	BAD("http://f%0red@www.example.com/");
2279275970Scy	BAD("http://www.example.com:9999999999999999999999999999999999999/");
2280275970Scy	BAD("http://www.example.com:hihi/");
2281275970Scy	BAD("://www.example.com/");
2282275970Scy
2283275970Scy	/* bad URIs: joining */
2284275970Scy	uri = evhttp_uri_new();
2285275970Scy	tt_want(0==evhttp_uri_set_host(uri, "www.example.com"));
2286275970Scy	tt_want(evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)) != NULL);
2287275970Scy	/* not enough space: */
2288275970Scy	tt_want(evhttp_uri_join(uri, url_tmp, 3) == NULL);
2289275970Scy	/* host is set, but path doesn't start with "/": */
2290275970Scy	tt_want(0==evhttp_uri_set_path(uri, "hi_mom"));
2291275970Scy	tt_want(evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)) == NULL);
2292275970Scy	tt_want(evhttp_uri_join(uri, NULL, sizeof(url_tmp))==NULL);
2293275970Scy	tt_want(evhttp_uri_join(uri, url_tmp, 0)==NULL);
2294275970Scy	evhttp_uri_free(uri);
2295275970Scy	uri = URI_PARSE("mailto:foo@bar");
2296275970Scy	tt_want(uri != NULL);
2297275970Scy	tt_want(evhttp_uri_get_host(uri) == NULL);
2298275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2299275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2300275970Scy	tt_want(!strcmp(evhttp_uri_get_scheme(uri), "mailto"));
2301275970Scy	tt_want(!strcmp(evhttp_uri_get_path(uri), "foo@bar"));
2302275970Scy	tt_want(evhttp_uri_get_query(uri) == NULL);
2303275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2304275970Scy	TT_URI("mailto:foo@bar");
2305275970Scy	evhttp_uri_free(uri);
2306275970Scy
2307275970Scy	uri = evhttp_uri_new();
2308275970Scy	/* Bad URI usage: setting invalid values */
2309275970Scy	tt_want(-1 == evhttp_uri_set_scheme(uri,""));
2310275970Scy	tt_want(-1 == evhttp_uri_set_scheme(uri,"33"));
2311275970Scy	tt_want(-1 == evhttp_uri_set_scheme(uri,"hi!"));
2312275970Scy	tt_want(-1 == evhttp_uri_set_userinfo(uri,"hello@"));
2313275970Scy	tt_want(-1 == evhttp_uri_set_host(uri,"[1.2.3.4]"));
2314275970Scy	tt_want(-1 == evhttp_uri_set_host(uri,"["));
2315275970Scy	tt_want(-1 == evhttp_uri_set_host(uri,"www.[foo].com"));
2316275970Scy	tt_want(-1 == evhttp_uri_set_port(uri,-3));
2317275970Scy	tt_want(-1 == evhttp_uri_set_path(uri,"hello?world"));
2318275970Scy	tt_want(-1 == evhttp_uri_set_query(uri,"hello#world"));
2319275970Scy	tt_want(-1 == evhttp_uri_set_fragment(uri,"hello#world"));
2320275970Scy	/* Valid URI usage: setting valid values */
2321275970Scy	tt_want(0 == evhttp_uri_set_scheme(uri,"http"));
2322275970Scy	tt_want(0 == evhttp_uri_set_scheme(uri,NULL));
2323275970Scy	tt_want(0 == evhttp_uri_set_userinfo(uri,"username:pass"));
2324275970Scy	tt_want(0 == evhttp_uri_set_userinfo(uri,NULL));
2325275970Scy	tt_want(0 == evhttp_uri_set_host(uri,"www.example.com"));
2326275970Scy	tt_want(0 == evhttp_uri_set_host(uri,"1.2.3.4"));
2327275970Scy	tt_want(0 == evhttp_uri_set_host(uri,"[1:2:3:4::]"));
2328275970Scy	tt_want(0 == evhttp_uri_set_host(uri,"[v7.wobblewobble]"));
2329275970Scy	tt_want(0 == evhttp_uri_set_host(uri,NULL));
2330275970Scy	tt_want(0 == evhttp_uri_set_host(uri,""));
2331275970Scy	tt_want(0 == evhttp_uri_set_port(uri, -1));
2332275970Scy	tt_want(0 == evhttp_uri_set_port(uri, 80));
2333275970Scy	tt_want(0 == evhttp_uri_set_port(uri, 65535));
2334275970Scy	tt_want(0 == evhttp_uri_set_path(uri, ""));
2335275970Scy	tt_want(0 == evhttp_uri_set_path(uri, "/documents/public/index.html"));
2336275970Scy	tt_want(0 == evhttp_uri_set_path(uri, NULL));
2337275970Scy	tt_want(0 == evhttp_uri_set_query(uri, "key=val&key2=val2"));
2338275970Scy	tt_want(0 == evhttp_uri_set_query(uri, "keyvalblarg"));
2339275970Scy	tt_want(0 == evhttp_uri_set_query(uri, ""));
2340275970Scy	tt_want(0 == evhttp_uri_set_query(uri, NULL));
2341275970Scy	tt_want(0 == evhttp_uri_set_fragment(uri, ""));
2342275970Scy	tt_want(0 == evhttp_uri_set_fragment(uri, "here?i?am"));
2343275970Scy	tt_want(0 == evhttp_uri_set_fragment(uri, NULL));
2344275970Scy	evhttp_uri_free(uri);
2345275970Scy
2346275970Scy	/* Valid parsing */
2347275970Scy	uri = URI_PARSE("http://www.test.com/?q=t%33est");
2348275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
2349275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
2350275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
2351275970Scy	tt_want(strcmp(evhttp_uri_get_query(uri), "q=t%33est") == 0);
2352275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2353275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2354275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2355275970Scy	TT_URI("http://www.test.com/?q=t%33est");
2356275970Scy	evhttp_uri_free(uri);
2357275970Scy
2358275970Scy	uri = URI_PARSE("http://%77ww.test.com");
2359275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
2360275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "%77ww.test.com") == 0);
2361275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
2362275970Scy	tt_want(evhttp_uri_get_query(uri) == NULL);
2363275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2364275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2365275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2366275970Scy	TT_URI("http://%77ww.test.com");
2367275970Scy	evhttp_uri_free(uri);
2368275970Scy
2369275970Scy	uri = URI_PARSE("http://www.test.com?q=test");
2370275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
2371275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
2372275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
2373275970Scy	tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
2374275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2375275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2376275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2377275970Scy	TT_URI("http://www.test.com?q=test");
2378275970Scy	evhttp_uri_free(uri);
2379275970Scy
2380275970Scy	uri = URI_PARSE("http://www.test.com#fragment");
2381275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
2382275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
2383275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
2384275970Scy	tt_want(evhttp_uri_get_query(uri) == NULL);
2385275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2386275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2387275970Scy	tt_want_str_op(evhttp_uri_get_fragment(uri), ==, "fragment");
2388275970Scy	TT_URI("http://www.test.com#fragment");
2389275970Scy	evhttp_uri_free(uri);
2390275970Scy
2391275970Scy	uri = URI_PARSE("http://8000/");
2392275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
2393275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "8000") == 0);
2394275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
2395275970Scy	tt_want(evhttp_uri_get_query(uri) == NULL);
2396275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2397275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2398275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2399275970Scy	TT_URI("http://8000/");
2400275970Scy	evhttp_uri_free(uri);
2401275970Scy
2402275970Scy	uri = URI_PARSE("http://:8000/");
2403275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
2404275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0);
2405275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
2406275970Scy	tt_want(evhttp_uri_get_query(uri) == NULL);
2407275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2408275970Scy	tt_want(evhttp_uri_get_port(uri) == 8000);
2409275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2410275970Scy	TT_URI("http://:8000/");
2411275970Scy	evhttp_uri_free(uri);
2412275970Scy
2413275970Scy	uri = URI_PARSE("http://www.test.com:/"); /* empty port */
2414275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
2415275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
2416275970Scy	tt_want_str_op(evhttp_uri_get_path(uri), ==, "/");
2417275970Scy	tt_want(evhttp_uri_get_query(uri) == NULL);
2418275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2419275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2420275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2421275970Scy	TT_URI("http://www.test.com/");
2422275970Scy	evhttp_uri_free(uri);
2423275970Scy
2424275970Scy	uri = URI_PARSE("http://www.test.com:"); /* empty port 2 */
2425275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
2426275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
2427275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
2428275970Scy	tt_want(evhttp_uri_get_query(uri) == NULL);
2429275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2430275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2431275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2432275970Scy	TT_URI("http://www.test.com");
2433275970Scy	evhttp_uri_free(uri);
2434275970Scy
2435275970Scy	uri = URI_PARSE("ftp://www.test.com/?q=test");
2436275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
2437275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
2438275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
2439275970Scy	tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
2440275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2441275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2442275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2443275970Scy	TT_URI("ftp://www.test.com/?q=test");
2444275970Scy	evhttp_uri_free(uri);
2445275970Scy
2446275970Scy	uri = URI_PARSE("ftp://[::1]:999/?q=test");
2447275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
2448275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "[::1]") == 0);
2449275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
2450275970Scy	tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
2451275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2452275970Scy	tt_want(evhttp_uri_get_port(uri) == 999);
2453275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2454275970Scy	TT_URI("ftp://[::1]:999/?q=test");
2455275970Scy	evhttp_uri_free(uri);
2456275970Scy
2457275970Scy	uri = URI_PARSE("ftp://[ff00::127.0.0.1]/?q=test");
2458275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
2459275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "[ff00::127.0.0.1]") == 0);
2460275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
2461275970Scy	tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
2462275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2463275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2464275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2465275970Scy	TT_URI("ftp://[ff00::127.0.0.1]/?q=test");
2466275970Scy	evhttp_uri_free(uri);
2467275970Scy
2468275970Scy	uri = URI_PARSE("ftp://[v99.not_(any:time)_soon]/?q=test");
2469275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
2470275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "[v99.not_(any:time)_soon]") == 0);
2471275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
2472275970Scy	tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
2473275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2474275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2475275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2476275970Scy	TT_URI("ftp://[v99.not_(any:time)_soon]/?q=test");
2477275970Scy	evhttp_uri_free(uri);
2478275970Scy
2479275970Scy	uri = URI_PARSE("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment");
2480275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0);
2481275970Scy	tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user:pass") == 0);
2482275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0);
2483275970Scy	tt_want(evhttp_uri_get_port(uri) == 42);
2484275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
2485275970Scy	tt_want(strcmp(evhttp_uri_get_query(uri), "q=test&s=some+thing") == 0);
2486275970Scy	tt_want(strcmp(evhttp_uri_get_fragment(uri), "fragment") == 0);
2487275970Scy	TT_URI("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment");
2488275970Scy	evhttp_uri_free(uri);
2489275970Scy
2490275970Scy	uri = URI_PARSE("scheme://user@foo.com/#fragment");
2491275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0);
2492275970Scy	tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user") == 0);
2493275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0);
2494275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2495275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
2496275970Scy	tt_want(evhttp_uri_get_query(uri) == NULL);
2497275970Scy	tt_want(strcmp(evhttp_uri_get_fragment(uri), "fragment") == 0);
2498275970Scy	TT_URI("scheme://user@foo.com/#fragment");
2499275970Scy	evhttp_uri_free(uri);
2500275970Scy
2501275970Scy	uri = URI_PARSE("scheme://%75ser@foo.com/#frag@ment");
2502275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0);
2503275970Scy	tt_want(strcmp(evhttp_uri_get_userinfo(uri), "%75ser") == 0);
2504275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0);
2505275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2506275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
2507275970Scy	tt_want(evhttp_uri_get_query(uri) == NULL);
2508275970Scy	tt_want(strcmp(evhttp_uri_get_fragment(uri), "frag@ment") == 0);
2509275970Scy	TT_URI("scheme://%75ser@foo.com/#frag@ment");
2510275970Scy	evhttp_uri_free(uri);
2511275970Scy
2512275970Scy	uri = URI_PARSE("file:///some/path/to/the/file");
2513275970Scy	tt_want(strcmp(evhttp_uri_get_scheme(uri), "file") == 0);
2514275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2515275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0);
2516275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2517275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "/some/path/to/the/file") == 0);
2518275970Scy	tt_want(evhttp_uri_get_query(uri) == NULL);
2519275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2520275970Scy	TT_URI("file:///some/path/to/the/file");
2521275970Scy	evhttp_uri_free(uri);
2522275970Scy
2523275970Scy	uri = URI_PARSE("///some/path/to/the-file");
2524275970Scy	tt_want(uri != NULL);
2525275970Scy	tt_want(evhttp_uri_get_scheme(uri) == NULL);
2526275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2527275970Scy	tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0);
2528275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2529275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "/some/path/to/the-file") == 0);
2530275970Scy	tt_want(evhttp_uri_get_query(uri) == NULL);
2531275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2532275970Scy	TT_URI("///some/path/to/the-file");
2533275970Scy	evhttp_uri_free(uri);
2534275970Scy
2535275970Scy	uri = URI_PARSE("/s:ome/path/to/the-file?q=99#fred");
2536275970Scy	tt_want(uri != NULL);
2537275970Scy	tt_want(evhttp_uri_get_scheme(uri) == NULL);
2538275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2539275970Scy	tt_want(evhttp_uri_get_host(uri) == NULL);
2540275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2541275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "/s:ome/path/to/the-file") == 0);
2542275970Scy	tt_want(strcmp(evhttp_uri_get_query(uri), "q=99") == 0);
2543275970Scy	tt_want(strcmp(evhttp_uri_get_fragment(uri), "fred") == 0);
2544275970Scy	TT_URI("/s:ome/path/to/the-file?q=99#fred");
2545275970Scy	evhttp_uri_free(uri);
2546275970Scy
2547275970Scy	uri = URI_PARSE("relative/path/with/co:lon");
2548275970Scy	tt_want(uri != NULL);
2549275970Scy	tt_want(evhttp_uri_get_scheme(uri) == NULL);
2550275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2551275970Scy	tt_want(evhttp_uri_get_host(uri) == NULL);
2552275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2553275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "relative/path/with/co:lon") == 0);
2554275970Scy	tt_want(evhttp_uri_get_query(uri) == NULL);
2555275970Scy	tt_want(evhttp_uri_get_fragment(uri) == NULL);
2556275970Scy	TT_URI("relative/path/with/co:lon");
2557275970Scy	evhttp_uri_free(uri);
2558275970Scy
2559275970Scy	uri = URI_PARSE("bob?q=99&q2=q?33#fr?ed");
2560275970Scy	tt_want(uri != NULL);
2561275970Scy	tt_want(evhttp_uri_get_scheme(uri) == NULL);
2562275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2563275970Scy	tt_want(evhttp_uri_get_host(uri) == NULL);
2564275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2565275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "bob") == 0);
2566275970Scy	tt_want(strcmp(evhttp_uri_get_query(uri), "q=99&q2=q?33") == 0);
2567275970Scy	tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0);
2568275970Scy	TT_URI("bob?q=99&q2=q?33#fr?ed");
2569275970Scy	evhttp_uri_free(uri);
2570275970Scy
2571275970Scy	uri = URI_PARSE("#fr?ed");
2572275970Scy	tt_want(uri != NULL);
2573275970Scy	tt_want(evhttp_uri_get_scheme(uri) == NULL);
2574275970Scy	tt_want(evhttp_uri_get_userinfo(uri) == NULL);
2575275970Scy	tt_want(evhttp_uri_get_host(uri) == NULL);
2576275970Scy	tt_want(evhttp_uri_get_port(uri) == -1);
2577275970Scy	tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
2578275970Scy	tt_want(evhttp_uri_get_query(uri) == NULL);
2579275970Scy	tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0);
2580275970Scy	TT_URI("#fr?ed");
2581275970Scy	evhttp_uri_free(uri);
2582275970Scy#undef URI_PARSE
2583275970Scy#undef TT_URI
2584275970Scy#undef BAD
2585275970Scy}
2586275970Scy
2587275970Scystatic void
2588275970Scyhttp_uriencode_test(void *ptr)
2589275970Scy{
2590275970Scy	char *s=NULL, *s2=NULL;
2591275970Scy	size_t sz;
2592275970Scy	int bytes_decoded;
2593275970Scy
2594275970Scy#define ENC(from,want,plus) do {				\
2595275970Scy		s = evhttp_uriencode((from), -1, (plus));	\
2596275970Scy		tt_assert(s);					\
2597275970Scy		tt_str_op(s,==,(want));				\
2598275970Scy		sz = -1;					\
2599275970Scy		s2 = evhttp_uridecode((s), (plus), &sz);	\
2600275970Scy		tt_assert(s2);					\
2601275970Scy		tt_str_op(s2,==,(from));			\
2602275970Scy		tt_int_op(sz,==,strlen(from));			\
2603275970Scy		free(s);					\
2604275970Scy		free(s2);					\
2605275970Scy		s = s2 = NULL;					\
2606275970Scy	} while (0)
2607275970Scy
2608275970Scy#define DEC(from,want,dp) do {					\
2609275970Scy		s = evhttp_uridecode((from),(dp),&sz);		\
2610275970Scy		tt_assert(s);					\
2611275970Scy		tt_str_op(s,==,(want));				\
2612275970Scy		tt_int_op(sz,==,strlen(want));			\
2613275970Scy		free(s);					\
2614275970Scy		s = NULL;					\
2615275970Scy	} while (0)
2616275970Scy
2617275970Scy#define OLD_DEC(from,want)  do {				\
2618275970Scy		s = evhttp_decode_uri((from));			\
2619275970Scy		tt_assert(s);					\
2620275970Scy		tt_str_op(s,==,(want));				\
2621275970Scy		free(s);					\
2622275970Scy		s = NULL;					\
2623275970Scy	} while (0)
2624275970Scy
2625275970Scy
2626275970Scy      	ENC("Hello", "Hello",0);
2627275970Scy	ENC("99", "99",0);
2628275970Scy	ENC("", "",0);
2629275970Scy	ENC(
2630275970Scy	 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_",
2631275970Scy	 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_",0);
2632275970Scy	ENC(" ", "%20",0);
2633275970Scy	ENC(" ", "+",1);
2634275970Scy	ENC("\xff\xf0\xe0", "%FF%F0%E0",0);
2635275970Scy	ENC("\x01\x19", "%01%19",1);
2636275970Scy	ENC("http://www.ietf.org/rfc/rfc3986.txt",
2637275970Scy	    "http%3A%2F%2Fwww.ietf.org%2Frfc%2Frfc3986.txt",1);
2638275970Scy
2639275970Scy	ENC("1+2=3", "1%2B2%3D3",1);
2640275970Scy	ENC("1+2=3", "1%2B2%3D3",0);
2641275970Scy
2642275970Scy	/* Now try encoding with internal NULs. */
2643275970Scy	s = evhttp_uriencode("hello\0world", 11, 0);
2644275970Scy	tt_assert(s);
2645275970Scy	tt_str_op(s,==,"hello%00world");
2646275970Scy	free(s);
2647275970Scy	s = NULL;
2648275970Scy
2649275970Scy	/* Now try decoding just part of string. */
2650275970Scy	s = malloc(6 + 1 /* NUL byte */);
2651275970Scy	bytes_decoded = evhttp_decode_uri_internal("hello%20%20", 6, s, 0);
2652275970Scy	tt_assert(s);
2653275970Scy	tt_int_op(bytes_decoded,==,6);
2654275970Scy	tt_str_op(s,==,"hello%");
2655275970Scy	free(s);
2656275970Scy	s = NULL;
2657275970Scy
2658275970Scy	/* Now try out some decoding cases that we don't generate with
2659275970Scy	 * encode_uri: Make sure that malformed stuff doesn't crash... */
2660275970Scy	DEC("%%xhello th+ere \xff",
2661275970Scy	    "%%xhello th+ere \xff", 0);
2662275970Scy	/* Make sure plus decoding works */
2663275970Scy	DEC("plus+should%20work+", "plus should work ",1);
2664275970Scy	/* Try some lowercase hex */
2665275970Scy	DEC("%f0%a0%b0", "\xf0\xa0\xb0",1);
2666275970Scy
2667275970Scy	/* Try an internal NUL. */
2668275970Scy	sz = 0;
2669275970Scy	s = evhttp_uridecode("%00%00x%00%00", 1, &sz);
2670275970Scy	tt_int_op(sz,==,5);
2671275970Scy	tt_assert(!memcmp(s, "\0\0x\0\0", 5));
2672275970Scy	free(s);
2673275970Scy	s = NULL;
2674275970Scy
2675275970Scy	/* Try with size == NULL */
2676275970Scy	sz = 0;
2677275970Scy	s = evhttp_uridecode("%00%00x%00%00", 1, NULL);
2678275970Scy	tt_assert(!memcmp(s, "\0\0x\0\0", 5));
2679275970Scy	free(s);
2680275970Scy	s = NULL;
2681275970Scy
2682275970Scy	/* Test out the crazy old behavior of the deprecated
2683275970Scy	 * evhttp_decode_uri */
2684275970Scy	OLD_DEC("http://example.com/normal+path/?key=val+with+spaces",
2685275970Scy	        "http://example.com/normal+path/?key=val with spaces");
2686275970Scy
2687275970Scyend:
2688275970Scy	if (s)
2689275970Scy		free(s);
2690275970Scy	if (s2)
2691275970Scy		free(s2);
2692275970Scy#undef ENC
2693275970Scy#undef DEC
2694275970Scy#undef OLD_DEC
2695275970Scy}
2696275970Scy
2697275970Scystatic void
2698275970Scyhttp_base_test(void *ptr)
2699275970Scy{
2700275970Scy	struct event_base *base = NULL;
2701275970Scy	struct bufferevent *bev;
2702275970Scy	evutil_socket_t fd;
2703275970Scy	const char *http_request;
2704275970Scy	ev_uint16_t port = 0;
2705275970Scy
2706275970Scy	test_ok = 0;
2707275970Scy	base = event_base_new();
2708275970Scy	tt_assert(base);
2709275970Scy	http = http_setup(&port, base, 0);
2710275970Scy
2711275970Scy	fd = http_connect("127.0.0.1", port);
2712275970Scy	tt_int_op(fd, >=, 0);
2713275970Scy
2714275970Scy	/* Stupid thing to send a request */
2715275970Scy	bev = bufferevent_socket_new(base, fd, 0);
2716275970Scy	bufferevent_setcb(bev, http_readcb, http_writecb,
2717275970Scy	    http_errorcb, base);
2718275970Scy	bufferevent_base_set(base, bev);
2719275970Scy
2720275970Scy	http_request =
2721275970Scy	    "GET /test HTTP/1.1\r\n"
2722275970Scy	    "Host: somehost\r\n"
2723275970Scy	    "Connection: close\r\n"
2724275970Scy	    "\r\n";
2725275970Scy
2726275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
2727275970Scy
2728275970Scy	event_base_dispatch(base);
2729275970Scy
2730275970Scy	bufferevent_free(bev);
2731275970Scy	evutil_closesocket(fd);
2732275970Scy
2733275970Scy	evhttp_free(http);
2734275970Scy
2735275970Scy	tt_int_op(test_ok, ==, 2);
2736275970Scy
2737275970Scyend:
2738275970Scy	if (base)
2739275970Scy		event_base_free(base);
2740275970Scy}
2741275970Scy
2742275970Scy/*
2743275970Scy * the server is just going to close the connection if it times out during
2744275970Scy * reading the headers.
2745275970Scy */
2746275970Scy
2747275970Scystatic void
2748275970Scyhttp_incomplete_readcb(struct bufferevent *bev, void *arg)
2749275970Scy{
2750275970Scy	test_ok = -1;
2751275970Scy	event_base_loopexit(exit_base,NULL);
2752275970Scy}
2753275970Scy
2754275970Scystatic void
2755275970Scyhttp_incomplete_errorcb(struct bufferevent *bev, short what, void *arg)
2756275970Scy{
2757275970Scy	if (what == (BEV_EVENT_READING|BEV_EVENT_EOF))
2758275970Scy		test_ok++;
2759275970Scy	else
2760275970Scy		test_ok = -2;
2761275970Scy	event_base_loopexit(exit_base,NULL);
2762275970Scy}
2763275970Scy
2764275970Scystatic void
2765275970Scyhttp_incomplete_writecb(struct bufferevent *bev, void *arg)
2766275970Scy{
2767275970Scy	if (arg != NULL) {
2768275970Scy		evutil_socket_t fd = *(evutil_socket_t *)arg;
2769275970Scy		/* terminate the write side to simulate EOF */
2770275970Scy		shutdown(fd, SHUT_WR);
2771275970Scy	}
2772275970Scy	if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) {
2773275970Scy		/* enable reading of the reply */
2774275970Scy		bufferevent_enable(bev, EV_READ);
2775275970Scy		test_ok++;
2776275970Scy	}
2777275970Scy}
2778275970Scy
2779275970Scystatic void
2780275970Scyhttp_incomplete_test_(struct basic_test_data *data, int use_timeout)
2781275970Scy{
2782275970Scy	struct bufferevent *bev;
2783275970Scy	evutil_socket_t fd;
2784275970Scy	const char *http_request;
2785275970Scy	ev_uint16_t port = 0;
2786275970Scy	struct timeval tv_start, tv_end;
2787275970Scy
2788275970Scy	exit_base = data->base;
2789275970Scy
2790275970Scy	test_ok = 0;
2791275970Scy
2792275970Scy	http = http_setup(&port, data->base, 0);
2793275970Scy	evhttp_set_timeout(http, 1);
2794275970Scy
2795275970Scy	fd = http_connect("127.0.0.1", port);
2796275970Scy	tt_int_op(fd, >=, 0);
2797275970Scy
2798275970Scy	/* Stupid thing to send a request */
2799275970Scy	bev = bufferevent_socket_new(data->base, fd, 0);
2800275970Scy	bufferevent_setcb(bev,
2801275970Scy	    http_incomplete_readcb, http_incomplete_writecb,
2802275970Scy	    http_incomplete_errorcb, use_timeout ? NULL : &fd);
2803275970Scy
2804275970Scy	http_request =
2805275970Scy	    "GET /test HTTP/1.1\r\n"
2806275970Scy	    "Host: somehost\r\n";
2807275970Scy
2808275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
2809275970Scy
2810275970Scy	evutil_gettimeofday(&tv_start, NULL);
2811275970Scy
2812275970Scy	event_base_dispatch(data->base);
2813275970Scy
2814275970Scy	evutil_gettimeofday(&tv_end, NULL);
2815275970Scy	evutil_timersub(&tv_end, &tv_start, &tv_end);
2816275970Scy
2817275970Scy	bufferevent_free(bev);
2818275970Scy	if (use_timeout) {
2819275970Scy		evutil_closesocket(fd);
2820282408Scy		fd = -1;
2821275970Scy	}
2822275970Scy
2823275970Scy	evhttp_free(http);
2824275970Scy
2825275970Scy	if (use_timeout && tv_end.tv_sec >= 3) {
2826275970Scy		tt_abort_msg("time");
2827275970Scy	} else if (!use_timeout && tv_end.tv_sec >= 1) {
2828275970Scy		/* we should be done immediately */
2829275970Scy		tt_abort_msg("time");
2830275970Scy	}
2831275970Scy
2832275970Scy	tt_int_op(test_ok, ==, 2);
2833275970Scy end:
2834275970Scy	if (fd >= 0)
2835275970Scy		evutil_closesocket(fd);
2836275970Scy}
2837275970Scystatic void
2838275970Scyhttp_incomplete_test(void *arg)
2839275970Scy{
2840275970Scy	http_incomplete_test_(arg, 0);
2841275970Scy}
2842275970Scystatic void
2843275970Scyhttp_incomplete_timeout_test(void *arg)
2844275970Scy{
2845275970Scy	http_incomplete_test_(arg, 1);
2846275970Scy}
2847275970Scy
2848275970Scy/*
2849275970Scy * the server is going to reply with chunked data.
2850275970Scy */
2851275970Scy
2852275970Scystatic void
2853275970Scyhttp_chunked_readcb(struct bufferevent *bev, void *arg)
2854275970Scy{
2855275970Scy	/* nothing here */
2856275970Scy}
2857275970Scy
2858275970Scystatic void
2859275970Scyhttp_chunked_errorcb(struct bufferevent *bev, short what, void *arg)
2860275970Scy{
2861275970Scy	struct evhttp_request *req = NULL;
2862275970Scy
2863275970Scy	if (!test_ok)
2864275970Scy		goto out;
2865275970Scy
2866275970Scy	test_ok = -1;
2867275970Scy
2868275970Scy	if ((what & BEV_EVENT_EOF) != 0) {
2869275970Scy		const char *header;
2870275970Scy		enum message_read_status done;
2871275970Scy		req = evhttp_request_new(NULL, NULL);
2872275970Scy
2873275970Scy		/* req->kind = EVHTTP_RESPONSE; */
2874275970Scy		done = evhttp_parse_firstline_(req, bufferevent_get_input(bev));
2875275970Scy		if (done != ALL_DATA_READ)
2876275970Scy			goto out;
2877275970Scy
2878275970Scy		done = evhttp_parse_headers_(req, bufferevent_get_input(bev));
2879275970Scy		if (done != ALL_DATA_READ)
2880275970Scy			goto out;
2881275970Scy
2882275970Scy		header = evhttp_find_header(evhttp_request_get_input_headers(req), "Transfer-Encoding");
2883275970Scy		if (header == NULL || strcmp(header, "chunked"))
2884275970Scy			goto out;
2885275970Scy
2886275970Scy		header = evhttp_find_header(evhttp_request_get_input_headers(req), "Connection");
2887275970Scy		if (header == NULL || strcmp(header, "close"))
2888275970Scy			goto out;
2889275970Scy
2890275970Scy		header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF);
2891275970Scy		if (header == NULL)
2892275970Scy			goto out;
2893275970Scy		/* 13 chars */
2894275970Scy		if (strcmp(header, "d")) {
2895275970Scy			free((void*)header);
2896275970Scy			goto out;
2897275970Scy		}
2898275970Scy		free((void*)header);
2899275970Scy
2900275970Scy		if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 13),
2901275970Scy			"This is funny", 13))
2902275970Scy			goto out;
2903275970Scy
2904275970Scy		evbuffer_drain(bufferevent_get_input(bev), 13 + 2);
2905275970Scy
2906275970Scy		header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF);
2907275970Scy		if (header == NULL)
2908275970Scy			goto out;
2909275970Scy		/* 18 chars */
2910275970Scy		if (strcmp(header, "12"))
2911275970Scy			goto out;
2912275970Scy		free((char *)header);
2913275970Scy
2914275970Scy		if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 18),
2915275970Scy			"but not hilarious.", 18))
2916275970Scy			goto out;
2917275970Scy
2918275970Scy		evbuffer_drain(bufferevent_get_input(bev), 18 + 2);
2919275970Scy
2920275970Scy		header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF);
2921275970Scy		if (header == NULL)
2922275970Scy			goto out;
2923275970Scy		/* 8 chars */
2924275970Scy		if (strcmp(header, "8")) {
2925275970Scy			free((void*)header);
2926275970Scy			goto out;
2927275970Scy		}
2928275970Scy		free((char *)header);
2929275970Scy
2930275970Scy		if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 8),
2931275970Scy			"bwv 1052.", 8))
2932275970Scy			goto out;
2933275970Scy
2934275970Scy		evbuffer_drain(bufferevent_get_input(bev), 8 + 2);
2935275970Scy
2936275970Scy		header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF);
2937275970Scy		if (header == NULL)
2938275970Scy			goto out;
2939275970Scy		/* 0 chars */
2940275970Scy		if (strcmp(header, "0")) {
2941275970Scy			free((void*)header);
2942275970Scy			goto out;
2943275970Scy		}
2944275970Scy		free((void *)header);
2945275970Scy
2946275970Scy		test_ok = 2;
2947275970Scy	}
2948275970Scy
2949275970Scyout:
2950275970Scy	if (req)
2951275970Scy		evhttp_request_free(req);
2952275970Scy
2953275970Scy	event_base_loopexit(arg, NULL);
2954275970Scy}
2955275970Scy
2956275970Scystatic void
2957275970Scyhttp_chunked_writecb(struct bufferevent *bev, void *arg)
2958275970Scy{
2959275970Scy	if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) {
2960275970Scy		/* enable reading of the reply */
2961275970Scy		bufferevent_enable(bev, EV_READ);
2962275970Scy		test_ok++;
2963275970Scy	}
2964275970Scy}
2965275970Scy
2966275970Scystatic void
2967275970Scyhttp_chunked_request_done(struct evhttp_request *req, void *arg)
2968275970Scy{
2969275970Scy	if (evhttp_request_get_response_code(req) != HTTP_OK) {
2970275970Scy		fprintf(stderr, "FAILED\n");
2971275970Scy		exit(1);
2972275970Scy	}
2973275970Scy
2974275970Scy	if (evhttp_find_header(evhttp_request_get_input_headers(req),
2975275970Scy		"Transfer-Encoding") == NULL) {
2976275970Scy		fprintf(stderr, "FAILED\n");
2977275970Scy		exit(1);
2978275970Scy	}
2979275970Scy
2980275970Scy	if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 13 + 18 + 8) {
2981275970Scy		fprintf(stderr, "FAILED\n");
2982275970Scy		exit(1);
2983275970Scy	}
2984275970Scy
2985275970Scy	if (strncmp((char *)evbuffer_pullup(evhttp_request_get_input_buffer(req), 13 + 18 + 8),
2986275970Scy		"This is funnybut not hilarious.bwv 1052",
2987275970Scy		13 + 18 + 8)) {
2988275970Scy		fprintf(stderr, "FAILED\n");
2989275970Scy		exit(1);
2990275970Scy	}
2991275970Scy
2992275970Scy	test_ok = 1;
2993275970Scy	event_base_loopexit(arg, NULL);
2994275970Scy}
2995275970Scy
2996275970Scystatic void
2997275970Scyhttp_chunk_out_test(void *arg)
2998275970Scy{
2999275970Scy	struct basic_test_data *data = arg;
3000275970Scy	struct bufferevent *bev;
3001275970Scy	evutil_socket_t fd;
3002275970Scy	const char *http_request;
3003275970Scy	ev_uint16_t port = 0;
3004275970Scy	struct timeval tv_start, tv_end;
3005275970Scy	struct evhttp_connection *evcon = NULL;
3006275970Scy	struct evhttp_request *req = NULL;
3007275970Scy	int i;
3008275970Scy
3009275970Scy	exit_base = data->base;
3010275970Scy	test_ok = 0;
3011275970Scy
3012275970Scy	http = http_setup(&port, data->base, 0);
3013275970Scy
3014275970Scy	fd = http_connect("127.0.0.1", port);
3015275970Scy
3016275970Scy	/* Stupid thing to send a request */
3017275970Scy	bev = bufferevent_socket_new(data->base, fd, 0);
3018275970Scy	bufferevent_setcb(bev,
3019275970Scy	    http_chunked_readcb, http_chunked_writecb,
3020275970Scy	    http_chunked_errorcb, data->base);
3021275970Scy
3022275970Scy	http_request =
3023275970Scy	    "GET /chunked HTTP/1.1\r\n"
3024275970Scy	    "Host: somehost\r\n"
3025275970Scy	    "Connection: close\r\n"
3026275970Scy	    "\r\n";
3027275970Scy
3028275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
3029275970Scy
3030275970Scy	evutil_gettimeofday(&tv_start, NULL);
3031275970Scy
3032275970Scy	event_base_dispatch(data->base);
3033275970Scy
3034275970Scy	bufferevent_free(bev);
3035275970Scy
3036275970Scy	evutil_gettimeofday(&tv_end, NULL);
3037275970Scy	evutil_timersub(&tv_end, &tv_start, &tv_end);
3038275970Scy
3039275970Scy	tt_int_op(tv_end.tv_sec, <, 1);
3040275970Scy
3041275970Scy	tt_int_op(test_ok, ==, 2);
3042275970Scy
3043275970Scy	/* now try again with the regular connection object */
3044275970Scy	evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
3045275970Scy	tt_assert(evcon);
3046275970Scy
3047275970Scy	/* make two requests to check the keepalive behavior */
3048275970Scy	for (i = 0; i < 2; i++) {
3049275970Scy		test_ok = 0;
3050275970Scy		req = evhttp_request_new(http_chunked_request_done,data->base);
3051275970Scy
3052275970Scy		/* Add the information that we care about */
3053275970Scy		evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
3054275970Scy
3055275970Scy		/* We give ownership of the request to the connection */
3056275970Scy		if (evhttp_make_request(evcon, req,
3057275970Scy			EVHTTP_REQ_GET, "/chunked") == -1) {
3058275970Scy			tt_abort_msg("Couldn't make request");
3059275970Scy		}
3060275970Scy
3061275970Scy		event_base_dispatch(data->base);
3062275970Scy
3063275970Scy		tt_assert(test_ok == 1);
3064275970Scy	}
3065275970Scy
3066275970Scy end:
3067275970Scy	if (evcon)
3068275970Scy		evhttp_connection_free(evcon);
3069275970Scy	if (http)
3070275970Scy		evhttp_free(http);
3071275970Scy}
3072275970Scy
3073275970Scystatic void
3074275970Scyhttp_stream_out_test(void *arg)
3075275970Scy{
3076275970Scy	struct basic_test_data *data = arg;
3077275970Scy	ev_uint16_t port = 0;
3078275970Scy	struct evhttp_connection *evcon = NULL;
3079275970Scy	struct evhttp_request *req = NULL;
3080275970Scy
3081275970Scy	test_ok = 0;
3082275970Scy	exit_base = data->base;
3083275970Scy
3084275970Scy	http = http_setup(&port, data->base, 0);
3085275970Scy
3086275970Scy	evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
3087275970Scy	tt_assert(evcon);
3088275970Scy
3089275970Scy	/*
3090275970Scy	 * At this point, we want to schedule a request to the HTTP
3091275970Scy	 * server using our make request method.
3092275970Scy	 */
3093275970Scy
3094275970Scy	req = evhttp_request_new(http_request_done,
3095275970Scy	    (void *)"This is funnybut not hilarious.bwv 1052");
3096275970Scy
3097275970Scy	/* Add the information that we care about */
3098275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
3099275970Scy
3100275970Scy	/* We give ownership of the request to the connection */
3101275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/streamed")
3102275970Scy	    == -1) {
3103275970Scy		tt_abort_msg("Couldn't make request");
3104275970Scy	}
3105275970Scy
3106275970Scy	event_base_dispatch(data->base);
3107275970Scy
3108275970Scy end:
3109275970Scy	if (evcon)
3110275970Scy		evhttp_connection_free(evcon);
3111275970Scy	if (http)
3112275970Scy		evhttp_free(http);
3113275970Scy}
3114275970Scy
3115275970Scystatic void
3116275970Scyhttp_stream_in_chunk(struct evhttp_request *req, void *arg)
3117275970Scy{
3118275970Scy	struct evbuffer *reply = arg;
3119275970Scy
3120275970Scy	if (evhttp_request_get_response_code(req) != HTTP_OK) {
3121275970Scy		fprintf(stderr, "FAILED\n");
3122275970Scy		exit(1);
3123275970Scy	}
3124275970Scy
3125275970Scy	evbuffer_add_buffer(reply, evhttp_request_get_input_buffer(req));
3126275970Scy}
3127275970Scy
3128275970Scystatic void
3129275970Scyhttp_stream_in_done(struct evhttp_request *req, void *arg)
3130275970Scy{
3131275970Scy	if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 0) {
3132275970Scy		fprintf(stderr, "FAILED\n");
3133275970Scy		exit(1);
3134275970Scy	}
3135275970Scy
3136275970Scy	event_base_loopexit(exit_base, NULL);
3137275970Scy}
3138275970Scy
3139275970Scy/**
3140275970Scy * Makes a request and reads the response in chunks.
3141275970Scy */
3142275970Scystatic void
3143275970Scyhttp_stream_in_test_(struct basic_test_data *data, char const *url,
3144275970Scy    size_t expected_len, char const *expected)
3145275970Scy{
3146275970Scy	struct evhttp_connection *evcon;
3147275970Scy	struct evbuffer *reply = evbuffer_new();
3148275970Scy	struct evhttp_request *req = NULL;
3149275970Scy	ev_uint16_t port = 0;
3150275970Scy
3151275970Scy	exit_base = data->base;
3152275970Scy	http = http_setup(&port, data->base, 0);
3153275970Scy
3154275970Scy	evcon = evhttp_connection_base_new(data->base, NULL,"127.0.0.1", port);
3155275970Scy	tt_assert(evcon);
3156275970Scy
3157275970Scy	req = evhttp_request_new(http_stream_in_done, reply);
3158275970Scy	evhttp_request_set_chunked_cb(req, http_stream_in_chunk);
3159275970Scy
3160275970Scy	/* We give ownership of the request to the connection */
3161275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, url) == -1) {
3162275970Scy		tt_abort_msg("Couldn't make request");
3163275970Scy	}
3164275970Scy
3165275970Scy	event_base_dispatch(data->base);
3166275970Scy
3167275970Scy	if (evbuffer_get_length(reply) != expected_len) {
3168275970Scy		TT_DIE(("reply length %lu; expected %lu; FAILED (%s)\n",
3169275970Scy				(unsigned long)evbuffer_get_length(reply),
3170275970Scy				(unsigned long)expected_len,
3171275970Scy				(char*)evbuffer_pullup(reply, -1)));
3172275970Scy	}
3173275970Scy
3174275970Scy	if (memcmp(evbuffer_pullup(reply, -1), expected, expected_len) != 0) {
3175275970Scy		tt_abort_msg("Memory mismatch");
3176275970Scy	}
3177275970Scy
3178275970Scy	test_ok = 1;
3179275970Scy end:
3180275970Scy	if (reply)
3181275970Scy		evbuffer_free(reply);
3182275970Scy	if (evcon)
3183275970Scy		evhttp_connection_free(evcon);
3184275970Scy	if (http)
3185275970Scy		evhttp_free(http);
3186275970Scy}
3187275970Scy
3188275970Scystatic void
3189275970Scyhttp_stream_in_test(void *arg)
3190275970Scy{
3191275970Scy	http_stream_in_test_(arg, "/chunked", 13 + 18 + 8,
3192275970Scy	    "This is funnybut not hilarious.bwv 1052");
3193275970Scy
3194275970Scy	http_stream_in_test_(arg, "/test", strlen(BASIC_REQUEST_BODY),
3195275970Scy	    BASIC_REQUEST_BODY);
3196275970Scy}
3197275970Scy
3198275970Scystatic void
3199275970Scyhttp_stream_in_cancel_chunk(struct evhttp_request *req, void *arg)
3200275970Scy{
3201275970Scy	tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_OK);
3202275970Scy
3203275970Scy end:
3204275970Scy	evhttp_cancel_request(req);
3205275970Scy	event_base_loopexit(arg, NULL);
3206275970Scy}
3207275970Scy
3208275970Scystatic void
3209275970Scyhttp_stream_in_cancel_done(struct evhttp_request *req, void *arg)
3210275970Scy{
3211275970Scy	/* should never be called */
3212275970Scy	tt_fail_msg("In cancel done");
3213275970Scy}
3214275970Scy
3215275970Scystatic void
3216275970Scyhttp_stream_in_cancel_test(void *arg)
3217275970Scy{
3218275970Scy	struct basic_test_data *data = arg;
3219275970Scy	struct evhttp_connection *evcon;
3220275970Scy	struct evhttp_request *req = NULL;
3221275970Scy	ev_uint16_t port = 0;
3222275970Scy
3223275970Scy	http = http_setup(&port, data->base, 0);
3224275970Scy
3225275970Scy	evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
3226275970Scy	tt_assert(evcon);
3227275970Scy
3228275970Scy	req = evhttp_request_new(http_stream_in_cancel_done, data->base);
3229275970Scy	evhttp_request_set_chunked_cb(req, http_stream_in_cancel_chunk);
3230275970Scy
3231275970Scy	/* We give ownership of the request to the connection */
3232275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/chunked") == -1) {
3233275970Scy		tt_abort_msg("Couldn't make request");
3234275970Scy	}
3235275970Scy
3236275970Scy	event_base_dispatch(data->base);
3237275970Scy
3238275970Scy	test_ok = 1;
3239275970Scy end:
3240275970Scy	evhttp_connection_free(evcon);
3241275970Scy	evhttp_free(http);
3242275970Scy
3243275970Scy}
3244275970Scy
3245275970Scystatic void
3246275970Scyhttp_connection_fail_done(struct evhttp_request *req, void *arg)
3247275970Scy{
3248275970Scy       struct evhttp_connection *evcon = arg;
3249275970Scy       struct event_base *base = evhttp_connection_get_base(evcon);
3250275970Scy
3251275970Scy       /* An ENETUNREACH error results in an unrecoverable
3252275970Scy        * evhttp_connection error (see evhttp_connection_fail_()).  The
3253275970Scy        * connection will be reset, and the user will be notified with a NULL
3254275970Scy        * req parameter. */
3255275970Scy       tt_assert(!req);
3256275970Scy
3257275970Scy       evhttp_connection_free(evcon);
3258275970Scy
3259275970Scy       test_ok = 1;
3260275970Scy
3261275970Scy end:
3262275970Scy       event_base_loopexit(base, NULL);
3263275970Scy}
3264275970Scy
3265275970Scy/* Test unrecoverable evhttp_connection errors by generating an ENETUNREACH
3266275970Scy * error on connection. */
3267275970Scystatic void
3268275970Scyhttp_connection_fail_test(void *arg)
3269275970Scy{
3270275970Scy       struct basic_test_data *data = arg;
3271275970Scy       ev_uint16_t port = 0;
3272275970Scy       struct evhttp_connection *evcon = NULL;
3273275970Scy       struct evhttp_request *req = NULL;
3274275970Scy
3275275970Scy       exit_base = data->base;
3276275970Scy       test_ok = 0;
3277275970Scy
3278275970Scy       /* auto detect a port */
3279275970Scy       http = http_setup(&port, data->base, 0);
3280275970Scy       evhttp_free(http);
3281275970Scy       http = NULL;
3282275970Scy
3283275970Scy       /* Pick an unroutable address.  This administratively scoped multicast
3284275970Scy	* address should do when working with TCP. */
3285275970Scy       evcon = evhttp_connection_base_new(data->base, NULL, "239.10.20.30", 80);
3286275970Scy       tt_assert(evcon);
3287275970Scy
3288275970Scy       /*
3289275970Scy        * At this point, we want to schedule an HTTP GET request
3290275970Scy        * server using our make request method.
3291275970Scy        */
3292275970Scy
3293275970Scy       req = evhttp_request_new(http_connection_fail_done, evcon);
3294275970Scy       tt_assert(req);
3295275970Scy
3296275970Scy       if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/") == -1) {
3297275970Scy               tt_abort_msg("Couldn't make request");
3298275970Scy       }
3299275970Scy
3300275970Scy       event_base_dispatch(data->base);
3301275970Scy
3302275970Scy       tt_int_op(test_ok, ==, 1);
3303275970Scy
3304275970Scy end:
3305275970Scy        ;
3306275970Scy}
3307275970Scy
3308275970Scystatic void
3309275970Scyhttp_connection_retry_done(struct evhttp_request *req, void *arg)
3310275970Scy{
3311275970Scy	tt_assert(req);
3312275970Scy	tt_int_op(evhttp_request_get_response_code(req), !=, HTTP_OK);
3313275970Scy	if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") != NULL) {
3314275970Scy		tt_abort_msg("(content type)\n");
3315275970Scy	}
3316275970Scy
3317275970Scy	tt_uint_op(evbuffer_get_length(evhttp_request_get_input_buffer(req)), ==, 0);
3318275970Scy
3319275970Scy	test_ok = 1;
3320275970Scy end:
3321275970Scy	event_base_loopexit(arg,NULL);
3322275970Scy}
3323275970Scy
3324275970Scystatic struct event_base *http_make_web_server_base=NULL;
3325275970Scystatic void
3326275970Scyhttp_make_web_server(evutil_socket_t fd, short what, void *arg)
3327275970Scy{
3328275970Scy	ev_uint16_t port = *(ev_uint16_t*)arg;
3329275970Scy	http = http_setup(&port, http_make_web_server_base, 0);
3330275970Scy}
3331275970Scy
3332275970Scystatic void
3333275970Scyhttp_connection_retry_test(void *arg)
3334275970Scy{
3335275970Scy	struct basic_test_data *data = arg;
3336275970Scy	ev_uint16_t port = 0;
3337275970Scy	struct evhttp_connection *evcon = NULL;
3338275970Scy	struct evhttp_request *req = NULL;
3339275970Scy	struct timeval tv, tv_start, tv_end;
3340275970Scy
3341275970Scy	exit_base = data->base;
3342275970Scy	test_ok = 0;
3343275970Scy
3344275970Scy	/* auto detect a port */
3345275970Scy	http = http_setup(&port, data->base, 0);
3346275970Scy	evhttp_free(http);
3347275970Scy	http = NULL;
3348275970Scy
3349275970Scy	evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
3350275970Scy	tt_assert(evcon);
3351275970Scy
3352275970Scy	evhttp_connection_set_timeout(evcon, 1);
3353275970Scy	/* also bind to local host */
3354275970Scy	evhttp_connection_set_local_address(evcon, "127.0.0.1");
3355275970Scy
3356275970Scy	/*
3357275970Scy	 * At this point, we want to schedule an HTTP GET request
3358275970Scy	 * server using our make request method.
3359275970Scy	 */
3360275970Scy
3361275970Scy	req = evhttp_request_new(http_connection_retry_done, data->base);
3362275970Scy	tt_assert(req);
3363275970Scy
3364275970Scy	/* Add the information that we care about */
3365275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
3366275970Scy
3367275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
3368275970Scy		"/?arg=val") == -1) {
3369275970Scy		tt_abort_msg("Couldn't make request");
3370275970Scy	}
3371275970Scy
3372275970Scy	evutil_gettimeofday(&tv_start, NULL);
3373275970Scy	event_base_dispatch(data->base);
3374275970Scy	evutil_gettimeofday(&tv_end, NULL);
3375275970Scy	evutil_timersub(&tv_end, &tv_start, &tv_end);
3376275970Scy	tt_int_op(tv_end.tv_sec, <, 1);
3377275970Scy
3378275970Scy	tt_int_op(test_ok, ==, 1);
3379275970Scy
3380275970Scy	/*
3381275970Scy	 * now test the same but with retries
3382275970Scy	 */
3383275970Scy	test_ok = 0;
3384275970Scy
3385275970Scy	{
3386275970Scy		const struct timeval tv_timeout = { 0, 500000 };
3387275970Scy		const struct timeval tv_retry = { 0, 500000 };
3388275970Scy		evhttp_connection_set_timeout_tv(evcon, &tv_timeout);
3389275970Scy		evhttp_connection_set_initial_retry_tv(evcon, &tv_retry);
3390275970Scy	}
3391275970Scy	evhttp_connection_set_retries(evcon, 1);
3392275970Scy
3393275970Scy	req = evhttp_request_new(http_connection_retry_done, data->base);
3394275970Scy	tt_assert(req);
3395275970Scy
3396275970Scy	/* Add the information that we care about */
3397275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
3398275970Scy
3399275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
3400275970Scy		"/?arg=val") == -1) {
3401275970Scy		tt_abort_msg("Couldn't make request");
3402275970Scy	}
3403275970Scy
3404275970Scy	evutil_gettimeofday(&tv_start, NULL);
3405275970Scy	event_base_dispatch(data->base);
3406275970Scy	evutil_gettimeofday(&tv_end, NULL);
3407275970Scy
3408275970Scy	/* fails fast, .5 sec to wait to retry, fails fast again. */
3409275970Scy	test_timeval_diff_leq(&tv_start, &tv_end, 500, 200);
3410275970Scy
3411275970Scy	tt_assert(test_ok == 1);
3412275970Scy
3413275970Scy	/*
3414275970Scy	 * now test the same but with retries and give it a web server
3415275970Scy	 * at the end
3416275970Scy	 */
3417275970Scy	test_ok = 0;
3418275970Scy
3419275970Scy	evhttp_connection_set_timeout(evcon, 1);
3420275970Scy	evhttp_connection_set_retries(evcon, 3);
3421275970Scy
3422275970Scy	req = evhttp_request_new(http_dispatcher_test_done, data->base);
3423275970Scy	tt_assert(req);
3424275970Scy
3425275970Scy	/* Add the information that we care about */
3426275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
3427275970Scy
3428275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
3429275970Scy		"/?arg=val") == -1) {
3430275970Scy		tt_abort_msg("Couldn't make request");
3431275970Scy	}
3432275970Scy
3433275970Scy	/* start up a web server .2 seconds after the connection tried
3434275970Scy	 * to send a request
3435275970Scy	 */
3436275970Scy	evutil_timerclear(&tv);
3437275970Scy	tv.tv_usec = 200000;
3438275970Scy	http_make_web_server_base = data->base;
3439275970Scy	event_base_once(data->base, -1, EV_TIMEOUT, http_make_web_server, &port, &tv);
3440275970Scy
3441275970Scy	evutil_gettimeofday(&tv_start, NULL);
3442275970Scy	event_base_dispatch(data->base);
3443275970Scy	evutil_gettimeofday(&tv_end, NULL);
3444275970Scy	/* We'll wait twice as long as we did last time. */
3445275970Scy	test_timeval_diff_leq(&tv_start, &tv_end, 1000, 400);
3446275970Scy
3447275970Scy	tt_int_op(test_ok, ==, 1);
3448275970Scy
3449275970Scy end:
3450275970Scy	if (evcon)
3451275970Scy		evhttp_connection_free(evcon);
3452275970Scy	if (http)
3453275970Scy		evhttp_free(http);
3454275970Scy}
3455275970Scy
3456275970Scystatic void
3457275970Scyhttp_primitives(void *ptr)
3458275970Scy{
3459275970Scy	char *escaped = NULL;
3460275970Scy	struct evhttp *http = NULL;
3461275970Scy
3462275970Scy	escaped = evhttp_htmlescape("<script>");
3463275970Scy	tt_assert(escaped);
3464275970Scy	tt_str_op(escaped, ==, "&lt;script&gt;");
3465275970Scy	free(escaped);
3466275970Scy
3467275970Scy	escaped = evhttp_htmlescape("\"\'&");
3468275970Scy	tt_assert(escaped);
3469275970Scy	tt_str_op(escaped, ==, "&quot;&#039;&amp;");
3470275970Scy
3471275970Scy	http = evhttp_new(NULL);
3472275970Scy	tt_assert(http);
3473275970Scy	tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, NULL), ==, 0);
3474275970Scy	tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, NULL), ==, -1);
3475275970Scy	tt_int_op(evhttp_del_cb(http, "/test"), ==, 0);
3476275970Scy	tt_int_op(evhttp_del_cb(http, "/test"), ==, -1);
3477275970Scy	tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, NULL), ==, 0);
3478275970Scy
3479275970Scy end:
3480275970Scy	if (escaped)
3481275970Scy		free(escaped);
3482275970Scy	if (http)
3483275970Scy		evhttp_free(http);
3484275970Scy}
3485275970Scy
3486275970Scystatic void
3487275970Scyhttp_multi_line_header_test(void *arg)
3488275970Scy{
3489275970Scy	struct basic_test_data *data = arg;
3490275970Scy	struct bufferevent *bev= NULL;
3491275970Scy	evutil_socket_t fd = -1;
3492275970Scy	const char *http_start_request;
3493275970Scy	ev_uint16_t port = 0;
3494275970Scy
3495275970Scy	test_ok = 0;
3496275970Scy
3497275970Scy	http = http_setup(&port, data->base, 0);
3498275970Scy
3499275970Scy	tt_ptr_op(http, !=, NULL);
3500275970Scy
3501275970Scy	fd = http_connect("127.0.0.1", port);
3502275970Scy
3503275970Scy	tt_int_op(fd, !=, -1);
3504275970Scy
3505275970Scy	/* Stupid thing to send a request */
3506275970Scy	bev = bufferevent_socket_new(data->base, fd, 0);
3507275970Scy	tt_ptr_op(bev, !=, NULL);
3508275970Scy	bufferevent_setcb(bev, http_readcb, http_writecb,
3509275970Scy	    http_errorcb, data->base);
3510275970Scy
3511275970Scy	http_start_request =
3512275970Scy	    "GET /test HTTP/1.1\r\n"
3513275970Scy	    "Host: somehost\r\n"
3514275970Scy	    "Connection: close\r\n"
3515275970Scy	    "X-Multi-Extra-WS:  libevent  \r\n"
3516275970Scy	    "\t\t\t2.1 \r\n"
3517275970Scy	    "X-Multi:  aaaaaaaa\r\n"
3518275970Scy	    " a\r\n"
3519275970Scy	    "\tEND\r\n"
3520275970Scy	    "X-Last: last\r\n"
3521275970Scy	    "\r\n";
3522275970Scy
3523275970Scy	bufferevent_write(bev, http_start_request, strlen(http_start_request));
3524275970Scy	found_multi = found_multi2 = 0;
3525275970Scy
3526275970Scy	event_base_dispatch(data->base);
3527275970Scy
3528275970Scy	tt_int_op(found_multi, ==, 1);
3529275970Scy	tt_int_op(found_multi2, ==, 1);
3530275970Scy	tt_int_op(test_ok, ==, 4);
3531275970Scy end:
3532275970Scy	if (bev)
3533275970Scy		bufferevent_free(bev);
3534275970Scy	if (fd >= 0)
3535275970Scy		evutil_closesocket(fd);
3536275970Scy	if (http)
3537275970Scy		evhttp_free(http);
3538275970Scy}
3539275970Scy
3540275970Scystatic void
3541275970Scyhttp_request_bad(struct evhttp_request *req, void *arg)
3542275970Scy{
3543275970Scy	if (req != NULL) {
3544275970Scy		fprintf(stderr, "FAILED\n");
3545275970Scy		exit(1);
3546275970Scy	}
3547275970Scy
3548275970Scy	test_ok = 1;
3549275970Scy	event_base_loopexit(arg, NULL);
3550275970Scy}
3551275970Scy
3552275970Scystatic void
3553275970Scyhttp_negative_content_length_test(void *arg)
3554275970Scy{
3555275970Scy	struct basic_test_data *data = arg;
3556275970Scy	ev_uint16_t port = 0;
3557275970Scy	struct evhttp_connection *evcon = NULL;
3558275970Scy	struct evhttp_request *req = NULL;
3559275970Scy
3560275970Scy	test_ok = 0;
3561275970Scy
3562275970Scy	http = http_setup(&port, data->base, 0);
3563275970Scy
3564275970Scy	evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
3565275970Scy	tt_assert(evcon);
3566275970Scy
3567275970Scy	/*
3568275970Scy	 * At this point, we want to schedule a request to the HTTP
3569275970Scy	 * server using our make request method.
3570275970Scy	 */
3571275970Scy
3572275970Scy	req = evhttp_request_new(http_request_bad, data->base);
3573275970Scy
3574275970Scy	/* Cause the response to have a negative content-length */
3575275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "X-Negative", "makeitso");
3576275970Scy
3577275970Scy	/* We give ownership of the request to the connection */
3578275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
3579275970Scy		tt_abort_msg("Couldn't make request");
3580275970Scy	}
3581275970Scy
3582275970Scy	event_base_dispatch(data->base);
3583275970Scy
3584275970Scy end:
3585275970Scy	if (evcon)
3586275970Scy		evhttp_connection_free(evcon);
3587275970Scy	if (http)
3588275970Scy		evhttp_free(http);
3589275970Scy}
3590275970Scy
3591275970Scy
3592275970Scystatic void
3593275970Scyhttp_data_length_constraints_test_done(struct evhttp_request *req, void *arg)
3594275970Scy{
3595275970Scy	tt_assert(req);
3596275970Scy	tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_BADREQUEST);
3597275970Scyend:
3598275970Scy	event_base_loopexit(arg, NULL);
3599275970Scy}
3600275970Scy
3601275970Scystatic void
3602275970Scyhttp_large_entity_test_done(struct evhttp_request *req, void *arg)
3603275970Scy{
3604275970Scy	tt_assert(req);
3605275970Scy	tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_ENTITYTOOLARGE);
3606275970Scyend:
3607275970Scy	event_base_loopexit(arg, NULL);
3608275970Scy}
3609275970Scy
3610275970Scystatic void
3611275970Scyhttp_data_length_constraints_test(void *arg)
3612275970Scy{
3613275970Scy	struct basic_test_data *data = arg;
3614275970Scy	ev_uint16_t port = 0;
3615275970Scy	struct evhttp_connection *evcon = NULL;
3616275970Scy	struct evhttp_request *req = NULL;
3617275970Scy	char long_str[8192];
3618275970Scy
3619275970Scy	test_ok = 0;
3620275970Scy
3621275970Scy	http = http_setup(&port, data->base, 0);
3622275970Scy
3623275970Scy	evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
3624275970Scy	tt_assert(evcon);
3625275970Scy
3626275970Scy	/* also bind to local host */
3627275970Scy	evhttp_connection_set_local_address(evcon, "127.0.0.1");
3628275970Scy
3629275970Scy	/*
3630275970Scy	 * At this point, we want to schedule an HTTP GET request
3631275970Scy	 * server using our make request method.
3632275970Scy	 */
3633275970Scy
3634275970Scy	req = evhttp_request_new(http_data_length_constraints_test_done, data->base);
3635275970Scy	tt_assert(req);
3636275970Scy
3637275970Scy	memset(long_str, 'a', 8192);
3638275970Scy	long_str[8191] = '\0';
3639275970Scy	/* Add the information that we care about */
3640275970Scy	evhttp_set_max_headers_size(http, 8191);
3641275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
3642275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Longheader", long_str);
3643275970Scy
3644275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) {
3645275970Scy		tt_abort_msg("Couldn't make request");
3646275970Scy	}
3647275970Scy	event_base_dispatch(data->base);
3648275970Scy
3649275970Scy	req = evhttp_request_new(http_data_length_constraints_test_done, data->base);
3650275970Scy	tt_assert(req);
3651275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
3652275970Scy
3653275970Scy	/* GET /?arg=verylongvalue HTTP/1.1 */
3654275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, long_str) == -1) {
3655275970Scy		tt_abort_msg("Couldn't make request");
3656275970Scy	}
3657275970Scy	event_base_dispatch(data->base);
3658275970Scy
3659275970Scy	evhttp_set_max_body_size(http, 8190);
3660275970Scy	req = evhttp_request_new(http_data_length_constraints_test_done, data->base);
3661275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
3662275970Scy	evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str);
3663275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) {
3664275970Scy		tt_abort_msg("Couldn't make request");
3665275970Scy	}
3666275970Scy	event_base_dispatch(data->base);
3667275970Scy
3668275970Scy	req = evhttp_request_new(http_large_entity_test_done, data->base);
3669275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
3670275970Scy	evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "100-continue");
3671275970Scy	evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str);
3672275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) {
3673275970Scy		tt_abort_msg("Couldn't make request");
3674275970Scy	}
3675275970Scy	event_base_dispatch(data->base);
3676275970Scy
3677275970Scy	test_ok = 1;
3678275970Scy end:
3679275970Scy	if (evcon)
3680275970Scy		evhttp_connection_free(evcon);
3681275970Scy	if (http)
3682275970Scy		evhttp_free(http);
3683275970Scy}
3684275970Scy
3685275970Scy/*
3686275970Scy * Testing client reset of server chunked connections
3687275970Scy */
3688275970Scy
3689275970Scystruct terminate_state {
3690275970Scy	struct event_base *base;
3691275970Scy	struct evhttp_request *req;
3692275970Scy	struct bufferevent *bev;
3693275970Scy	evutil_socket_t fd;
3694275970Scy	int gotclosecb: 1;
3695275970Scy};
3696275970Scy
3697275970Scystatic void
3698275970Scyterminate_chunked_trickle_cb(evutil_socket_t fd, short events, void *arg)
3699275970Scy{
3700275970Scy	struct terminate_state *state = arg;
3701275970Scy	struct evbuffer *evb;
3702275970Scy	struct timeval tv;
3703275970Scy
3704275970Scy	if (evhttp_request_get_connection(state->req) == NULL) {
3705275970Scy		test_ok = 1;
3706275970Scy		evhttp_request_free(state->req);
3707275970Scy		event_base_loopexit(state->base,NULL);
3708275970Scy		return;
3709275970Scy	}
3710275970Scy
3711275970Scy	evb = evbuffer_new();
3712275970Scy	evbuffer_add_printf(evb, "%p", evb);
3713275970Scy	evhttp_send_reply_chunk(state->req, evb);
3714275970Scy	evbuffer_free(evb);
3715275970Scy
3716275970Scy	tv.tv_sec = 0;
3717275970Scy	tv.tv_usec = 3000;
3718275970Scy	EVUTIL_ASSERT(state);
3719275970Scy	EVUTIL_ASSERT(state->base);
3720275970Scy	event_base_once(state->base, -1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv);
3721275970Scy}
3722275970Scy
3723275970Scystatic void
3724275970Scyterminate_chunked_close_cb(struct evhttp_connection *evcon, void *arg)
3725275970Scy{
3726275970Scy	struct terminate_state *state = arg;
3727275970Scy	state->gotclosecb = 1;
3728275970Scy}
3729275970Scy
3730275970Scystatic void
3731275970Scyterminate_chunked_cb(struct evhttp_request *req, void *arg)
3732275970Scy{
3733275970Scy	struct terminate_state *state = arg;
3734275970Scy	struct timeval tv;
3735275970Scy
3736275970Scy	/* we want to know if this connection closes on us */
3737275970Scy	evhttp_connection_set_closecb(
3738275970Scy		evhttp_request_get_connection(req),
3739275970Scy		terminate_chunked_close_cb, arg);
3740275970Scy
3741275970Scy	state->req = req;
3742275970Scy
3743275970Scy	evhttp_send_reply_start(req, HTTP_OK, "OK");
3744275970Scy
3745275970Scy	tv.tv_sec = 0;
3746275970Scy	tv.tv_usec = 3000;
3747275970Scy	event_base_once(state->base, -1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv);
3748275970Scy}
3749275970Scy
3750275970Scystatic void
3751275970Scyterminate_chunked_client(evutil_socket_t fd, short event, void *arg)
3752275970Scy{
3753275970Scy	struct terminate_state *state = arg;
3754275970Scy	bufferevent_free(state->bev);
3755275970Scy	evutil_closesocket(state->fd);
3756275970Scy}
3757275970Scy
3758275970Scystatic void
3759275970Scyterminate_readcb(struct bufferevent *bev, void *arg)
3760275970Scy{
3761275970Scy	/* just drop the data */
3762275970Scy	evbuffer_drain(bufferevent_get_input(bev), -1);
3763275970Scy}
3764275970Scy
3765275970Scy
3766275970Scystatic void
3767275970Scyhttp_terminate_chunked_test(void *arg)
3768275970Scy{
3769275970Scy	struct basic_test_data *data = arg;
3770275970Scy	struct bufferevent *bev = NULL;
3771275970Scy	struct timeval tv;
3772275970Scy	const char *http_request;
3773275970Scy	ev_uint16_t port = 0;
3774275970Scy	evutil_socket_t fd = -1;
3775275970Scy	struct terminate_state terminate_state;
3776275970Scy
3777275970Scy	test_ok = 0;
3778275970Scy
3779275970Scy	http = http_setup(&port, data->base, 0);
3780275970Scy	evhttp_del_cb(http, "/test");
3781275970Scy	tt_assert(evhttp_set_cb(http, "/test",
3782275970Scy		terminate_chunked_cb, &terminate_state) == 0);
3783275970Scy
3784275970Scy	fd = http_connect("127.0.0.1", port);
3785275970Scy
3786275970Scy	/* Stupid thing to send a request */
3787275970Scy	bev = bufferevent_socket_new(data->base, fd, 0);
3788275970Scy	bufferevent_setcb(bev, terminate_readcb, http_writecb,
3789275970Scy	    http_errorcb, data->base);
3790275970Scy
3791275970Scy	memset(&terminate_state, 0, sizeof(terminate_state));
3792275970Scy	terminate_state.base = data->base;
3793275970Scy	terminate_state.fd = fd;
3794275970Scy	terminate_state.bev = bev;
3795275970Scy	terminate_state.gotclosecb = 0;
3796275970Scy
3797275970Scy	/* first half of the http request */
3798275970Scy	http_request =
3799275970Scy	    "GET /test HTTP/1.1\r\n"
3800275970Scy	    "Host: some\r\n\r\n";
3801275970Scy
3802275970Scy	bufferevent_write(bev, http_request, strlen(http_request));
3803275970Scy	evutil_timerclear(&tv);
3804275970Scy	tv.tv_usec = 10000;
3805275970Scy	event_base_once(data->base, -1, EV_TIMEOUT, terminate_chunked_client, &terminate_state,
3806275970Scy	    &tv);
3807275970Scy
3808275970Scy	event_base_dispatch(data->base);
3809275970Scy
3810275970Scy	if (terminate_state.gotclosecb == 0)
3811275970Scy		test_ok = 0;
3812275970Scy
3813275970Scy end:
3814275970Scy	if (fd >= 0)
3815275970Scy		evutil_closesocket(fd);
3816275970Scy	if (http)
3817275970Scy		evhttp_free(http);
3818275970Scy}
3819275970Scy
3820275970Scystatic struct regress_dns_server_table ipv6_search_table[] = {
3821275970Scy	{ "localhost", "AAAA", "::1", 0 },
3822275970Scy	{ NULL, NULL, NULL, 0 }
3823275970Scy};
3824275970Scy
3825275970Scystatic void
3826282408Scyhttp_ipv6_for_domain_test_impl(void *arg, int family)
3827275970Scy{
3828275970Scy	struct basic_test_data *data = arg;
3829275970Scy	struct evdns_base *dns_base = NULL;
3830275970Scy	ev_uint16_t portnum = 0;
3831275970Scy	char address[64];
3832275970Scy
3833275970Scy	tt_assert(regress_dnsserver(data->base, &portnum, ipv6_search_table));
3834275970Scy
3835275970Scy	dns_base = evdns_base_new(data->base, 0/* init name servers */);
3836275970Scy	tt_assert(dns_base);
3837275970Scy
3838275970Scy	/* Add ourself as the only nameserver, and make sure we really are
3839275970Scy	 * the only nameserver. */
3840275970Scy	evutil_snprintf(address, sizeof(address), "127.0.0.1:%d", portnum);
3841275970Scy	evdns_base_nameserver_ip_add(dns_base, address);
3842275970Scy
3843282408Scy	http_connection_test_(arg, 0 /* not persistent */, "localhost", dns_base,
3844282408Scy		1 /* ipv6 */, family);
3845275970Scy
3846275970Scy end:
3847275970Scy	if (dns_base)
3848275970Scy		evdns_base_free(dns_base, 0);
3849275970Scy	regress_clean_dnsserver();
3850275970Scy}
3851282408Scystatic void
3852282408Scyhttp_ipv6_for_domain_test(void *arg)
3853282408Scy{
3854282408Scy	http_ipv6_for_domain_test_impl(arg, AF_UNSPEC);
3855282408Scy}
3856275970Scy
3857275970Scystatic void
3858275970Scyhttp_request_get_addr_on_close(struct evhttp_connection *evcon, void *arg)
3859275970Scy{
3860275970Scy	const struct sockaddr *storage;
3861275970Scy	char addrbuf[128];
3862275970Scy	char local[] = "127.0.0.1:";
3863275970Scy
3864275970Scy	test_ok = 0;
3865275970Scy	tt_assert(evcon);
3866275970Scy
3867275970Scy	storage = evhttp_connection_get_addr(evcon);
3868275970Scy	tt_assert(storage);
3869275970Scy
3870275970Scy	evutil_format_sockaddr_port_((struct sockaddr *)storage, addrbuf, sizeof(addrbuf));
3871275970Scy	tt_assert(!strncmp(addrbuf, local, sizeof(local) - 1));
3872275970Scy
3873275970Scy	test_ok = 1;
3874275970Scy	return;
3875275970Scy
3876275970Scyend:
3877275970Scy	test_ok = 0;
3878275970Scy}
3879275970Scy
3880275970Scystatic void
3881275970Scyhttp_get_addr_test(void *arg)
3882275970Scy{
3883275970Scy	struct basic_test_data *data = arg;
3884275970Scy	ev_uint16_t port = 0;
3885275970Scy	struct evhttp_connection *evcon = NULL;
3886275970Scy	struct evhttp_request *req = NULL;
3887275970Scy
3888275970Scy	test_ok = 0;
3889275970Scy	exit_base = data->base;
3890275970Scy
3891275970Scy	http = http_setup(&port, data->base, 0);
3892275970Scy
3893275970Scy	evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
3894275970Scy	tt_assert(evcon);
3895275970Scy	evhttp_connection_set_closecb(evcon, http_request_get_addr_on_close, arg);
3896275970Scy
3897275970Scy	/*
3898275970Scy	 * At this point, we want to schedule a request to the HTTP
3899275970Scy	 * server using our make request method.
3900275970Scy	 */
3901275970Scy
3902275970Scy	req = evhttp_request_new(http_request_done, (void *)BASIC_REQUEST_BODY);
3903275970Scy
3904275970Scy	/* We give ownership of the request to the connection */
3905275970Scy	if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
3906275970Scy		tt_abort_msg("Couldn't make request");
3907275970Scy	}
3908275970Scy
3909275970Scy	event_base_dispatch(data->base);
3910275970Scy
3911275970Scy	http_request_get_addr_on_close(evcon, NULL);
3912275970Scy
3913275970Scy end:
3914275970Scy	if (evcon)
3915275970Scy		evhttp_connection_free(evcon);
3916275970Scy	if (http)
3917275970Scy		evhttp_free(http);
3918275970Scy}
3919275970Scy
3920282408Scystatic void
3921282408Scyhttp_set_family_test(void *arg)
3922282408Scy{
3923282408Scy	http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_UNSPEC);
3924282408Scy}
3925282408Scystatic void
3926282408Scyhttp_set_family_ipv4_test(void *arg)
3927282408Scy{
3928282408Scy	http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_INET);
3929282408Scy}
3930282408Scystatic void
3931282408Scyhttp_set_family_ipv6_test(void *arg)
3932282408Scy{
3933282408Scy	http_ipv6_for_domain_test_impl(arg, AF_INET6);
3934282408Scy}
3935282408Scy
3936275970Scy#define HTTP_LEGACY(name)						\
3937275970Scy	{ #name, run_legacy_test_fn, TT_ISOLATED|TT_LEGACY, &legacy_setup, \
3938275970Scy		    http_##name##_test }
3939275970Scy
3940275970Scy#define HTTP(name) \
3941275970Scy	{ #name, http_##name##_test, TT_ISOLATED, &basic_setup, NULL }
3942275970Scy
3943275970Scystruct testcase_t http_testcases[] = {
3944275970Scy	{ "primitives", http_primitives, 0, NULL, NULL },
3945275970Scy	{ "base", http_base_test, TT_FORK, NULL, NULL },
3946275970Scy	{ "bad_headers", http_bad_header_test, 0, NULL, NULL },
3947275970Scy	{ "parse_query", http_parse_query_test, 0, NULL, NULL },
3948275970Scy	{ "parse_uri", http_parse_uri_test, 0, NULL, NULL },
3949275970Scy	{ "parse_uri_nc", http_parse_uri_test, 0, &basic_setup, (void*)"nc" },
3950275970Scy	{ "uriencode", http_uriencode_test, 0, NULL, NULL },
3951275970Scy	HTTP(basic),
3952275970Scy	HTTP(cancel),
3953275970Scy	HTTP(virtual_host),
3954275970Scy	HTTP(post),
3955275970Scy	HTTP(put),
3956275970Scy	HTTP(delete),
3957275970Scy	HTTP(allowed_methods),
3958275970Scy	HTTP(failure),
3959275970Scy	HTTP(connection),
3960275970Scy	HTTP(persist_connection),
3961282408Scy	HTTP(autofree_connection),
3962275970Scy	HTTP(connection_async),
3963275970Scy	HTTP(close_detection),
3964275970Scy	HTTP(close_detection_delay),
3965275970Scy	HTTP(bad_request),
3966275970Scy	HTTP(incomplete),
3967275970Scy	HTTP(incomplete_timeout),
3968275970Scy	HTTP(terminate_chunked),
3969275970Scy	HTTP(on_complete),
3970275970Scy
3971275970Scy	HTTP(highport),
3972275970Scy	HTTP(dispatcher),
3973275970Scy	HTTP(multi_line_header),
3974275970Scy	HTTP(negative_content_length),
3975275970Scy	HTTP(chunk_out),
3976275970Scy	HTTP(stream_out),
3977275970Scy
3978275970Scy	HTTP(stream_in),
3979275970Scy	HTTP(stream_in_cancel),
3980275970Scy
3981275970Scy	HTTP(connection_fail),
3982275970Scy	{ "connection_retry", http_connection_retry_test, TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL },
3983275970Scy
3984275970Scy	HTTP(data_length_constraints),
3985275970Scy
3986275970Scy	HTTP(ipv6_for_domain),
3987275970Scy	HTTP(get_addr),
3988275970Scy
3989282408Scy	HTTP(set_family),
3990282408Scy	HTTP(set_family_ipv4),
3991282408Scy	HTTP(set_family_ipv6),
3992282408Scy
3993275970Scy	END_OF_TESTCASES
3994275970Scy};
3995275970Scy
3996