3 * Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #include <sys/types.h>
40 #ifdef HAVE_SYS_TIME_H
43 #include <sys/queue.h>
45 #include <sys/socket.h>
59 #include "http-internal.h"
64 static struct evhttp
*http
;
65 /* set if a test needs to call loopexit on a base */
66 static struct event_base
*base
;
68 void http_suite(void);
70 void http_basic_cb(struct evhttp_request
*req
, void *arg
);
71 static void http_chunked_cb(struct evhttp_request
*req
, void *arg
);
72 void http_post_cb(struct evhttp_request
*req
, void *arg
);
73 void http_dispatcher_cb(struct evhttp_request
*req
, void *arg
);
74 static void http_large_delay_cb(struct evhttp_request
*req
, void *arg
);
76 static struct evhttp
*
77 http_setup(short *pport
, struct event_base
*base
)
80 struct evhttp
*myhttp
;
83 /* Try a few different ports */
84 myhttp
= evhttp_new(base
);
85 for (i
= 0; i
< 50; ++i
) {
86 if (evhttp_bind_socket(myhttp
, "127.0.0.1", 8080 + i
) != -1) {
93 event_errx(1, "Could not start web server");
95 /* Register a callback for certain types of requests */
96 evhttp_set_cb(myhttp
, "/test", http_basic_cb
, NULL
);
97 evhttp_set_cb(myhttp
, "/chunked", http_chunked_cb
, NULL
);
98 evhttp_set_cb(myhttp
, "/postit", http_post_cb
, NULL
);
99 evhttp_set_cb(myhttp
, "/largedelay", http_large_delay_cb
, NULL
);
100 evhttp_set_cb(myhttp
, "/", http_dispatcher_cb
, NULL
);
107 #define NI_MAXSERV 1024
111 http_connect(const char *address
, u_short port
)
113 /* Stupid code for connecting */
116 struct sockaddr_in sin
;
118 struct addrinfo ai
, *aitop
;
119 char strport
[NI_MAXSERV
];
126 if (!(he
= gethostbyname(address
))) {
127 event_warn("gethostbyname");
129 memcpy(&sin
.sin_addr
, he
->h_addr_list
[0], he
->h_length
);
130 sin
.sin_family
= AF_INET
;
131 sin
.sin_port
= htons(port
);
132 slen
= sizeof(struct sockaddr_in
);
133 sa
= (struct sockaddr
*)&sin
;
135 memset(&ai
, 0, sizeof (ai
));
136 ai
.ai_family
= AF_INET
;
137 ai
.ai_socktype
= SOCK_STREAM
;
138 snprintf(strport
, sizeof (strport
), "%d", port
);
139 if (getaddrinfo(address
, strport
, &ai
, &aitop
) != 0) {
140 event_warn("getaddrinfo");
144 slen
= aitop
->ai_addrlen
;
147 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
149 event_err(1, "socket failed");
151 if (connect(fd
, sa
, slen
) == -1)
152 event_err(1, "connect failed");
162 http_readcb(struct bufferevent
*bev
, void *arg
)
164 const char *what
= "This is funny";
166 event_debug(("%s: %s\n", __func__
, EVBUFFER_DATA(bev
->input
)));
168 if (evbuffer_find(bev
->input
,
169 (const unsigned char*) what
, strlen(what
)) != NULL
) {
170 struct evhttp_request
*req
= evhttp_request_new(NULL
, NULL
);
171 enum message_read_status done
;
173 req
->kind
= EVHTTP_RESPONSE
;
174 done
= evhttp_parse_firstline(req
, bev
->input
);
175 if (done
!= ALL_DATA_READ
)
178 done
= evhttp_parse_headers(req
, bev
->input
);
179 if (done
!= ALL_DATA_READ
)
183 evhttp_find_header(req
->input_headers
,
184 "Content-Type") != NULL
)
188 evhttp_request_free(req
);
189 bufferevent_disable(bev
, EV_READ
);
191 event_base_loopexit(base
, NULL
);
193 event_loopexit(NULL
);
198 http_writecb(struct bufferevent
*bev
, void *arg
)
200 if (EVBUFFER_LENGTH(bev
->output
) == 0) {
201 /* enable reading of the reply */
202 bufferevent_enable(bev
, EV_READ
);
208 http_errorcb(struct bufferevent
*bev
, short what
, void *arg
)
211 event_loopexit(NULL
);
215 http_basic_cb(struct evhttp_request
*req
, void *arg
)
217 struct evbuffer
*evb
= evbuffer_new();
218 int empty
= evhttp_find_header(req
->input_headers
, "Empty") != NULL
;
219 event_debug(("%s: called\n", __func__
));
220 evbuffer_add_printf(evb
, "This is funny");
222 /* For multi-line headers test */
225 evhttp_find_header(req
->input_headers
,"X-multi");
227 if (strcmp("END", multi
+ strlen(multi
) - 3) == 0)
229 if (evhttp_find_header(req
->input_headers
, "X-Last"))
234 /* injecting a bad content-length */
235 if (evhttp_find_header(req
->input_headers
, "X-Negative"))
236 evhttp_add_header(req
->output_headers
,
237 "Content-Length", "-100");
239 /* allow sending of an empty reply */
240 evhttp_send_reply(req
, HTTP_OK
, "Everything is fine",
241 !empty
? evb
: NULL
);
246 static char const* const CHUNKS
[] = {
248 "but not hilarious.",
252 struct chunk_req_state
{
253 struct evhttp_request
*req
;
258 http_chunked_trickle_cb(int fd
, short events
, void *arg
)
260 struct evbuffer
*evb
= evbuffer_new();
261 struct chunk_req_state
*state
= arg
;
262 struct timeval when
= { 0, 0 };
264 evbuffer_add_printf(evb
, "%s", CHUNKS
[state
->i
]);
265 evhttp_send_reply_chunk(state
->req
, evb
);
268 if (++state
->i
< sizeof(CHUNKS
)/sizeof(CHUNKS
[0])) {
269 event_once(-1, EV_TIMEOUT
,
270 http_chunked_trickle_cb
, state
, &when
);
272 evhttp_send_reply_end(state
->req
);
278 http_chunked_cb(struct evhttp_request
*req
, void *arg
)
280 struct timeval when
= { 0, 0 };
281 struct chunk_req_state
*state
= malloc(sizeof(struct chunk_req_state
));
282 event_debug(("%s: called\n", __func__
));
284 memset(state
, 0, sizeof(struct chunk_req_state
));
287 /* generate a chunked reply */
288 evhttp_send_reply_start(req
, HTTP_OK
, "Everything is fine");
290 /* but trickle it across several iterations to ensure we're not
291 * assuming it comes all at once */
292 event_once(-1, EV_TIMEOUT
, http_chunked_trickle_cb
, state
, &when
);
296 http_complete_write(int fd
, short what
, void *arg
)
298 struct bufferevent
*bev
= arg
;
299 const char *http_request
= "host\r\n"
300 "Connection: close\r\n"
302 bufferevent_write(bev
, http_request
, strlen(http_request
));
306 http_basic_test(void)
309 struct bufferevent
*bev
;
311 const char *http_request
;
315 fprintf(stdout
, "Testing Basic HTTP Server: ");
317 http
= http_setup(&port
, NULL
);
319 /* bind to a second socket */
320 if (evhttp_bind_socket(http
, "127.0.0.1", port
+ 1) == -1) {
321 fprintf(stdout
, "FAILED (bind)\n");
325 fd
= http_connect("127.0.0.1", port
);
327 /* Stupid thing to send a request */
328 bev
= bufferevent_new(fd
, http_readcb
, http_writecb
,
331 /* first half of the http request */
333 "GET /test HTTP/1.1\r\n"
336 bufferevent_write(bev
, http_request
, strlen(http_request
));
339 event_once(-1, EV_TIMEOUT
, http_complete_write
, bev
, &tv
);
344 fprintf(stdout
, "FAILED\n");
348 /* connect to the second port */
349 bufferevent_free(bev
);
350 EVUTIL_CLOSESOCKET(fd
);
352 fd
= http_connect("127.0.0.1", port
+ 1);
354 /* Stupid thing to send a request */
355 bev
= bufferevent_new(fd
, http_readcb
, http_writecb
,
359 "GET /test HTTP/1.1\r\n"
361 "Connection: close\r\n"
364 bufferevent_write(bev
, http_request
, strlen(http_request
));
368 bufferevent_free(bev
);
369 EVUTIL_CLOSESOCKET(fd
);
374 fprintf(stdout
, "FAILED\n");
378 fprintf(stdout
, "OK\n");
381 static struct evhttp_connection
*delayed_client
;
384 http_delay_reply(int fd
, short what
, void *arg
)
386 struct evhttp_request
*req
= arg
;
388 evhttp_send_reply(req
, HTTP_OK
, "Everything is fine", NULL
);
394 http_large_delay_cb(struct evhttp_request
*req
, void *arg
)
400 event_once(-1, EV_TIMEOUT
, http_delay_reply
, req
, &tv
);
402 /* here we close the client connection which will cause an EOF */
403 evhttp_connection_fail(delayed_client
, EVCON_HTTP_EOF
);
406 void http_request_done(struct evhttp_request
*, void *);
407 void http_request_empty_done(struct evhttp_request
*, void *);
410 http_connection_test(int persistent
)
413 struct evhttp_connection
*evcon
= NULL
;
414 struct evhttp_request
*req
= NULL
;
417 fprintf(stdout
, "Testing Request Connection Pipeline %s: ",
418 persistent
? "(persistent)" : "");
420 http
= http_setup(&port
, NULL
);
422 evcon
= evhttp_connection_new("127.0.0.1", port
);
424 fprintf(stdout
, "FAILED\n");
429 * At this point, we want to schedule a request to the HTTP
430 * server using our make request method.
433 req
= evhttp_request_new(http_request_done
, NULL
);
435 /* Add the information that we care about */
436 evhttp_add_header(req
->output_headers
, "Host", "somehost");
438 /* We give ownership of the request to the connection */
439 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
440 fprintf(stdout
, "FAILED\n");
447 fprintf(stdout
, "FAILED\n");
451 /* try to make another request over the same connection */
454 req
= evhttp_request_new(http_request_done
, NULL
);
456 /* Add the information that we care about */
457 evhttp_add_header(req
->output_headers
, "Host", "somehost");
460 * if our connections are not supposed to be persistent; request
461 * a close from the server.
464 evhttp_add_header(req
->output_headers
, "Connection", "close");
466 /* We give ownership of the request to the connection */
467 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
468 fprintf(stdout
, "FAILED\n");
474 /* make another request: request empty reply */
477 req
= evhttp_request_new(http_request_empty_done
, NULL
);
479 /* Add the information that we care about */
480 evhttp_add_header(req
->output_headers
, "Empty", "itis");
482 /* We give ownership of the request to the connection */
483 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
484 fprintf(stdout
, "FAILED\n");
491 fprintf(stdout
, "FAILED\n");
495 evhttp_connection_free(evcon
);
498 fprintf(stdout
, "OK\n");
502 http_request_done(struct evhttp_request
*req
, void *arg
)
504 const char *what
= "This is funny";
506 if (req
->response_code
!= HTTP_OK
) {
507 fprintf(stderr
, "FAILED\n");
511 if (evhttp_find_header(req
->input_headers
, "Content-Type") == NULL
) {
512 fprintf(stderr
, "FAILED\n");
516 if (EVBUFFER_LENGTH(req
->input_buffer
) != strlen(what
)) {
517 fprintf(stderr
, "FAILED\n");
521 if (memcmp(EVBUFFER_DATA(req
->input_buffer
), what
, strlen(what
)) != 0) {
522 fprintf(stderr
, "FAILED\n");
527 event_loopexit(NULL
);
530 /* test date header and content length */
533 http_request_empty_done(struct evhttp_request
*req
, void *arg
)
535 if (req
->response_code
!= HTTP_OK
) {
536 fprintf(stderr
, "FAILED\n");
540 if (evhttp_find_header(req
->input_headers
, "Date") == NULL
) {
541 fprintf(stderr
, "FAILED\n");
546 if (evhttp_find_header(req
->input_headers
, "Content-Length") == NULL
) {
547 fprintf(stderr
, "FAILED\n");
551 if (strcmp(evhttp_find_header(req
->input_headers
, "Content-Length"),
553 fprintf(stderr
, "FAILED\n");
557 if (EVBUFFER_LENGTH(req
->input_buffer
) != 0) {
558 fprintf(stderr
, "FAILED\n");
563 event_loopexit(NULL
);
567 * HTTP DISPATCHER test
571 http_dispatcher_cb(struct evhttp_request
*req
, void *arg
)
574 struct evbuffer
*evb
= evbuffer_new();
575 event_debug(("%s: called\n", __func__
));
576 evbuffer_add_printf(evb
, "DISPATCHER_TEST");
578 evhttp_send_reply(req
, HTTP_OK
, "Everything is fine", evb
);
584 http_dispatcher_test_done(struct evhttp_request
*req
, void *arg
)
586 const char *what
= "DISPATCHER_TEST";
588 if (req
->response_code
!= HTTP_OK
) {
589 fprintf(stderr
, "FAILED\n");
593 if (evhttp_find_header(req
->input_headers
, "Content-Type") == NULL
) {
594 fprintf(stderr
, "FAILED (content type)\n");
598 if (EVBUFFER_LENGTH(req
->input_buffer
) != strlen(what
)) {
599 fprintf(stderr
, "FAILED (length %zu vs %zu)\n",
600 EVBUFFER_LENGTH(req
->input_buffer
), strlen(what
));
604 if (memcmp(EVBUFFER_DATA(req
->input_buffer
), what
, strlen(what
)) != 0) {
605 fprintf(stderr
, "FAILED (data)\n");
610 event_loopexit(NULL
);
614 http_dispatcher_test(void)
617 struct evhttp_connection
*evcon
= NULL
;
618 struct evhttp_request
*req
= NULL
;
621 fprintf(stdout
, "Testing HTTP Dispatcher: ");
623 http
= http_setup(&port
, NULL
);
625 evcon
= evhttp_connection_new("127.0.0.1", port
);
627 fprintf(stdout
, "FAILED\n");
631 /* also bind to local host */
632 evhttp_connection_set_local_address(evcon
, "127.0.0.1");
635 * At this point, we want to schedule an HTTP GET request
636 * server using our make request method.
639 req
= evhttp_request_new(http_dispatcher_test_done
, NULL
);
641 fprintf(stdout
, "FAILED\n");
645 /* Add the information that we care about */
646 evhttp_add_header(req
->output_headers
, "Host", "somehost");
648 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/?arg=val") == -1) {
649 fprintf(stdout
, "FAILED\n");
655 evhttp_connection_free(evcon
);
659 fprintf(stdout
, "FAILED: %d\n", test_ok
);
663 fprintf(stdout
, "OK\n");
670 void http_postrequest_done(struct evhttp_request
*, void *);
672 #define POST_DATA "Okay. Not really printf"
678 struct evhttp_connection
*evcon
= NULL
;
679 struct evhttp_request
*req
= NULL
;
682 fprintf(stdout
, "Testing HTTP POST Request: ");
684 http
= http_setup(&port
, NULL
);
686 evcon
= evhttp_connection_new("127.0.0.1", port
);
688 fprintf(stdout
, "FAILED\n");
693 * At this point, we want to schedule an HTTP POST request
694 * server using our make request method.
697 req
= evhttp_request_new(http_postrequest_done
, NULL
);
699 fprintf(stdout
, "FAILED\n");
703 /* Add the information that we care about */
704 evhttp_add_header(req
->output_headers
, "Host", "somehost");
705 evbuffer_add_printf(req
->output_buffer
, POST_DATA
);
707 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_POST
, "/postit") == -1) {
708 fprintf(stdout
, "FAILED\n");
714 evhttp_connection_free(evcon
);
718 fprintf(stdout
, "FAILED: %d\n", test_ok
);
722 fprintf(stdout
, "OK\n");
726 http_post_cb(struct evhttp_request
*req
, void *arg
)
728 struct evbuffer
*evb
;
729 event_debug(("%s: called\n", __func__
));
731 /* Yes, we are expecting a post request */
732 if (req
->type
!= EVHTTP_REQ_POST
) {
733 fprintf(stdout
, "FAILED (post type)\n");
737 if (EVBUFFER_LENGTH(req
->input_buffer
) != strlen(POST_DATA
)) {
738 fprintf(stdout
, "FAILED (length: %zu vs %zu)\n",
739 EVBUFFER_LENGTH(req
->input_buffer
), strlen(POST_DATA
));
743 if (memcmp(EVBUFFER_DATA(req
->input_buffer
), POST_DATA
,
744 strlen(POST_DATA
))) {
745 fprintf(stdout
, "FAILED (data)\n");
746 fprintf(stdout
, "Got :%s\n", EVBUFFER_DATA(req
->input_buffer
));
747 fprintf(stdout
, "Want:%s\n", POST_DATA
);
751 evb
= evbuffer_new();
752 evbuffer_add_printf(evb
, "This is funny");
754 evhttp_send_reply(req
, HTTP_OK
, "Everything is fine", evb
);
760 http_postrequest_done(struct evhttp_request
*req
, void *arg
)
762 const char *what
= "This is funny";
765 fprintf(stderr
, "FAILED (timeout)\n");
769 if (req
->response_code
!= HTTP_OK
) {
771 fprintf(stderr
, "FAILED (response code)\n");
775 if (evhttp_find_header(req
->input_headers
, "Content-Type") == NULL
) {
776 fprintf(stderr
, "FAILED (content type)\n");
780 if (EVBUFFER_LENGTH(req
->input_buffer
) != strlen(what
)) {
781 fprintf(stderr
, "FAILED (length %zu vs %zu)\n",
782 EVBUFFER_LENGTH(req
->input_buffer
), strlen(what
));
786 if (memcmp(EVBUFFER_DATA(req
->input_buffer
), what
, strlen(what
)) != 0) {
787 fprintf(stderr
, "FAILED (data)\n");
792 event_loopexit(NULL
);
796 http_failure_readcb(struct bufferevent
*bev
, void *arg
)
798 const char *what
= "400 Bad Request";
799 if (evbuffer_find(bev
->input
, (const unsigned char*) what
, strlen(what
)) != NULL
) {
801 bufferevent_disable(bev
, EV_READ
);
802 event_loopexit(NULL
);
807 * Testing that the HTTP server can deal with a malformed request.
810 http_failure_test(void)
812 struct bufferevent
*bev
;
814 const char *http_request
;
818 fprintf(stdout
, "Testing Bad HTTP Request: ");
820 http
= http_setup(&port
, NULL
);
822 fd
= http_connect("127.0.0.1", port
);
824 /* Stupid thing to send a request */
825 bev
= bufferevent_new(fd
, http_failure_readcb
, http_writecb
,
828 http_request
= "illegal request\r\n";
830 bufferevent_write(bev
, http_request
, strlen(http_request
));
834 bufferevent_free(bev
);
835 EVUTIL_CLOSESOCKET(fd
);
840 fprintf(stdout
, "FAILED\n");
844 fprintf(stdout
, "OK\n");
848 close_detect_done(struct evhttp_request
*req
, void *arg
)
851 if (req
== NULL
|| req
->response_code
!= HTTP_OK
) {
853 fprintf(stderr
, "FAILED\n");
860 tv
.tv_sec
= 3; /* longer than the http time out */
866 close_detect_launch(int fd
, short what
, void *arg
)
868 struct evhttp_connection
*evcon
= arg
;
869 struct evhttp_request
*req
;
871 req
= evhttp_request_new(close_detect_done
, NULL
);
873 /* Add the information that we care about */
874 evhttp_add_header(req
->output_headers
, "Host", "somehost");
876 /* We give ownership of the request to the connection */
877 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
878 fprintf(stdout
, "FAILED\n");
884 close_detect_cb(struct evhttp_request
*req
, void *arg
)
886 struct evhttp_connection
*evcon
= arg
;
889 if (req
!= NULL
&& req
->response_code
!= HTTP_OK
) {
891 fprintf(stderr
, "FAILED\n");
896 tv
.tv_sec
= 3; /* longer than the http time out */
898 /* launch a new request on the persistent connection in 6 seconds */
899 event_once(-1, EV_TIMEOUT
, close_detect_launch
, evcon
, &tv
);
904 http_close_detection(int with_delay
)
907 struct evhttp_connection
*evcon
= NULL
;
908 struct evhttp_request
*req
= NULL
;
911 fprintf(stdout
, "Testing Connection Close Detection%s: ",
912 with_delay
? " (with delay)" : "");
914 http
= http_setup(&port
, NULL
);
916 /* 2 second timeout */
917 evhttp_set_timeout(http
, 2);
919 evcon
= evhttp_connection_new("127.0.0.1", port
);
921 fprintf(stdout
, "FAILED\n");
925 delayed_client
= evcon
;
928 * At this point, we want to schedule a request to the HTTP
929 * server using our make request method.
932 req
= evhttp_request_new(close_detect_cb
, evcon
);
934 /* Add the information that we care about */
935 evhttp_add_header(req
->output_headers
, "Host", "somehost");
937 /* We give ownership of the request to the connection */
938 if (evhttp_make_request(evcon
,
939 req
, EVHTTP_REQ_GET
, with_delay
? "/largedelay" : "/test") == -1) {
940 fprintf(stdout
, "FAILED\n");
947 fprintf(stdout
, "FAILED\n");
951 /* at this point, the http server should have no connection */
952 if (TAILQ_FIRST(&http
->connections
) != NULL
) {
953 fprintf(stdout
, "FAILED (left connections)\n");
957 evhttp_connection_free(evcon
);
960 fprintf(stdout
, "OK\n");
964 http_highport_test(void)
967 struct evhttp
*myhttp
= NULL
;
969 fprintf(stdout
, "Testing HTTP Server with high port: ");
971 /* Try a few different ports */
972 for (i
= 0; i
< 50; ++i
) {
973 myhttp
= evhttp_start("127.0.0.1", 65535 - i
);
974 if (myhttp
!= NULL
) {
975 fprintf(stdout
, "OK\n");
981 fprintf(stdout
, "FAILED\n");
986 http_bad_header_test(void)
988 struct evkeyvalq headers
;
990 fprintf(stdout
, "Testing HTTP Header filtering: ");
992 TAILQ_INIT(&headers
);
994 if (evhttp_add_header(&headers
, "One", "Two") != 0)
997 if (evhttp_add_header(&headers
, "One\r", "Two") != -1)
999 if (evhttp_add_header(&headers
, "One", "Two") != 0)
1001 if (evhttp_add_header(&headers
, "One", "Two\r\n Three") != 0)
1003 if (evhttp_add_header(&headers
, "One\r", "Two") != -1)
1005 if (evhttp_add_header(&headers
, "One\n", "Two") != -1)
1007 if (evhttp_add_header(&headers
, "One", "Two\r") != -1)
1009 if (evhttp_add_header(&headers
, "One", "Two\n") != -1)
1012 evhttp_clear_headers(&headers
);
1014 fprintf(stdout
, "OK\n");
1017 fprintf(stdout
, "FAILED\n");
1021 static int validate_header(
1022 const struct evkeyvalq
* headers
,
1023 const char *key
, const char *value
)
1025 const char *real_val
= evhttp_find_header(headers
, key
);
1026 if (real_val
== NULL
)
1028 if (strcmp(real_val
, value
) != 0)
1034 http_parse_query_test(void)
1036 struct evkeyvalq headers
;
1038 fprintf(stdout
, "Testing HTTP query parsing: ");
1040 TAILQ_INIT(&headers
);
1042 evhttp_parse_query("http://www.test.com/?q=test", &headers
);
1043 if (validate_header(&headers
, "q", "test") != 0)
1045 evhttp_clear_headers(&headers
);
1047 evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers
);
1048 if (validate_header(&headers
, "q", "test") != 0)
1050 if (validate_header(&headers
, "foo", "bar") != 0)
1052 evhttp_clear_headers(&headers
);
1054 evhttp_parse_query("http://www.test.com/?q=test+foo", &headers
);
1055 if (validate_header(&headers
, "q", "test foo") != 0)
1057 evhttp_clear_headers(&headers
);
1059 evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers
);
1060 if (validate_header(&headers
, "q", "test\nfoo") != 0)
1062 evhttp_clear_headers(&headers
);
1064 evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers
);
1065 if (validate_header(&headers
, "q", "test\rfoo") != 0)
1067 evhttp_clear_headers(&headers
);
1069 fprintf(stdout
, "OK\n");
1072 fprintf(stdout
, "FAILED\n");
1077 http_base_test(void)
1079 struct bufferevent
*bev
;
1081 const char *http_request
;
1085 fprintf(stdout
, "Testing HTTP Server Event Base: ");
1087 base
= event_init();
1090 * create another bogus base - which is being used by all subsequen
1095 http
= http_setup(&port
, base
);
1097 fd
= http_connect("127.0.0.1", port
);
1099 /* Stupid thing to send a request */
1100 bev
= bufferevent_new(fd
, http_readcb
, http_writecb
,
1101 http_errorcb
, NULL
);
1102 bufferevent_base_set(base
, bev
);
1105 "GET /test HTTP/1.1\r\n"
1106 "Host: somehost\r\n"
1107 "Connection: close\r\n"
1110 bufferevent_write(bev
, http_request
, strlen(http_request
));
1112 event_base_dispatch(base
);
1114 bufferevent_free(bev
);
1115 EVUTIL_CLOSESOCKET(fd
);
1119 event_base_free(base
);
1123 fprintf(stdout
, "FAILED\n");
1127 fprintf(stdout
, "OK\n");
1131 * the server is going to reply with chunked data.
1135 http_chunked_readcb(struct bufferevent
*bev
, void *arg
)
1141 http_chunked_errorcb(struct bufferevent
*bev
, short what
, void *arg
)
1148 if ((what
& EVBUFFER_EOF
) != 0) {
1149 struct evhttp_request
*req
= evhttp_request_new(NULL
, NULL
);
1151 enum message_read_status done
;
1153 req
->kind
= EVHTTP_RESPONSE
;
1154 done
= evhttp_parse_firstline(req
, EVBUFFER_INPUT(bev
));
1155 if (done
!= ALL_DATA_READ
)
1158 done
= evhttp_parse_headers(req
, EVBUFFER_INPUT(bev
));
1159 if (done
!= ALL_DATA_READ
)
1162 header
= evhttp_find_header(req
->input_headers
, "Transfer-Encoding");
1163 if (header
== NULL
|| strcmp(header
, "chunked"))
1166 header
= evhttp_find_header(req
->input_headers
, "Connection");
1167 if (header
== NULL
|| strcmp(header
, "close"))
1170 header
= evbuffer_readline(EVBUFFER_INPUT(bev
));
1174 if (strcmp(header
, "d"))
1176 free((char*)header
);
1178 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev
)),
1179 "This is funny", 13))
1182 evbuffer_drain(EVBUFFER_INPUT(bev
), 13 + 2);
1184 header
= evbuffer_readline(EVBUFFER_INPUT(bev
));
1188 if (strcmp(header
, "12"))
1190 free((char *)header
);
1192 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev
)),
1193 "but not hilarious.", 18))
1196 evbuffer_drain(EVBUFFER_INPUT(bev
), 18 + 2);
1198 header
= evbuffer_readline(EVBUFFER_INPUT(bev
));
1202 if (strcmp(header
, "8"))
1204 free((char *)header
);
1206 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev
)),
1210 evbuffer_drain(EVBUFFER_INPUT(bev
), 8 + 2);
1212 header
= evbuffer_readline(EVBUFFER_INPUT(bev
));
1216 if (strcmp(header
, "0"))
1218 free((char *)header
);
1224 event_loopexit(NULL
);
1228 http_chunked_writecb(struct bufferevent
*bev
, void *arg
)
1230 if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(bev
)) == 0) {
1231 /* enable reading of the reply */
1232 bufferevent_enable(bev
, EV_READ
);
1238 http_chunked_request_done(struct evhttp_request
*req
, void *arg
)
1240 if (req
->response_code
!= HTTP_OK
) {
1241 fprintf(stderr
, "FAILED\n");
1245 if (evhttp_find_header(req
->input_headers
,
1246 "Transfer-Encoding") == NULL
) {
1247 fprintf(stderr
, "FAILED\n");
1251 if (EVBUFFER_LENGTH(req
->input_buffer
) != 13 + 18 + 8) {
1252 fprintf(stderr
, "FAILED\n");
1256 if (strncmp((char *)EVBUFFER_DATA(req
->input_buffer
),
1257 "This is funnybut not hilarious.bwv 1052",
1259 fprintf(stderr
, "FAILED\n");
1264 event_loopexit(NULL
);
1268 http_chunked_test(void)
1270 struct bufferevent
*bev
;
1272 const char *http_request
;
1274 struct timeval tv_start
, tv_end
;
1275 struct evhttp_connection
*evcon
= NULL
;
1276 struct evhttp_request
*req
= NULL
;
1280 fprintf(stdout
, "Testing Chunked HTTP Reply: ");
1282 http
= http_setup(&port
, NULL
);
1284 fd
= http_connect("127.0.0.1", port
);
1286 /* Stupid thing to send a request */
1287 bev
= bufferevent_new(fd
,
1288 http_chunked_readcb
, http_chunked_writecb
,
1289 http_chunked_errorcb
, NULL
);
1292 "GET /chunked HTTP/1.1\r\n"
1293 "Host: somehost\r\n"
1294 "Connection: close\r\n"
1297 bufferevent_write(bev
, http_request
, strlen(http_request
));
1299 evutil_gettimeofday(&tv_start
, NULL
);
1303 evutil_gettimeofday(&tv_end
, NULL
);
1304 evutil_timersub(&tv_end
, &tv_start
, &tv_end
);
1306 if (tv_end
.tv_sec
>= 1) {
1307 fprintf(stdout
, "FAILED (time)\n");
1313 fprintf(stdout
, "FAILED\n");
1317 /* now try again with the regular connection object */
1318 evcon
= evhttp_connection_new("127.0.0.1", port
);
1319 if (evcon
== NULL
) {
1320 fprintf(stdout
, "FAILED\n");
1324 /* make two requests to check the keepalive behavior */
1325 for (i
= 0; i
< 2; i
++) {
1327 req
= evhttp_request_new(http_chunked_request_done
, NULL
);
1329 /* Add the information that we care about */
1330 evhttp_add_header(req
->output_headers
, "Host", "somehost");
1332 /* We give ownership of the request to the connection */
1333 if (evhttp_make_request(evcon
, req
,
1334 EVHTTP_REQ_GET
, "/chunked") == -1) {
1335 fprintf(stdout
, "FAILED\n");
1342 fprintf(stdout
, "FAILED\n");
1347 evhttp_connection_free(evcon
);
1350 fprintf(stdout
, "OK\n");
1354 http_multi_line_header_test(void)
1356 struct bufferevent
*bev
;
1358 const char *http_start_request
;
1362 fprintf(stdout
, "Testing HTTP Server with multi line: ");
1364 http
= http_setup(&port
, NULL
);
1366 fd
= http_connect("127.0.0.1", port
);
1368 /* Stupid thing to send a request */
1369 bev
= bufferevent_new(fd
, http_readcb
, http_writecb
,
1370 http_errorcb
, NULL
);
1372 http_start_request
=
1373 "GET /test HTTP/1.1\r\n"
1374 "Host: somehost\r\n"
1375 "Connection: close\r\n"
1376 "X-Multi: aaaaaaaa\r\n"
1382 bufferevent_write(bev
, http_start_request
, strlen(http_start_request
));
1386 bufferevent_free(bev
);
1387 EVUTIL_CLOSESOCKET(fd
);
1392 fprintf(stdout
, "FAILED\n");
1396 fprintf(stdout
, "OK\n");
1400 http_request_bad(struct evhttp_request
*req
, void *arg
)
1403 fprintf(stderr
, "FAILED\n");
1408 event_loopexit(NULL
);
1412 http_negative_content_length_test(void)
1415 struct evhttp_connection
*evcon
= NULL
;
1416 struct evhttp_request
*req
= NULL
;
1419 fprintf(stdout
, "Testing HTTP Negative Content Length: ");
1421 http
= http_setup(&port
, NULL
);
1423 evcon
= evhttp_connection_new("127.0.0.1", port
);
1424 if (evcon
== NULL
) {
1425 fprintf(stdout
, "FAILED\n");
1430 * At this point, we want to schedule a request to the HTTP
1431 * server using our make request method.
1434 req
= evhttp_request_new(http_request_bad
, NULL
);
1436 /* Cause the response to have a negative content-length */
1437 evhttp_add_header(req
->output_headers
, "X-Negative", "makeitso");
1439 /* We give ownership of the request to the connection */
1440 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
1441 fprintf(stdout
, "FAILED\n");
1450 fprintf(stdout
, "FAILED\n");
1454 fprintf(stdout
, "OK\n");
1461 http_bad_header_test();
1462 http_parse_query_test();
1464 http_connection_test(0 /* not-persistent */);
1465 http_connection_test(1 /* persistent */);
1466 http_close_detection(0 /* with delay */);
1467 http_close_detection(1 /* with delay */);
1469 http_failure_test();
1470 http_highport_test();
1471 http_dispatcher_test();
1473 http_multi_line_header_test();
1474 http_negative_content_length_test();
1476 http_chunked_test();