2 * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu>
3 * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include "event2/event-config.h"
36 #include <sys/types.h>
38 #ifdef _EVENT_HAVE_SYS_TIME_H
41 #include <sys/queue.h>
43 #include <sys/socket.h>
54 #include "event2/dns.h"
56 #include "event2/event.h"
57 #include "event2/http.h"
58 #include "event2/buffer.h"
59 #include "event2/bufferevent.h"
60 #include "event2/util.h"
61 #include "log-internal.h"
62 #include "util-internal.h"
63 #include "http-internal.h"
65 #include "regress_testutils.h"
67 static struct evhttp
*http
;
68 /* set if a test needs to call loopexit on a base */
69 static struct event_base
*exit_base
;
71 static char const BASIC_REQUEST_BODY
[] = "This is funny";
73 static void http_basic_cb(struct evhttp_request
*req
, void *arg
);
74 static void http_chunked_cb(struct evhttp_request
*req
, void *arg
);
75 static void http_post_cb(struct evhttp_request
*req
, void *arg
);
76 static void http_put_cb(struct evhttp_request
*req
, void *arg
);
77 static void http_delete_cb(struct evhttp_request
*req
, void *arg
);
78 static void http_delay_cb(struct evhttp_request
*req
, void *arg
);
79 static void http_large_delay_cb(struct evhttp_request
*req
, void *arg
);
80 static void http_badreq_cb(struct evhttp_request
*req
, void *arg
);
81 static void http_dispatcher_cb(struct evhttp_request
*req
, void *arg
);
83 http_bind(struct evhttp
*myhttp
, ev_uint16_t
*pport
)
86 struct evhttp_bound_socket
*sock
;
88 sock
= evhttp_bind_socket_with_handle(myhttp
, "127.0.0.1", *pport
);
90 event_errx(1, "Could not start web server");
92 port
= regress_get_socket_port(evhttp_bound_socket_get_fd(sock
));
95 *pport
= (ev_uint16_t
) port
;
100 static struct evhttp
*
101 http_setup(ev_uint16_t
*pport
, struct event_base
*base
)
103 struct evhttp
*myhttp
;
105 /* Try a few different ports */
106 myhttp
= evhttp_new(base
);
108 if (http_bind(myhttp
, pport
) < 0)
111 /* Register a callback for certain types of requests */
112 evhttp_set_cb(myhttp
, "/test", http_basic_cb
, base
);
113 evhttp_set_cb(myhttp
, "/chunked", http_chunked_cb
, base
);
114 evhttp_set_cb(myhttp
, "/streamed", http_chunked_cb
, base
);
115 evhttp_set_cb(myhttp
, "/postit", http_post_cb
, base
);
116 evhttp_set_cb(myhttp
, "/putit", http_put_cb
, base
);
117 evhttp_set_cb(myhttp
, "/deleteit", http_delete_cb
, base
);
118 evhttp_set_cb(myhttp
, "/delay", http_delay_cb
, base
);
119 evhttp_set_cb(myhttp
, "/largedelay", http_large_delay_cb
, base
);
120 evhttp_set_cb(myhttp
, "/badrequest", http_badreq_cb
, base
);
121 evhttp_set_cb(myhttp
, "/", http_dispatcher_cb
, base
);
126 #define NI_MAXSERV 1024
129 static evutil_socket_t
130 http_connect(const char *address
, u_short port
)
132 /* Stupid code for connecting */
133 struct evutil_addrinfo ai
, *aitop
;
134 char strport
[NI_MAXSERV
];
140 memset(&ai
, 0, sizeof(ai
));
141 ai
.ai_family
= AF_INET
;
142 ai
.ai_socktype
= SOCK_STREAM
;
143 evutil_snprintf(strport
, sizeof(strport
), "%d", port
);
144 if (evutil_getaddrinfo(address
, strport
, &ai
, &aitop
) != 0) {
145 event_warn("getaddrinfo");
149 slen
= aitop
->ai_addrlen
;
151 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
153 event_err(1, "socket failed");
155 evutil_make_socket_nonblocking(fd
);
156 if (connect(fd
, sa
, slen
) == -1) {
158 int tmp_err
= WSAGetLastError();
159 if (tmp_err
!= WSAEINPROGRESS
&& tmp_err
!= WSAEINVAL
&&
160 tmp_err
!= WSAEWOULDBLOCK
)
161 event_err(1, "connect failed");
163 if (errno
!= EINPROGRESS
)
164 event_err(1, "connect failed");
168 evutil_freeaddrinfo(aitop
);
173 /* Helper: do a strcmp on the contents of buf and the string s. */
175 evbuffer_datacmp(struct evbuffer
*buf
, const char *s
)
177 size_t b_sz
= evbuffer_get_length(buf
);
178 size_t s_sz
= strlen(s
);
185 d
= evbuffer_pullup(buf
, s_sz
);
186 if ((r
= memcmp(d
, s
, s_sz
)))
195 /* Helper: Return true iff buf contains s */
197 evbuffer_contains(struct evbuffer
*buf
, const char *s
)
199 struct evbuffer_ptr ptr
;
200 ptr
= evbuffer_search(buf
, s
, strlen(s
), NULL
);
201 return ptr
.pos
!= -1;
205 http_readcb(struct bufferevent
*bev
, void *arg
)
207 const char *what
= BASIC_REQUEST_BODY
;
208 struct event_base
*my_base
= arg
;
210 if (evbuffer_contains(bufferevent_get_input(bev
), what
)) {
211 struct evhttp_request
*req
= evhttp_request_new(NULL
, NULL
);
212 enum message_read_status done
;
214 /* req->kind = EVHTTP_RESPONSE; */
215 done
= evhttp_parse_firstline(req
, bufferevent_get_input(bev
));
216 if (done
!= ALL_DATA_READ
)
219 done
= evhttp_parse_headers(req
, bufferevent_get_input(bev
));
220 if (done
!= ALL_DATA_READ
)
224 evhttp_find_header(evhttp_request_get_input_headers(req
),
225 "Content-Type") != NULL
)
229 evhttp_request_free(req
);
230 bufferevent_disable(bev
, EV_READ
);
232 event_base_loopexit(exit_base
, NULL
);
234 event_base_loopexit(my_base
, NULL
);
236 fprintf(stderr
, "No way to exit loop!\n");
243 http_writecb(struct bufferevent
*bev
, void *arg
)
245 if (evbuffer_get_length(bufferevent_get_output(bev
)) == 0) {
246 /* enable reading of the reply */
247 bufferevent_enable(bev
, EV_READ
);
253 http_errorcb(struct bufferevent
*bev
, short what
, void *arg
)
256 event_base_loopexit(arg
, NULL
);
260 http_basic_cb(struct evhttp_request
*req
, void *arg
)
262 struct evbuffer
*evb
= evbuffer_new();
263 int empty
= evhttp_find_header(evhttp_request_get_input_headers(req
), "Empty") != NULL
;
264 event_debug(("%s: called\n", __func__
));
265 evbuffer_add_printf(evb
, BASIC_REQUEST_BODY
);
267 /* For multi-line headers test */
270 evhttp_find_header(evhttp_request_get_input_headers(req
),"X-multi");
272 if (strcmp("END", multi
+ strlen(multi
) - 3) == 0)
274 if (evhttp_find_header(evhttp_request_get_input_headers(req
), "X-Last"))
279 /* injecting a bad content-length */
280 if (evhttp_find_header(evhttp_request_get_input_headers(req
), "X-Negative"))
281 evhttp_add_header(evhttp_request_get_output_headers(req
),
282 "Content-Length", "-100");
284 /* allow sending of an empty reply */
285 evhttp_send_reply(req
, HTTP_OK
, "Everything is fine",
286 !empty
? evb
: NULL
);
291 static char const* const CHUNKS
[] = {
293 "but not hilarious.",
297 struct chunk_req_state
{
298 struct event_base
*base
;
299 struct evhttp_request
*req
;
304 http_chunked_trickle_cb(evutil_socket_t fd
, short events
, void *arg
)
306 struct evbuffer
*evb
= evbuffer_new();
307 struct chunk_req_state
*state
= arg
;
308 struct timeval when
= { 0, 0 };
310 evbuffer_add_printf(evb
, "%s", CHUNKS
[state
->i
]);
311 evhttp_send_reply_chunk(state
->req
, evb
);
314 if (++state
->i
< (int) (sizeof(CHUNKS
)/sizeof(CHUNKS
[0]))) {
315 event_base_once(state
->base
, -1, EV_TIMEOUT
,
316 http_chunked_trickle_cb
, state
, &when
);
318 evhttp_send_reply_end(state
->req
);
324 http_chunked_cb(struct evhttp_request
*req
, void *arg
)
326 struct timeval when
= { 0, 0 };
327 struct chunk_req_state
*state
= malloc(sizeof(struct chunk_req_state
));
328 event_debug(("%s: called\n", __func__
));
330 memset(state
, 0, sizeof(struct chunk_req_state
));
334 if (strcmp(evhttp_request_get_uri(req
), "/streamed") == 0) {
335 evhttp_add_header(evhttp_request_get_output_headers(req
), "Content-Length", "39");
338 /* generate a chunked/streamed reply */
339 evhttp_send_reply_start(req
, HTTP_OK
, "Everything is fine");
341 /* but trickle it across several iterations to ensure we're not
342 * assuming it comes all at once */
343 event_base_once(arg
, -1, EV_TIMEOUT
, http_chunked_trickle_cb
, state
, &when
);
347 http_complete_write(evutil_socket_t fd
, short what
, void *arg
)
349 struct bufferevent
*bev
= arg
;
350 const char *http_request
= "host\r\n"
351 "Connection: close\r\n"
353 bufferevent_write(bev
, http_request
, strlen(http_request
));
357 http_basic_test(void *arg
)
359 struct basic_test_data
*data
= arg
;
361 struct bufferevent
*bev
;
363 const char *http_request
;
364 ev_uint16_t port
= 0, port2
= 0;
368 http
= http_setup(&port
, data
->base
);
370 /* bind to a second socket */
371 if (http_bind(http
, &port2
) == -1) {
372 fprintf(stdout
, "FAILED (bind)\n");
376 fd
= http_connect("127.0.0.1", port
);
378 /* Stupid thing to send a request */
379 bev
= bufferevent_socket_new(data
->base
, fd
, 0);
380 bufferevent_setcb(bev
, http_readcb
, http_writecb
,
381 http_errorcb
, data
->base
);
383 /* first half of the http request */
385 "GET /test HTTP/1.1\r\n"
388 bufferevent_write(bev
, http_request
, strlen(http_request
));
389 evutil_timerclear(&tv
);
391 event_base_once(data
->base
,
392 -1, EV_TIMEOUT
, http_complete_write
, bev
, &tv
);
394 event_base_dispatch(data
->base
);
396 tt_assert(test_ok
== 3);
398 /* connect to the second port */
399 bufferevent_free(bev
);
400 evutil_closesocket(fd
);
402 fd
= http_connect("127.0.0.1", port2
);
404 /* Stupid thing to send a request */
405 bev
= bufferevent_socket_new(data
->base
, fd
, 0);
406 bufferevent_setcb(bev
, http_readcb
, http_writecb
,
407 http_errorcb
, data
->base
);
410 "GET /test HTTP/1.1\r\n"
412 "Connection: close\r\n"
415 bufferevent_write(bev
, http_request
, strlen(http_request
));
417 event_base_dispatch(data
->base
);
419 tt_assert(test_ok
== 5);
421 /* Connect to the second port again. This time, send an absolute uri. */
422 bufferevent_free(bev
);
423 evutil_closesocket(fd
);
425 fd
= http_connect("127.0.0.1", port2
);
427 /* Stupid thing to send a request */
428 bev
= bufferevent_socket_new(data
->base
, fd
, 0);
429 bufferevent_setcb(bev
, http_readcb
, http_writecb
,
430 http_errorcb
, data
->base
);
433 "GET http://somehost.net/test HTTP/1.1\r\n"
435 "Connection: close\r\n"
438 bufferevent_write(bev
, http_request
, strlen(http_request
));
440 event_base_dispatch(data
->base
);
442 tt_assert(test_ok
== 7);
450 http_delay_reply(evutil_socket_t fd
, short what
, void *arg
)
452 struct evhttp_request
*req
= arg
;
454 evhttp_send_reply(req
, HTTP_OK
, "Everything is fine", NULL
);
460 http_delay_cb(struct evhttp_request
*req
, void *arg
)
463 evutil_timerclear(&tv
);
465 tv
.tv_usec
= 200 * 1000;
467 event_base_once(arg
, -1, EV_TIMEOUT
, http_delay_reply
, req
, &tv
);
471 http_badreq_cb(struct evhttp_request
*req
, void *arg
)
473 struct evbuffer
*buf
= evbuffer_new();
475 evhttp_add_header(evhttp_request_get_output_headers(req
), "Content-Type", "text/xml; charset=UTF-8");
476 evbuffer_add_printf(buf
, "Hello, %s!", "127.0.0.1");
478 evhttp_send_reply(req
, HTTP_OK
, "OK", buf
);
483 http_badreq_errorcb(struct bufferevent
*bev
, short what
, void *arg
)
485 event_debug(("%s: called (what=%04x, arg=%p)", __func__
, what
, arg
));
491 #define SHUT_WR SD_SEND
498 http_badreq_readcb(struct bufferevent
*bev
, void *arg
)
500 const char *what
= "Hello, 127.0.0.1";
501 const char *bad_request
= "400 Bad Request";
503 if (evbuffer_contains(bufferevent_get_input(bev
), bad_request
)) {
504 TT_FAIL(("%s:bad request detected", __func__
));
505 bufferevent_disable(bev
, EV_READ
);
506 event_base_loopexit(arg
, NULL
);
510 if (evbuffer_contains(bufferevent_get_input(bev
), what
)) {
511 struct evhttp_request
*req
= evhttp_request_new(NULL
, NULL
);
512 enum message_read_status done
;
514 /* req->kind = EVHTTP_RESPONSE; */
515 done
= evhttp_parse_firstline(req
, bufferevent_get_input(bev
));
516 if (done
!= ALL_DATA_READ
)
519 done
= evhttp_parse_headers(req
, bufferevent_get_input(bev
));
520 if (done
!= ALL_DATA_READ
)
524 evhttp_find_header(evhttp_request_get_input_headers(req
),
525 "Content-Type") != NULL
)
529 evhttp_request_free(req
);
530 evbuffer_drain(bufferevent_get_input(bev
), evbuffer_get_length(bufferevent_get_input(bev
)));
533 shutdown(bufferevent_getfd(bev
), SHUT_WR
);
537 http_badreq_successcb(evutil_socket_t fd
, short what
, void *arg
)
539 event_debug(("%s: called (what=%04x, arg=%p)", __func__
, what
, arg
));
540 event_base_loopexit(exit_base
, NULL
);
544 http_bad_request_test(void *arg
)
546 struct basic_test_data
*data
= arg
;
548 struct bufferevent
*bev
= NULL
;
550 const char *http_request
;
551 ev_uint16_t port
=0, port2
=0;
554 exit_base
= data
->base
;
556 http
= http_setup(&port
, data
->base
);
558 /* bind to a second socket */
559 if (http_bind(http
, &port2
) == -1)
560 TT_DIE(("Bind socket failed"));
562 /* NULL request test */
563 fd
= http_connect("127.0.0.1", port
);
565 /* Stupid thing to send a request */
566 bev
= bufferevent_socket_new(data
->base
, fd
, 0);
567 bufferevent_setcb(bev
, http_badreq_readcb
, http_writecb
,
568 http_badreq_errorcb
, data
->base
);
569 bufferevent_enable(bev
, EV_READ
);
571 /* real NULL request */
574 bufferevent_write(bev
, http_request
, strlen(http_request
));
576 shutdown(fd
, SHUT_WR
);
579 event_base_once(data
->base
, -1, EV_TIMEOUT
, http_badreq_successcb
, bev
, &tv
);
581 event_base_dispatch(data
->base
);
583 bufferevent_free(bev
);
584 evutil_closesocket(fd
);
587 fprintf(stdout
, "FAILED\n");
591 /* Second answer (BAD REQUEST) on connection close */
593 /* connect to the second port */
594 fd
= http_connect("127.0.0.1", port2
);
596 /* Stupid thing to send a request */
597 bev
= bufferevent_socket_new(data
->base
, fd
, 0);
598 bufferevent_setcb(bev
, http_badreq_readcb
, http_writecb
,
599 http_badreq_errorcb
, data
->base
);
600 bufferevent_enable(bev
, EV_READ
);
602 /* first half of the http request */
604 "GET /badrequest HTTP/1.0\r\n" \
605 "Connection: Keep-Alive\r\n" \
608 bufferevent_write(bev
, http_request
, strlen(http_request
));
612 event_base_once(data
->base
, -1, EV_TIMEOUT
, http_badreq_successcb
, bev
, &tv
);
614 event_base_dispatch(data
->base
);
616 tt_int_op(test_ok
, ==, 2);
621 bufferevent_free(bev
);
624 static struct evhttp_connection
*delayed_client
;
627 http_large_delay_cb(struct evhttp_request
*req
, void *arg
)
630 evutil_timerclear(&tv
);
633 event_base_once(arg
, -1, EV_TIMEOUT
, http_delay_reply
, req
, &tv
);
634 evhttp_connection_fail(delayed_client
, EVCON_HTTP_EOF
);
638 * HTTP DELETE test, just piggyback on the basic test
642 http_delete_cb(struct evhttp_request
*req
, void *arg
)
644 struct evbuffer
*evb
= evbuffer_new();
645 int empty
= evhttp_find_header(evhttp_request_get_input_headers(req
), "Empty") != NULL
;
647 /* Expecting a DELETE request */
648 if (evhttp_request_get_command(req
) != EVHTTP_REQ_DELETE
) {
649 fprintf(stdout
, "FAILED (delete type)\n");
653 event_debug(("%s: called\n", __func__
));
654 evbuffer_add_printf(evb
, BASIC_REQUEST_BODY
);
656 /* allow sending of an empty reply */
657 evhttp_send_reply(req
, HTTP_OK
, "Everything is fine",
658 !empty
? evb
: NULL
);
664 http_delete_test(void *arg
)
666 struct basic_test_data
*data
= arg
;
667 struct bufferevent
*bev
;
669 const char *http_request
;
670 ev_uint16_t port
= 0;
674 http
= http_setup(&port
, data
->base
);
676 fd
= http_connect("127.0.0.1", port
);
678 /* Stupid thing to send a request */
679 bev
= bufferevent_socket_new(data
->base
, fd
, 0);
680 bufferevent_setcb(bev
, http_readcb
, http_writecb
,
681 http_errorcb
, data
->base
);
684 "DELETE /deleteit HTTP/1.1\r\n"
686 "Connection: close\r\n"
689 bufferevent_write(bev
, http_request
, strlen(http_request
));
691 event_base_dispatch(data
->base
);
693 bufferevent_free(bev
);
694 evutil_closesocket(fd
);
698 tt_int_op(test_ok
, ==, 2);
704 http_allowed_methods_eventcb(struct bufferevent
*bev
, short what
, void *arg
)
707 if ((what
& (BEV_EVENT_ERROR
|BEV_EVENT_EOF
))) {
710 n
= evbuffer_remove(bufferevent_get_input(bev
), buf
,
716 *output
= strdup(buf
);
718 event_base_loopexit(exit_base
, NULL
);
723 http_allowed_methods_test(void *arg
)
725 struct basic_test_data
*data
= arg
;
726 struct bufferevent
*bev1
, *bev2
, *bev3
;
727 evutil_socket_t fd1
, fd2
, fd3
;
728 const char *http_request
;
729 char *result1
=NULL
, *result2
=NULL
, *result3
=NULL
;
730 ev_uint16_t port
= 0;
732 exit_base
= data
->base
;
735 http
= http_setup(&port
, data
->base
);
737 fd1
= http_connect("127.0.0.1", port
);
739 /* GET is out; PATCH is in. */
740 evhttp_set_allowed_methods(http
, EVHTTP_REQ_PATCH
);
742 /* Stupid thing to send a request */
743 bev1
= bufferevent_socket_new(data
->base
, fd1
, 0);
744 bufferevent_enable(bev1
, EV_READ
|EV_WRITE
);
745 bufferevent_setcb(bev1
, NULL
, NULL
,
746 http_allowed_methods_eventcb
, &result1
);
749 "GET /index.html HTTP/1.1\r\n"
751 "Connection: close\r\n"
754 bufferevent_write(bev1
, http_request
, strlen(http_request
));
756 event_base_dispatch(data
->base
);
758 fd2
= http_connect("127.0.0.1", port
);
760 bev2
= bufferevent_socket_new(data
->base
, fd2
, 0);
761 bufferevent_enable(bev2
, EV_READ
|EV_WRITE
);
762 bufferevent_setcb(bev2
, NULL
, NULL
,
763 http_allowed_methods_eventcb
, &result2
);
766 "PATCH /test HTTP/1.1\r\n"
768 "Connection: close\r\n"
771 bufferevent_write(bev2
, http_request
, strlen(http_request
));
773 event_base_dispatch(data
->base
);
775 fd3
= http_connect("127.0.0.1", port
);
777 bev3
= bufferevent_socket_new(data
->base
, fd3
, 0);
778 bufferevent_enable(bev3
, EV_READ
|EV_WRITE
);
779 bufferevent_setcb(bev3
, NULL
, NULL
,
780 http_allowed_methods_eventcb
, &result3
);
783 "FLOOP /test HTTP/1.1\r\n"
785 "Connection: close\r\n"
788 bufferevent_write(bev3
, http_request
, strlen(http_request
));
790 event_base_dispatch(data
->base
);
792 bufferevent_free(bev1
);
793 bufferevent_free(bev2
);
794 bufferevent_free(bev3
);
795 evutil_closesocket(fd1
);
796 evutil_closesocket(fd2
);
797 evutil_closesocket(fd3
);
801 /* Method known but disallowed */
803 tt_assert(!strncmp(result1
, "HTTP/1.1 501 ", strlen("HTTP/1.1 501 ")));
805 /* Method known and allowed */
807 tt_assert(!strncmp(result2
, "HTTP/1.1 200 ", strlen("HTTP/1.1 200 ")));
811 tt_assert(!strncmp(result3
, "HTTP/1.1 501 ", strlen("HTTP/1.1 501 ")));
822 static void http_request_done(struct evhttp_request
*, void *);
823 static void http_request_empty_done(struct evhttp_request
*, void *);
826 _http_connection_test(struct basic_test_data
*data
, int persistent
)
828 ev_uint16_t port
= 0;
829 struct evhttp_connection
*evcon
= NULL
;
830 struct evhttp_request
*req
= NULL
;
834 http
= http_setup(&port
, data
->base
);
836 evcon
= evhttp_connection_base_new(data
->base
, NULL
, "127.0.0.1", port
);
839 tt_assert(evhttp_connection_get_base(evcon
) == data
->base
);
841 exit_base
= data
->base
;
843 * At this point, we want to schedule a request to the HTTP
844 * server using our make request method.
847 req
= evhttp_request_new(http_request_done
, (void*) BASIC_REQUEST_BODY
);
849 /* Add the information that we care about */
850 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
852 /* We give ownership of the request to the connection */
853 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
854 fprintf(stdout
, "FAILED\n");
858 event_base_dispatch(data
->base
);
862 /* try to make another request over the same connection */
865 req
= evhttp_request_new(http_request_done
, (void*) BASIC_REQUEST_BODY
);
867 /* Add the information that we care about */
868 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
871 * if our connections are not supposed to be persistent; request
872 * a close from the server.
875 evhttp_add_header(evhttp_request_get_output_headers(req
), "Connection", "close");
877 /* We give ownership of the request to the connection */
878 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
879 tt_abort_msg("couldn't make request");
882 event_base_dispatch(data
->base
);
884 /* make another request: request empty reply */
887 req
= evhttp_request_new(http_request_empty_done
, data
->base
);
889 /* Add the information that we care about */
890 evhttp_add_header(evhttp_request_get_output_headers(req
), "Empty", "itis");
892 /* We give ownership of the request to the connection */
893 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
894 tt_abort_msg("Couldn't make request");
897 event_base_dispatch(data
->base
);
901 evhttp_connection_free(evcon
);
907 http_connection_test(void *arg
)
909 _http_connection_test(arg
, 0);
912 http_persist_connection_test(void *arg
)
914 _http_connection_test(arg
, 1);
917 static struct regress_dns_server_table search_table
[] = {
918 { "localhost", "A", "127.0.0.1", 0 },
919 { NULL
, NULL
, NULL
, 0 }
923 http_connection_async_test(void *arg
)
925 struct basic_test_data
*data
= arg
;
926 ev_uint16_t port
= 0;
927 struct evhttp_connection
*evcon
= NULL
;
928 struct evhttp_request
*req
= NULL
;
929 struct evdns_base
*dns_base
= NULL
;
930 ev_uint16_t portnum
= 0;
933 exit_base
= data
->base
;
934 tt_assert(regress_dnsserver(data
->base
, &portnum
, search_table
));
936 dns_base
= evdns_base_new(data
->base
, 0/* init name servers */);
939 /* Add ourself as the only nameserver, and make sure we really are
940 * the only nameserver. */
941 evutil_snprintf(address
, sizeof(address
), "127.0.0.1:%d", portnum
);
942 evdns_base_nameserver_ip_add(dns_base
, address
);
946 http
= http_setup(&port
, data
->base
);
948 evcon
= evhttp_connection_base_new(data
->base
, dns_base
, "127.0.0.1", port
);
952 * At this point, we want to schedule a request to the HTTP
953 * server using our make request method.
956 req
= evhttp_request_new(http_request_done
, (void*) BASIC_REQUEST_BODY
);
958 /* Add the information that we care about */
959 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
961 /* We give ownership of the request to the connection */
962 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
963 fprintf(stdout
, "FAILED\n");
967 event_base_dispatch(data
->base
);
971 /* try to make another request over the same connection */
974 req
= evhttp_request_new(http_request_done
, (void*) BASIC_REQUEST_BODY
);
976 /* Add the information that we care about */
977 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
980 * if our connections are not supposed to be persistent; request
981 * a close from the server.
983 evhttp_add_header(evhttp_request_get_output_headers(req
), "Connection", "close");
985 /* We give ownership of the request to the connection */
986 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
987 tt_abort_msg("couldn't make request");
990 event_base_dispatch(data
->base
);
992 /* make another request: request empty reply */
995 req
= evhttp_request_new(http_request_empty_done
, data
->base
);
997 /* Add the information that we care about */
998 evhttp_add_header(evhttp_request_get_output_headers(req
), "Empty", "itis");
1000 /* We give ownership of the request to the connection */
1001 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
1002 tt_abort_msg("Couldn't make request");
1005 event_base_dispatch(data
->base
);
1009 evhttp_connection_free(evcon
);
1013 evdns_base_free(dns_base
, 0);
1014 regress_clean_dnsserver();
1018 http_request_never_call(struct evhttp_request
*req
, void *arg
)
1020 fprintf(stdout
, "FAILED\n");
1025 http_do_cancel(evutil_socket_t fd
, short what
, void *arg
)
1027 struct evhttp_request
*req
= arg
;
1029 struct event_base
*base
;
1030 evutil_timerclear(&tv
);
1032 tv
.tv_usec
= 500 * 1000;
1034 base
= evhttp_connection_get_base(evhttp_request_get_connection(req
));
1035 evhttp_cancel_request(req
);
1037 event_base_loopexit(base
, &tv
);
1043 http_cancel_test(void *arg
)
1045 struct basic_test_data
*data
= arg
;
1046 ev_uint16_t port
= 0;
1047 struct evhttp_connection
*evcon
= NULL
;
1048 struct evhttp_request
*req
= NULL
;
1051 exit_base
= data
->base
;
1055 http
= http_setup(&port
, data
->base
);
1057 evcon
= evhttp_connection_base_new(data
->base
, NULL
, "127.0.0.1", port
);
1061 * At this point, we want to schedule a request to the HTTP
1062 * server using our make request method.
1065 req
= evhttp_request_new(http_request_never_call
, NULL
);
1067 /* Add the information that we care about */
1068 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
1070 /* We give ownership of the request to the connection */
1071 tt_int_op(evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/delay"),
1074 evutil_timerclear(&tv
);
1076 tv
.tv_usec
= 100 * 1000;
1078 event_base_once(data
->base
, -1, EV_TIMEOUT
, http_do_cancel
, req
, &tv
);
1080 event_base_dispatch(data
->base
);
1082 tt_int_op(test_ok
, ==, 2);
1084 /* try to make another request over the same connection */
1087 req
= evhttp_request_new(http_request_done
, (void*) BASIC_REQUEST_BODY
);
1089 /* Add the information that we care about */
1090 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
1092 /* We give ownership of the request to the connection */
1093 tt_int_op(evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test"),
1096 event_base_dispatch(data
->base
);
1098 /* make another request: request empty reply */
1101 req
= evhttp_request_new(http_request_empty_done
, data
->base
);
1103 /* Add the information that we care about */
1104 evhttp_add_header(evhttp_request_get_output_headers(req
), "Empty", "itis");
1106 /* We give ownership of the request to the connection */
1107 tt_int_op(evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test"),
1110 event_base_dispatch(data
->base
);
1114 evhttp_connection_free(evcon
);
1120 http_request_done(struct evhttp_request
*req
, void *arg
)
1122 const char *what
= arg
;
1124 if (evhttp_request_get_response_code(req
) != HTTP_OK
) {
1125 fprintf(stderr
, "FAILED\n");
1129 if (evhttp_find_header(evhttp_request_get_input_headers(req
), "Content-Type") == NULL
) {
1130 fprintf(stderr
, "FAILED\n");
1134 if (evbuffer_get_length(evhttp_request_get_input_buffer(req
)) != strlen(what
)) {
1135 fprintf(stderr
, "FAILED\n");
1139 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req
), what
) != 0) {
1140 fprintf(stderr
, "FAILED\n");
1145 EVUTIL_ASSERT(exit_base
);
1146 event_base_loopexit(exit_base
, NULL
);
1150 http_request_expect_error(struct evhttp_request
*req
, void *arg
)
1152 if (evhttp_request_get_response_code(req
) == HTTP_OK
) {
1153 fprintf(stderr
, "FAILED\n");
1159 event_base_loopexit(arg
, NULL
);
1162 /* test virtual hosts */
1164 http_virtual_host_test(void *arg
)
1166 struct basic_test_data
*data
= arg
;
1167 ev_uint16_t port
= 0;
1168 struct evhttp_connection
*evcon
= NULL
;
1169 struct evhttp_request
*req
= NULL
;
1170 struct evhttp
*second
= NULL
, *third
= NULL
;
1172 struct bufferevent
*bev
;
1173 const char *http_request
;
1175 exit_base
= data
->base
;
1177 http
= http_setup(&port
, data
->base
);
1180 second
= evhttp_new(NULL
);
1181 evhttp_set_cb(second
, "/funnybunny", http_basic_cb
, NULL
);
1182 third
= evhttp_new(NULL
);
1183 evhttp_set_cb(third
, "/blackcoffee", http_basic_cb
, NULL
);
1185 if (evhttp_add_virtual_host(http
, "foo.com", second
) == -1) {
1186 tt_abort_msg("Couldn't add vhost");
1189 if (evhttp_add_virtual_host(http
, "bar.*.foo.com", third
) == -1) {
1190 tt_abort_msg("Couldn't add wildcarded vhost");
1193 /* add some aliases to the vhosts */
1194 tt_assert(evhttp_add_server_alias(second
, "manolito.info") == 0);
1195 tt_assert(evhttp_add_server_alias(third
, "bonkers.org") == 0);
1197 evcon
= evhttp_connection_base_new(data
->base
, NULL
, "127.0.0.1", port
);
1200 /* make a request with a different host and expect an error */
1201 req
= evhttp_request_new(http_request_expect_error
, data
->base
);
1203 /* Add the information that we care about */
1204 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
1206 /* We give ownership of the request to the connection */
1207 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
,
1208 "/funnybunny") == -1) {
1209 tt_abort_msg("Couldn't make request");
1212 event_base_dispatch(data
->base
);
1214 tt_assert(test_ok
== 1);
1218 /* make a request with the right host and expect a response */
1219 req
= evhttp_request_new(http_request_done
, (void*) BASIC_REQUEST_BODY
);
1221 /* Add the information that we care about */
1222 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "foo.com");
1224 /* We give ownership of the request to the connection */
1225 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
,
1226 "/funnybunny") == -1) {
1227 fprintf(stdout
, "FAILED\n");
1231 event_base_dispatch(data
->base
);
1233 tt_assert(test_ok
== 1);
1237 /* make a request with the right host and expect a response */
1238 req
= evhttp_request_new(http_request_done
, (void*) BASIC_REQUEST_BODY
);
1240 /* Add the information that we care about */
1241 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "bar.magic.foo.com");
1243 /* We give ownership of the request to the connection */
1244 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
,
1245 "/blackcoffee") == -1) {
1246 tt_abort_msg("Couldn't make request");
1249 event_base_dispatch(data
->base
);
1251 tt_assert(test_ok
== 1)
1255 /* make a request with the right host and expect a response */
1256 req
= evhttp_request_new(http_request_done
, (void*) BASIC_REQUEST_BODY
);
1258 /* Add the information that we care about */
1259 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "manolito.info");
1261 /* We give ownership of the request to the connection */
1262 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
,
1263 "/funnybunny") == -1) {
1264 tt_abort_msg("Couldn't make request");
1267 event_base_dispatch(data
->base
);
1269 tt_assert(test_ok
== 1)
1273 /* make a request with the right host and expect a response */
1274 req
= evhttp_request_new(http_request_done
, (void*) BASIC_REQUEST_BODY
);
1276 /* Add the Host header. This time with the optional port. */
1277 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "bonkers.org:8000");
1279 /* We give ownership of the request to the connection */
1280 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
,
1281 "/blackcoffee") == -1) {
1282 tt_abort_msg("Couldn't make request");
1285 event_base_dispatch(data
->base
);
1287 tt_assert(test_ok
== 1)
1291 /* Now make a raw request with an absolute URI. */
1292 fd
= http_connect("127.0.0.1", port
);
1294 /* Stupid thing to send a request */
1295 bev
= bufferevent_socket_new(data
->base
, fd
, 0);
1296 bufferevent_setcb(bev
, http_readcb
, http_writecb
,
1297 http_errorcb
, NULL
);
1299 /* The host in the URI should override the Host: header */
1301 "GET http://manolito.info/funnybunny HTTP/1.1\r\n"
1302 "Host: somehost\r\n"
1303 "Connection: close\r\n"
1306 bufferevent_write(bev
, http_request
, strlen(http_request
));
1308 event_base_dispatch(data
->base
);
1310 tt_int_op(test_ok
, ==, 2);
1312 bufferevent_free(bev
);
1313 evutil_closesocket(fd
);
1317 evhttp_connection_free(evcon
);
1323 /* test date header and content length */
1326 http_request_empty_done(struct evhttp_request
*req
, void *arg
)
1328 if (evhttp_request_get_response_code(req
) != HTTP_OK
) {
1329 fprintf(stderr
, "FAILED\n");
1333 if (evhttp_find_header(evhttp_request_get_input_headers(req
), "Date") == NULL
) {
1334 fprintf(stderr
, "FAILED\n");
1339 if (evhttp_find_header(evhttp_request_get_input_headers(req
), "Content-Length") == NULL
) {
1340 fprintf(stderr
, "FAILED\n");
1344 if (strcmp(evhttp_find_header(evhttp_request_get_input_headers(req
), "Content-Length"),
1346 fprintf(stderr
, "FAILED\n");
1350 if (evbuffer_get_length(evhttp_request_get_input_buffer(req
)) != 0) {
1351 fprintf(stderr
, "FAILED\n");
1357 event_base_loopexit(arg
, NULL
);
1361 * HTTP DISPATCHER test
1365 http_dispatcher_cb(struct evhttp_request
*req
, void *arg
)
1368 struct evbuffer
*evb
= evbuffer_new();
1369 event_debug(("%s: called\n", __func__
));
1370 evbuffer_add_printf(evb
, "DISPATCHER_TEST");
1372 evhttp_send_reply(req
, HTTP_OK
, "Everything is fine", evb
);
1378 http_dispatcher_test_done(struct evhttp_request
*req
, void *arg
)
1380 struct event_base
*base
= arg
;
1381 const char *what
= "DISPATCHER_TEST";
1383 if (evhttp_request_get_response_code(req
) != HTTP_OK
) {
1384 fprintf(stderr
, "FAILED\n");
1388 if (evhttp_find_header(evhttp_request_get_input_headers(req
), "Content-Type") == NULL
) {
1389 fprintf(stderr
, "FAILED (content type)\n");
1393 if (evbuffer_get_length(evhttp_request_get_input_buffer(req
)) != strlen(what
)) {
1394 fprintf(stderr
, "FAILED (length %lu vs %lu)\n",
1395 (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req
)), (unsigned long)strlen(what
));
1399 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req
), what
) != 0) {
1400 fprintf(stderr
, "FAILED (data)\n");
1405 event_base_loopexit(base
, NULL
);
1409 http_dispatcher_test(void *arg
)
1411 struct basic_test_data
*data
= arg
;
1412 ev_uint16_t port
= 0;
1413 struct evhttp_connection
*evcon
= NULL
;
1414 struct evhttp_request
*req
= NULL
;
1418 http
= http_setup(&port
, data
->base
);
1420 evcon
= evhttp_connection_base_new(data
->base
, NULL
, "127.0.0.1", port
);
1423 /* also bind to local host */
1424 evhttp_connection_set_local_address(evcon
, "127.0.0.1");
1427 * At this point, we want to schedule an HTTP GET request
1428 * server using our make request method.
1431 req
= evhttp_request_new(http_dispatcher_test_done
, data
->base
);
1434 /* Add the information that we care about */
1435 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
1437 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/?arg=val") == -1) {
1438 tt_abort_msg("Couldn't make request");
1441 event_base_dispatch(data
->base
);
1445 evhttp_connection_free(evcon
);
1454 void http_postrequest_done(struct evhttp_request
*, void *);
1456 #define POST_DATA "Okay. Not really printf"
1459 http_post_test(void *arg
)
1461 struct basic_test_data
*data
= arg
;
1462 ev_uint16_t port
= 0;
1463 struct evhttp_connection
*evcon
= NULL
;
1464 struct evhttp_request
*req
= NULL
;
1468 http
= http_setup(&port
, data
->base
);
1470 evcon
= evhttp_connection_base_new(data
->base
, NULL
, "127.0.0.1", port
);
1474 * At this point, we want to schedule an HTTP POST request
1475 * server using our make request method.
1478 req
= evhttp_request_new(http_postrequest_done
, data
->base
);
1481 /* Add the information that we care about */
1482 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
1483 evbuffer_add_printf(evhttp_request_get_output_buffer(req
), POST_DATA
);
1485 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_POST
, "/postit") == -1) {
1486 tt_abort_msg("Couldn't make request");
1489 event_base_dispatch(data
->base
);
1491 tt_int_op(test_ok
, ==, 1);
1495 req
= evhttp_request_new(http_postrequest_done
, data
->base
);
1498 /* Now try with 100-continue. */
1500 /* Add the information that we care about */
1501 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
1502 evhttp_add_header(evhttp_request_get_output_headers(req
), "Expect", "100-continue");
1503 evbuffer_add_printf(evhttp_request_get_output_buffer(req
), POST_DATA
);
1505 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_POST
, "/postit") == -1) {
1506 tt_abort_msg("Couldn't make request");
1509 event_base_dispatch(data
->base
);
1511 tt_int_op(test_ok
, ==, 1);
1513 evhttp_connection_free(evcon
);
1521 http_post_cb(struct evhttp_request
*req
, void *arg
)
1523 struct evbuffer
*evb
;
1524 event_debug(("%s: called\n", __func__
));
1526 /* Yes, we are expecting a post request */
1527 if (evhttp_request_get_command(req
) != EVHTTP_REQ_POST
) {
1528 fprintf(stdout
, "FAILED (post type)\n");
1532 if (evbuffer_get_length(evhttp_request_get_input_buffer(req
)) != strlen(POST_DATA
)) {
1533 fprintf(stdout
, "FAILED (length: %lu vs %lu)\n",
1534 (unsigned long) evbuffer_get_length(evhttp_request_get_input_buffer(req
)), (unsigned long) strlen(POST_DATA
));
1538 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req
), POST_DATA
) != 0) {
1539 fprintf(stdout
, "FAILED (data)\n");
1540 fprintf(stdout
, "Got :%s\n", evbuffer_pullup(evhttp_request_get_input_buffer(req
),-1));
1541 fprintf(stdout
, "Want:%s\n", POST_DATA
);
1545 evb
= evbuffer_new();
1546 evbuffer_add_printf(evb
, BASIC_REQUEST_BODY
);
1548 evhttp_send_reply(req
, HTTP_OK
, "Everything is fine", evb
);
1554 http_postrequest_done(struct evhttp_request
*req
, void *arg
)
1556 const char *what
= BASIC_REQUEST_BODY
;
1557 struct event_base
*base
= arg
;
1560 fprintf(stderr
, "FAILED (timeout)\n");
1564 if (evhttp_request_get_response_code(req
) != HTTP_OK
) {
1566 fprintf(stderr
, "FAILED (response code)\n");
1570 if (evhttp_find_header(evhttp_request_get_input_headers(req
), "Content-Type") == NULL
) {
1571 fprintf(stderr
, "FAILED (content type)\n");
1575 if (evbuffer_get_length(evhttp_request_get_input_buffer(req
)) != strlen(what
)) {
1576 fprintf(stderr
, "FAILED (length %lu vs %lu)\n",
1577 (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req
)), (unsigned long)strlen(what
));
1581 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req
), what
) != 0) {
1582 fprintf(stderr
, "FAILED (data)\n");
1587 event_base_loopexit(base
, NULL
);
1591 * HTTP PUT test, basically just like POST, but ...
1594 void http_putrequest_done(struct evhttp_request
*, void *);
1596 #define PUT_DATA "Hi, I'm some PUT data"
1599 http_put_test(void *arg
)
1601 struct basic_test_data
*data
= arg
;
1602 ev_uint16_t port
= 0;
1603 struct evhttp_connection
*evcon
= NULL
;
1604 struct evhttp_request
*req
= NULL
;
1608 http
= http_setup(&port
, data
->base
);
1610 evcon
= evhttp_connection_base_new(data
->base
, NULL
, "127.0.0.1", port
);
1614 * Schedule the HTTP PUT request
1617 req
= evhttp_request_new(http_putrequest_done
, data
->base
);
1620 /* Add the information that we care about */
1621 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "someotherhost");
1622 evbuffer_add_printf(evhttp_request_get_output_buffer(req
), PUT_DATA
);
1624 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_PUT
, "/putit") == -1) {
1625 tt_abort_msg("Couldn't make request");
1628 event_base_dispatch(data
->base
);
1630 evhttp_connection_free(evcon
);
1633 tt_int_op(test_ok
, ==, 1);
1639 http_put_cb(struct evhttp_request
*req
, void *arg
)
1641 struct evbuffer
*evb
;
1642 event_debug(("%s: called\n", __func__
));
1644 /* Expecting a PUT request */
1645 if (evhttp_request_get_command(req
) != EVHTTP_REQ_PUT
) {
1646 fprintf(stdout
, "FAILED (put type)\n");
1650 if (evbuffer_get_length(evhttp_request_get_input_buffer(req
)) != strlen(PUT_DATA
)) {
1651 fprintf(stdout
, "FAILED (length: %lu vs %lu)\n",
1652 (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req
)), (unsigned long)strlen(PUT_DATA
));
1656 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req
), PUT_DATA
) != 0) {
1657 fprintf(stdout
, "FAILED (data)\n");
1658 fprintf(stdout
, "Got :%s\n", evbuffer_pullup(evhttp_request_get_input_buffer(req
),-1));
1659 fprintf(stdout
, "Want:%s\n", PUT_DATA
);
1663 evb
= evbuffer_new();
1664 evbuffer_add_printf(evb
, "That ain't funny");
1666 evhttp_send_reply(req
, HTTP_OK
, "Everything is great", evb
);
1672 http_putrequest_done(struct evhttp_request
*req
, void *arg
)
1674 struct event_base
*base
= arg
;
1675 const char *what
= "That ain't funny";
1678 fprintf(stderr
, "FAILED (timeout)\n");
1682 if (evhttp_request_get_response_code(req
) != HTTP_OK
) {
1684 fprintf(stderr
, "FAILED (response code)\n");
1688 if (evhttp_find_header(evhttp_request_get_input_headers(req
), "Content-Type") == NULL
) {
1689 fprintf(stderr
, "FAILED (content type)\n");
1693 if (evbuffer_get_length(evhttp_request_get_input_buffer(req
)) != strlen(what
)) {
1694 fprintf(stderr
, "FAILED (length %lu vs %lu)\n",
1695 (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req
)), (unsigned long)strlen(what
));
1700 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req
), what
) != 0) {
1701 fprintf(stderr
, "FAILED (data)\n");
1706 event_base_loopexit(base
, NULL
);
1710 http_failure_readcb(struct bufferevent
*bev
, void *arg
)
1712 const char *what
= "400 Bad Request";
1713 if (evbuffer_contains(bufferevent_get_input(bev
), what
)) {
1715 bufferevent_disable(bev
, EV_READ
);
1716 event_base_loopexit(arg
, NULL
);
1721 * Testing that the HTTP server can deal with a malformed request.
1724 http_failure_test(void *arg
)
1726 struct basic_test_data
*data
= arg
;
1727 struct bufferevent
*bev
;
1729 const char *http_request
;
1730 ev_uint16_t port
= 0;
1734 http
= http_setup(&port
, data
->base
);
1736 fd
= http_connect("127.0.0.1", port
);
1738 /* Stupid thing to send a request */
1739 bev
= bufferevent_socket_new(data
->base
, fd
, 0);
1740 bufferevent_setcb(bev
, http_failure_readcb
, http_writecb
,
1741 http_errorcb
, data
->base
);
1743 http_request
= "illegal request\r\n";
1745 bufferevent_write(bev
, http_request
, strlen(http_request
));
1747 event_base_dispatch(data
->base
);
1749 bufferevent_free(bev
);
1750 evutil_closesocket(fd
);
1754 tt_int_op(test_ok
, ==, 2);
1760 close_detect_done(struct evhttp_request
*req
, void *arg
)
1764 tt_assert(evhttp_request_get_response_code(req
) == HTTP_OK
);
1769 evutil_timerclear(&tv
);
1771 event_base_loopexit(arg
, &tv
);
1775 close_detect_launch(evutil_socket_t fd
, short what
, void *arg
)
1777 struct evhttp_connection
*evcon
= arg
;
1778 struct event_base
*base
= evhttp_connection_get_base(evcon
);
1779 struct evhttp_request
*req
;
1781 req
= evhttp_request_new(close_detect_done
, base
);
1783 /* Add the information that we care about */
1784 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
1786 /* We give ownership of the request to the connection */
1787 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
1788 tt_fail_msg("Couldn't make request");
1793 close_detect_cb(struct evhttp_request
*req
, void *arg
)
1795 struct evhttp_connection
*evcon
= arg
;
1796 struct event_base
*base
= evhttp_connection_get_base(evcon
);
1799 if (req
!= NULL
&& evhttp_request_get_response_code(req
) != HTTP_OK
) {
1800 tt_abort_msg("Failed");
1803 evutil_timerclear(&tv
);
1804 tv
.tv_sec
= 3; /* longer than the http time out */
1806 /* launch a new request on the persistent connection in 3 seconds */
1807 event_base_once(base
, -1, EV_TIMEOUT
, close_detect_launch
, evcon
, &tv
);
1814 _http_close_detection(struct basic_test_data
*data
, int with_delay
)
1816 ev_uint16_t port
= 0;
1817 struct evhttp_connection
*evcon
= NULL
;
1818 struct evhttp_request
*req
= NULL
;
1821 http
= http_setup(&port
, data
->base
);
1823 /* 2 second timeout */
1824 evhttp_set_timeout(http
, 1);
1826 evcon
= evhttp_connection_base_new(data
->base
, NULL
,
1829 delayed_client
= evcon
;
1832 * At this point, we want to schedule a request to the HTTP
1833 * server using our make request method.
1836 req
= evhttp_request_new(close_detect_cb
, evcon
);
1838 /* Add the information that we care about */
1839 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
1841 /* We give ownership of the request to the connection */
1842 if (evhttp_make_request(evcon
,
1843 req
, EVHTTP_REQ_GET
, with_delay
? "/largedelay" : "/test") == -1) {
1844 tt_abort_msg("couldn't make request");
1847 event_base_dispatch(data
->base
);
1849 /* at this point, the http server should have no connection */
1850 tt_assert(TAILQ_FIRST(&http
->connections
) == NULL
);
1854 evhttp_connection_free(evcon
);
1859 http_close_detection_test(void *arg
)
1861 _http_close_detection(arg
, 0);
1864 http_close_detection_delay_test(void *arg
)
1866 _http_close_detection(arg
, 1);
1870 http_highport_test(void *arg
)
1872 struct basic_test_data
*data
= arg
;
1874 struct evhttp
*myhttp
= NULL
;
1876 /* Try a few different ports */
1877 for (i
= 0; i
< 50; ++i
) {
1878 myhttp
= evhttp_new(data
->base
);
1879 if (evhttp_bind_socket(myhttp
, "127.0.0.1", 65535 - i
) == 0) {
1881 evhttp_free(myhttp
);
1884 evhttp_free(myhttp
);
1887 tt_fail_msg("Couldn't get a high port");
1891 http_bad_header_test(void *ptr
)
1893 struct evkeyvalq headers
;
1895 TAILQ_INIT(&headers
);
1897 tt_want(evhttp_add_header(&headers
, "One", "Two") == 0);
1898 tt_want(evhttp_add_header(&headers
, "One", "Two\r\n Three") == 0);
1899 tt_want(evhttp_add_header(&headers
, "One\r", "Two") == -1);
1900 tt_want(evhttp_add_header(&headers
, "One\n", "Two") == -1);
1901 tt_want(evhttp_add_header(&headers
, "One", "Two\r") == -1);
1902 tt_want(evhttp_add_header(&headers
, "One", "Two\n") == -1);
1904 evhttp_clear_headers(&headers
);
1907 static int validate_header(
1908 const struct evkeyvalq
* headers
,
1909 const char *key
, const char *value
)
1911 const char *real_val
= evhttp_find_header(headers
, key
);
1912 tt_assert(real_val
!= NULL
);
1913 tt_want(strcmp(real_val
, value
) == 0);
1919 http_parse_query_test(void *ptr
)
1921 struct evkeyvalq headers
;
1924 TAILQ_INIT(&headers
);
1926 r
= evhttp_parse_query("http://www.test.com/?q=test", &headers
);
1927 tt_want(validate_header(&headers
, "q", "test") == 0);
1928 tt_int_op(r
, ==, 0);
1929 evhttp_clear_headers(&headers
);
1931 r
= evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers
);
1932 tt_want(validate_header(&headers
, "q", "test") == 0);
1933 tt_want(validate_header(&headers
, "foo", "bar") == 0);
1934 tt_int_op(r
, ==, 0);
1935 evhttp_clear_headers(&headers
);
1937 r
= evhttp_parse_query("http://www.test.com/?q=test+foo", &headers
);
1938 tt_want(validate_header(&headers
, "q", "test foo") == 0);
1939 tt_int_op(r
, ==, 0);
1940 evhttp_clear_headers(&headers
);
1942 r
= evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers
);
1943 tt_want(validate_header(&headers
, "q", "test\nfoo") == 0);
1944 tt_int_op(r
, ==, 0);
1945 evhttp_clear_headers(&headers
);
1947 r
= evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers
);
1948 tt_want(validate_header(&headers
, "q", "test\rfoo") == 0);
1949 tt_int_op(r
, ==, 0);
1950 evhttp_clear_headers(&headers
);
1952 r
= evhttp_parse_query("http://www.test.com/?q=test&&q2", &headers
);
1953 tt_int_op(r
, ==, -1);
1954 evhttp_clear_headers(&headers
);
1956 r
= evhttp_parse_query("http://www.test.com/?q=test+this", &headers
);
1957 tt_want(validate_header(&headers
, "q", "test this") == 0);
1958 tt_int_op(r
, ==, 0);
1959 evhttp_clear_headers(&headers
);
1961 r
= evhttp_parse_query("http://www.test.com/?q=test&q2=foo", &headers
);
1962 tt_int_op(r
, ==, 0);
1963 tt_want(validate_header(&headers
, "q", "test") == 0);
1964 tt_want(validate_header(&headers
, "q2", "foo") == 0);
1965 evhttp_clear_headers(&headers
);
1967 r
= evhttp_parse_query("http://www.test.com/?q&q2=foo", &headers
);
1968 tt_int_op(r
, ==, -1);
1969 evhttp_clear_headers(&headers
);
1971 r
= evhttp_parse_query("http://www.test.com/?q=foo&q2", &headers
);
1972 tt_int_op(r
, ==, -1);
1973 evhttp_clear_headers(&headers
);
1975 r
= evhttp_parse_query("http://www.test.com/?q=foo&q2&q3=x", &headers
);
1976 tt_int_op(r
, ==, -1);
1977 evhttp_clear_headers(&headers
);
1979 r
= evhttp_parse_query("http://www.test.com/?q=&q2=&q3=", &headers
);
1980 tt_int_op(r
, ==, 0);
1981 tt_want(validate_header(&headers
, "q", "") == 0);
1982 tt_want(validate_header(&headers
, "q2", "") == 0);
1983 tt_want(validate_header(&headers
, "q3", "") == 0);
1984 evhttp_clear_headers(&headers
);
1987 evhttp_clear_headers(&headers
);
1991 http_parse_uri_test(void *ptr
)
1993 const int nonconform
= (ptr
!= NULL
);
1994 const unsigned parse_flags
=
1995 nonconform
? EVHTTP_URI_NONCONFORMANT
: 0;
1996 struct evhttp_uri
*uri
= NULL
;
1998 #define URI_PARSE(uri) \
1999 evhttp_uri_parse_with_flags((uri), parse_flags)
2001 #define TT_URI(want) do { \
2002 char *ret = evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)); \
2003 tt_want(ret != NULL); \
2004 tt_want(ret == url_tmp); \
2005 if (strcmp(ret,want) != 0) \
2006 TT_FAIL(("\"%s\" != \"%s\"",ret,want)); \
2009 tt_want(evhttp_uri_join(NULL
, 0, 0) == NULL
);
2010 tt_want(evhttp_uri_join(NULL
, url_tmp
, 0) == NULL
);
2011 tt_want(evhttp_uri_join(NULL
, url_tmp
, sizeof(url_tmp
)) == NULL
);
2013 /* bad URIs: parsing */
2014 #define BAD(s) do { \
2015 if (URI_PARSE(s) != NULL) \
2016 TT_FAIL(("Expected error parsing \"%s\"",s)); \
2018 /* Nonconformant URIs we can parse: parsing */
2019 #define NCF(s) do { \
2020 uri = URI_PARSE(s); \
2021 if (uri != NULL && !nonconform) { \
2022 TT_FAIL(("Expected error parsing \"%s\"",s)); \
2023 } else if (uri == NULL && nonconform) { \
2024 TT_FAIL(("Couldn't parse nonconformant URI \"%s\"", \
2028 tt_want(evhttp_uri_join(uri, url_tmp, \
2029 sizeof(url_tmp))); \
2030 evhttp_uri_free(uri); \
2034 NCF("http://www.test.com/ why hello");
2035 NCF("http://www.test.com/why-hello\x01");
2036 NCF("http://www.test.com/why-hello?\x01");
2037 NCF("http://www.test.com/why-hello#\x01");
2038 BAD("http://www.\x01.test.com/why-hello");
2039 BAD("http://www.%7test.com/why-hello");
2040 NCF("http://www.test.com/why-hell%7o");
2041 BAD("h%3ttp://www.test.com/why-hello");
2042 NCF("http://www.test.com/why-hello%7");
2043 NCF("http://www.test.com/why-hell%7o");
2044 NCF("http://www.test.com/foo?ba%r");
2045 NCF("http://www.test.com/foo#ba%r");
2047 BAD("http://www.test.com:999x/");
2048 BAD("http://www.test.com:x/");
2049 BAD("http://[hello-there]/");
2050 BAD("http://[::1]]/");
2051 BAD("http://[::1/");
2052 BAD("http://[foob/");
2054 BAD("http://[ffff:ffff:ffff:ffff:Ffff:ffff:ffff:"
2055 "ffff:ffff:ffff:ffff:ffff:ffff:ffff]/");
2056 BAD("http://[vX.foo]/");
2057 BAD("http://[vX.foo]/");
2058 BAD("http://[v.foo]/");
2059 BAD("http://[v5.fo%o]/");
2060 BAD("http://[v5X]/");
2061 BAD("http://[v5]/");
2063 BAD("http://f\x01red@www.example.com/");
2064 BAD("http://f%0red@www.example.com/");
2065 BAD("http://www.example.com:9999999999999999999999999999999999999/");
2066 BAD("http://www.example.com:hihi/");
2067 BAD("://www.example.com/");
2069 /* bad URIs: joining */
2070 uri
= evhttp_uri_new();
2071 tt_want(0==evhttp_uri_set_host(uri
, "www.example.com"));
2072 tt_want(evhttp_uri_join(uri
, url_tmp
, sizeof(url_tmp
)) != NULL
);
2073 /* not enough space: */
2074 tt_want(evhttp_uri_join(uri
, url_tmp
, 3) == NULL
);
2075 /* host is set, but path doesn't start with "/": */
2076 tt_want(0==evhttp_uri_set_path(uri
, "hi_mom"));
2077 tt_want(evhttp_uri_join(uri
, url_tmp
, sizeof(url_tmp
)) == NULL
);
2078 tt_want(evhttp_uri_join(uri
, NULL
, sizeof(url_tmp
))==NULL
);
2079 tt_want(evhttp_uri_join(uri
, url_tmp
, 0)==NULL
);
2080 evhttp_uri_free(uri
);
2081 uri
= URI_PARSE("mailto:foo@bar");
2082 tt_want(uri
!= NULL
);
2083 tt_want(evhttp_uri_get_host(uri
) == NULL
);
2084 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2085 tt_want(evhttp_uri_get_port(uri
) == -1);
2086 tt_want(!strcmp(evhttp_uri_get_scheme(uri
), "mailto"));
2087 tt_want(!strcmp(evhttp_uri_get_path(uri
), "foo@bar"));
2088 tt_want(evhttp_uri_get_query(uri
) == NULL
);
2089 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2090 TT_URI("mailto:foo@bar");
2091 evhttp_uri_free(uri
);
2093 uri
= evhttp_uri_new();
2094 /* Bad URI usage: setting invalid values */
2095 tt_want(-1 == evhttp_uri_set_scheme(uri
,""));
2096 tt_want(-1 == evhttp_uri_set_scheme(uri
,"33"));
2097 tt_want(-1 == evhttp_uri_set_scheme(uri
,"hi!"));
2098 tt_want(-1 == evhttp_uri_set_userinfo(uri
,"hello@"));
2099 tt_want(-1 == evhttp_uri_set_host(uri
,"[1.2.3.4]"));
2100 tt_want(-1 == evhttp_uri_set_host(uri
,"["));
2101 tt_want(-1 == evhttp_uri_set_host(uri
,"www.[foo].com"));
2102 tt_want(-1 == evhttp_uri_set_port(uri
,-3));
2103 tt_want(-1 == evhttp_uri_set_path(uri
,"hello?world"));
2104 tt_want(-1 == evhttp_uri_set_query(uri
,"hello#world"));
2105 tt_want(-1 == evhttp_uri_set_fragment(uri
,"hello#world"));
2106 /* Valid URI usage: setting valid values */
2107 tt_want(0 == evhttp_uri_set_scheme(uri
,"http"));
2108 tt_want(0 == evhttp_uri_set_scheme(uri
,NULL
));
2109 tt_want(0 == evhttp_uri_set_userinfo(uri
,"username:pass"));
2110 tt_want(0 == evhttp_uri_set_userinfo(uri
,NULL
));
2111 tt_want(0 == evhttp_uri_set_host(uri
,"www.example.com"));
2112 tt_want(0 == evhttp_uri_set_host(uri
,"1.2.3.4"));
2113 tt_want(0 == evhttp_uri_set_host(uri
,"[1:2:3:4::]"));
2114 tt_want(0 == evhttp_uri_set_host(uri
,"[v7.wobblewobble]"));
2115 tt_want(0 == evhttp_uri_set_host(uri
,NULL
));
2116 tt_want(0 == evhttp_uri_set_host(uri
,""));
2117 tt_want(0 == evhttp_uri_set_port(uri
, -1));
2118 tt_want(0 == evhttp_uri_set_port(uri
, 80));
2119 tt_want(0 == evhttp_uri_set_port(uri
, 65535));
2120 tt_want(0 == evhttp_uri_set_path(uri
, ""));
2121 tt_want(0 == evhttp_uri_set_path(uri
, "/documents/public/index.html"));
2122 tt_want(0 == evhttp_uri_set_path(uri
, NULL
));
2123 tt_want(0 == evhttp_uri_set_query(uri
, "key=val&key2=val2"));
2124 tt_want(0 == evhttp_uri_set_query(uri
, "keyvalblarg"));
2125 tt_want(0 == evhttp_uri_set_query(uri
, ""));
2126 tt_want(0 == evhttp_uri_set_query(uri
, NULL
));
2127 tt_want(0 == evhttp_uri_set_fragment(uri
, ""));
2128 tt_want(0 == evhttp_uri_set_fragment(uri
, "here?i?am"));
2129 tt_want(0 == evhttp_uri_set_fragment(uri
, NULL
));
2130 evhttp_uri_free(uri
);
2133 uri
= URI_PARSE("http://www.test.com/?q=t%33est");
2134 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "http") == 0);
2135 tt_want(strcmp(evhttp_uri_get_host(uri
), "www.test.com") == 0);
2136 tt_want(strcmp(evhttp_uri_get_path(uri
), "/") == 0);
2137 tt_want(strcmp(evhttp_uri_get_query(uri
), "q=t%33est") == 0);
2138 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2139 tt_want(evhttp_uri_get_port(uri
) == -1);
2140 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2141 TT_URI("http://www.test.com/?q=t%33est");
2142 evhttp_uri_free(uri
);
2144 uri
= URI_PARSE("http://%77ww.test.com");
2145 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "http") == 0);
2146 tt_want(strcmp(evhttp_uri_get_host(uri
), "%77ww.test.com") == 0);
2147 tt_want(strcmp(evhttp_uri_get_path(uri
), "") == 0);
2148 tt_want(evhttp_uri_get_query(uri
) == NULL
);
2149 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2150 tt_want(evhttp_uri_get_port(uri
) == -1);
2151 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2152 TT_URI("http://%77ww.test.com");
2153 evhttp_uri_free(uri
);
2155 uri
= URI_PARSE("http://www.test.com?q=test");
2156 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "http") == 0);
2157 tt_want(strcmp(evhttp_uri_get_host(uri
), "www.test.com") == 0);
2158 tt_want(strcmp(evhttp_uri_get_path(uri
), "") == 0);
2159 tt_want(strcmp(evhttp_uri_get_query(uri
), "q=test") == 0);
2160 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2161 tt_want(evhttp_uri_get_port(uri
) == -1);
2162 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2163 TT_URI("http://www.test.com?q=test");
2164 evhttp_uri_free(uri
);
2166 uri
= URI_PARSE("http://www.test.com#fragment");
2167 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "http") == 0);
2168 tt_want(strcmp(evhttp_uri_get_host(uri
), "www.test.com") == 0);
2169 tt_want(strcmp(evhttp_uri_get_path(uri
), "") == 0);
2170 tt_want(evhttp_uri_get_query(uri
) == NULL
);
2171 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2172 tt_want(evhttp_uri_get_port(uri
) == -1);
2173 tt_want_str_op(evhttp_uri_get_fragment(uri
), ==, "fragment");
2174 TT_URI("http://www.test.com#fragment");
2175 evhttp_uri_free(uri
);
2177 uri
= URI_PARSE("http://8000/");
2178 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "http") == 0);
2179 tt_want(strcmp(evhttp_uri_get_host(uri
), "8000") == 0);
2180 tt_want(strcmp(evhttp_uri_get_path(uri
), "/") == 0);
2181 tt_want(evhttp_uri_get_query(uri
) == NULL
);
2182 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2183 tt_want(evhttp_uri_get_port(uri
) == -1);
2184 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2185 TT_URI("http://8000/");
2186 evhttp_uri_free(uri
);
2188 uri
= URI_PARSE("http://:8000/");
2189 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "http") == 0);
2190 tt_want(strcmp(evhttp_uri_get_host(uri
), "") == 0);
2191 tt_want(strcmp(evhttp_uri_get_path(uri
), "/") == 0);
2192 tt_want(evhttp_uri_get_query(uri
) == NULL
);
2193 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2194 tt_want(evhttp_uri_get_port(uri
) == 8000);
2195 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2196 TT_URI("http://:8000/");
2197 evhttp_uri_free(uri
);
2199 uri
= URI_PARSE("http://www.test.com:/"); /* empty port */
2200 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "http") == 0);
2201 tt_want(strcmp(evhttp_uri_get_host(uri
), "www.test.com") == 0);
2202 tt_want_str_op(evhttp_uri_get_path(uri
), ==, "/");
2203 tt_want(evhttp_uri_get_query(uri
) == NULL
);
2204 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2205 tt_want(evhttp_uri_get_port(uri
) == -1);
2206 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2207 TT_URI("http://www.test.com/");
2208 evhttp_uri_free(uri
);
2210 uri
= URI_PARSE("http://www.test.com:"); /* empty port 2 */
2211 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "http") == 0);
2212 tt_want(strcmp(evhttp_uri_get_host(uri
), "www.test.com") == 0);
2213 tt_want(strcmp(evhttp_uri_get_path(uri
), "") == 0);
2214 tt_want(evhttp_uri_get_query(uri
) == NULL
);
2215 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2216 tt_want(evhttp_uri_get_port(uri
) == -1);
2217 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2218 TT_URI("http://www.test.com");
2219 evhttp_uri_free(uri
);
2221 uri
= URI_PARSE("ftp://www.test.com/?q=test");
2222 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "ftp") == 0);
2223 tt_want(strcmp(evhttp_uri_get_host(uri
), "www.test.com") == 0);
2224 tt_want(strcmp(evhttp_uri_get_path(uri
), "/") == 0);
2225 tt_want(strcmp(evhttp_uri_get_query(uri
), "q=test") == 0);
2226 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2227 tt_want(evhttp_uri_get_port(uri
) == -1);
2228 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2229 TT_URI("ftp://www.test.com/?q=test");
2230 evhttp_uri_free(uri
);
2232 uri
= URI_PARSE("ftp://[::1]:999/?q=test");
2233 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "ftp") == 0);
2234 tt_want(strcmp(evhttp_uri_get_host(uri
), "[::1]") == 0);
2235 tt_want(strcmp(evhttp_uri_get_path(uri
), "/") == 0);
2236 tt_want(strcmp(evhttp_uri_get_query(uri
), "q=test") == 0);
2237 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2238 tt_want(evhttp_uri_get_port(uri
) == 999);
2239 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2240 TT_URI("ftp://[::1]:999/?q=test");
2241 evhttp_uri_free(uri
);
2243 uri
= URI_PARSE("ftp://[ff00::127.0.0.1]/?q=test");
2244 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "ftp") == 0);
2245 tt_want(strcmp(evhttp_uri_get_host(uri
), "[ff00::127.0.0.1]") == 0);
2246 tt_want(strcmp(evhttp_uri_get_path(uri
), "/") == 0);
2247 tt_want(strcmp(evhttp_uri_get_query(uri
), "q=test") == 0);
2248 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2249 tt_want(evhttp_uri_get_port(uri
) == -1);
2250 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2251 TT_URI("ftp://[ff00::127.0.0.1]/?q=test");
2252 evhttp_uri_free(uri
);
2254 uri
= URI_PARSE("ftp://[v99.not_(any:time)_soon]/?q=test");
2255 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "ftp") == 0);
2256 tt_want(strcmp(evhttp_uri_get_host(uri
), "[v99.not_(any:time)_soon]") == 0);
2257 tt_want(strcmp(evhttp_uri_get_path(uri
), "/") == 0);
2258 tt_want(strcmp(evhttp_uri_get_query(uri
), "q=test") == 0);
2259 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2260 tt_want(evhttp_uri_get_port(uri
) == -1);
2261 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2262 TT_URI("ftp://[v99.not_(any:time)_soon]/?q=test");
2263 evhttp_uri_free(uri
);
2265 uri
= URI_PARSE("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment");
2266 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "scheme") == 0);
2267 tt_want(strcmp(evhttp_uri_get_userinfo(uri
), "user:pass") == 0);
2268 tt_want(strcmp(evhttp_uri_get_host(uri
), "foo.com") == 0);
2269 tt_want(evhttp_uri_get_port(uri
) == 42);
2270 tt_want(strcmp(evhttp_uri_get_path(uri
), "/") == 0);
2271 tt_want(strcmp(evhttp_uri_get_query(uri
), "q=test&s=some+thing") == 0);
2272 tt_want(strcmp(evhttp_uri_get_fragment(uri
), "fragment") == 0);
2273 TT_URI("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment");
2274 evhttp_uri_free(uri
);
2276 uri
= URI_PARSE("scheme://user@foo.com/#fragment");
2277 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "scheme") == 0);
2278 tt_want(strcmp(evhttp_uri_get_userinfo(uri
), "user") == 0);
2279 tt_want(strcmp(evhttp_uri_get_host(uri
), "foo.com") == 0);
2280 tt_want(evhttp_uri_get_port(uri
) == -1);
2281 tt_want(strcmp(evhttp_uri_get_path(uri
), "/") == 0);
2282 tt_want(evhttp_uri_get_query(uri
) == NULL
);
2283 tt_want(strcmp(evhttp_uri_get_fragment(uri
), "fragment") == 0);
2284 TT_URI("scheme://user@foo.com/#fragment");
2285 evhttp_uri_free(uri
);
2287 uri
= URI_PARSE("scheme://%75ser@foo.com/#frag@ment");
2288 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "scheme") == 0);
2289 tt_want(strcmp(evhttp_uri_get_userinfo(uri
), "%75ser") == 0);
2290 tt_want(strcmp(evhttp_uri_get_host(uri
), "foo.com") == 0);
2291 tt_want(evhttp_uri_get_port(uri
) == -1);
2292 tt_want(strcmp(evhttp_uri_get_path(uri
), "/") == 0);
2293 tt_want(evhttp_uri_get_query(uri
) == NULL
);
2294 tt_want(strcmp(evhttp_uri_get_fragment(uri
), "frag@ment") == 0);
2295 TT_URI("scheme://%75ser@foo.com/#frag@ment");
2296 evhttp_uri_free(uri
);
2298 uri
= URI_PARSE("file:///some/path/to/the/file");
2299 tt_want(strcmp(evhttp_uri_get_scheme(uri
), "file") == 0);
2300 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2301 tt_want(strcmp(evhttp_uri_get_host(uri
), "") == 0);
2302 tt_want(evhttp_uri_get_port(uri
) == -1);
2303 tt_want(strcmp(evhttp_uri_get_path(uri
), "/some/path/to/the/file") == 0);
2304 tt_want(evhttp_uri_get_query(uri
) == NULL
);
2305 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2306 TT_URI("file:///some/path/to/the/file");
2307 evhttp_uri_free(uri
);
2309 uri
= URI_PARSE("///some/path/to/the-file");
2310 tt_want(uri
!= NULL
);
2311 tt_want(evhttp_uri_get_scheme(uri
) == NULL
);
2312 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2313 tt_want(strcmp(evhttp_uri_get_host(uri
), "") == 0);
2314 tt_want(evhttp_uri_get_port(uri
) == -1);
2315 tt_want(strcmp(evhttp_uri_get_path(uri
), "/some/path/to/the-file") == 0);
2316 tt_want(evhttp_uri_get_query(uri
) == NULL
);
2317 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2318 TT_URI("///some/path/to/the-file");
2319 evhttp_uri_free(uri
);
2321 uri
= URI_PARSE("/s:ome/path/to/the-file?q=99#fred");
2322 tt_want(uri
!= NULL
);
2323 tt_want(evhttp_uri_get_scheme(uri
) == NULL
);
2324 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2325 tt_want(evhttp_uri_get_host(uri
) == NULL
);
2326 tt_want(evhttp_uri_get_port(uri
) == -1);
2327 tt_want(strcmp(evhttp_uri_get_path(uri
), "/s:ome/path/to/the-file") == 0);
2328 tt_want(strcmp(evhttp_uri_get_query(uri
), "q=99") == 0);
2329 tt_want(strcmp(evhttp_uri_get_fragment(uri
), "fred") == 0);
2330 TT_URI("/s:ome/path/to/the-file?q=99#fred");
2331 evhttp_uri_free(uri
);
2333 uri
= URI_PARSE("relative/path/with/co:lon");
2334 tt_want(uri
!= NULL
);
2335 tt_want(evhttp_uri_get_scheme(uri
) == NULL
);
2336 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2337 tt_want(evhttp_uri_get_host(uri
) == NULL
);
2338 tt_want(evhttp_uri_get_port(uri
) == -1);
2339 tt_want(strcmp(evhttp_uri_get_path(uri
), "relative/path/with/co:lon") == 0);
2340 tt_want(evhttp_uri_get_query(uri
) == NULL
);
2341 tt_want(evhttp_uri_get_fragment(uri
) == NULL
);
2342 TT_URI("relative/path/with/co:lon");
2343 evhttp_uri_free(uri
);
2345 uri
= URI_PARSE("bob?q=99&q2=q?33#fr?ed");
2346 tt_want(uri
!= NULL
);
2347 tt_want(evhttp_uri_get_scheme(uri
) == NULL
);
2348 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2349 tt_want(evhttp_uri_get_host(uri
) == NULL
);
2350 tt_want(evhttp_uri_get_port(uri
) == -1);
2351 tt_want(strcmp(evhttp_uri_get_path(uri
), "bob") == 0);
2352 tt_want(strcmp(evhttp_uri_get_query(uri
), "q=99&q2=q?33") == 0);
2353 tt_want(strcmp(evhttp_uri_get_fragment(uri
), "fr?ed") == 0);
2354 TT_URI("bob?q=99&q2=q?33#fr?ed");
2355 evhttp_uri_free(uri
);
2357 uri
= URI_PARSE("#fr?ed");
2358 tt_want(uri
!= NULL
);
2359 tt_want(evhttp_uri_get_scheme(uri
) == NULL
);
2360 tt_want(evhttp_uri_get_userinfo(uri
) == NULL
);
2361 tt_want(evhttp_uri_get_host(uri
) == NULL
);
2362 tt_want(evhttp_uri_get_port(uri
) == -1);
2363 tt_want(strcmp(evhttp_uri_get_path(uri
), "") == 0);
2364 tt_want(evhttp_uri_get_query(uri
) == NULL
);
2365 tt_want(strcmp(evhttp_uri_get_fragment(uri
), "fr?ed") == 0);
2367 evhttp_uri_free(uri
);
2374 http_uriencode_test(void *ptr
)
2376 char *s
=NULL
, *s2
=NULL
;
2379 #define ENC(from,want,plus) do { \
2380 s = evhttp_uriencode((from), -1, (plus)); \
2382 tt_str_op(s,==,(want)); \
2384 s2 = evhttp_uridecode((s), (plus), &sz); \
2386 tt_str_op(s2,==,(from)); \
2387 tt_int_op(sz,==,strlen(from)); \
2393 #define DEC(from,want,dp) do { \
2394 s = evhttp_uridecode((from),(dp),&sz); \
2396 tt_str_op(s,==,(want)); \
2397 tt_int_op(sz,==,strlen(want)); \
2402 #define OLD_DEC(from,want) do { \
2403 s = evhttp_decode_uri((from)); \
2405 tt_str_op(s,==,(want)); \
2411 ENC("Hello", "Hello",0);
2415 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_",
2416 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_",0);
2419 ENC("\xff\xf0\xe0", "%FF%F0%E0",0);
2420 ENC("\x01\x19", "%01%19",1);
2421 ENC("http://www.ietf.org/rfc/rfc3986.txt",
2422 "http%3A%2F%2Fwww.ietf.org%2Frfc%2Frfc3986.txt",1);
2424 ENC("1+2=3", "1%2B2%3D3",1);
2425 ENC("1+2=3", "1%2B2%3D3",0);
2427 /* Now try encoding with internal NULs. */
2428 s
= evhttp_uriencode("hello\0world", 11, 0);
2430 tt_str_op(s
,==,"hello%00world");
2434 /* Now try out some decoding cases that we don't generate with
2435 * encode_uri: Make sure that malformed stuff doesn't crash... */
2436 DEC("%%xhello th+ere \xff",
2437 "%%xhello th+ere \xff", 0);
2438 /* Make sure plus decoding works */
2439 DEC("plus+should%20work+", "plus should work ",1);
2440 /* Try some lowercase hex */
2441 DEC("%f0%a0%b0", "\xf0\xa0\xb0",1);
2443 /* Try an internal NUL. */
2445 s
= evhttp_uridecode("%00%00x%00%00", 1, &sz
);
2447 tt_assert(!memcmp(s
, "\0\0x\0\0", 5));
2451 /* Try with size == NULL */
2453 s
= evhttp_uridecode("%00%00x%00%00", 1, NULL
);
2454 tt_assert(!memcmp(s
, "\0\0x\0\0", 5));
2458 /* Test out the crazy old behavior of the deprecated
2459 * evhttp_decode_uri */
2460 OLD_DEC("http://example.com/normal+path/?key=val+with+spaces",
2461 "http://example.com/normal+path/?key=val with spaces");
2474 http_base_test(void *ptr
)
2476 struct event_base
*base
= NULL
;
2477 struct bufferevent
*bev
;
2479 const char *http_request
;
2480 ev_uint16_t port
= 0;
2483 base
= event_base_new();
2484 http
= http_setup(&port
, base
);
2486 fd
= http_connect("127.0.0.1", port
);
2488 /* Stupid thing to send a request */
2489 bev
= bufferevent_socket_new(base
, fd
, 0);
2490 bufferevent_setcb(bev
, http_readcb
, http_writecb
,
2491 http_errorcb
, base
);
2492 bufferevent_base_set(base
, bev
);
2495 "GET /test HTTP/1.1\r\n"
2496 "Host: somehost\r\n"
2497 "Connection: close\r\n"
2500 bufferevent_write(bev
, http_request
, strlen(http_request
));
2502 event_base_dispatch(base
);
2504 bufferevent_free(bev
);
2505 evutil_closesocket(fd
);
2509 tt_int_op(test_ok
, ==, 2);
2513 event_base_free(base
);
2517 * the server is just going to close the connection if it times out during
2518 * reading the headers.
2522 http_incomplete_readcb(struct bufferevent
*bev
, void *arg
)
2525 event_base_loopexit(exit_base
,NULL
);
2529 http_incomplete_errorcb(struct bufferevent
*bev
, short what
, void *arg
)
2531 if (what
== (BEV_EVENT_READING
|BEV_EVENT_EOF
))
2535 event_base_loopexit(exit_base
,NULL
);
2539 http_incomplete_writecb(struct bufferevent
*bev
, void *arg
)
2542 evutil_socket_t fd
= *(evutil_socket_t
*)arg
;
2543 /* terminate the write side to simulate EOF */
2544 shutdown(fd
, SHUT_WR
);
2546 if (evbuffer_get_length(bufferevent_get_output(bev
)) == 0) {
2547 /* enable reading of the reply */
2548 bufferevent_enable(bev
, EV_READ
);
2554 _http_incomplete_test(struct basic_test_data
*data
, int use_timeout
)
2556 struct bufferevent
*bev
;
2558 const char *http_request
;
2559 ev_uint16_t port
= 0;
2560 struct timeval tv_start
, tv_end
;
2562 exit_base
= data
->base
;
2566 http
= http_setup(&port
, data
->base
);
2567 evhttp_set_timeout(http
, 1);
2569 fd
= http_connect("127.0.0.1", port
);
2571 /* Stupid thing to send a request */
2572 bev
= bufferevent_socket_new(data
->base
, fd
, 0);
2573 bufferevent_setcb(bev
,
2574 http_incomplete_readcb
, http_incomplete_writecb
,
2575 http_incomplete_errorcb
, use_timeout
? NULL
: &fd
);
2578 "GET /test HTTP/1.1\r\n"
2579 "Host: somehost\r\n";
2581 bufferevent_write(bev
, http_request
, strlen(http_request
));
2583 evutil_gettimeofday(&tv_start
, NULL
);
2585 event_base_dispatch(data
->base
);
2587 evutil_gettimeofday(&tv_end
, NULL
);
2588 evutil_timersub(&tv_end
, &tv_start
, &tv_end
);
2590 bufferevent_free(bev
);
2592 evutil_closesocket(fd
);
2597 if (use_timeout
&& tv_end
.tv_sec
>= 3) {
2598 tt_abort_msg("time");
2599 } else if (!use_timeout
&& tv_end
.tv_sec
>= 1) {
2600 /* we should be done immediately */
2601 tt_abort_msg("time");
2604 tt_int_op(test_ok
, ==, 2);
2609 http_incomplete_test(void *arg
)
2611 _http_incomplete_test(arg
, 0);
2614 http_incomplete_timeout_test(void *arg
)
2616 _http_incomplete_test(arg
, 1);
2620 * the server is going to reply with chunked data.
2624 http_chunked_readcb(struct bufferevent
*bev
, void *arg
)
2630 http_chunked_errorcb(struct bufferevent
*bev
, short what
, void *arg
)
2637 if ((what
& BEV_EVENT_EOF
) != 0) {
2638 struct evhttp_request
*req
= evhttp_request_new(NULL
, NULL
);
2640 enum message_read_status done
;
2642 /* req->kind = EVHTTP_RESPONSE; */
2643 done
= evhttp_parse_firstline(req
, bufferevent_get_input(bev
));
2644 if (done
!= ALL_DATA_READ
)
2647 done
= evhttp_parse_headers(req
, bufferevent_get_input(bev
));
2648 if (done
!= ALL_DATA_READ
)
2651 header
= evhttp_find_header(evhttp_request_get_input_headers(req
), "Transfer-Encoding");
2652 if (header
== NULL
|| strcmp(header
, "chunked"))
2655 header
= evhttp_find_header(evhttp_request_get_input_headers(req
), "Connection");
2656 if (header
== NULL
|| strcmp(header
, "close"))
2659 header
= evbuffer_readln(bufferevent_get_input(bev
), NULL
, EVBUFFER_EOL_CRLF
);
2663 if (strcmp(header
, "d")) {
2664 free((void*)header
);
2667 free((void*)header
);
2669 if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev
), 13),
2670 "This is funny", 13))
2673 evbuffer_drain(bufferevent_get_input(bev
), 13 + 2);
2675 header
= evbuffer_readln(bufferevent_get_input(bev
), NULL
, EVBUFFER_EOL_CRLF
);
2679 if (strcmp(header
, "12"))
2681 free((char *)header
);
2683 if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev
), 18),
2684 "but not hilarious.", 18))
2687 evbuffer_drain(bufferevent_get_input(bev
), 18 + 2);
2689 header
= evbuffer_readln(bufferevent_get_input(bev
), NULL
, EVBUFFER_EOL_CRLF
);
2693 if (strcmp(header
, "8")) {
2694 free((void*)header
);
2697 free((char *)header
);
2699 if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev
), 8),
2703 evbuffer_drain(bufferevent_get_input(bev
), 8 + 2);
2705 header
= evbuffer_readln(bufferevent_get_input(bev
), NULL
, EVBUFFER_EOL_CRLF
);
2709 if (strcmp(header
, "0")) {
2710 free((void*)header
);
2713 free((void *)header
);
2717 evhttp_request_free(req
);
2721 event_base_loopexit(arg
, NULL
);
2725 http_chunked_writecb(struct bufferevent
*bev
, void *arg
)
2727 if (evbuffer_get_length(bufferevent_get_output(bev
)) == 0) {
2728 /* enable reading of the reply */
2729 bufferevent_enable(bev
, EV_READ
);
2735 http_chunked_request_done(struct evhttp_request
*req
, void *arg
)
2737 if (evhttp_request_get_response_code(req
) != HTTP_OK
) {
2738 fprintf(stderr
, "FAILED\n");
2742 if (evhttp_find_header(evhttp_request_get_input_headers(req
),
2743 "Transfer-Encoding") == NULL
) {
2744 fprintf(stderr
, "FAILED\n");
2748 if (evbuffer_get_length(evhttp_request_get_input_buffer(req
)) != 13 + 18 + 8) {
2749 fprintf(stderr
, "FAILED\n");
2753 if (strncmp((char *)evbuffer_pullup(evhttp_request_get_input_buffer(req
), 13 + 18 + 8),
2754 "This is funnybut not hilarious.bwv 1052",
2756 fprintf(stderr
, "FAILED\n");
2761 event_base_loopexit(arg
, NULL
);
2765 http_chunk_out_test(void *arg
)
2767 struct basic_test_data
*data
= arg
;
2768 struct bufferevent
*bev
;
2770 const char *http_request
;
2771 ev_uint16_t port
= 0;
2772 struct timeval tv_start
, tv_end
;
2773 struct evhttp_connection
*evcon
= NULL
;
2774 struct evhttp_request
*req
= NULL
;
2777 exit_base
= data
->base
;
2780 http
= http_setup(&port
, data
->base
);
2782 fd
= http_connect("127.0.0.1", port
);
2784 /* Stupid thing to send a request */
2785 bev
= bufferevent_socket_new(data
->base
, fd
, 0);
2786 bufferevent_setcb(bev
,
2787 http_chunked_readcb
, http_chunked_writecb
,
2788 http_chunked_errorcb
, data
->base
);
2791 "GET /chunked HTTP/1.1\r\n"
2792 "Host: somehost\r\n"
2793 "Connection: close\r\n"
2796 bufferevent_write(bev
, http_request
, strlen(http_request
));
2798 evutil_gettimeofday(&tv_start
, NULL
);
2800 event_base_dispatch(data
->base
);
2802 bufferevent_free(bev
);
2804 evutil_gettimeofday(&tv_end
, NULL
);
2805 evutil_timersub(&tv_end
, &tv_start
, &tv_end
);
2807 tt_int_op(tv_end
.tv_sec
, <, 1);
2809 tt_int_op(test_ok
, ==, 2);
2811 /* now try again with the regular connection object */
2812 evcon
= evhttp_connection_base_new(data
->base
, NULL
, "127.0.0.1", port
);
2815 /* make two requests to check the keepalive behavior */
2816 for (i
= 0; i
< 2; i
++) {
2818 req
= evhttp_request_new(http_chunked_request_done
,data
->base
);
2820 /* Add the information that we care about */
2821 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
2823 /* We give ownership of the request to the connection */
2824 if (evhttp_make_request(evcon
, req
,
2825 EVHTTP_REQ_GET
, "/chunked") == -1) {
2826 tt_abort_msg("Couldn't make request");
2829 event_base_dispatch(data
->base
);
2831 tt_assert(test_ok
== 1);
2836 evhttp_connection_free(evcon
);
2842 http_stream_out_test(void *arg
)
2844 struct basic_test_data
*data
= arg
;
2845 ev_uint16_t port
= 0;
2846 struct evhttp_connection
*evcon
= NULL
;
2847 struct evhttp_request
*req
= NULL
;
2850 exit_base
= data
->base
;
2852 http
= http_setup(&port
, data
->base
);
2854 evcon
= evhttp_connection_base_new(data
->base
, NULL
, "127.0.0.1", port
);
2858 * At this point, we want to schedule a request to the HTTP
2859 * server using our make request method.
2862 req
= evhttp_request_new(http_request_done
,
2863 (void *)"This is funnybut not hilarious.bwv 1052");
2865 /* Add the information that we care about */
2866 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
2868 /* We give ownership of the request to the connection */
2869 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/streamed")
2871 tt_abort_msg("Couldn't make request");
2874 event_base_dispatch(data
->base
);
2878 evhttp_connection_free(evcon
);
2884 http_stream_in_chunk(struct evhttp_request
*req
, void *arg
)
2886 struct evbuffer
*reply
= arg
;
2888 if (evhttp_request_get_response_code(req
) != HTTP_OK
) {
2889 fprintf(stderr
, "FAILED\n");
2893 evbuffer_add_buffer(reply
, evhttp_request_get_input_buffer(req
));
2897 http_stream_in_done(struct evhttp_request
*req
, void *arg
)
2899 if (evbuffer_get_length(evhttp_request_get_input_buffer(req
)) != 0) {
2900 fprintf(stderr
, "FAILED\n");
2904 event_base_loopexit(exit_base
, NULL
);
2908 * Makes a request and reads the response in chunks.
2911 _http_stream_in_test(struct basic_test_data
*data
, char const *url
,
2912 size_t expected_len
, char const *expected
)
2914 struct evhttp_connection
*evcon
;
2915 struct evbuffer
*reply
= evbuffer_new();
2916 struct evhttp_request
*req
= NULL
;
2917 ev_uint16_t port
= 0;
2919 exit_base
= data
->base
;
2920 http
= http_setup(&port
, data
->base
);
2922 evcon
= evhttp_connection_base_new(data
->base
, NULL
,"127.0.0.1", port
);
2925 req
= evhttp_request_new(http_stream_in_done
, reply
);
2926 evhttp_request_set_chunked_cb(req
, http_stream_in_chunk
);
2928 /* We give ownership of the request to the connection */
2929 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, url
) == -1) {
2930 tt_abort_msg("Couldn't make request");
2933 event_base_dispatch(data
->base
);
2935 if (evbuffer_get_length(reply
) != expected_len
) {
2936 TT_DIE(("reply length %lu; expected %lu; FAILED (%s)\n",
2937 (unsigned long)evbuffer_get_length(reply
),
2938 (unsigned long)expected_len
,
2939 (char*)evbuffer_pullup(reply
, -1)));
2942 if (memcmp(evbuffer_pullup(reply
, -1), expected
, expected_len
) != 0) {
2943 tt_abort_msg("Memory mismatch");
2949 evbuffer_free(reply
);
2951 evhttp_connection_free(evcon
);
2957 http_stream_in_test(void *arg
)
2959 _http_stream_in_test(arg
, "/chunked", 13 + 18 + 8,
2960 "This is funnybut not hilarious.bwv 1052");
2962 _http_stream_in_test(arg
, "/test", strlen(BASIC_REQUEST_BODY
),
2963 BASIC_REQUEST_BODY
);
2967 http_stream_in_cancel_chunk(struct evhttp_request
*req
, void *arg
)
2969 tt_int_op(evhttp_request_get_response_code(req
), ==, HTTP_OK
);
2972 evhttp_cancel_request(req
);
2973 event_base_loopexit(arg
, NULL
);
2977 http_stream_in_cancel_done(struct evhttp_request
*req
, void *arg
)
2979 /* should never be called */
2980 tt_fail_msg("In cancel done");
2984 http_stream_in_cancel_test(void *arg
)
2986 struct basic_test_data
*data
= arg
;
2987 struct evhttp_connection
*evcon
;
2988 struct evhttp_request
*req
= NULL
;
2989 ev_uint16_t port
= 0;
2991 http
= http_setup(&port
, data
->base
);
2993 evcon
= evhttp_connection_base_new(data
->base
, NULL
, "127.0.0.1", port
);
2996 req
= evhttp_request_new(http_stream_in_cancel_done
, data
->base
);
2997 evhttp_request_set_chunked_cb(req
, http_stream_in_cancel_chunk
);
2999 /* We give ownership of the request to the connection */
3000 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/chunked") == -1) {
3001 tt_abort_msg("Couldn't make request");
3004 event_base_dispatch(data
->base
);
3008 evhttp_connection_free(evcon
);
3014 http_connection_fail_done(struct evhttp_request
*req
, void *arg
)
3016 /* An ENETUNREACH error results in an unrecoverable
3017 * evhttp_connection error (see evhttp_connection_fail()). The
3018 * connection will be reset, and the user will be notified with a NULL
3025 event_base_loopexit(arg
, NULL
);
3028 /* Test unrecoverable evhttp_connection errors by generating an ENETUNREACH
3029 * error on connection. */
3031 http_connection_fail_test(void *arg
)
3033 struct basic_test_data
*data
= arg
;
3034 ev_uint16_t port
= 0;
3035 struct evhttp_connection
*evcon
= NULL
;
3036 struct evhttp_request
*req
= NULL
;
3038 exit_base
= data
->base
;
3041 /* auto detect a port */
3042 http
= http_setup(&port
, data
->base
);
3046 /* Pick an unroutable address. This administratively scoped multicast
3047 * address should do when working with TCP. */
3048 evcon
= evhttp_connection_base_new(data
->base
, NULL
, "239.10.20.30", 80);
3052 * At this point, we want to schedule an HTTP GET request
3053 * server using our make request method.
3056 req
= evhttp_request_new(http_connection_fail_done
, data
->base
);
3059 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/") == -1) {
3060 tt_abort_msg("Couldn't make request");
3063 event_base_dispatch(data
->base
);
3065 tt_int_op(test_ok
, ==, 1);
3069 evhttp_connection_free(evcon
);
3073 http_connection_retry_done(struct evhttp_request
*req
, void *arg
)
3076 tt_int_op(evhttp_request_get_response_code(req
), !=, HTTP_OK
);
3077 if (evhttp_find_header(evhttp_request_get_input_headers(req
), "Content-Type") != NULL
) {
3078 tt_abort_msg("(content type)\n");
3081 tt_uint_op(evbuffer_get_length(evhttp_request_get_input_buffer(req
)), ==, 0);
3085 event_base_loopexit(arg
,NULL
);
3088 static struct event_base
*http_make_web_server_base
=NULL
;
3090 http_make_web_server(evutil_socket_t fd
, short what
, void *arg
)
3092 ev_uint16_t port
= *(ev_uint16_t
*)arg
;
3093 http
= http_setup(&port
, http_make_web_server_base
);
3097 http_connection_retry_test(void *arg
)
3099 struct basic_test_data
*data
= arg
;
3100 ev_uint16_t port
= 0;
3101 struct evhttp_connection
*evcon
= NULL
;
3102 struct evhttp_request
*req
= NULL
;
3103 struct timeval tv
, tv_start
, tv_end
;
3105 exit_base
= data
->base
;
3108 /* auto detect a port */
3109 http
= http_setup(&port
, data
->base
);
3113 evcon
= evhttp_connection_base_new(data
->base
, NULL
, "127.0.0.1", port
);
3116 evhttp_connection_set_timeout(evcon
, 1);
3117 /* also bind to local host */
3118 evhttp_connection_set_local_address(evcon
, "127.0.0.1");
3121 * At this point, we want to schedule an HTTP GET request
3122 * server using our make request method.
3125 req
= evhttp_request_new(http_connection_retry_done
, data
->base
);
3128 /* Add the information that we care about */
3129 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
3131 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
,
3132 "/?arg=val") == -1) {
3133 tt_abort_msg("Couldn't make request");
3136 evutil_gettimeofday(&tv_start
, NULL
);
3137 event_base_dispatch(data
->base
);
3138 evutil_gettimeofday(&tv_end
, NULL
);
3139 evutil_timersub(&tv_end
, &tv_start
, &tv_end
);
3140 tt_int_op(tv_end
.tv_sec
, <, 1);
3142 tt_int_op(test_ok
, ==, 1);
3145 * now test the same but with retries
3149 evhttp_connection_set_timeout(evcon
, 1);
3150 evhttp_connection_set_retries(evcon
, 1);
3152 req
= evhttp_request_new(http_connection_retry_done
, data
->base
);
3155 /* Add the information that we care about */
3156 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
3158 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
,
3159 "/?arg=val") == -1) {
3160 tt_abort_msg("Couldn't make request");
3163 evutil_gettimeofday(&tv_start
, NULL
);
3164 event_base_dispatch(data
->base
);
3165 evutil_gettimeofday(&tv_end
, NULL
);
3166 evutil_timersub(&tv_end
, &tv_start
, &tv_end
);
3167 tt_int_op(tv_end
.tv_sec
, >, 1);
3168 tt_int_op(tv_end
.tv_sec
, <, 6);
3170 tt_assert(test_ok
== 1);
3173 * now test the same but with retries and give it a web server
3178 evhttp_connection_set_timeout(evcon
, 1);
3179 evhttp_connection_set_retries(evcon
, 3);
3181 req
= evhttp_request_new(http_dispatcher_test_done
, data
->base
);
3184 /* Add the information that we care about */
3185 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
3187 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
,
3188 "/?arg=val") == -1) {
3189 tt_abort_msg("Couldn't make request");
3192 /* start up a web server one second after the connection tried
3195 evutil_timerclear(&tv
);
3197 http_make_web_server_base
= data
->base
;
3198 event_base_once(data
->base
, -1, EV_TIMEOUT
, http_make_web_server
, &port
, &tv
);
3200 evutil_gettimeofday(&tv_start
, NULL
);
3201 event_base_dispatch(data
->base
);
3202 evutil_gettimeofday(&tv_end
, NULL
);
3204 evutil_timersub(&tv_end
, &tv_start
, &tv_end
);
3206 tt_int_op(tv_end
.tv_sec
, >, 1);
3207 tt_int_op(tv_end
.tv_sec
, <, 6);
3209 tt_int_op(test_ok
, ==, 1);
3213 evhttp_connection_free(evcon
);
3219 http_primitives(void *ptr
)
3221 char *escaped
= NULL
;
3222 struct evhttp
*http
= NULL
;
3224 escaped
= evhttp_htmlescape("<script>");
3226 tt_str_op(escaped
, ==, "<script>");
3229 escaped
= evhttp_htmlescape("\"\'&");
3231 tt_str_op(escaped
, ==, ""'&");
3233 http
= evhttp_new(NULL
);
3235 tt_int_op(evhttp_set_cb(http
, "/test", http_basic_cb
, NULL
), ==, 0);
3236 tt_int_op(evhttp_set_cb(http
, "/test", http_basic_cb
, NULL
), ==, -1);
3237 tt_int_op(evhttp_del_cb(http
, "/test"), ==, 0);
3238 tt_int_op(evhttp_del_cb(http
, "/test"), ==, -1);
3239 tt_int_op(evhttp_set_cb(http
, "/test", http_basic_cb
, NULL
), ==, 0);
3249 http_multi_line_header_test(void *arg
)
3251 struct basic_test_data
*data
= arg
;
3252 struct bufferevent
*bev
= NULL
;
3253 evutil_socket_t fd
= -1;
3254 const char *http_start_request
;
3255 ev_uint16_t port
= 0;
3259 http
= http_setup(&port
, data
->base
);
3261 fd
= http_connect("127.0.0.1", port
);
3263 /* Stupid thing to send a request */
3264 bev
= bufferevent_socket_new(data
->base
, fd
, 0);
3265 bufferevent_setcb(bev
, http_readcb
, http_writecb
,
3266 http_errorcb
, data
->base
);
3268 http_start_request
=
3269 "GET /test HTTP/1.1\r\n"
3270 "Host: somehost\r\n"
3271 "Connection: close\r\n"
3272 "X-Multi: aaaaaaaa\r\n"
3278 bufferevent_write(bev
, http_start_request
, strlen(http_start_request
));
3280 event_base_dispatch(data
->base
);
3282 tt_int_op(test_ok
, ==, 4);
3285 bufferevent_free(bev
);
3287 evutil_closesocket(fd
);
3293 http_request_bad(struct evhttp_request
*req
, void *arg
)
3296 fprintf(stderr
, "FAILED\n");
3301 event_base_loopexit(arg
, NULL
);
3305 http_negative_content_length_test(void *arg
)
3307 struct basic_test_data
*data
= arg
;
3308 ev_uint16_t port
= 0;
3309 struct evhttp_connection
*evcon
= NULL
;
3310 struct evhttp_request
*req
= NULL
;
3314 http
= http_setup(&port
, data
->base
);
3316 evcon
= evhttp_connection_base_new(data
->base
, NULL
, "127.0.0.1", port
);
3320 * At this point, we want to schedule a request to the HTTP
3321 * server using our make request method.
3324 req
= evhttp_request_new(http_request_bad
, data
->base
);
3326 /* Cause the response to have a negative content-length */
3327 evhttp_add_header(evhttp_request_get_output_headers(req
), "X-Negative", "makeitso");
3329 /* We give ownership of the request to the connection */
3330 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
3331 tt_abort_msg("Couldn't make request");
3334 event_base_dispatch(data
->base
);
3338 evhttp_connection_free(evcon
);
3345 http_data_length_constraints_test_done(struct evhttp_request
*req
, void *arg
)
3348 tt_int_op(evhttp_request_get_response_code(req
), ==, HTTP_BADREQUEST
);
3350 event_base_loopexit(arg
, NULL
);
3354 http_large_entity_test_done(struct evhttp_request
*req
, void *arg
)
3357 tt_int_op(evhttp_request_get_response_code(req
), ==, HTTP_ENTITYTOOLARGE
);
3359 event_base_loopexit(arg
, NULL
);
3363 http_data_length_constraints_test(void *arg
)
3365 struct basic_test_data
*data
= arg
;
3366 ev_uint16_t port
= 0;
3367 struct evhttp_connection
*evcon
= NULL
;
3368 struct evhttp_request
*req
= NULL
;
3369 char long_str
[8192];
3373 http
= http_setup(&port
, data
->base
);
3375 evcon
= evhttp_connection_base_new(data
->base
, NULL
, "127.0.0.1", port
);
3378 /* also bind to local host */
3379 evhttp_connection_set_local_address(evcon
, "127.0.0.1");
3382 * At this point, we want to schedule an HTTP GET request
3383 * server using our make request method.
3386 req
= evhttp_request_new(http_data_length_constraints_test_done
, data
->base
);
3389 memset(long_str
, 'a', 8192);
3390 long_str
[8191] = '\0';
3391 /* Add the information that we care about */
3392 evhttp_set_max_headers_size(http
, 8191);
3393 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
3394 evhttp_add_header(evhttp_request_get_output_headers(req
), "Longheader", long_str
);
3396 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/?arg=val") == -1) {
3397 tt_abort_msg("Couldn't make request");
3399 event_base_dispatch(data
->base
);
3401 req
= evhttp_request_new(http_data_length_constraints_test_done
, data
->base
);
3403 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
3405 /* GET /?arg=verylongvalue HTTP/1.1 */
3406 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, long_str
) == -1) {
3407 tt_abort_msg("Couldn't make request");
3409 event_base_dispatch(data
->base
);
3411 evhttp_set_max_body_size(http
, 8190);
3412 req
= evhttp_request_new(http_data_length_constraints_test_done
, data
->base
);
3413 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
3414 evbuffer_add_printf(evhttp_request_get_output_buffer(req
), "%s", long_str
);
3415 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_POST
, "/") == -1) {
3416 tt_abort_msg("Couldn't make request");
3418 event_base_dispatch(data
->base
);
3420 req
= evhttp_request_new(http_large_entity_test_done
, data
->base
);
3421 evhttp_add_header(evhttp_request_get_output_headers(req
), "Host", "somehost");
3422 evhttp_add_header(evhttp_request_get_output_headers(req
), "Expect", "100-continue");
3423 evbuffer_add_printf(evhttp_request_get_output_buffer(req
), "%s", long_str
);
3424 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_POST
, "/") == -1) {
3425 tt_abort_msg("Couldn't make request");
3427 event_base_dispatch(data
->base
);
3432 evhttp_connection_free(evcon
);
3438 * Testing client reset of server chunked connections
3441 struct terminate_state
{
3442 struct event_base
*base
;
3443 struct evhttp_request
*req
;
3444 struct bufferevent
*bev
;
3450 terminate_chunked_trickle_cb(evutil_socket_t fd
, short events
, void *arg
)
3452 struct terminate_state
*state
= arg
;
3453 struct evbuffer
*evb
;
3456 if (evhttp_request_get_connection(state
->req
) == NULL
) {
3458 evhttp_request_free(state
->req
);
3459 event_base_loopexit(state
->base
,NULL
);
3463 evb
= evbuffer_new();
3464 evbuffer_add_printf(evb
, "%p", evb
);
3465 evhttp_send_reply_chunk(state
->req
, evb
);
3470 EVUTIL_ASSERT(state
);
3471 EVUTIL_ASSERT(state
->base
);
3472 event_base_once(state
->base
, -1, EV_TIMEOUT
, terminate_chunked_trickle_cb
, arg
, &tv
);
3476 terminate_chunked_close_cb(struct evhttp_connection
*evcon
, void *arg
)
3478 struct terminate_state
*state
= arg
;
3479 state
->gotclosecb
= 1;
3483 terminate_chunked_cb(struct evhttp_request
*req
, void *arg
)
3485 struct terminate_state
*state
= arg
;
3488 /* we want to know if this connection closes on us */
3489 evhttp_connection_set_closecb(
3490 evhttp_request_get_connection(req
),
3491 terminate_chunked_close_cb
, arg
);
3495 evhttp_send_reply_start(req
, HTTP_OK
, "OK");
3499 event_base_once(state
->base
, -1, EV_TIMEOUT
, terminate_chunked_trickle_cb
, arg
, &tv
);
3503 terminate_chunked_client(evutil_socket_t fd
, short event
, void *arg
)
3505 struct terminate_state
*state
= arg
;
3506 bufferevent_free(state
->bev
);
3507 evutil_closesocket(state
->fd
);
3511 terminate_readcb(struct bufferevent
*bev
, void *arg
)
3513 /* just drop the data */
3514 evbuffer_drain(bufferevent_get_input(bev
), -1);
3519 http_terminate_chunked_test(void *arg
)
3521 struct basic_test_data
*data
= arg
;
3522 struct bufferevent
*bev
= NULL
;
3524 const char *http_request
;
3525 ev_uint16_t port
= 0;
3526 evutil_socket_t fd
= -1;
3527 struct terminate_state terminate_state
;
3531 http
= http_setup(&port
, data
->base
);
3532 evhttp_del_cb(http
, "/test");
3533 tt_assert(evhttp_set_cb(http
, "/test",
3534 terminate_chunked_cb
, &terminate_state
) == 0);
3536 fd
= http_connect("127.0.0.1", port
);
3538 /* Stupid thing to send a request */
3539 bev
= bufferevent_socket_new(data
->base
, fd
, 0);
3540 bufferevent_setcb(bev
, terminate_readcb
, http_writecb
,
3541 http_errorcb
, data
->base
);
3543 memset(&terminate_state
, 0, sizeof(terminate_state
));
3544 terminate_state
.base
= data
->base
;
3545 terminate_state
.fd
= fd
;
3546 terminate_state
.bev
= bev
;
3547 terminate_state
.gotclosecb
= 0;
3549 /* first half of the http request */
3551 "GET /test HTTP/1.1\r\n"
3552 "Host: some\r\n\r\n";
3554 bufferevent_write(bev
, http_request
, strlen(http_request
));
3555 evutil_timerclear(&tv
);
3557 event_base_once(data
->base
, -1, EV_TIMEOUT
, terminate_chunked_client
, &terminate_state
,
3560 event_base_dispatch(data
->base
);
3562 if (terminate_state
.gotclosecb
== 0)
3567 evutil_closesocket(fd
);
3572 #define HTTP_LEGACY(name) \
3573 { #name, run_legacy_test_fn, TT_ISOLATED|TT_LEGACY, &legacy_setup, \
3574 http_##name##_test }
3576 #define HTTP(name) \
3577 { #name, http_##name##_test, TT_ISOLATED, &basic_setup, NULL }
3579 struct testcase_t http_testcases
[] = {
3580 { "primitives", http_primitives
, 0, NULL
, NULL
},
3581 { "base", http_base_test
, TT_FORK
, NULL
, NULL
},
3582 { "bad_headers", http_bad_header_test
, 0, NULL
, NULL
},
3583 { "parse_query", http_parse_query_test
, 0, NULL
, NULL
},
3584 { "parse_uri", http_parse_uri_test
, 0, NULL
, NULL
},
3585 { "parse_uri_nc", http_parse_uri_test
, 0, &basic_setup
, (void*)"nc" },
3586 { "uriencode", http_uriencode_test
, 0, NULL
, NULL
},
3593 HTTP(allowed_methods
),
3596 HTTP(persist_connection
),
3597 HTTP(connection_async
),
3598 HTTP(close_detection
),
3599 HTTP(close_detection_delay
),
3602 HTTP(incomplete_timeout
),
3603 HTTP(terminate_chunked
),
3607 HTTP(multi_line_header
),
3608 HTTP(negative_content_length
),
3613 HTTP(stream_in_cancel
),
3615 HTTP(connection_fail
),
3616 HTTP(connection_retry
),
3617 HTTP(data_length_constraints
),