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