2 * Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu>
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.
37 #include <sys/types.h>
39 #ifdef HAVE_SYS_TIME_H
42 #include <sys/queue.h>
44 #include <sys/socket.h>
58 #include "http-internal.h"
63 static struct evhttp
*http
;
64 /* set if a test needs to call loopexit on a base */
65 static struct event_base
*base
;
67 void http_suite(void);
69 void http_basic_cb(struct evhttp_request
*req
, void *arg
);
70 static void http_chunked_cb(struct evhttp_request
*req
, void *arg
);
71 void http_post_cb(struct evhttp_request
*req
, void *arg
);
72 void http_dispatcher_cb(struct evhttp_request
*req
, void *arg
);
73 static void http_large_delay_cb(struct evhttp_request
*req
, void *arg
);
75 static struct evhttp
*
76 http_setup(short *pport
, struct event_base
*base
)
79 struct evhttp
*myhttp
;
82 /* Try a few different ports */
83 myhttp
= evhttp_new(base
);
84 for (i
= 0; i
< 50; ++i
) {
85 if (evhttp_bind_socket(myhttp
, "127.0.0.1", 8080 + i
) != -1) {
92 event_errx(1, "Could not start web server");
94 /* Register a callback for certain types of requests */
95 evhttp_set_cb(myhttp
, "/test", http_basic_cb
, NULL
);
96 evhttp_set_cb(myhttp
, "/chunked", http_chunked_cb
, NULL
);
97 evhttp_set_cb(myhttp
, "/postit", http_post_cb
, NULL
);
98 evhttp_set_cb(myhttp
, "/largedelay", http_large_delay_cb
, NULL
);
99 evhttp_set_cb(myhttp
, "/", http_dispatcher_cb
, NULL
);
106 #define NI_MAXSERV 1024
110 http_connect(const char *address
, u_short port
)
112 /* Stupid code for connecting */
115 struct sockaddr_in sin
;
117 struct addrinfo ai
, *aitop
;
118 char strport
[NI_MAXSERV
];
125 if (!(he
= gethostbyname(address
))) {
126 event_warn("gethostbyname");
128 memcpy(&sin
.sin_addr
, he
->h_addr_list
[0], he
->h_length
);
129 sin
.sin_family
= AF_INET
;
130 sin
.sin_port
= htons(port
);
131 slen
= sizeof(struct sockaddr_in
);
132 sa
= (struct sockaddr
*)&sin
;
134 memset(&ai
, 0, sizeof (ai
));
135 ai
.ai_family
= AF_INET
;
136 ai
.ai_socktype
= SOCK_STREAM
;
137 snprintf(strport
, sizeof (strport
), "%d", port
);
138 if (getaddrinfo(address
, strport
, &ai
, &aitop
) != 0) {
139 event_warn("getaddrinfo");
143 slen
= aitop
->ai_addrlen
;
146 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
148 event_err(1, "socket failed");
150 if (connect(fd
, sa
, slen
) == -1)
151 event_err(1, "connect failed");
161 http_readcb(struct bufferevent
*bev
, void *arg
)
163 const char *what
= "This is funny";
165 event_debug(("%s: %s\n", __func__
, EVBUFFER_DATA(bev
->input
)));
167 if (evbuffer_find(bev
->input
,
168 (const unsigned char*) what
, strlen(what
)) != NULL
) {
169 struct evhttp_request
*req
= evhttp_request_new(NULL
, NULL
);
170 enum message_read_status done
;
172 req
->kind
= EVHTTP_RESPONSE
;
173 done
= evhttp_parse_firstline(req
, bev
->input
);
174 if (done
!= ALL_DATA_READ
)
177 done
= evhttp_parse_headers(req
, bev
->input
);
178 if (done
!= ALL_DATA_READ
)
182 evhttp_find_header(req
->input_headers
,
183 "Content-Type") != NULL
)
187 evhttp_request_free(req
);
188 bufferevent_disable(bev
, EV_READ
);
190 event_base_loopexit(base
, NULL
);
192 event_loopexit(NULL
);
197 http_writecb(struct bufferevent
*bev
, void *arg
)
199 if (EVBUFFER_LENGTH(bev
->output
) == 0) {
200 /* enable reading of the reply */
201 bufferevent_enable(bev
, EV_READ
);
207 http_errorcb(struct bufferevent
*bev
, short what
, void *arg
)
210 event_loopexit(NULL
);
214 http_basic_cb(struct evhttp_request
*req
, void *arg
)
216 struct evbuffer
*evb
= evbuffer_new();
217 int empty
= evhttp_find_header(req
->input_headers
, "Empty") != NULL
;
218 event_debug(("%s: called\n", __func__
));
219 evbuffer_add_printf(evb
, "This is funny");
221 /* For multi-line headers test */
224 evhttp_find_header(req
->input_headers
,"X-multi");
226 if (strcmp("END", multi
+ strlen(multi
) - 3) == 0)
228 if (evhttp_find_header(req
->input_headers
, "X-Last"))
233 /* injecting a bad content-length */
234 if (evhttp_find_header(req
->input_headers
, "X-Negative"))
235 evhttp_add_header(req
->output_headers
,
236 "Content-Length", "-100");
238 /* allow sending of an empty reply */
239 evhttp_send_reply(req
, HTTP_OK
, "Everything is fine",
240 !empty
? evb
: NULL
);
245 static char const* const CHUNKS
[] = {
247 "but not hilarious.",
251 struct chunk_req_state
{
252 struct evhttp_request
*req
;
257 http_chunked_trickle_cb(int fd
, short events
, void *arg
)
259 struct evbuffer
*evb
= evbuffer_new();
260 struct chunk_req_state
*state
= arg
;
261 struct timeval when
= { 0, 0 };
263 evbuffer_add_printf(evb
, "%s", CHUNKS
[state
->i
]);
264 evhttp_send_reply_chunk(state
->req
, evb
);
267 if (++state
->i
< sizeof(CHUNKS
)/sizeof(CHUNKS
[0])) {
268 event_once(-1, EV_TIMEOUT
,
269 http_chunked_trickle_cb
, state
, &when
);
271 evhttp_send_reply_end(state
->req
);
277 http_chunked_cb(struct evhttp_request
*req
, void *arg
)
279 struct timeval when
= { 0, 0 };
280 struct chunk_req_state
*state
= malloc(sizeof(struct chunk_req_state
));
281 event_debug(("%s: called\n", __func__
));
283 memset(state
, 0, sizeof(struct chunk_req_state
));
286 /* generate a chunked reply */
287 evhttp_send_reply_start(req
, HTTP_OK
, "Everything is fine");
289 /* but trickle it across several iterations to ensure we're not
290 * assuming it comes all at once */
291 event_once(-1, EV_TIMEOUT
, http_chunked_trickle_cb
, state
, &when
);
295 http_complete_write(int fd
, short what
, void *arg
)
297 struct bufferevent
*bev
= arg
;
298 const char *http_request
= "host\r\n"
299 "Connection: close\r\n"
301 bufferevent_write(bev
, http_request
, strlen(http_request
));
305 http_basic_test(void)
308 struct bufferevent
*bev
;
310 const char *http_request
;
314 fprintf(stdout
, "Testing Basic HTTP Server: ");
316 http
= http_setup(&port
, NULL
);
318 /* bind to a second socket */
319 if (evhttp_bind_socket(http
, "127.0.0.1", port
+ 1) == -1) {
320 fprintf(stdout
, "FAILED (bind)\n");
324 fd
= http_connect("127.0.0.1", port
);
326 /* Stupid thing to send a request */
327 bev
= bufferevent_new(fd
, http_readcb
, http_writecb
,
330 /* first half of the http request */
332 "GET /test HTTP/1.1\r\n"
335 bufferevent_write(bev
, http_request
, strlen(http_request
));
338 event_once(-1, EV_TIMEOUT
, http_complete_write
, bev
, &tv
);
343 fprintf(stdout
, "FAILED\n");
347 /* connect to the second port */
348 bufferevent_free(bev
);
349 EVUTIL_CLOSESOCKET(fd
);
351 fd
= http_connect("127.0.0.1", port
+ 1);
353 /* Stupid thing to send a request */
354 bev
= bufferevent_new(fd
, http_readcb
, http_writecb
,
358 "GET /test HTTP/1.1\r\n"
360 "Connection: close\r\n"
363 bufferevent_write(bev
, http_request
, strlen(http_request
));
367 bufferevent_free(bev
);
368 EVUTIL_CLOSESOCKET(fd
);
373 fprintf(stdout
, "FAILED\n");
377 fprintf(stdout
, "OK\n");
380 static struct evhttp_connection
*delayed_client
;
383 http_delay_reply(int fd
, short what
, void *arg
)
385 struct evhttp_request
*req
= arg
;
387 evhttp_send_reply(req
, HTTP_OK
, "Everything is fine", NULL
);
393 http_large_delay_cb(struct evhttp_request
*req
, void *arg
)
399 event_once(-1, EV_TIMEOUT
, http_delay_reply
, req
, &tv
);
401 /* here we close the client connection which will cause an EOF */
402 evhttp_connection_fail(delayed_client
, EVCON_HTTP_EOF
);
405 void http_request_done(struct evhttp_request
*, void *);
406 void http_request_empty_done(struct evhttp_request
*, void *);
409 http_connection_test(int persistent
)
412 struct evhttp_connection
*evcon
= NULL
;
413 struct evhttp_request
*req
= NULL
;
416 fprintf(stdout
, "Testing Request Connection Pipeline %s: ",
417 persistent
? "(persistent)" : "");
419 http
= http_setup(&port
, NULL
);
421 evcon
= evhttp_connection_new("127.0.0.1", port
);
423 fprintf(stdout
, "FAILED\n");
428 * At this point, we want to schedule a request to the HTTP
429 * server using our make request method.
432 req
= evhttp_request_new(http_request_done
, NULL
);
434 /* Add the information that we care about */
435 evhttp_add_header(req
->output_headers
, "Host", "somehost");
437 /* We give ownership of the request to the connection */
438 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
439 fprintf(stdout
, "FAILED\n");
446 fprintf(stdout
, "FAILED\n");
450 /* try to make another request over the same connection */
453 req
= evhttp_request_new(http_request_done
, NULL
);
455 /* Add the information that we care about */
456 evhttp_add_header(req
->output_headers
, "Host", "somehost");
459 * if our connections are not supposed to be persistent; request
460 * a close from the server.
463 evhttp_add_header(req
->output_headers
, "Connection", "close");
465 /* We give ownership of the request to the connection */
466 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
467 fprintf(stdout
, "FAILED\n");
473 /* make another request: request empty reply */
476 req
= evhttp_request_new(http_request_empty_done
, NULL
);
478 /* Add the information that we care about */
479 evhttp_add_header(req
->output_headers
, "Empty", "itis");
481 /* We give ownership of the request to the connection */
482 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
483 fprintf(stdout
, "FAILED\n");
490 fprintf(stdout
, "FAILED\n");
494 evhttp_connection_free(evcon
);
497 fprintf(stdout
, "OK\n");
501 http_request_done(struct evhttp_request
*req
, void *arg
)
503 const char *what
= "This is funny";
505 if (req
->response_code
!= HTTP_OK
) {
506 fprintf(stderr
, "FAILED\n");
510 if (evhttp_find_header(req
->input_headers
, "Content-Type") == NULL
) {
511 fprintf(stderr
, "FAILED\n");
515 if (EVBUFFER_LENGTH(req
->input_buffer
) != strlen(what
)) {
516 fprintf(stderr
, "FAILED\n");
520 if (memcmp(EVBUFFER_DATA(req
->input_buffer
), what
, strlen(what
)) != 0) {
521 fprintf(stderr
, "FAILED\n");
526 event_loopexit(NULL
);
529 /* test date header and content length */
532 http_request_empty_done(struct evhttp_request
*req
, void *arg
)
534 if (req
->response_code
!= HTTP_OK
) {
535 fprintf(stderr
, "FAILED\n");
539 if (evhttp_find_header(req
->input_headers
, "Date") == NULL
) {
540 fprintf(stderr
, "FAILED\n");
545 if (evhttp_find_header(req
->input_headers
, "Content-Length") == NULL
) {
546 fprintf(stderr
, "FAILED\n");
550 if (strcmp(evhttp_find_header(req
->input_headers
, "Content-Length"),
552 fprintf(stderr
, "FAILED\n");
556 if (EVBUFFER_LENGTH(req
->input_buffer
) != 0) {
557 fprintf(stderr
, "FAILED\n");
562 event_loopexit(NULL
);
566 * HTTP DISPATCHER test
570 http_dispatcher_cb(struct evhttp_request
*req
, void *arg
)
573 struct evbuffer
*evb
= evbuffer_new();
574 event_debug(("%s: called\n", __func__
));
575 evbuffer_add_printf(evb
, "DISPATCHER_TEST");
577 evhttp_send_reply(req
, HTTP_OK
, "Everything is fine", evb
);
583 http_dispatcher_test_done(struct evhttp_request
*req
, void *arg
)
585 const char *what
= "DISPATCHER_TEST";
587 if (req
->response_code
!= HTTP_OK
) {
588 fprintf(stderr
, "FAILED\n");
592 if (evhttp_find_header(req
->input_headers
, "Content-Type") == NULL
) {
593 fprintf(stderr
, "FAILED (content type)\n");
597 if (EVBUFFER_LENGTH(req
->input_buffer
) != strlen(what
)) {
598 fprintf(stderr
, "FAILED (length %zu vs %zu)\n",
599 EVBUFFER_LENGTH(req
->input_buffer
), strlen(what
));
603 if (memcmp(EVBUFFER_DATA(req
->input_buffer
), what
, strlen(what
)) != 0) {
604 fprintf(stderr
, "FAILED (data)\n");
609 event_loopexit(NULL
);
613 http_dispatcher_test(void)
616 struct evhttp_connection
*evcon
= NULL
;
617 struct evhttp_request
*req
= NULL
;
620 fprintf(stdout
, "Testing HTTP Dispatcher: ");
622 http
= http_setup(&port
, NULL
);
624 evcon
= evhttp_connection_new("127.0.0.1", port
);
626 fprintf(stdout
, "FAILED\n");
630 /* also bind to local host */
631 evhttp_connection_set_local_address(evcon
, "127.0.0.1");
634 * At this point, we want to schedule an HTTP GET request
635 * server using our make request method.
638 req
= evhttp_request_new(http_dispatcher_test_done
, NULL
);
640 fprintf(stdout
, "FAILED\n");
644 /* Add the information that we care about */
645 evhttp_add_header(req
->output_headers
, "Host", "somehost");
647 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/?arg=val") == -1) {
648 fprintf(stdout
, "FAILED\n");
654 evhttp_connection_free(evcon
);
658 fprintf(stdout
, "FAILED: %d\n", test_ok
);
662 fprintf(stdout
, "OK\n");
669 void http_postrequest_done(struct evhttp_request
*, void *);
671 #define POST_DATA "Okay. Not really printf"
677 struct evhttp_connection
*evcon
= NULL
;
678 struct evhttp_request
*req
= NULL
;
681 fprintf(stdout
, "Testing HTTP POST Request: ");
683 http
= http_setup(&port
, NULL
);
685 evcon
= evhttp_connection_new("127.0.0.1", port
);
687 fprintf(stdout
, "FAILED\n");
692 * At this point, we want to schedule an HTTP POST request
693 * server using our make request method.
696 req
= evhttp_request_new(http_postrequest_done
, NULL
);
698 fprintf(stdout
, "FAILED\n");
702 /* Add the information that we care about */
703 evhttp_add_header(req
->output_headers
, "Host", "somehost");
704 evbuffer_add_printf(req
->output_buffer
, POST_DATA
);
706 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_POST
, "/postit") == -1) {
707 fprintf(stdout
, "FAILED\n");
713 evhttp_connection_free(evcon
);
717 fprintf(stdout
, "FAILED: %d\n", test_ok
);
721 fprintf(stdout
, "OK\n");
725 http_post_cb(struct evhttp_request
*req
, void *arg
)
727 struct evbuffer
*evb
;
728 event_debug(("%s: called\n", __func__
));
730 /* Yes, we are expecting a post request */
731 if (req
->type
!= EVHTTP_REQ_POST
) {
732 fprintf(stdout
, "FAILED (post type)\n");
736 if (EVBUFFER_LENGTH(req
->input_buffer
) != strlen(POST_DATA
)) {
737 fprintf(stdout
, "FAILED (length: %zu vs %zu)\n",
738 EVBUFFER_LENGTH(req
->input_buffer
), strlen(POST_DATA
));
742 if (memcmp(EVBUFFER_DATA(req
->input_buffer
), POST_DATA
,
743 strlen(POST_DATA
))) {
744 fprintf(stdout
, "FAILED (data)\n");
745 fprintf(stdout
, "Got :%s\n", EVBUFFER_DATA(req
->input_buffer
));
746 fprintf(stdout
, "Want:%s\n", POST_DATA
);
750 evb
= evbuffer_new();
751 evbuffer_add_printf(evb
, "This is funny");
753 evhttp_send_reply(req
, HTTP_OK
, "Everything is fine", evb
);
759 http_postrequest_done(struct evhttp_request
*req
, void *arg
)
761 const char *what
= "This is funny";
764 fprintf(stderr
, "FAILED (timeout)\n");
768 if (req
->response_code
!= HTTP_OK
) {
770 fprintf(stderr
, "FAILED (response code)\n");
774 if (evhttp_find_header(req
->input_headers
, "Content-Type") == NULL
) {
775 fprintf(stderr
, "FAILED (content type)\n");
779 if (EVBUFFER_LENGTH(req
->input_buffer
) != strlen(what
)) {
780 fprintf(stderr
, "FAILED (length %zu vs %zu)\n",
781 EVBUFFER_LENGTH(req
->input_buffer
), strlen(what
));
785 if (memcmp(EVBUFFER_DATA(req
->input_buffer
), what
, strlen(what
)) != 0) {
786 fprintf(stderr
, "FAILED (data)\n");
791 event_loopexit(NULL
);
795 http_failure_readcb(struct bufferevent
*bev
, void *arg
)
797 const char *what
= "400 Bad Request";
798 if (evbuffer_find(bev
->input
, (const unsigned char*) what
, strlen(what
)) != NULL
) {
800 bufferevent_disable(bev
, EV_READ
);
801 event_loopexit(NULL
);
806 * Testing that the HTTP server can deal with a malformed request.
809 http_failure_test(void)
811 struct bufferevent
*bev
;
813 const char *http_request
;
817 fprintf(stdout
, "Testing Bad HTTP Request: ");
819 http
= http_setup(&port
, NULL
);
821 fd
= http_connect("127.0.0.1", port
);
823 /* Stupid thing to send a request */
824 bev
= bufferevent_new(fd
, http_failure_readcb
, http_writecb
,
827 http_request
= "illegal request\r\n";
829 bufferevent_write(bev
, http_request
, strlen(http_request
));
833 bufferevent_free(bev
);
834 EVUTIL_CLOSESOCKET(fd
);
839 fprintf(stdout
, "FAILED\n");
843 fprintf(stdout
, "OK\n");
847 close_detect_done(struct evhttp_request
*req
, void *arg
)
850 if (req
== NULL
|| req
->response_code
!= HTTP_OK
) {
852 fprintf(stderr
, "FAILED\n");
859 tv
.tv_sec
= 3; /* longer than the http time out */
865 close_detect_launch(int fd
, short what
, void *arg
)
867 struct evhttp_connection
*evcon
= arg
;
868 struct evhttp_request
*req
;
870 req
= evhttp_request_new(close_detect_done
, NULL
);
872 /* Add the information that we care about */
873 evhttp_add_header(req
->output_headers
, "Host", "somehost");
875 /* We give ownership of the request to the connection */
876 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
877 fprintf(stdout
, "FAILED\n");
883 close_detect_cb(struct evhttp_request
*req
, void *arg
)
885 struct evhttp_connection
*evcon
= arg
;
888 if (req
!= NULL
&& req
->response_code
!= HTTP_OK
) {
890 fprintf(stderr
, "FAILED\n");
895 tv
.tv_sec
= 3; /* longer than the http time out */
897 /* launch a new request on the persistent connection in 6 seconds */
898 event_once(-1, EV_TIMEOUT
, close_detect_launch
, evcon
, &tv
);
903 http_close_detection(int with_delay
)
906 struct evhttp_connection
*evcon
= NULL
;
907 struct evhttp_request
*req
= NULL
;
910 fprintf(stdout
, "Testing Connection Close Detection%s: ",
911 with_delay
? " (with delay)" : "");
913 http
= http_setup(&port
, NULL
);
915 /* 2 second timeout */
916 evhttp_set_timeout(http
, 2);
918 evcon
= evhttp_connection_new("127.0.0.1", port
);
920 fprintf(stdout
, "FAILED\n");
924 delayed_client
= evcon
;
927 * At this point, we want to schedule a request to the HTTP
928 * server using our make request method.
931 req
= evhttp_request_new(close_detect_cb
, evcon
);
933 /* Add the information that we care about */
934 evhttp_add_header(req
->output_headers
, "Host", "somehost");
936 /* We give ownership of the request to the connection */
937 if (evhttp_make_request(evcon
,
938 req
, EVHTTP_REQ_GET
, with_delay
? "/largedelay" : "/test") == -1) {
939 fprintf(stdout
, "FAILED\n");
946 fprintf(stdout
, "FAILED\n");
950 /* at this point, the http server should have no connection */
951 if (TAILQ_FIRST(&http
->connections
) != NULL
) {
952 fprintf(stdout
, "FAILED (left connections)\n");
956 evhttp_connection_free(evcon
);
959 fprintf(stdout
, "OK\n");
963 http_highport_test(void)
966 struct evhttp
*myhttp
= NULL
;
968 fprintf(stdout
, "Testing HTTP Server with high port: ");
970 /* Try a few different ports */
971 for (i
= 0; i
< 50; ++i
) {
972 myhttp
= evhttp_start("127.0.0.1", 65535 - i
);
973 if (myhttp
!= NULL
) {
974 fprintf(stdout
, "OK\n");
980 fprintf(stdout
, "FAILED\n");
985 http_bad_header_test(void)
987 struct evkeyvalq headers
;
989 fprintf(stdout
, "Testing HTTP Header filtering: ");
991 TAILQ_INIT(&headers
);
993 if (evhttp_add_header(&headers
, "One", "Two") != 0)
996 if (evhttp_add_header(&headers
, "One\r", "Two") != -1)
998 if (evhttp_add_header(&headers
, "One", "Two") != 0)
1000 if (evhttp_add_header(&headers
, "One", "Two\r\n Three") != 0)
1002 if (evhttp_add_header(&headers
, "One\r", "Two") != -1)
1004 if (evhttp_add_header(&headers
, "One\n", "Two") != -1)
1006 if (evhttp_add_header(&headers
, "One", "Two\r") != -1)
1008 if (evhttp_add_header(&headers
, "One", "Two\n") != -1)
1011 evhttp_clear_headers(&headers
);
1013 fprintf(stdout
, "OK\n");
1016 fprintf(stdout
, "FAILED\n");
1020 static int validate_header(
1021 const struct evkeyvalq
* headers
,
1022 const char *key
, const char *value
)
1024 const char *real_val
= evhttp_find_header(headers
, key
);
1025 if (real_val
== NULL
)
1027 if (strcmp(real_val
, value
) != 0)
1033 http_parse_query_test(void)
1035 struct evkeyvalq headers
;
1037 fprintf(stdout
, "Testing HTTP query parsing: ");
1039 TAILQ_INIT(&headers
);
1041 evhttp_parse_query("http://www.test.com/?q=test", &headers
);
1042 if (validate_header(&headers
, "q", "test") != 0)
1044 evhttp_clear_headers(&headers
);
1046 evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers
);
1047 if (validate_header(&headers
, "q", "test") != 0)
1049 if (validate_header(&headers
, "foo", "bar") != 0)
1051 evhttp_clear_headers(&headers
);
1053 evhttp_parse_query("http://www.test.com/?q=test+foo", &headers
);
1054 if (validate_header(&headers
, "q", "test foo") != 0)
1056 evhttp_clear_headers(&headers
);
1058 evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers
);
1059 if (validate_header(&headers
, "q", "test\nfoo") != 0)
1061 evhttp_clear_headers(&headers
);
1063 evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers
);
1064 if (validate_header(&headers
, "q", "test\rfoo") != 0)
1066 evhttp_clear_headers(&headers
);
1068 fprintf(stdout
, "OK\n");
1071 fprintf(stdout
, "FAILED\n");
1076 http_base_test(void)
1078 struct bufferevent
*bev
;
1080 const char *http_request
;
1084 fprintf(stdout
, "Testing HTTP Server Event Base: ");
1086 base
= event_init();
1089 * create another bogus base - which is being used by all subsequen
1094 http
= http_setup(&port
, base
);
1096 fd
= http_connect("127.0.0.1", port
);
1098 /* Stupid thing to send a request */
1099 bev
= bufferevent_new(fd
, http_readcb
, http_writecb
,
1100 http_errorcb
, NULL
);
1101 bufferevent_base_set(base
, bev
);
1104 "GET /test HTTP/1.1\r\n"
1105 "Host: somehost\r\n"
1106 "Connection: close\r\n"
1109 bufferevent_write(bev
, http_request
, strlen(http_request
));
1111 event_base_dispatch(base
);
1113 bufferevent_free(bev
);
1114 EVUTIL_CLOSESOCKET(fd
);
1118 event_base_free(base
);
1122 fprintf(stdout
, "FAILED\n");
1126 fprintf(stdout
, "OK\n");
1130 * the server is going to reply with chunked data.
1134 http_chunked_readcb(struct bufferevent
*bev
, void *arg
)
1140 http_chunked_errorcb(struct bufferevent
*bev
, short what
, void *arg
)
1147 if ((what
& EVBUFFER_EOF
) != 0) {
1148 struct evhttp_request
*req
= evhttp_request_new(NULL
, NULL
);
1150 enum message_read_status done
;
1152 req
->kind
= EVHTTP_RESPONSE
;
1153 done
= evhttp_parse_firstline(req
, EVBUFFER_INPUT(bev
));
1154 if (done
!= ALL_DATA_READ
)
1157 done
= evhttp_parse_headers(req
, EVBUFFER_INPUT(bev
));
1158 if (done
!= ALL_DATA_READ
)
1161 header
= evhttp_find_header(req
->input_headers
, "Transfer-Encoding");
1162 if (header
== NULL
|| strcmp(header
, "chunked"))
1165 header
= evhttp_find_header(req
->input_headers
, "Connection");
1166 if (header
== NULL
|| strcmp(header
, "close"))
1169 header
= evbuffer_readline(EVBUFFER_INPUT(bev
));
1173 if (strcmp(header
, "d"))
1175 free((char*)header
);
1177 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev
)),
1178 "This is funny", 13))
1181 evbuffer_drain(EVBUFFER_INPUT(bev
), 13 + 2);
1183 header
= evbuffer_readline(EVBUFFER_INPUT(bev
));
1187 if (strcmp(header
, "12"))
1189 free((char *)header
);
1191 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev
)),
1192 "but not hilarious.", 18))
1195 evbuffer_drain(EVBUFFER_INPUT(bev
), 18 + 2);
1197 header
= evbuffer_readline(EVBUFFER_INPUT(bev
));
1201 if (strcmp(header
, "8"))
1203 free((char *)header
);
1205 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev
)),
1209 evbuffer_drain(EVBUFFER_INPUT(bev
), 8 + 2);
1211 header
= evbuffer_readline(EVBUFFER_INPUT(bev
));
1215 if (strcmp(header
, "0"))
1217 free((char *)header
);
1223 event_loopexit(NULL
);
1227 http_chunked_writecb(struct bufferevent
*bev
, void *arg
)
1229 if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(bev
)) == 0) {
1230 /* enable reading of the reply */
1231 bufferevent_enable(bev
, EV_READ
);
1237 http_chunked_request_done(struct evhttp_request
*req
, void *arg
)
1239 if (req
->response_code
!= HTTP_OK
) {
1240 fprintf(stderr
, "FAILED\n");
1244 if (evhttp_find_header(req
->input_headers
,
1245 "Transfer-Encoding") == NULL
) {
1246 fprintf(stderr
, "FAILED\n");
1250 if (EVBUFFER_LENGTH(req
->input_buffer
) != 13 + 18 + 8) {
1251 fprintf(stderr
, "FAILED\n");
1255 if (strncmp((char *)EVBUFFER_DATA(req
->input_buffer
),
1256 "This is funnybut not hilarious.bwv 1052",
1258 fprintf(stderr
, "FAILED\n");
1263 event_loopexit(NULL
);
1267 http_chunked_test(void)
1269 struct bufferevent
*bev
;
1271 const char *http_request
;
1273 struct timeval tv_start
, tv_end
;
1274 struct evhttp_connection
*evcon
= NULL
;
1275 struct evhttp_request
*req
= NULL
;
1279 fprintf(stdout
, "Testing Chunked HTTP Reply: ");
1281 http
= http_setup(&port
, NULL
);
1283 fd
= http_connect("127.0.0.1", port
);
1285 /* Stupid thing to send a request */
1286 bev
= bufferevent_new(fd
,
1287 http_chunked_readcb
, http_chunked_writecb
,
1288 http_chunked_errorcb
, NULL
);
1291 "GET /chunked HTTP/1.1\r\n"
1292 "Host: somehost\r\n"
1293 "Connection: close\r\n"
1296 bufferevent_write(bev
, http_request
, strlen(http_request
));
1298 evutil_gettimeofday(&tv_start
, NULL
);
1302 evutil_gettimeofday(&tv_end
, NULL
);
1303 evutil_timersub(&tv_end
, &tv_start
, &tv_end
);
1305 if (tv_end
.tv_sec
>= 1) {
1306 fprintf(stdout
, "FAILED (time)\n");
1312 fprintf(stdout
, "FAILED\n");
1316 /* now try again with the regular connection object */
1317 evcon
= evhttp_connection_new("127.0.0.1", port
);
1318 if (evcon
== NULL
) {
1319 fprintf(stdout
, "FAILED\n");
1323 /* make two requests to check the keepalive behavior */
1324 for (i
= 0; i
< 2; i
++) {
1326 req
= evhttp_request_new(http_chunked_request_done
, NULL
);
1328 /* Add the information that we care about */
1329 evhttp_add_header(req
->output_headers
, "Host", "somehost");
1331 /* We give ownership of the request to the connection */
1332 if (evhttp_make_request(evcon
, req
,
1333 EVHTTP_REQ_GET
, "/chunked") == -1) {
1334 fprintf(stdout
, "FAILED\n");
1341 fprintf(stdout
, "FAILED\n");
1346 evhttp_connection_free(evcon
);
1349 fprintf(stdout
, "OK\n");
1353 http_multi_line_header_test(void)
1355 struct bufferevent
*bev
;
1357 const char *http_start_request
;
1361 fprintf(stdout
, "Testing HTTP Server with multi line: ");
1363 http
= http_setup(&port
, NULL
);
1365 fd
= http_connect("127.0.0.1", port
);
1367 /* Stupid thing to send a request */
1368 bev
= bufferevent_new(fd
, http_readcb
, http_writecb
,
1369 http_errorcb
, NULL
);
1371 http_start_request
=
1372 "GET /test HTTP/1.1\r\n"
1373 "Host: somehost\r\n"
1374 "Connection: close\r\n"
1375 "X-Multi: aaaaaaaa\r\n"
1381 bufferevent_write(bev
, http_start_request
, strlen(http_start_request
));
1385 bufferevent_free(bev
);
1386 EVUTIL_CLOSESOCKET(fd
);
1391 fprintf(stdout
, "FAILED\n");
1395 fprintf(stdout
, "OK\n");
1399 http_request_bad(struct evhttp_request
*req
, void *arg
)
1402 fprintf(stderr
, "FAILED\n");
1407 event_loopexit(NULL
);
1411 http_negative_content_length_test(void)
1414 struct evhttp_connection
*evcon
= NULL
;
1415 struct evhttp_request
*req
= NULL
;
1418 fprintf(stdout
, "Testing HTTP Negative Content Length: ");
1420 http
= http_setup(&port
, NULL
);
1422 evcon
= evhttp_connection_new("127.0.0.1", port
);
1423 if (evcon
== NULL
) {
1424 fprintf(stdout
, "FAILED\n");
1429 * At this point, we want to schedule a request to the HTTP
1430 * server using our make request method.
1433 req
= evhttp_request_new(http_request_bad
, NULL
);
1435 /* Cause the response to have a negative content-length */
1436 evhttp_add_header(req
->output_headers
, "X-Negative", "makeitso");
1438 /* We give ownership of the request to the connection */
1439 if (evhttp_make_request(evcon
, req
, EVHTTP_REQ_GET
, "/test") == -1) {
1440 fprintf(stdout
, "FAILED\n");
1449 fprintf(stdout
, "FAILED\n");
1453 fprintf(stdout
, "OK\n");
1460 http_bad_header_test();
1461 http_parse_query_test();
1463 http_connection_test(0 /* not-persistent */);
1464 http_connection_test(1 /* persistent */);
1465 http_close_detection(0 /* with delay */);
1466 http_close_detection(1 /* with delay */);
1468 http_failure_test();
1469 http_highport_test();
1470 http_dispatcher_test();
1472 http_multi_line_header_test();
1473 http_negative_content_length_test();
1475 http_chunked_test();