ipc/chromium/src/third_party/libevent/test/regress_http.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /*
michael@0 2 * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu>
michael@0 3 * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
michael@0 4 *
michael@0 5 * Redistribution and use in source and binary forms, with or without
michael@0 6 * modification, are permitted provided that the following conditions
michael@0 7 * are met:
michael@0 8 * 1. Redistributions of source code must retain the above copyright
michael@0 9 * notice, this list of conditions and the following disclaimer.
michael@0 10 * 2. Redistributions in binary form must reproduce the above copyright
michael@0 11 * notice, this list of conditions and the following disclaimer in the
michael@0 12 * documentation and/or other materials provided with the distribution.
michael@0 13 * 3. The name of the author may not be used to endorse or promote products
michael@0 14 * derived from this software without specific prior written permission.
michael@0 15 *
michael@0 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
michael@0 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
michael@0 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
michael@0 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
michael@0 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
michael@0 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
michael@0 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 26 */
michael@0 27
michael@0 28 #ifdef WIN32
michael@0 29 #include <winsock2.h>
michael@0 30 #include <ws2tcpip.h>
michael@0 31 #include <windows.h>
michael@0 32 #endif
michael@0 33
michael@0 34 #include "event2/event-config.h"
michael@0 35
michael@0 36 #include <sys/types.h>
michael@0 37 #include <sys/stat.h>
michael@0 38 #ifdef _EVENT_HAVE_SYS_TIME_H
michael@0 39 #include <sys/time.h>
michael@0 40 #endif
michael@0 41 #include <sys/queue.h>
michael@0 42 #ifndef WIN32
michael@0 43 #include <sys/socket.h>
michael@0 44 #include <signal.h>
michael@0 45 #include <unistd.h>
michael@0 46 #include <netdb.h>
michael@0 47 #endif
michael@0 48 #include <fcntl.h>
michael@0 49 #include <stdlib.h>
michael@0 50 #include <stdio.h>
michael@0 51 #include <string.h>
michael@0 52 #include <errno.h>
michael@0 53
michael@0 54 #include "event2/dns.h"
michael@0 55
michael@0 56 #include "event2/event.h"
michael@0 57 #include "event2/http.h"
michael@0 58 #include "event2/buffer.h"
michael@0 59 #include "event2/bufferevent.h"
michael@0 60 #include "event2/util.h"
michael@0 61 #include "log-internal.h"
michael@0 62 #include "util-internal.h"
michael@0 63 #include "http-internal.h"
michael@0 64 #include "regress.h"
michael@0 65 #include "regress_testutils.h"
michael@0 66
michael@0 67 static struct evhttp *http;
michael@0 68 /* set if a test needs to call loopexit on a base */
michael@0 69 static struct event_base *exit_base;
michael@0 70
michael@0 71 static char const BASIC_REQUEST_BODY[] = "This is funny";
michael@0 72
michael@0 73 static void http_basic_cb(struct evhttp_request *req, void *arg);
michael@0 74 static void http_chunked_cb(struct evhttp_request *req, void *arg);
michael@0 75 static void http_post_cb(struct evhttp_request *req, void *arg);
michael@0 76 static void http_put_cb(struct evhttp_request *req, void *arg);
michael@0 77 static void http_delete_cb(struct evhttp_request *req, void *arg);
michael@0 78 static void http_delay_cb(struct evhttp_request *req, void *arg);
michael@0 79 static void http_large_delay_cb(struct evhttp_request *req, void *arg);
michael@0 80 static void http_badreq_cb(struct evhttp_request *req, void *arg);
michael@0 81 static void http_dispatcher_cb(struct evhttp_request *req, void *arg);
michael@0 82 static int
michael@0 83 http_bind(struct evhttp *myhttp, ev_uint16_t *pport)
michael@0 84 {
michael@0 85 int port;
michael@0 86 struct evhttp_bound_socket *sock;
michael@0 87
michael@0 88 sock = evhttp_bind_socket_with_handle(myhttp, "127.0.0.1", *pport);
michael@0 89 if (sock == NULL)
michael@0 90 event_errx(1, "Could not start web server");
michael@0 91
michael@0 92 port = regress_get_socket_port(evhttp_bound_socket_get_fd(sock));
michael@0 93 if (port < 0)
michael@0 94 return -1;
michael@0 95 *pport = (ev_uint16_t) port;
michael@0 96
michael@0 97 return 0;
michael@0 98 }
michael@0 99
michael@0 100 static struct evhttp *
michael@0 101 http_setup(ev_uint16_t *pport, struct event_base *base)
michael@0 102 {
michael@0 103 struct evhttp *myhttp;
michael@0 104
michael@0 105 /* Try a few different ports */
michael@0 106 myhttp = evhttp_new(base);
michael@0 107
michael@0 108 if (http_bind(myhttp, pport) < 0)
michael@0 109 return NULL;
michael@0 110
michael@0 111 /* Register a callback for certain types of requests */
michael@0 112 evhttp_set_cb(myhttp, "/test", http_basic_cb, base);
michael@0 113 evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, base);
michael@0 114 evhttp_set_cb(myhttp, "/streamed", http_chunked_cb, base);
michael@0 115 evhttp_set_cb(myhttp, "/postit", http_post_cb, base);
michael@0 116 evhttp_set_cb(myhttp, "/putit", http_put_cb, base);
michael@0 117 evhttp_set_cb(myhttp, "/deleteit", http_delete_cb, base);
michael@0 118 evhttp_set_cb(myhttp, "/delay", http_delay_cb, base);
michael@0 119 evhttp_set_cb(myhttp, "/largedelay", http_large_delay_cb, base);
michael@0 120 evhttp_set_cb(myhttp, "/badrequest", http_badreq_cb, base);
michael@0 121 evhttp_set_cb(myhttp, "/", http_dispatcher_cb, base);
michael@0 122 return (myhttp);
michael@0 123 }
michael@0 124
michael@0 125 #ifndef NI_MAXSERV
michael@0 126 #define NI_MAXSERV 1024
michael@0 127 #endif
michael@0 128
michael@0 129 static evutil_socket_t
michael@0 130 http_connect(const char *address, u_short port)
michael@0 131 {
michael@0 132 /* Stupid code for connecting */
michael@0 133 struct evutil_addrinfo ai, *aitop;
michael@0 134 char strport[NI_MAXSERV];
michael@0 135
michael@0 136 struct sockaddr *sa;
michael@0 137 int slen;
michael@0 138 evutil_socket_t fd;
michael@0 139
michael@0 140 memset(&ai, 0, sizeof(ai));
michael@0 141 ai.ai_family = AF_INET;
michael@0 142 ai.ai_socktype = SOCK_STREAM;
michael@0 143 evutil_snprintf(strport, sizeof(strport), "%d", port);
michael@0 144 if (evutil_getaddrinfo(address, strport, &ai, &aitop) != 0) {
michael@0 145 event_warn("getaddrinfo");
michael@0 146 return (-1);
michael@0 147 }
michael@0 148 sa = aitop->ai_addr;
michael@0 149 slen = aitop->ai_addrlen;
michael@0 150
michael@0 151 fd = socket(AF_INET, SOCK_STREAM, 0);
michael@0 152 if (fd == -1)
michael@0 153 event_err(1, "socket failed");
michael@0 154
michael@0 155 evutil_make_socket_nonblocking(fd);
michael@0 156 if (connect(fd, sa, slen) == -1) {
michael@0 157 #ifdef WIN32
michael@0 158 int tmp_err = WSAGetLastError();
michael@0 159 if (tmp_err != WSAEINPROGRESS && tmp_err != WSAEINVAL &&
michael@0 160 tmp_err != WSAEWOULDBLOCK)
michael@0 161 event_err(1, "connect failed");
michael@0 162 #else
michael@0 163 if (errno != EINPROGRESS)
michael@0 164 event_err(1, "connect failed");
michael@0 165 #endif
michael@0 166 }
michael@0 167
michael@0 168 evutil_freeaddrinfo(aitop);
michael@0 169
michael@0 170 return (fd);
michael@0 171 }
michael@0 172
michael@0 173 /* Helper: do a strcmp on the contents of buf and the string s. */
michael@0 174 static int
michael@0 175 evbuffer_datacmp(struct evbuffer *buf, const char *s)
michael@0 176 {
michael@0 177 size_t b_sz = evbuffer_get_length(buf);
michael@0 178 size_t s_sz = strlen(s);
michael@0 179 unsigned char *d;
michael@0 180 int r;
michael@0 181
michael@0 182 if (b_sz < s_sz)
michael@0 183 return -1;
michael@0 184
michael@0 185 d = evbuffer_pullup(buf, s_sz);
michael@0 186 if ((r = memcmp(d, s, s_sz)))
michael@0 187 return r;
michael@0 188
michael@0 189 if (b_sz > s_sz)
michael@0 190 return 1;
michael@0 191 else
michael@0 192 return 0;
michael@0 193 }
michael@0 194
michael@0 195 /* Helper: Return true iff buf contains s */
michael@0 196 static int
michael@0 197 evbuffer_contains(struct evbuffer *buf, const char *s)
michael@0 198 {
michael@0 199 struct evbuffer_ptr ptr;
michael@0 200 ptr = evbuffer_search(buf, s, strlen(s), NULL);
michael@0 201 return ptr.pos != -1;
michael@0 202 }
michael@0 203
michael@0 204 static void
michael@0 205 http_readcb(struct bufferevent *bev, void *arg)
michael@0 206 {
michael@0 207 const char *what = BASIC_REQUEST_BODY;
michael@0 208 struct event_base *my_base = arg;
michael@0 209
michael@0 210 if (evbuffer_contains(bufferevent_get_input(bev), what)) {
michael@0 211 struct evhttp_request *req = evhttp_request_new(NULL, NULL);
michael@0 212 enum message_read_status done;
michael@0 213
michael@0 214 /* req->kind = EVHTTP_RESPONSE; */
michael@0 215 done = evhttp_parse_firstline(req, bufferevent_get_input(bev));
michael@0 216 if (done != ALL_DATA_READ)
michael@0 217 goto out;
michael@0 218
michael@0 219 done = evhttp_parse_headers(req, bufferevent_get_input(bev));
michael@0 220 if (done != ALL_DATA_READ)
michael@0 221 goto out;
michael@0 222
michael@0 223 if (done == 1 &&
michael@0 224 evhttp_find_header(evhttp_request_get_input_headers(req),
michael@0 225 "Content-Type") != NULL)
michael@0 226 test_ok++;
michael@0 227
michael@0 228 out:
michael@0 229 evhttp_request_free(req);
michael@0 230 bufferevent_disable(bev, EV_READ);
michael@0 231 if (exit_base)
michael@0 232 event_base_loopexit(exit_base, NULL);
michael@0 233 else if (my_base)
michael@0 234 event_base_loopexit(my_base, NULL);
michael@0 235 else {
michael@0 236 fprintf(stderr, "No way to exit loop!\n");
michael@0 237 exit(1);
michael@0 238 }
michael@0 239 }
michael@0 240 }
michael@0 241
michael@0 242 static void
michael@0 243 http_writecb(struct bufferevent *bev, void *arg)
michael@0 244 {
michael@0 245 if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) {
michael@0 246 /* enable reading of the reply */
michael@0 247 bufferevent_enable(bev, EV_READ);
michael@0 248 test_ok++;
michael@0 249 }
michael@0 250 }
michael@0 251
michael@0 252 static void
michael@0 253 http_errorcb(struct bufferevent *bev, short what, void *arg)
michael@0 254 {
michael@0 255 test_ok = -2;
michael@0 256 event_base_loopexit(arg, NULL);
michael@0 257 }
michael@0 258
michael@0 259 static void
michael@0 260 http_basic_cb(struct evhttp_request *req, void *arg)
michael@0 261 {
michael@0 262 struct evbuffer *evb = evbuffer_new();
michael@0 263 int empty = evhttp_find_header(evhttp_request_get_input_headers(req), "Empty") != NULL;
michael@0 264 event_debug(("%s: called\n", __func__));
michael@0 265 evbuffer_add_printf(evb, BASIC_REQUEST_BODY);
michael@0 266
michael@0 267 /* For multi-line headers test */
michael@0 268 {
michael@0 269 const char *multi =
michael@0 270 evhttp_find_header(evhttp_request_get_input_headers(req),"X-multi");
michael@0 271 if (multi) {
michael@0 272 if (strcmp("END", multi + strlen(multi) - 3) == 0)
michael@0 273 test_ok++;
michael@0 274 if (evhttp_find_header(evhttp_request_get_input_headers(req), "X-Last"))
michael@0 275 test_ok++;
michael@0 276 }
michael@0 277 }
michael@0 278
michael@0 279 /* injecting a bad content-length */
michael@0 280 if (evhttp_find_header(evhttp_request_get_input_headers(req), "X-Negative"))
michael@0 281 evhttp_add_header(evhttp_request_get_output_headers(req),
michael@0 282 "Content-Length", "-100");
michael@0 283
michael@0 284 /* allow sending of an empty reply */
michael@0 285 evhttp_send_reply(req, HTTP_OK, "Everything is fine",
michael@0 286 !empty ? evb : NULL);
michael@0 287
michael@0 288 evbuffer_free(evb);
michael@0 289 }
michael@0 290
michael@0 291 static char const* const CHUNKS[] = {
michael@0 292 "This is funny",
michael@0 293 "but not hilarious.",
michael@0 294 "bwv 1052"
michael@0 295 };
michael@0 296
michael@0 297 struct chunk_req_state {
michael@0 298 struct event_base *base;
michael@0 299 struct evhttp_request *req;
michael@0 300 int i;
michael@0 301 };
michael@0 302
michael@0 303 static void
michael@0 304 http_chunked_trickle_cb(evutil_socket_t fd, short events, void *arg)
michael@0 305 {
michael@0 306 struct evbuffer *evb = evbuffer_new();
michael@0 307 struct chunk_req_state *state = arg;
michael@0 308 struct timeval when = { 0, 0 };
michael@0 309
michael@0 310 evbuffer_add_printf(evb, "%s", CHUNKS[state->i]);
michael@0 311 evhttp_send_reply_chunk(state->req, evb);
michael@0 312 evbuffer_free(evb);
michael@0 313
michael@0 314 if (++state->i < (int) (sizeof(CHUNKS)/sizeof(CHUNKS[0]))) {
michael@0 315 event_base_once(state->base, -1, EV_TIMEOUT,
michael@0 316 http_chunked_trickle_cb, state, &when);
michael@0 317 } else {
michael@0 318 evhttp_send_reply_end(state->req);
michael@0 319 free(state);
michael@0 320 }
michael@0 321 }
michael@0 322
michael@0 323 static void
michael@0 324 http_chunked_cb(struct evhttp_request *req, void *arg)
michael@0 325 {
michael@0 326 struct timeval when = { 0, 0 };
michael@0 327 struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state));
michael@0 328 event_debug(("%s: called\n", __func__));
michael@0 329
michael@0 330 memset(state, 0, sizeof(struct chunk_req_state));
michael@0 331 state->req = req;
michael@0 332 state->base = arg;
michael@0 333
michael@0 334 if (strcmp(evhttp_request_get_uri(req), "/streamed") == 0) {
michael@0 335 evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Length", "39");
michael@0 336 }
michael@0 337
michael@0 338 /* generate a chunked/streamed reply */
michael@0 339 evhttp_send_reply_start(req, HTTP_OK, "Everything is fine");
michael@0 340
michael@0 341 /* but trickle it across several iterations to ensure we're not
michael@0 342 * assuming it comes all at once */
michael@0 343 event_base_once(arg, -1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when);
michael@0 344 }
michael@0 345
michael@0 346 static void
michael@0 347 http_complete_write(evutil_socket_t fd, short what, void *arg)
michael@0 348 {
michael@0 349 struct bufferevent *bev = arg;
michael@0 350 const char *http_request = "host\r\n"
michael@0 351 "Connection: close\r\n"
michael@0 352 "\r\n";
michael@0 353 bufferevent_write(bev, http_request, strlen(http_request));
michael@0 354 }
michael@0 355
michael@0 356 static void
michael@0 357 http_basic_test(void *arg)
michael@0 358 {
michael@0 359 struct basic_test_data *data = arg;
michael@0 360 struct timeval tv;
michael@0 361 struct bufferevent *bev;
michael@0 362 evutil_socket_t fd;
michael@0 363 const char *http_request;
michael@0 364 ev_uint16_t port = 0, port2 = 0;
michael@0 365
michael@0 366 test_ok = 0;
michael@0 367
michael@0 368 http = http_setup(&port, data->base);
michael@0 369
michael@0 370 /* bind to a second socket */
michael@0 371 if (http_bind(http, &port2) == -1) {
michael@0 372 fprintf(stdout, "FAILED (bind)\n");
michael@0 373 exit(1);
michael@0 374 }
michael@0 375
michael@0 376 fd = http_connect("127.0.0.1", port);
michael@0 377
michael@0 378 /* Stupid thing to send a request */
michael@0 379 bev = bufferevent_socket_new(data->base, fd, 0);
michael@0 380 bufferevent_setcb(bev, http_readcb, http_writecb,
michael@0 381 http_errorcb, data->base);
michael@0 382
michael@0 383 /* first half of the http request */
michael@0 384 http_request =
michael@0 385 "GET /test HTTP/1.1\r\n"
michael@0 386 "Host: some";
michael@0 387
michael@0 388 bufferevent_write(bev, http_request, strlen(http_request));
michael@0 389 evutil_timerclear(&tv);
michael@0 390 tv.tv_usec = 10000;
michael@0 391 event_base_once(data->base,
michael@0 392 -1, EV_TIMEOUT, http_complete_write, bev, &tv);
michael@0 393
michael@0 394 event_base_dispatch(data->base);
michael@0 395
michael@0 396 tt_assert(test_ok == 3);
michael@0 397
michael@0 398 /* connect to the second port */
michael@0 399 bufferevent_free(bev);
michael@0 400 evutil_closesocket(fd);
michael@0 401
michael@0 402 fd = http_connect("127.0.0.1", port2);
michael@0 403
michael@0 404 /* Stupid thing to send a request */
michael@0 405 bev = bufferevent_socket_new(data->base, fd, 0);
michael@0 406 bufferevent_setcb(bev, http_readcb, http_writecb,
michael@0 407 http_errorcb, data->base);
michael@0 408
michael@0 409 http_request =
michael@0 410 "GET /test HTTP/1.1\r\n"
michael@0 411 "Host: somehost\r\n"
michael@0 412 "Connection: close\r\n"
michael@0 413 "\r\n";
michael@0 414
michael@0 415 bufferevent_write(bev, http_request, strlen(http_request));
michael@0 416
michael@0 417 event_base_dispatch(data->base);
michael@0 418
michael@0 419 tt_assert(test_ok == 5);
michael@0 420
michael@0 421 /* Connect to the second port again. This time, send an absolute uri. */
michael@0 422 bufferevent_free(bev);
michael@0 423 evutil_closesocket(fd);
michael@0 424
michael@0 425 fd = http_connect("127.0.0.1", port2);
michael@0 426
michael@0 427 /* Stupid thing to send a request */
michael@0 428 bev = bufferevent_socket_new(data->base, fd, 0);
michael@0 429 bufferevent_setcb(bev, http_readcb, http_writecb,
michael@0 430 http_errorcb, data->base);
michael@0 431
michael@0 432 http_request =
michael@0 433 "GET http://somehost.net/test HTTP/1.1\r\n"
michael@0 434 "Host: somehost\r\n"
michael@0 435 "Connection: close\r\n"
michael@0 436 "\r\n";
michael@0 437
michael@0 438 bufferevent_write(bev, http_request, strlen(http_request));
michael@0 439
michael@0 440 event_base_dispatch(data->base);
michael@0 441
michael@0 442 tt_assert(test_ok == 7);
michael@0 443
michael@0 444 evhttp_free(http);
michael@0 445 end:
michael@0 446 ;
michael@0 447 }
michael@0 448
michael@0 449 static void
michael@0 450 http_delay_reply(evutil_socket_t fd, short what, void *arg)
michael@0 451 {
michael@0 452 struct evhttp_request *req = arg;
michael@0 453
michael@0 454 evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL);
michael@0 455
michael@0 456 ++test_ok;
michael@0 457 }
michael@0 458
michael@0 459 static void
michael@0 460 http_delay_cb(struct evhttp_request *req, void *arg)
michael@0 461 {
michael@0 462 struct timeval tv;
michael@0 463 evutil_timerclear(&tv);
michael@0 464 tv.tv_sec = 0;
michael@0 465 tv.tv_usec = 200 * 1000;
michael@0 466
michael@0 467 event_base_once(arg, -1, EV_TIMEOUT, http_delay_reply, req, &tv);
michael@0 468 }
michael@0 469
michael@0 470 static void
michael@0 471 http_badreq_cb(struct evhttp_request *req, void *arg)
michael@0 472 {
michael@0 473 struct evbuffer *buf = evbuffer_new();
michael@0 474
michael@0 475 evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/xml; charset=UTF-8");
michael@0 476 evbuffer_add_printf(buf, "Hello, %s!", "127.0.0.1");
michael@0 477
michael@0 478 evhttp_send_reply(req, HTTP_OK, "OK", buf);
michael@0 479 evbuffer_free(buf);
michael@0 480 }
michael@0 481
michael@0 482 static void
michael@0 483 http_badreq_errorcb(struct bufferevent *bev, short what, void *arg)
michael@0 484 {
michael@0 485 event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg));
michael@0 486 /* ignore */
michael@0 487 }
michael@0 488
michael@0 489 #ifndef SHUT_WR
michael@0 490 #ifdef WIN32
michael@0 491 #define SHUT_WR SD_SEND
michael@0 492 #else
michael@0 493 #define SHUT_WR 1
michael@0 494 #endif
michael@0 495 #endif
michael@0 496
michael@0 497 static void
michael@0 498 http_badreq_readcb(struct bufferevent *bev, void *arg)
michael@0 499 {
michael@0 500 const char *what = "Hello, 127.0.0.1";
michael@0 501 const char *bad_request = "400 Bad Request";
michael@0 502
michael@0 503 if (evbuffer_contains(bufferevent_get_input(bev), bad_request)) {
michael@0 504 TT_FAIL(("%s:bad request detected", __func__));
michael@0 505 bufferevent_disable(bev, EV_READ);
michael@0 506 event_base_loopexit(arg, NULL);
michael@0 507 return;
michael@0 508 }
michael@0 509
michael@0 510 if (evbuffer_contains(bufferevent_get_input(bev), what)) {
michael@0 511 struct evhttp_request *req = evhttp_request_new(NULL, NULL);
michael@0 512 enum message_read_status done;
michael@0 513
michael@0 514 /* req->kind = EVHTTP_RESPONSE; */
michael@0 515 done = evhttp_parse_firstline(req, bufferevent_get_input(bev));
michael@0 516 if (done != ALL_DATA_READ)
michael@0 517 goto out;
michael@0 518
michael@0 519 done = evhttp_parse_headers(req, bufferevent_get_input(bev));
michael@0 520 if (done != ALL_DATA_READ)
michael@0 521 goto out;
michael@0 522
michael@0 523 if (done == 1 &&
michael@0 524 evhttp_find_header(evhttp_request_get_input_headers(req),
michael@0 525 "Content-Type") != NULL)
michael@0 526 test_ok++;
michael@0 527
michael@0 528 out:
michael@0 529 evhttp_request_free(req);
michael@0 530 evbuffer_drain(bufferevent_get_input(bev), evbuffer_get_length(bufferevent_get_input(bev)));
michael@0 531 }
michael@0 532
michael@0 533 shutdown(bufferevent_getfd(bev), SHUT_WR);
michael@0 534 }
michael@0 535
michael@0 536 static void
michael@0 537 http_badreq_successcb(evutil_socket_t fd, short what, void *arg)
michael@0 538 {
michael@0 539 event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg));
michael@0 540 event_base_loopexit(exit_base, NULL);
michael@0 541 }
michael@0 542
michael@0 543 static void
michael@0 544 http_bad_request_test(void *arg)
michael@0 545 {
michael@0 546 struct basic_test_data *data = arg;
michael@0 547 struct timeval tv;
michael@0 548 struct bufferevent *bev = NULL;
michael@0 549 evutil_socket_t fd;
michael@0 550 const char *http_request;
michael@0 551 ev_uint16_t port=0, port2=0;
michael@0 552
michael@0 553 test_ok = 0;
michael@0 554 exit_base = data->base;
michael@0 555
michael@0 556 http = http_setup(&port, data->base);
michael@0 557
michael@0 558 /* bind to a second socket */
michael@0 559 if (http_bind(http, &port2) == -1)
michael@0 560 TT_DIE(("Bind socket failed"));
michael@0 561
michael@0 562 /* NULL request test */
michael@0 563 fd = http_connect("127.0.0.1", port);
michael@0 564
michael@0 565 /* Stupid thing to send a request */
michael@0 566 bev = bufferevent_socket_new(data->base, fd, 0);
michael@0 567 bufferevent_setcb(bev, http_badreq_readcb, http_writecb,
michael@0 568 http_badreq_errorcb, data->base);
michael@0 569 bufferevent_enable(bev, EV_READ);
michael@0 570
michael@0 571 /* real NULL request */
michael@0 572 http_request = "";
michael@0 573
michael@0 574 bufferevent_write(bev, http_request, strlen(http_request));
michael@0 575
michael@0 576 shutdown(fd, SHUT_WR);
michael@0 577 timerclear(&tv);
michael@0 578 tv.tv_usec = 10000;
michael@0 579 event_base_once(data->base, -1, EV_TIMEOUT, http_badreq_successcb, bev, &tv);
michael@0 580
michael@0 581 event_base_dispatch(data->base);
michael@0 582
michael@0 583 bufferevent_free(bev);
michael@0 584 evutil_closesocket(fd);
michael@0 585
michael@0 586 if (test_ok != 0) {
michael@0 587 fprintf(stdout, "FAILED\n");
michael@0 588 exit(1);
michael@0 589 }
michael@0 590
michael@0 591 /* Second answer (BAD REQUEST) on connection close */
michael@0 592
michael@0 593 /* connect to the second port */
michael@0 594 fd = http_connect("127.0.0.1", port2);
michael@0 595
michael@0 596 /* Stupid thing to send a request */
michael@0 597 bev = bufferevent_socket_new(data->base, fd, 0);
michael@0 598 bufferevent_setcb(bev, http_badreq_readcb, http_writecb,
michael@0 599 http_badreq_errorcb, data->base);
michael@0 600 bufferevent_enable(bev, EV_READ);
michael@0 601
michael@0 602 /* first half of the http request */
michael@0 603 http_request =
michael@0 604 "GET /badrequest HTTP/1.0\r\n" \
michael@0 605 "Connection: Keep-Alive\r\n" \
michael@0 606 "\r\n";
michael@0 607
michael@0 608 bufferevent_write(bev, http_request, strlen(http_request));
michael@0 609
michael@0 610 timerclear(&tv);
michael@0 611 tv.tv_usec = 10000;
michael@0 612 event_base_once(data->base, -1, EV_TIMEOUT, http_badreq_successcb, bev, &tv);
michael@0 613
michael@0 614 event_base_dispatch(data->base);
michael@0 615
michael@0 616 tt_int_op(test_ok, ==, 2);
michael@0 617
michael@0 618 end:
michael@0 619 evhttp_free(http);
michael@0 620 if (bev)
michael@0 621 bufferevent_free(bev);
michael@0 622 }
michael@0 623
michael@0 624 static struct evhttp_connection *delayed_client;
michael@0 625
michael@0 626 static void
michael@0 627 http_large_delay_cb(struct evhttp_request *req, void *arg)
michael@0 628 {
michael@0 629 struct timeval tv;
michael@0 630 evutil_timerclear(&tv);
michael@0 631 tv.tv_sec = 3;
michael@0 632
michael@0 633 event_base_once(arg, -1, EV_TIMEOUT, http_delay_reply, req, &tv);
michael@0 634 evhttp_connection_fail(delayed_client, EVCON_HTTP_EOF);
michael@0 635 }
michael@0 636
michael@0 637 /*
michael@0 638 * HTTP DELETE test, just piggyback on the basic test
michael@0 639 */
michael@0 640
michael@0 641 static void
michael@0 642 http_delete_cb(struct evhttp_request *req, void *arg)
michael@0 643 {
michael@0 644 struct evbuffer *evb = evbuffer_new();
michael@0 645 int empty = evhttp_find_header(evhttp_request_get_input_headers(req), "Empty") != NULL;
michael@0 646
michael@0 647 /* Expecting a DELETE request */
michael@0 648 if (evhttp_request_get_command(req) != EVHTTP_REQ_DELETE) {
michael@0 649 fprintf(stdout, "FAILED (delete type)\n");
michael@0 650 exit(1);
michael@0 651 }
michael@0 652
michael@0 653 event_debug(("%s: called\n", __func__));
michael@0 654 evbuffer_add_printf(evb, BASIC_REQUEST_BODY);
michael@0 655
michael@0 656 /* allow sending of an empty reply */
michael@0 657 evhttp_send_reply(req, HTTP_OK, "Everything is fine",
michael@0 658 !empty ? evb : NULL);
michael@0 659
michael@0 660 evbuffer_free(evb);
michael@0 661 }
michael@0 662
michael@0 663 static void
michael@0 664 http_delete_test(void *arg)
michael@0 665 {
michael@0 666 struct basic_test_data *data = arg;
michael@0 667 struct bufferevent *bev;
michael@0 668 evutil_socket_t fd;
michael@0 669 const char *http_request;
michael@0 670 ev_uint16_t port = 0;
michael@0 671
michael@0 672 test_ok = 0;
michael@0 673
michael@0 674 http = http_setup(&port, data->base);
michael@0 675
michael@0 676 fd = http_connect("127.0.0.1", port);
michael@0 677
michael@0 678 /* Stupid thing to send a request */
michael@0 679 bev = bufferevent_socket_new(data->base, fd, 0);
michael@0 680 bufferevent_setcb(bev, http_readcb, http_writecb,
michael@0 681 http_errorcb, data->base);
michael@0 682
michael@0 683 http_request =
michael@0 684 "DELETE /deleteit HTTP/1.1\r\n"
michael@0 685 "Host: somehost\r\n"
michael@0 686 "Connection: close\r\n"
michael@0 687 "\r\n";
michael@0 688
michael@0 689 bufferevent_write(bev, http_request, strlen(http_request));
michael@0 690
michael@0 691 event_base_dispatch(data->base);
michael@0 692
michael@0 693 bufferevent_free(bev);
michael@0 694 evutil_closesocket(fd);
michael@0 695
michael@0 696 evhttp_free(http);
michael@0 697
michael@0 698 tt_int_op(test_ok, ==, 2);
michael@0 699 end:
michael@0 700 ;
michael@0 701 }
michael@0 702
michael@0 703 static void
michael@0 704 http_allowed_methods_eventcb(struct bufferevent *bev, short what, void *arg)
michael@0 705 {
michael@0 706 char **output = arg;
michael@0 707 if ((what & (BEV_EVENT_ERROR|BEV_EVENT_EOF))) {
michael@0 708 char buf[4096];
michael@0 709 int n;
michael@0 710 n = evbuffer_remove(bufferevent_get_input(bev), buf,
michael@0 711 sizeof(buf)-1);
michael@0 712 if (n >= 0) {
michael@0 713 buf[n]='\0';
michael@0 714 if (*output)
michael@0 715 free(*output);
michael@0 716 *output = strdup(buf);
michael@0 717 }
michael@0 718 event_base_loopexit(exit_base, NULL);
michael@0 719 }
michael@0 720 }
michael@0 721
michael@0 722 static void
michael@0 723 http_allowed_methods_test(void *arg)
michael@0 724 {
michael@0 725 struct basic_test_data *data = arg;
michael@0 726 struct bufferevent *bev1, *bev2, *bev3;
michael@0 727 evutil_socket_t fd1, fd2, fd3;
michael@0 728 const char *http_request;
michael@0 729 char *result1=NULL, *result2=NULL, *result3=NULL;
michael@0 730 ev_uint16_t port = 0;
michael@0 731
michael@0 732 exit_base = data->base;
michael@0 733 test_ok = 0;
michael@0 734
michael@0 735 http = http_setup(&port, data->base);
michael@0 736
michael@0 737 fd1 = http_connect("127.0.0.1", port);
michael@0 738
michael@0 739 /* GET is out; PATCH is in. */
michael@0 740 evhttp_set_allowed_methods(http, EVHTTP_REQ_PATCH);
michael@0 741
michael@0 742 /* Stupid thing to send a request */
michael@0 743 bev1 = bufferevent_socket_new(data->base, fd1, 0);
michael@0 744 bufferevent_enable(bev1, EV_READ|EV_WRITE);
michael@0 745 bufferevent_setcb(bev1, NULL, NULL,
michael@0 746 http_allowed_methods_eventcb, &result1);
michael@0 747
michael@0 748 http_request =
michael@0 749 "GET /index.html HTTP/1.1\r\n"
michael@0 750 "Host: somehost\r\n"
michael@0 751 "Connection: close\r\n"
michael@0 752 "\r\n";
michael@0 753
michael@0 754 bufferevent_write(bev1, http_request, strlen(http_request));
michael@0 755
michael@0 756 event_base_dispatch(data->base);
michael@0 757
michael@0 758 fd2 = http_connect("127.0.0.1", port);
michael@0 759
michael@0 760 bev2 = bufferevent_socket_new(data->base, fd2, 0);
michael@0 761 bufferevent_enable(bev2, EV_READ|EV_WRITE);
michael@0 762 bufferevent_setcb(bev2, NULL, NULL,
michael@0 763 http_allowed_methods_eventcb, &result2);
michael@0 764
michael@0 765 http_request =
michael@0 766 "PATCH /test HTTP/1.1\r\n"
michael@0 767 "Host: somehost\r\n"
michael@0 768 "Connection: close\r\n"
michael@0 769 "\r\n";
michael@0 770
michael@0 771 bufferevent_write(bev2, http_request, strlen(http_request));
michael@0 772
michael@0 773 event_base_dispatch(data->base);
michael@0 774
michael@0 775 fd3 = http_connect("127.0.0.1", port);
michael@0 776
michael@0 777 bev3 = bufferevent_socket_new(data->base, fd3, 0);
michael@0 778 bufferevent_enable(bev3, EV_READ|EV_WRITE);
michael@0 779 bufferevent_setcb(bev3, NULL, NULL,
michael@0 780 http_allowed_methods_eventcb, &result3);
michael@0 781
michael@0 782 http_request =
michael@0 783 "FLOOP /test HTTP/1.1\r\n"
michael@0 784 "Host: somehost\r\n"
michael@0 785 "Connection: close\r\n"
michael@0 786 "\r\n";
michael@0 787
michael@0 788 bufferevent_write(bev3, http_request, strlen(http_request));
michael@0 789
michael@0 790 event_base_dispatch(data->base);
michael@0 791
michael@0 792 bufferevent_free(bev1);
michael@0 793 bufferevent_free(bev2);
michael@0 794 bufferevent_free(bev3);
michael@0 795 evutil_closesocket(fd1);
michael@0 796 evutil_closesocket(fd2);
michael@0 797 evutil_closesocket(fd3);
michael@0 798
michael@0 799 evhttp_free(http);
michael@0 800
michael@0 801 /* Method known but disallowed */
michael@0 802 tt_assert(result1);
michael@0 803 tt_assert(!strncmp(result1, "HTTP/1.1 501 ", strlen("HTTP/1.1 501 ")));
michael@0 804
michael@0 805 /* Method known and allowed */
michael@0 806 tt_assert(result2);
michael@0 807 tt_assert(!strncmp(result2, "HTTP/1.1 200 ", strlen("HTTP/1.1 200 ")));
michael@0 808
michael@0 809 /* Method unknown */
michael@0 810 tt_assert(result3);
michael@0 811 tt_assert(!strncmp(result3, "HTTP/1.1 501 ", strlen("HTTP/1.1 501 ")));
michael@0 812
michael@0 813 end:
michael@0 814 if (result1)
michael@0 815 free(result1);
michael@0 816 if (result2)
michael@0 817 free(result2);
michael@0 818 if (result3)
michael@0 819 free(result3);
michael@0 820 }
michael@0 821
michael@0 822 static void http_request_done(struct evhttp_request *, void *);
michael@0 823 static void http_request_empty_done(struct evhttp_request *, void *);
michael@0 824
michael@0 825 static void
michael@0 826 _http_connection_test(struct basic_test_data *data, int persistent)
michael@0 827 {
michael@0 828 ev_uint16_t port = 0;
michael@0 829 struct evhttp_connection *evcon = NULL;
michael@0 830 struct evhttp_request *req = NULL;
michael@0 831
michael@0 832 test_ok = 0;
michael@0 833
michael@0 834 http = http_setup(&port, data->base);
michael@0 835
michael@0 836 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
michael@0 837 tt_assert(evcon);
michael@0 838
michael@0 839 tt_assert(evhttp_connection_get_base(evcon) == data->base);
michael@0 840
michael@0 841 exit_base = data->base;
michael@0 842 /*
michael@0 843 * At this point, we want to schedule a request to the HTTP
michael@0 844 * server using our make request method.
michael@0 845 */
michael@0 846
michael@0 847 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
michael@0 848
michael@0 849 /* Add the information that we care about */
michael@0 850 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 851
michael@0 852 /* We give ownership of the request to the connection */
michael@0 853 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
michael@0 854 fprintf(stdout, "FAILED\n");
michael@0 855 exit(1);
michael@0 856 }
michael@0 857
michael@0 858 event_base_dispatch(data->base);
michael@0 859
michael@0 860 tt_assert(test_ok);
michael@0 861
michael@0 862 /* try to make another request over the same connection */
michael@0 863 test_ok = 0;
michael@0 864
michael@0 865 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
michael@0 866
michael@0 867 /* Add the information that we care about */
michael@0 868 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 869
michael@0 870 /*
michael@0 871 * if our connections are not supposed to be persistent; request
michael@0 872 * a close from the server.
michael@0 873 */
michael@0 874 if (!persistent)
michael@0 875 evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close");
michael@0 876
michael@0 877 /* We give ownership of the request to the connection */
michael@0 878 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
michael@0 879 tt_abort_msg("couldn't make request");
michael@0 880 }
michael@0 881
michael@0 882 event_base_dispatch(data->base);
michael@0 883
michael@0 884 /* make another request: request empty reply */
michael@0 885 test_ok = 0;
michael@0 886
michael@0 887 req = evhttp_request_new(http_request_empty_done, data->base);
michael@0 888
michael@0 889 /* Add the information that we care about */
michael@0 890 evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis");
michael@0 891
michael@0 892 /* We give ownership of the request to the connection */
michael@0 893 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
michael@0 894 tt_abort_msg("Couldn't make request");
michael@0 895 }
michael@0 896
michael@0 897 event_base_dispatch(data->base);
michael@0 898
michael@0 899 end:
michael@0 900 if (evcon)
michael@0 901 evhttp_connection_free(evcon);
michael@0 902 if (http)
michael@0 903 evhttp_free(http);
michael@0 904 }
michael@0 905
michael@0 906 static void
michael@0 907 http_connection_test(void *arg)
michael@0 908 {
michael@0 909 _http_connection_test(arg, 0);
michael@0 910 }
michael@0 911 static void
michael@0 912 http_persist_connection_test(void *arg)
michael@0 913 {
michael@0 914 _http_connection_test(arg, 1);
michael@0 915 }
michael@0 916
michael@0 917 static struct regress_dns_server_table search_table[] = {
michael@0 918 { "localhost", "A", "127.0.0.1", 0 },
michael@0 919 { NULL, NULL, NULL, 0 }
michael@0 920 };
michael@0 921
michael@0 922 static void
michael@0 923 http_connection_async_test(void *arg)
michael@0 924 {
michael@0 925 struct basic_test_data *data = arg;
michael@0 926 ev_uint16_t port = 0;
michael@0 927 struct evhttp_connection *evcon = NULL;
michael@0 928 struct evhttp_request *req = NULL;
michael@0 929 struct evdns_base *dns_base = NULL;
michael@0 930 ev_uint16_t portnum = 0;
michael@0 931 char address[64];
michael@0 932
michael@0 933 exit_base = data->base;
michael@0 934 tt_assert(regress_dnsserver(data->base, &portnum, search_table));
michael@0 935
michael@0 936 dns_base = evdns_base_new(data->base, 0/* init name servers */);
michael@0 937 tt_assert(dns_base);
michael@0 938
michael@0 939 /* Add ourself as the only nameserver, and make sure we really are
michael@0 940 * the only nameserver. */
michael@0 941 evutil_snprintf(address, sizeof(address), "127.0.0.1:%d", portnum);
michael@0 942 evdns_base_nameserver_ip_add(dns_base, address);
michael@0 943
michael@0 944 test_ok = 0;
michael@0 945
michael@0 946 http = http_setup(&port, data->base);
michael@0 947
michael@0 948 evcon = evhttp_connection_base_new(data->base, dns_base, "127.0.0.1", port);
michael@0 949 tt_assert(evcon);
michael@0 950
michael@0 951 /*
michael@0 952 * At this point, we want to schedule a request to the HTTP
michael@0 953 * server using our make request method.
michael@0 954 */
michael@0 955
michael@0 956 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
michael@0 957
michael@0 958 /* Add the information that we care about */
michael@0 959 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 960
michael@0 961 /* We give ownership of the request to the connection */
michael@0 962 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
michael@0 963 fprintf(stdout, "FAILED\n");
michael@0 964 exit(1);
michael@0 965 }
michael@0 966
michael@0 967 event_base_dispatch(data->base);
michael@0 968
michael@0 969 tt_assert(test_ok);
michael@0 970
michael@0 971 /* try to make another request over the same connection */
michael@0 972 test_ok = 0;
michael@0 973
michael@0 974 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
michael@0 975
michael@0 976 /* Add the information that we care about */
michael@0 977 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 978
michael@0 979 /*
michael@0 980 * if our connections are not supposed to be persistent; request
michael@0 981 * a close from the server.
michael@0 982 */
michael@0 983 evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close");
michael@0 984
michael@0 985 /* We give ownership of the request to the connection */
michael@0 986 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
michael@0 987 tt_abort_msg("couldn't make request");
michael@0 988 }
michael@0 989
michael@0 990 event_base_dispatch(data->base);
michael@0 991
michael@0 992 /* make another request: request empty reply */
michael@0 993 test_ok = 0;
michael@0 994
michael@0 995 req = evhttp_request_new(http_request_empty_done, data->base);
michael@0 996
michael@0 997 /* Add the information that we care about */
michael@0 998 evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis");
michael@0 999
michael@0 1000 /* We give ownership of the request to the connection */
michael@0 1001 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
michael@0 1002 tt_abort_msg("Couldn't make request");
michael@0 1003 }
michael@0 1004
michael@0 1005 event_base_dispatch(data->base);
michael@0 1006
michael@0 1007 end:
michael@0 1008 if (evcon)
michael@0 1009 evhttp_connection_free(evcon);
michael@0 1010 if (http)
michael@0 1011 evhttp_free(http);
michael@0 1012 if (dns_base)
michael@0 1013 evdns_base_free(dns_base, 0);
michael@0 1014 regress_clean_dnsserver();
michael@0 1015 }
michael@0 1016
michael@0 1017 static void
michael@0 1018 http_request_never_call(struct evhttp_request *req, void *arg)
michael@0 1019 {
michael@0 1020 fprintf(stdout, "FAILED\n");
michael@0 1021 exit(1);
michael@0 1022 }
michael@0 1023
michael@0 1024 static void
michael@0 1025 http_do_cancel(evutil_socket_t fd, short what, void *arg)
michael@0 1026 {
michael@0 1027 struct evhttp_request *req = arg;
michael@0 1028 struct timeval tv;
michael@0 1029 struct event_base *base;
michael@0 1030 evutil_timerclear(&tv);
michael@0 1031 tv.tv_sec = 0;
michael@0 1032 tv.tv_usec = 500 * 1000;
michael@0 1033
michael@0 1034 base = evhttp_connection_get_base(evhttp_request_get_connection(req));
michael@0 1035 evhttp_cancel_request(req);
michael@0 1036
michael@0 1037 event_base_loopexit(base, &tv);
michael@0 1038
michael@0 1039 ++test_ok;
michael@0 1040 }
michael@0 1041
michael@0 1042 static void
michael@0 1043 http_cancel_test(void *arg)
michael@0 1044 {
michael@0 1045 struct basic_test_data *data = arg;
michael@0 1046 ev_uint16_t port = 0;
michael@0 1047 struct evhttp_connection *evcon = NULL;
michael@0 1048 struct evhttp_request *req = NULL;
michael@0 1049 struct timeval tv;
michael@0 1050
michael@0 1051 exit_base = data->base;
michael@0 1052
michael@0 1053 test_ok = 0;
michael@0 1054
michael@0 1055 http = http_setup(&port, data->base);
michael@0 1056
michael@0 1057 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
michael@0 1058 tt_assert(evcon);
michael@0 1059
michael@0 1060 /*
michael@0 1061 * At this point, we want to schedule a request to the HTTP
michael@0 1062 * server using our make request method.
michael@0 1063 */
michael@0 1064
michael@0 1065 req = evhttp_request_new(http_request_never_call, NULL);
michael@0 1066
michael@0 1067 /* Add the information that we care about */
michael@0 1068 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 1069
michael@0 1070 /* We give ownership of the request to the connection */
michael@0 1071 tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/delay"),
michael@0 1072 !=, -1);
michael@0 1073
michael@0 1074 evutil_timerclear(&tv);
michael@0 1075 tv.tv_sec = 0;
michael@0 1076 tv.tv_usec = 100 * 1000;
michael@0 1077
michael@0 1078 event_base_once(data->base, -1, EV_TIMEOUT, http_do_cancel, req, &tv);
michael@0 1079
michael@0 1080 event_base_dispatch(data->base);
michael@0 1081
michael@0 1082 tt_int_op(test_ok, ==, 2);
michael@0 1083
michael@0 1084 /* try to make another request over the same connection */
michael@0 1085 test_ok = 0;
michael@0 1086
michael@0 1087 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
michael@0 1088
michael@0 1089 /* Add the information that we care about */
michael@0 1090 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 1091
michael@0 1092 /* We give ownership of the request to the connection */
michael@0 1093 tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test"),
michael@0 1094 !=, -1);
michael@0 1095
michael@0 1096 event_base_dispatch(data->base);
michael@0 1097
michael@0 1098 /* make another request: request empty reply */
michael@0 1099 test_ok = 0;
michael@0 1100
michael@0 1101 req = evhttp_request_new(http_request_empty_done, data->base);
michael@0 1102
michael@0 1103 /* Add the information that we care about */
michael@0 1104 evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis");
michael@0 1105
michael@0 1106 /* We give ownership of the request to the connection */
michael@0 1107 tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test"),
michael@0 1108 !=, -1);
michael@0 1109
michael@0 1110 event_base_dispatch(data->base);
michael@0 1111
michael@0 1112 end:
michael@0 1113 if (evcon)
michael@0 1114 evhttp_connection_free(evcon);
michael@0 1115 if (http)
michael@0 1116 evhttp_free(http);
michael@0 1117 }
michael@0 1118
michael@0 1119 static void
michael@0 1120 http_request_done(struct evhttp_request *req, void *arg)
michael@0 1121 {
michael@0 1122 const char *what = arg;
michael@0 1123
michael@0 1124 if (evhttp_request_get_response_code(req) != HTTP_OK) {
michael@0 1125 fprintf(stderr, "FAILED\n");
michael@0 1126 exit(1);
michael@0 1127 }
michael@0 1128
michael@0 1129 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) {
michael@0 1130 fprintf(stderr, "FAILED\n");
michael@0 1131 exit(1);
michael@0 1132 }
michael@0 1133
michael@0 1134 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) {
michael@0 1135 fprintf(stderr, "FAILED\n");
michael@0 1136 exit(1);
michael@0 1137 }
michael@0 1138
michael@0 1139 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) {
michael@0 1140 fprintf(stderr, "FAILED\n");
michael@0 1141 exit(1);
michael@0 1142 }
michael@0 1143
michael@0 1144 test_ok = 1;
michael@0 1145 EVUTIL_ASSERT(exit_base);
michael@0 1146 event_base_loopexit(exit_base, NULL);
michael@0 1147 }
michael@0 1148
michael@0 1149 static void
michael@0 1150 http_request_expect_error(struct evhttp_request *req, void *arg)
michael@0 1151 {
michael@0 1152 if (evhttp_request_get_response_code(req) == HTTP_OK) {
michael@0 1153 fprintf(stderr, "FAILED\n");
michael@0 1154 exit(1);
michael@0 1155 }
michael@0 1156
michael@0 1157 test_ok = 1;
michael@0 1158 EVUTIL_ASSERT(arg);
michael@0 1159 event_base_loopexit(arg, NULL);
michael@0 1160 }
michael@0 1161
michael@0 1162 /* test virtual hosts */
michael@0 1163 static void
michael@0 1164 http_virtual_host_test(void *arg)
michael@0 1165 {
michael@0 1166 struct basic_test_data *data = arg;
michael@0 1167 ev_uint16_t port = 0;
michael@0 1168 struct evhttp_connection *evcon = NULL;
michael@0 1169 struct evhttp_request *req = NULL;
michael@0 1170 struct evhttp *second = NULL, *third = NULL;
michael@0 1171 evutil_socket_t fd;
michael@0 1172 struct bufferevent *bev;
michael@0 1173 const char *http_request;
michael@0 1174
michael@0 1175 exit_base = data->base;
michael@0 1176
michael@0 1177 http = http_setup(&port, data->base);
michael@0 1178
michael@0 1179 /* virtual host */
michael@0 1180 second = evhttp_new(NULL);
michael@0 1181 evhttp_set_cb(second, "/funnybunny", http_basic_cb, NULL);
michael@0 1182 third = evhttp_new(NULL);
michael@0 1183 evhttp_set_cb(third, "/blackcoffee", http_basic_cb, NULL);
michael@0 1184
michael@0 1185 if (evhttp_add_virtual_host(http, "foo.com", second) == -1) {
michael@0 1186 tt_abort_msg("Couldn't add vhost");
michael@0 1187 }
michael@0 1188
michael@0 1189 if (evhttp_add_virtual_host(http, "bar.*.foo.com", third) == -1) {
michael@0 1190 tt_abort_msg("Couldn't add wildcarded vhost");
michael@0 1191 }
michael@0 1192
michael@0 1193 /* add some aliases to the vhosts */
michael@0 1194 tt_assert(evhttp_add_server_alias(second, "manolito.info") == 0);
michael@0 1195 tt_assert(evhttp_add_server_alias(third, "bonkers.org") == 0);
michael@0 1196
michael@0 1197 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
michael@0 1198 tt_assert(evcon);
michael@0 1199
michael@0 1200 /* make a request with a different host and expect an error */
michael@0 1201 req = evhttp_request_new(http_request_expect_error, data->base);
michael@0 1202
michael@0 1203 /* Add the information that we care about */
michael@0 1204 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 1205
michael@0 1206 /* We give ownership of the request to the connection */
michael@0 1207 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
michael@0 1208 "/funnybunny") == -1) {
michael@0 1209 tt_abort_msg("Couldn't make request");
michael@0 1210 }
michael@0 1211
michael@0 1212 event_base_dispatch(data->base);
michael@0 1213
michael@0 1214 tt_assert(test_ok == 1);
michael@0 1215
michael@0 1216 test_ok = 0;
michael@0 1217
michael@0 1218 /* make a request with the right host and expect a response */
michael@0 1219 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
michael@0 1220
michael@0 1221 /* Add the information that we care about */
michael@0 1222 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "foo.com");
michael@0 1223
michael@0 1224 /* We give ownership of the request to the connection */
michael@0 1225 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
michael@0 1226 "/funnybunny") == -1) {
michael@0 1227 fprintf(stdout, "FAILED\n");
michael@0 1228 exit(1);
michael@0 1229 }
michael@0 1230
michael@0 1231 event_base_dispatch(data->base);
michael@0 1232
michael@0 1233 tt_assert(test_ok == 1);
michael@0 1234
michael@0 1235 test_ok = 0;
michael@0 1236
michael@0 1237 /* make a request with the right host and expect a response */
michael@0 1238 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
michael@0 1239
michael@0 1240 /* Add the information that we care about */
michael@0 1241 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "bar.magic.foo.com");
michael@0 1242
michael@0 1243 /* We give ownership of the request to the connection */
michael@0 1244 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
michael@0 1245 "/blackcoffee") == -1) {
michael@0 1246 tt_abort_msg("Couldn't make request");
michael@0 1247 }
michael@0 1248
michael@0 1249 event_base_dispatch(data->base);
michael@0 1250
michael@0 1251 tt_assert(test_ok == 1)
michael@0 1252
michael@0 1253 test_ok = 0;
michael@0 1254
michael@0 1255 /* make a request with the right host and expect a response */
michael@0 1256 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
michael@0 1257
michael@0 1258 /* Add the information that we care about */
michael@0 1259 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "manolito.info");
michael@0 1260
michael@0 1261 /* We give ownership of the request to the connection */
michael@0 1262 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
michael@0 1263 "/funnybunny") == -1) {
michael@0 1264 tt_abort_msg("Couldn't make request");
michael@0 1265 }
michael@0 1266
michael@0 1267 event_base_dispatch(data->base);
michael@0 1268
michael@0 1269 tt_assert(test_ok == 1)
michael@0 1270
michael@0 1271 test_ok = 0;
michael@0 1272
michael@0 1273 /* make a request with the right host and expect a response */
michael@0 1274 req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
michael@0 1275
michael@0 1276 /* Add the Host header. This time with the optional port. */
michael@0 1277 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "bonkers.org:8000");
michael@0 1278
michael@0 1279 /* We give ownership of the request to the connection */
michael@0 1280 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
michael@0 1281 "/blackcoffee") == -1) {
michael@0 1282 tt_abort_msg("Couldn't make request");
michael@0 1283 }
michael@0 1284
michael@0 1285 event_base_dispatch(data->base);
michael@0 1286
michael@0 1287 tt_assert(test_ok == 1)
michael@0 1288
michael@0 1289 test_ok = 0;
michael@0 1290
michael@0 1291 /* Now make a raw request with an absolute URI. */
michael@0 1292 fd = http_connect("127.0.0.1", port);
michael@0 1293
michael@0 1294 /* Stupid thing to send a request */
michael@0 1295 bev = bufferevent_socket_new(data->base, fd, 0);
michael@0 1296 bufferevent_setcb(bev, http_readcb, http_writecb,
michael@0 1297 http_errorcb, NULL);
michael@0 1298
michael@0 1299 /* The host in the URI should override the Host: header */
michael@0 1300 http_request =
michael@0 1301 "GET http://manolito.info/funnybunny HTTP/1.1\r\n"
michael@0 1302 "Host: somehost\r\n"
michael@0 1303 "Connection: close\r\n"
michael@0 1304 "\r\n";
michael@0 1305
michael@0 1306 bufferevent_write(bev, http_request, strlen(http_request));
michael@0 1307
michael@0 1308 event_base_dispatch(data->base);
michael@0 1309
michael@0 1310 tt_int_op(test_ok, ==, 2);
michael@0 1311
michael@0 1312 bufferevent_free(bev);
michael@0 1313 evutil_closesocket(fd);
michael@0 1314
michael@0 1315 end:
michael@0 1316 if (evcon)
michael@0 1317 evhttp_connection_free(evcon);
michael@0 1318 if (http)
michael@0 1319 evhttp_free(http);
michael@0 1320 }
michael@0 1321
michael@0 1322
michael@0 1323 /* test date header and content length */
michael@0 1324
michael@0 1325 static void
michael@0 1326 http_request_empty_done(struct evhttp_request *req, void *arg)
michael@0 1327 {
michael@0 1328 if (evhttp_request_get_response_code(req) != HTTP_OK) {
michael@0 1329 fprintf(stderr, "FAILED\n");
michael@0 1330 exit(1);
michael@0 1331 }
michael@0 1332
michael@0 1333 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Date") == NULL) {
michael@0 1334 fprintf(stderr, "FAILED\n");
michael@0 1335 exit(1);
michael@0 1336 }
michael@0 1337
michael@0 1338
michael@0 1339 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Length") == NULL) {
michael@0 1340 fprintf(stderr, "FAILED\n");
michael@0 1341 exit(1);
michael@0 1342 }
michael@0 1343
michael@0 1344 if (strcmp(evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Length"),
michael@0 1345 "0")) {
michael@0 1346 fprintf(stderr, "FAILED\n");
michael@0 1347 exit(1);
michael@0 1348 }
michael@0 1349
michael@0 1350 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 0) {
michael@0 1351 fprintf(stderr, "FAILED\n");
michael@0 1352 exit(1);
michael@0 1353 }
michael@0 1354
michael@0 1355 test_ok = 1;
michael@0 1356 EVUTIL_ASSERT(arg);
michael@0 1357 event_base_loopexit(arg, NULL);
michael@0 1358 }
michael@0 1359
michael@0 1360 /*
michael@0 1361 * HTTP DISPATCHER test
michael@0 1362 */
michael@0 1363
michael@0 1364 void
michael@0 1365 http_dispatcher_cb(struct evhttp_request *req, void *arg)
michael@0 1366 {
michael@0 1367
michael@0 1368 struct evbuffer *evb = evbuffer_new();
michael@0 1369 event_debug(("%s: called\n", __func__));
michael@0 1370 evbuffer_add_printf(evb, "DISPATCHER_TEST");
michael@0 1371
michael@0 1372 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
michael@0 1373
michael@0 1374 evbuffer_free(evb);
michael@0 1375 }
michael@0 1376
michael@0 1377 static void
michael@0 1378 http_dispatcher_test_done(struct evhttp_request *req, void *arg)
michael@0 1379 {
michael@0 1380 struct event_base *base = arg;
michael@0 1381 const char *what = "DISPATCHER_TEST";
michael@0 1382
michael@0 1383 if (evhttp_request_get_response_code(req) != HTTP_OK) {
michael@0 1384 fprintf(stderr, "FAILED\n");
michael@0 1385 exit(1);
michael@0 1386 }
michael@0 1387
michael@0 1388 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) {
michael@0 1389 fprintf(stderr, "FAILED (content type)\n");
michael@0 1390 exit(1);
michael@0 1391 }
michael@0 1392
michael@0 1393 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) {
michael@0 1394 fprintf(stderr, "FAILED (length %lu vs %lu)\n",
michael@0 1395 (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what));
michael@0 1396 exit(1);
michael@0 1397 }
michael@0 1398
michael@0 1399 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) {
michael@0 1400 fprintf(stderr, "FAILED (data)\n");
michael@0 1401 exit(1);
michael@0 1402 }
michael@0 1403
michael@0 1404 test_ok = 1;
michael@0 1405 event_base_loopexit(base, NULL);
michael@0 1406 }
michael@0 1407
michael@0 1408 static void
michael@0 1409 http_dispatcher_test(void *arg)
michael@0 1410 {
michael@0 1411 struct basic_test_data *data = arg;
michael@0 1412 ev_uint16_t port = 0;
michael@0 1413 struct evhttp_connection *evcon = NULL;
michael@0 1414 struct evhttp_request *req = NULL;
michael@0 1415
michael@0 1416 test_ok = 0;
michael@0 1417
michael@0 1418 http = http_setup(&port, data->base);
michael@0 1419
michael@0 1420 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
michael@0 1421 tt_assert(evcon);
michael@0 1422
michael@0 1423 /* also bind to local host */
michael@0 1424 evhttp_connection_set_local_address(evcon, "127.0.0.1");
michael@0 1425
michael@0 1426 /*
michael@0 1427 * At this point, we want to schedule an HTTP GET request
michael@0 1428 * server using our make request method.
michael@0 1429 */
michael@0 1430
michael@0 1431 req = evhttp_request_new(http_dispatcher_test_done, data->base);
michael@0 1432 tt_assert(req);
michael@0 1433
michael@0 1434 /* Add the information that we care about */
michael@0 1435 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 1436
michael@0 1437 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) {
michael@0 1438 tt_abort_msg("Couldn't make request");
michael@0 1439 }
michael@0 1440
michael@0 1441 event_base_dispatch(data->base);
michael@0 1442
michael@0 1443 end:
michael@0 1444 if (evcon)
michael@0 1445 evhttp_connection_free(evcon);
michael@0 1446 if (http)
michael@0 1447 evhttp_free(http);
michael@0 1448 }
michael@0 1449
michael@0 1450 /*
michael@0 1451 * HTTP POST test.
michael@0 1452 */
michael@0 1453
michael@0 1454 void http_postrequest_done(struct evhttp_request *, void *);
michael@0 1455
michael@0 1456 #define POST_DATA "Okay. Not really printf"
michael@0 1457
michael@0 1458 static void
michael@0 1459 http_post_test(void *arg)
michael@0 1460 {
michael@0 1461 struct basic_test_data *data = arg;
michael@0 1462 ev_uint16_t port = 0;
michael@0 1463 struct evhttp_connection *evcon = NULL;
michael@0 1464 struct evhttp_request *req = NULL;
michael@0 1465
michael@0 1466 test_ok = 0;
michael@0 1467
michael@0 1468 http = http_setup(&port, data->base);
michael@0 1469
michael@0 1470 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
michael@0 1471 tt_assert(evcon);
michael@0 1472
michael@0 1473 /*
michael@0 1474 * At this point, we want to schedule an HTTP POST request
michael@0 1475 * server using our make request method.
michael@0 1476 */
michael@0 1477
michael@0 1478 req = evhttp_request_new(http_postrequest_done, data->base);
michael@0 1479 tt_assert(req);
michael@0 1480
michael@0 1481 /* Add the information that we care about */
michael@0 1482 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 1483 evbuffer_add_printf(evhttp_request_get_output_buffer(req), POST_DATA);
michael@0 1484
michael@0 1485 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) {
michael@0 1486 tt_abort_msg("Couldn't make request");
michael@0 1487 }
michael@0 1488
michael@0 1489 event_base_dispatch(data->base);
michael@0 1490
michael@0 1491 tt_int_op(test_ok, ==, 1);
michael@0 1492
michael@0 1493 test_ok = 0;
michael@0 1494
michael@0 1495 req = evhttp_request_new(http_postrequest_done, data->base);
michael@0 1496 tt_assert(req);
michael@0 1497
michael@0 1498 /* Now try with 100-continue. */
michael@0 1499
michael@0 1500 /* Add the information that we care about */
michael@0 1501 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 1502 evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "100-continue");
michael@0 1503 evbuffer_add_printf(evhttp_request_get_output_buffer(req), POST_DATA);
michael@0 1504
michael@0 1505 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) {
michael@0 1506 tt_abort_msg("Couldn't make request");
michael@0 1507 }
michael@0 1508
michael@0 1509 event_base_dispatch(data->base);
michael@0 1510
michael@0 1511 tt_int_op(test_ok, ==, 1);
michael@0 1512
michael@0 1513 evhttp_connection_free(evcon);
michael@0 1514 evhttp_free(http);
michael@0 1515
michael@0 1516 end:
michael@0 1517 ;
michael@0 1518 }
michael@0 1519
michael@0 1520 void
michael@0 1521 http_post_cb(struct evhttp_request *req, void *arg)
michael@0 1522 {
michael@0 1523 struct evbuffer *evb;
michael@0 1524 event_debug(("%s: called\n", __func__));
michael@0 1525
michael@0 1526 /* Yes, we are expecting a post request */
michael@0 1527 if (evhttp_request_get_command(req) != EVHTTP_REQ_POST) {
michael@0 1528 fprintf(stdout, "FAILED (post type)\n");
michael@0 1529 exit(1);
michael@0 1530 }
michael@0 1531
michael@0 1532 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(POST_DATA)) {
michael@0 1533 fprintf(stdout, "FAILED (length: %lu vs %lu)\n",
michael@0 1534 (unsigned long) evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long) strlen(POST_DATA));
michael@0 1535 exit(1);
michael@0 1536 }
michael@0 1537
michael@0 1538 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), POST_DATA) != 0) {
michael@0 1539 fprintf(stdout, "FAILED (data)\n");
michael@0 1540 fprintf(stdout, "Got :%s\n", evbuffer_pullup(evhttp_request_get_input_buffer(req),-1));
michael@0 1541 fprintf(stdout, "Want:%s\n", POST_DATA);
michael@0 1542 exit(1);
michael@0 1543 }
michael@0 1544
michael@0 1545 evb = evbuffer_new();
michael@0 1546 evbuffer_add_printf(evb, BASIC_REQUEST_BODY);
michael@0 1547
michael@0 1548 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb);
michael@0 1549
michael@0 1550 evbuffer_free(evb);
michael@0 1551 }
michael@0 1552
michael@0 1553 void
michael@0 1554 http_postrequest_done(struct evhttp_request *req, void *arg)
michael@0 1555 {
michael@0 1556 const char *what = BASIC_REQUEST_BODY;
michael@0 1557 struct event_base *base = arg;
michael@0 1558
michael@0 1559 if (req == NULL) {
michael@0 1560 fprintf(stderr, "FAILED (timeout)\n");
michael@0 1561 exit(1);
michael@0 1562 }
michael@0 1563
michael@0 1564 if (evhttp_request_get_response_code(req) != HTTP_OK) {
michael@0 1565
michael@0 1566 fprintf(stderr, "FAILED (response code)\n");
michael@0 1567 exit(1);
michael@0 1568 }
michael@0 1569
michael@0 1570 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) {
michael@0 1571 fprintf(stderr, "FAILED (content type)\n");
michael@0 1572 exit(1);
michael@0 1573 }
michael@0 1574
michael@0 1575 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) {
michael@0 1576 fprintf(stderr, "FAILED (length %lu vs %lu)\n",
michael@0 1577 (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what));
michael@0 1578 exit(1);
michael@0 1579 }
michael@0 1580
michael@0 1581 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) {
michael@0 1582 fprintf(stderr, "FAILED (data)\n");
michael@0 1583 exit(1);
michael@0 1584 }
michael@0 1585
michael@0 1586 test_ok = 1;
michael@0 1587 event_base_loopexit(base, NULL);
michael@0 1588 }
michael@0 1589
michael@0 1590 /*
michael@0 1591 * HTTP PUT test, basically just like POST, but ...
michael@0 1592 */
michael@0 1593
michael@0 1594 void http_putrequest_done(struct evhttp_request *, void *);
michael@0 1595
michael@0 1596 #define PUT_DATA "Hi, I'm some PUT data"
michael@0 1597
michael@0 1598 static void
michael@0 1599 http_put_test(void *arg)
michael@0 1600 {
michael@0 1601 struct basic_test_data *data = arg;
michael@0 1602 ev_uint16_t port = 0;
michael@0 1603 struct evhttp_connection *evcon = NULL;
michael@0 1604 struct evhttp_request *req = NULL;
michael@0 1605
michael@0 1606 test_ok = 0;
michael@0 1607
michael@0 1608 http = http_setup(&port, data->base);
michael@0 1609
michael@0 1610 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
michael@0 1611 tt_assert(evcon);
michael@0 1612
michael@0 1613 /*
michael@0 1614 * Schedule the HTTP PUT request
michael@0 1615 */
michael@0 1616
michael@0 1617 req = evhttp_request_new(http_putrequest_done, data->base);
michael@0 1618 tt_assert(req);
michael@0 1619
michael@0 1620 /* Add the information that we care about */
michael@0 1621 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "someotherhost");
michael@0 1622 evbuffer_add_printf(evhttp_request_get_output_buffer(req), PUT_DATA);
michael@0 1623
michael@0 1624 if (evhttp_make_request(evcon, req, EVHTTP_REQ_PUT, "/putit") == -1) {
michael@0 1625 tt_abort_msg("Couldn't make request");
michael@0 1626 }
michael@0 1627
michael@0 1628 event_base_dispatch(data->base);
michael@0 1629
michael@0 1630 evhttp_connection_free(evcon);
michael@0 1631 evhttp_free(http);
michael@0 1632
michael@0 1633 tt_int_op(test_ok, ==, 1);
michael@0 1634 end:
michael@0 1635 ;
michael@0 1636 }
michael@0 1637
michael@0 1638 void
michael@0 1639 http_put_cb(struct evhttp_request *req, void *arg)
michael@0 1640 {
michael@0 1641 struct evbuffer *evb;
michael@0 1642 event_debug(("%s: called\n", __func__));
michael@0 1643
michael@0 1644 /* Expecting a PUT request */
michael@0 1645 if (evhttp_request_get_command(req) != EVHTTP_REQ_PUT) {
michael@0 1646 fprintf(stdout, "FAILED (put type)\n");
michael@0 1647 exit(1);
michael@0 1648 }
michael@0 1649
michael@0 1650 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(PUT_DATA)) {
michael@0 1651 fprintf(stdout, "FAILED (length: %lu vs %lu)\n",
michael@0 1652 (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(PUT_DATA));
michael@0 1653 exit(1);
michael@0 1654 }
michael@0 1655
michael@0 1656 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), PUT_DATA) != 0) {
michael@0 1657 fprintf(stdout, "FAILED (data)\n");
michael@0 1658 fprintf(stdout, "Got :%s\n", evbuffer_pullup(evhttp_request_get_input_buffer(req),-1));
michael@0 1659 fprintf(stdout, "Want:%s\n", PUT_DATA);
michael@0 1660 exit(1);
michael@0 1661 }
michael@0 1662
michael@0 1663 evb = evbuffer_new();
michael@0 1664 evbuffer_add_printf(evb, "That ain't funny");
michael@0 1665
michael@0 1666 evhttp_send_reply(req, HTTP_OK, "Everything is great", evb);
michael@0 1667
michael@0 1668 evbuffer_free(evb);
michael@0 1669 }
michael@0 1670
michael@0 1671 void
michael@0 1672 http_putrequest_done(struct evhttp_request *req, void *arg)
michael@0 1673 {
michael@0 1674 struct event_base *base = arg;
michael@0 1675 const char *what = "That ain't funny";
michael@0 1676
michael@0 1677 if (req == NULL) {
michael@0 1678 fprintf(stderr, "FAILED (timeout)\n");
michael@0 1679 exit(1);
michael@0 1680 }
michael@0 1681
michael@0 1682 if (evhttp_request_get_response_code(req) != HTTP_OK) {
michael@0 1683
michael@0 1684 fprintf(stderr, "FAILED (response code)\n");
michael@0 1685 exit(1);
michael@0 1686 }
michael@0 1687
michael@0 1688 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) {
michael@0 1689 fprintf(stderr, "FAILED (content type)\n");
michael@0 1690 exit(1);
michael@0 1691 }
michael@0 1692
michael@0 1693 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) {
michael@0 1694 fprintf(stderr, "FAILED (length %lu vs %lu)\n",
michael@0 1695 (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what));
michael@0 1696 exit(1);
michael@0 1697 }
michael@0 1698
michael@0 1699
michael@0 1700 if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) {
michael@0 1701 fprintf(stderr, "FAILED (data)\n");
michael@0 1702 exit(1);
michael@0 1703 }
michael@0 1704
michael@0 1705 test_ok = 1;
michael@0 1706 event_base_loopexit(base, NULL);
michael@0 1707 }
michael@0 1708
michael@0 1709 static void
michael@0 1710 http_failure_readcb(struct bufferevent *bev, void *arg)
michael@0 1711 {
michael@0 1712 const char *what = "400 Bad Request";
michael@0 1713 if (evbuffer_contains(bufferevent_get_input(bev), what)) {
michael@0 1714 test_ok = 2;
michael@0 1715 bufferevent_disable(bev, EV_READ);
michael@0 1716 event_base_loopexit(arg, NULL);
michael@0 1717 }
michael@0 1718 }
michael@0 1719
michael@0 1720 /*
michael@0 1721 * Testing that the HTTP server can deal with a malformed request.
michael@0 1722 */
michael@0 1723 static void
michael@0 1724 http_failure_test(void *arg)
michael@0 1725 {
michael@0 1726 struct basic_test_data *data = arg;
michael@0 1727 struct bufferevent *bev;
michael@0 1728 evutil_socket_t fd;
michael@0 1729 const char *http_request;
michael@0 1730 ev_uint16_t port = 0;
michael@0 1731
michael@0 1732 test_ok = 0;
michael@0 1733
michael@0 1734 http = http_setup(&port, data->base);
michael@0 1735
michael@0 1736 fd = http_connect("127.0.0.1", port);
michael@0 1737
michael@0 1738 /* Stupid thing to send a request */
michael@0 1739 bev = bufferevent_socket_new(data->base, fd, 0);
michael@0 1740 bufferevent_setcb(bev, http_failure_readcb, http_writecb,
michael@0 1741 http_errorcb, data->base);
michael@0 1742
michael@0 1743 http_request = "illegal request\r\n";
michael@0 1744
michael@0 1745 bufferevent_write(bev, http_request, strlen(http_request));
michael@0 1746
michael@0 1747 event_base_dispatch(data->base);
michael@0 1748
michael@0 1749 bufferevent_free(bev);
michael@0 1750 evutil_closesocket(fd);
michael@0 1751
michael@0 1752 evhttp_free(http);
michael@0 1753
michael@0 1754 tt_int_op(test_ok, ==, 2);
michael@0 1755 end:
michael@0 1756 ;
michael@0 1757 }
michael@0 1758
michael@0 1759 static void
michael@0 1760 close_detect_done(struct evhttp_request *req, void *arg)
michael@0 1761 {
michael@0 1762 struct timeval tv;
michael@0 1763 tt_assert(req);
michael@0 1764 tt_assert(evhttp_request_get_response_code(req) == HTTP_OK);
michael@0 1765
michael@0 1766 test_ok = 1;
michael@0 1767
michael@0 1768 end:
michael@0 1769 evutil_timerclear(&tv);
michael@0 1770 tv.tv_sec = 3;
michael@0 1771 event_base_loopexit(arg, &tv);
michael@0 1772 }
michael@0 1773
michael@0 1774 static void
michael@0 1775 close_detect_launch(evutil_socket_t fd, short what, void *arg)
michael@0 1776 {
michael@0 1777 struct evhttp_connection *evcon = arg;
michael@0 1778 struct event_base *base = evhttp_connection_get_base(evcon);
michael@0 1779 struct evhttp_request *req;
michael@0 1780
michael@0 1781 req = evhttp_request_new(close_detect_done, base);
michael@0 1782
michael@0 1783 /* Add the information that we care about */
michael@0 1784 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 1785
michael@0 1786 /* We give ownership of the request to the connection */
michael@0 1787 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
michael@0 1788 tt_fail_msg("Couldn't make request");
michael@0 1789 }
michael@0 1790 }
michael@0 1791
michael@0 1792 static void
michael@0 1793 close_detect_cb(struct evhttp_request *req, void *arg)
michael@0 1794 {
michael@0 1795 struct evhttp_connection *evcon = arg;
michael@0 1796 struct event_base *base = evhttp_connection_get_base(evcon);
michael@0 1797 struct timeval tv;
michael@0 1798
michael@0 1799 if (req != NULL && evhttp_request_get_response_code(req) != HTTP_OK) {
michael@0 1800 tt_abort_msg("Failed");
michael@0 1801 }
michael@0 1802
michael@0 1803 evutil_timerclear(&tv);
michael@0 1804 tv.tv_sec = 3; /* longer than the http time out */
michael@0 1805
michael@0 1806 /* launch a new request on the persistent connection in 3 seconds */
michael@0 1807 event_base_once(base, -1, EV_TIMEOUT, close_detect_launch, evcon, &tv);
michael@0 1808 end:
michael@0 1809 ;
michael@0 1810 }
michael@0 1811
michael@0 1812
michael@0 1813 static void
michael@0 1814 _http_close_detection(struct basic_test_data *data, int with_delay)
michael@0 1815 {
michael@0 1816 ev_uint16_t port = 0;
michael@0 1817 struct evhttp_connection *evcon = NULL;
michael@0 1818 struct evhttp_request *req = NULL;
michael@0 1819
michael@0 1820 test_ok = 0;
michael@0 1821 http = http_setup(&port, data->base);
michael@0 1822
michael@0 1823 /* 2 second timeout */
michael@0 1824 evhttp_set_timeout(http, 1);
michael@0 1825
michael@0 1826 evcon = evhttp_connection_base_new(data->base, NULL,
michael@0 1827 "127.0.0.1", port);
michael@0 1828 tt_assert(evcon);
michael@0 1829 delayed_client = evcon;
michael@0 1830
michael@0 1831 /*
michael@0 1832 * At this point, we want to schedule a request to the HTTP
michael@0 1833 * server using our make request method.
michael@0 1834 */
michael@0 1835
michael@0 1836 req = evhttp_request_new(close_detect_cb, evcon);
michael@0 1837
michael@0 1838 /* Add the information that we care about */
michael@0 1839 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 1840
michael@0 1841 /* We give ownership of the request to the connection */
michael@0 1842 if (evhttp_make_request(evcon,
michael@0 1843 req, EVHTTP_REQ_GET, with_delay ? "/largedelay" : "/test") == -1) {
michael@0 1844 tt_abort_msg("couldn't make request");
michael@0 1845 }
michael@0 1846
michael@0 1847 event_base_dispatch(data->base);
michael@0 1848
michael@0 1849 /* at this point, the http server should have no connection */
michael@0 1850 tt_assert(TAILQ_FIRST(&http->connections) == NULL);
michael@0 1851
michael@0 1852 end:
michael@0 1853 if (evcon)
michael@0 1854 evhttp_connection_free(evcon);
michael@0 1855 if (http)
michael@0 1856 evhttp_free(http);
michael@0 1857 }
michael@0 1858 static void
michael@0 1859 http_close_detection_test(void *arg)
michael@0 1860 {
michael@0 1861 _http_close_detection(arg, 0);
michael@0 1862 }
michael@0 1863 static void
michael@0 1864 http_close_detection_delay_test(void *arg)
michael@0 1865 {
michael@0 1866 _http_close_detection(arg, 1);
michael@0 1867 }
michael@0 1868
michael@0 1869 static void
michael@0 1870 http_highport_test(void *arg)
michael@0 1871 {
michael@0 1872 struct basic_test_data *data = arg;
michael@0 1873 int i = -1;
michael@0 1874 struct evhttp *myhttp = NULL;
michael@0 1875
michael@0 1876 /* Try a few different ports */
michael@0 1877 for (i = 0; i < 50; ++i) {
michael@0 1878 myhttp = evhttp_new(data->base);
michael@0 1879 if (evhttp_bind_socket(myhttp, "127.0.0.1", 65535 - i) == 0) {
michael@0 1880 test_ok = 1;
michael@0 1881 evhttp_free(myhttp);
michael@0 1882 return;
michael@0 1883 }
michael@0 1884 evhttp_free(myhttp);
michael@0 1885 }
michael@0 1886
michael@0 1887 tt_fail_msg("Couldn't get a high port");
michael@0 1888 }
michael@0 1889
michael@0 1890 static void
michael@0 1891 http_bad_header_test(void *ptr)
michael@0 1892 {
michael@0 1893 struct evkeyvalq headers;
michael@0 1894
michael@0 1895 TAILQ_INIT(&headers);
michael@0 1896
michael@0 1897 tt_want(evhttp_add_header(&headers, "One", "Two") == 0);
michael@0 1898 tt_want(evhttp_add_header(&headers, "One", "Two\r\n Three") == 0);
michael@0 1899 tt_want(evhttp_add_header(&headers, "One\r", "Two") == -1);
michael@0 1900 tt_want(evhttp_add_header(&headers, "One\n", "Two") == -1);
michael@0 1901 tt_want(evhttp_add_header(&headers, "One", "Two\r") == -1);
michael@0 1902 tt_want(evhttp_add_header(&headers, "One", "Two\n") == -1);
michael@0 1903
michael@0 1904 evhttp_clear_headers(&headers);
michael@0 1905 }
michael@0 1906
michael@0 1907 static int validate_header(
michael@0 1908 const struct evkeyvalq* headers,
michael@0 1909 const char *key, const char *value)
michael@0 1910 {
michael@0 1911 const char *real_val = evhttp_find_header(headers, key);
michael@0 1912 tt_assert(real_val != NULL);
michael@0 1913 tt_want(strcmp(real_val, value) == 0);
michael@0 1914 end:
michael@0 1915 return (0);
michael@0 1916 }
michael@0 1917
michael@0 1918 static void
michael@0 1919 http_parse_query_test(void *ptr)
michael@0 1920 {
michael@0 1921 struct evkeyvalq headers;
michael@0 1922 int r;
michael@0 1923
michael@0 1924 TAILQ_INIT(&headers);
michael@0 1925
michael@0 1926 r = evhttp_parse_query("http://www.test.com/?q=test", &headers);
michael@0 1927 tt_want(validate_header(&headers, "q", "test") == 0);
michael@0 1928 tt_int_op(r, ==, 0);
michael@0 1929 evhttp_clear_headers(&headers);
michael@0 1930
michael@0 1931 r = evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers);
michael@0 1932 tt_want(validate_header(&headers, "q", "test") == 0);
michael@0 1933 tt_want(validate_header(&headers, "foo", "bar") == 0);
michael@0 1934 tt_int_op(r, ==, 0);
michael@0 1935 evhttp_clear_headers(&headers);
michael@0 1936
michael@0 1937 r = evhttp_parse_query("http://www.test.com/?q=test+foo", &headers);
michael@0 1938 tt_want(validate_header(&headers, "q", "test foo") == 0);
michael@0 1939 tt_int_op(r, ==, 0);
michael@0 1940 evhttp_clear_headers(&headers);
michael@0 1941
michael@0 1942 r = evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers);
michael@0 1943 tt_want(validate_header(&headers, "q", "test\nfoo") == 0);
michael@0 1944 tt_int_op(r, ==, 0);
michael@0 1945 evhttp_clear_headers(&headers);
michael@0 1946
michael@0 1947 r = evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers);
michael@0 1948 tt_want(validate_header(&headers, "q", "test\rfoo") == 0);
michael@0 1949 tt_int_op(r, ==, 0);
michael@0 1950 evhttp_clear_headers(&headers);
michael@0 1951
michael@0 1952 r = evhttp_parse_query("http://www.test.com/?q=test&&q2", &headers);
michael@0 1953 tt_int_op(r, ==, -1);
michael@0 1954 evhttp_clear_headers(&headers);
michael@0 1955
michael@0 1956 r = evhttp_parse_query("http://www.test.com/?q=test+this", &headers);
michael@0 1957 tt_want(validate_header(&headers, "q", "test this") == 0);
michael@0 1958 tt_int_op(r, ==, 0);
michael@0 1959 evhttp_clear_headers(&headers);
michael@0 1960
michael@0 1961 r = evhttp_parse_query("http://www.test.com/?q=test&q2=foo", &headers);
michael@0 1962 tt_int_op(r, ==, 0);
michael@0 1963 tt_want(validate_header(&headers, "q", "test") == 0);
michael@0 1964 tt_want(validate_header(&headers, "q2", "foo") == 0);
michael@0 1965 evhttp_clear_headers(&headers);
michael@0 1966
michael@0 1967 r = evhttp_parse_query("http://www.test.com/?q&q2=foo", &headers);
michael@0 1968 tt_int_op(r, ==, -1);
michael@0 1969 evhttp_clear_headers(&headers);
michael@0 1970
michael@0 1971 r = evhttp_parse_query("http://www.test.com/?q=foo&q2", &headers);
michael@0 1972 tt_int_op(r, ==, -1);
michael@0 1973 evhttp_clear_headers(&headers);
michael@0 1974
michael@0 1975 r = evhttp_parse_query("http://www.test.com/?q=foo&q2&q3=x", &headers);
michael@0 1976 tt_int_op(r, ==, -1);
michael@0 1977 evhttp_clear_headers(&headers);
michael@0 1978
michael@0 1979 r = evhttp_parse_query("http://www.test.com/?q=&q2=&q3=", &headers);
michael@0 1980 tt_int_op(r, ==, 0);
michael@0 1981 tt_want(validate_header(&headers, "q", "") == 0);
michael@0 1982 tt_want(validate_header(&headers, "q2", "") == 0);
michael@0 1983 tt_want(validate_header(&headers, "q3", "") == 0);
michael@0 1984 evhttp_clear_headers(&headers);
michael@0 1985
michael@0 1986 end:
michael@0 1987 evhttp_clear_headers(&headers);
michael@0 1988 }
michael@0 1989
michael@0 1990 static void
michael@0 1991 http_parse_uri_test(void *ptr)
michael@0 1992 {
michael@0 1993 const int nonconform = (ptr != NULL);
michael@0 1994 const unsigned parse_flags =
michael@0 1995 nonconform ? EVHTTP_URI_NONCONFORMANT : 0;
michael@0 1996 struct evhttp_uri *uri = NULL;
michael@0 1997 char url_tmp[4096];
michael@0 1998 #define URI_PARSE(uri) \
michael@0 1999 evhttp_uri_parse_with_flags((uri), parse_flags)
michael@0 2000
michael@0 2001 #define TT_URI(want) do { \
michael@0 2002 char *ret = evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)); \
michael@0 2003 tt_want(ret != NULL); \
michael@0 2004 tt_want(ret == url_tmp); \
michael@0 2005 if (strcmp(ret,want) != 0) \
michael@0 2006 TT_FAIL(("\"%s\" != \"%s\"",ret,want)); \
michael@0 2007 } while(0)
michael@0 2008
michael@0 2009 tt_want(evhttp_uri_join(NULL, 0, 0) == NULL);
michael@0 2010 tt_want(evhttp_uri_join(NULL, url_tmp, 0) == NULL);
michael@0 2011 tt_want(evhttp_uri_join(NULL, url_tmp, sizeof(url_tmp)) == NULL);
michael@0 2012
michael@0 2013 /* bad URIs: parsing */
michael@0 2014 #define BAD(s) do { \
michael@0 2015 if (URI_PARSE(s) != NULL) \
michael@0 2016 TT_FAIL(("Expected error parsing \"%s\"",s)); \
michael@0 2017 } while(0)
michael@0 2018 /* Nonconformant URIs we can parse: parsing */
michael@0 2019 #define NCF(s) do { \
michael@0 2020 uri = URI_PARSE(s); \
michael@0 2021 if (uri != NULL && !nonconform) { \
michael@0 2022 TT_FAIL(("Expected error parsing \"%s\"",s)); \
michael@0 2023 } else if (uri == NULL && nonconform) { \
michael@0 2024 TT_FAIL(("Couldn't parse nonconformant URI \"%s\"", \
michael@0 2025 s)); \
michael@0 2026 } \
michael@0 2027 if (uri) { \
michael@0 2028 tt_want(evhttp_uri_join(uri, url_tmp, \
michael@0 2029 sizeof(url_tmp))); \
michael@0 2030 evhttp_uri_free(uri); \
michael@0 2031 } \
michael@0 2032 } while(0)
michael@0 2033
michael@0 2034 NCF("http://www.test.com/ why hello");
michael@0 2035 NCF("http://www.test.com/why-hello\x01");
michael@0 2036 NCF("http://www.test.com/why-hello?\x01");
michael@0 2037 NCF("http://www.test.com/why-hello#\x01");
michael@0 2038 BAD("http://www.\x01.test.com/why-hello");
michael@0 2039 BAD("http://www.%7test.com/why-hello");
michael@0 2040 NCF("http://www.test.com/why-hell%7o");
michael@0 2041 BAD("h%3ttp://www.test.com/why-hello");
michael@0 2042 NCF("http://www.test.com/why-hello%7");
michael@0 2043 NCF("http://www.test.com/why-hell%7o");
michael@0 2044 NCF("http://www.test.com/foo?ba%r");
michael@0 2045 NCF("http://www.test.com/foo#ba%r");
michael@0 2046 BAD("99:99/foo");
michael@0 2047 BAD("http://www.test.com:999x/");
michael@0 2048 BAD("http://www.test.com:x/");
michael@0 2049 BAD("http://[hello-there]/");
michael@0 2050 BAD("http://[::1]]/");
michael@0 2051 BAD("http://[::1/");
michael@0 2052 BAD("http://[foob/");
michael@0 2053 BAD("http://[/");
michael@0 2054 BAD("http://[ffff:ffff:ffff:ffff:Ffff:ffff:ffff:"
michael@0 2055 "ffff:ffff:ffff:ffff:ffff:ffff:ffff]/");
michael@0 2056 BAD("http://[vX.foo]/");
michael@0 2057 BAD("http://[vX.foo]/");
michael@0 2058 BAD("http://[v.foo]/");
michael@0 2059 BAD("http://[v5.fo%o]/");
michael@0 2060 BAD("http://[v5X]/");
michael@0 2061 BAD("http://[v5]/");
michael@0 2062 BAD("http://[]/");
michael@0 2063 BAD("http://f\x01red@www.example.com/");
michael@0 2064 BAD("http://f%0red@www.example.com/");
michael@0 2065 BAD("http://www.example.com:9999999999999999999999999999999999999/");
michael@0 2066 BAD("http://www.example.com:hihi/");
michael@0 2067 BAD("://www.example.com/");
michael@0 2068
michael@0 2069 /* bad URIs: joining */
michael@0 2070 uri = evhttp_uri_new();
michael@0 2071 tt_want(0==evhttp_uri_set_host(uri, "www.example.com"));
michael@0 2072 tt_want(evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)) != NULL);
michael@0 2073 /* not enough space: */
michael@0 2074 tt_want(evhttp_uri_join(uri, url_tmp, 3) == NULL);
michael@0 2075 /* host is set, but path doesn't start with "/": */
michael@0 2076 tt_want(0==evhttp_uri_set_path(uri, "hi_mom"));
michael@0 2077 tt_want(evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)) == NULL);
michael@0 2078 tt_want(evhttp_uri_join(uri, NULL, sizeof(url_tmp))==NULL);
michael@0 2079 tt_want(evhttp_uri_join(uri, url_tmp, 0)==NULL);
michael@0 2080 evhttp_uri_free(uri);
michael@0 2081 uri = URI_PARSE("mailto:foo@bar");
michael@0 2082 tt_want(uri != NULL);
michael@0 2083 tt_want(evhttp_uri_get_host(uri) == NULL);
michael@0 2084 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2085 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2086 tt_want(!strcmp(evhttp_uri_get_scheme(uri), "mailto"));
michael@0 2087 tt_want(!strcmp(evhttp_uri_get_path(uri), "foo@bar"));
michael@0 2088 tt_want(evhttp_uri_get_query(uri) == NULL);
michael@0 2089 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2090 TT_URI("mailto:foo@bar");
michael@0 2091 evhttp_uri_free(uri);
michael@0 2092
michael@0 2093 uri = evhttp_uri_new();
michael@0 2094 /* Bad URI usage: setting invalid values */
michael@0 2095 tt_want(-1 == evhttp_uri_set_scheme(uri,""));
michael@0 2096 tt_want(-1 == evhttp_uri_set_scheme(uri,"33"));
michael@0 2097 tt_want(-1 == evhttp_uri_set_scheme(uri,"hi!"));
michael@0 2098 tt_want(-1 == evhttp_uri_set_userinfo(uri,"hello@"));
michael@0 2099 tt_want(-1 == evhttp_uri_set_host(uri,"[1.2.3.4]"));
michael@0 2100 tt_want(-1 == evhttp_uri_set_host(uri,"["));
michael@0 2101 tt_want(-1 == evhttp_uri_set_host(uri,"www.[foo].com"));
michael@0 2102 tt_want(-1 == evhttp_uri_set_port(uri,-3));
michael@0 2103 tt_want(-1 == evhttp_uri_set_path(uri,"hello?world"));
michael@0 2104 tt_want(-1 == evhttp_uri_set_query(uri,"hello#world"));
michael@0 2105 tt_want(-1 == evhttp_uri_set_fragment(uri,"hello#world"));
michael@0 2106 /* Valid URI usage: setting valid values */
michael@0 2107 tt_want(0 == evhttp_uri_set_scheme(uri,"http"));
michael@0 2108 tt_want(0 == evhttp_uri_set_scheme(uri,NULL));
michael@0 2109 tt_want(0 == evhttp_uri_set_userinfo(uri,"username:pass"));
michael@0 2110 tt_want(0 == evhttp_uri_set_userinfo(uri,NULL));
michael@0 2111 tt_want(0 == evhttp_uri_set_host(uri,"www.example.com"));
michael@0 2112 tt_want(0 == evhttp_uri_set_host(uri,"1.2.3.4"));
michael@0 2113 tt_want(0 == evhttp_uri_set_host(uri,"[1:2:3:4::]"));
michael@0 2114 tt_want(0 == evhttp_uri_set_host(uri,"[v7.wobblewobble]"));
michael@0 2115 tt_want(0 == evhttp_uri_set_host(uri,NULL));
michael@0 2116 tt_want(0 == evhttp_uri_set_host(uri,""));
michael@0 2117 tt_want(0 == evhttp_uri_set_port(uri, -1));
michael@0 2118 tt_want(0 == evhttp_uri_set_port(uri, 80));
michael@0 2119 tt_want(0 == evhttp_uri_set_port(uri, 65535));
michael@0 2120 tt_want(0 == evhttp_uri_set_path(uri, ""));
michael@0 2121 tt_want(0 == evhttp_uri_set_path(uri, "/documents/public/index.html"));
michael@0 2122 tt_want(0 == evhttp_uri_set_path(uri, NULL));
michael@0 2123 tt_want(0 == evhttp_uri_set_query(uri, "key=val&key2=val2"));
michael@0 2124 tt_want(0 == evhttp_uri_set_query(uri, "keyvalblarg"));
michael@0 2125 tt_want(0 == evhttp_uri_set_query(uri, ""));
michael@0 2126 tt_want(0 == evhttp_uri_set_query(uri, NULL));
michael@0 2127 tt_want(0 == evhttp_uri_set_fragment(uri, ""));
michael@0 2128 tt_want(0 == evhttp_uri_set_fragment(uri, "here?i?am"));
michael@0 2129 tt_want(0 == evhttp_uri_set_fragment(uri, NULL));
michael@0 2130 evhttp_uri_free(uri);
michael@0 2131
michael@0 2132 /* Valid parsing */
michael@0 2133 uri = URI_PARSE("http://www.test.com/?q=t%33est");
michael@0 2134 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
michael@0 2135 tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
michael@0 2136 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
michael@0 2137 tt_want(strcmp(evhttp_uri_get_query(uri), "q=t%33est") == 0);
michael@0 2138 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2139 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2140 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2141 TT_URI("http://www.test.com/?q=t%33est");
michael@0 2142 evhttp_uri_free(uri);
michael@0 2143
michael@0 2144 uri = URI_PARSE("http://%77ww.test.com");
michael@0 2145 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
michael@0 2146 tt_want(strcmp(evhttp_uri_get_host(uri), "%77ww.test.com") == 0);
michael@0 2147 tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
michael@0 2148 tt_want(evhttp_uri_get_query(uri) == NULL);
michael@0 2149 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2150 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2151 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2152 TT_URI("http://%77ww.test.com");
michael@0 2153 evhttp_uri_free(uri);
michael@0 2154
michael@0 2155 uri = URI_PARSE("http://www.test.com?q=test");
michael@0 2156 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
michael@0 2157 tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
michael@0 2158 tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
michael@0 2159 tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
michael@0 2160 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2161 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2162 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2163 TT_URI("http://www.test.com?q=test");
michael@0 2164 evhttp_uri_free(uri);
michael@0 2165
michael@0 2166 uri = URI_PARSE("http://www.test.com#fragment");
michael@0 2167 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
michael@0 2168 tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
michael@0 2169 tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
michael@0 2170 tt_want(evhttp_uri_get_query(uri) == NULL);
michael@0 2171 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2172 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2173 tt_want_str_op(evhttp_uri_get_fragment(uri), ==, "fragment");
michael@0 2174 TT_URI("http://www.test.com#fragment");
michael@0 2175 evhttp_uri_free(uri);
michael@0 2176
michael@0 2177 uri = URI_PARSE("http://8000/");
michael@0 2178 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
michael@0 2179 tt_want(strcmp(evhttp_uri_get_host(uri), "8000") == 0);
michael@0 2180 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
michael@0 2181 tt_want(evhttp_uri_get_query(uri) == NULL);
michael@0 2182 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2183 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2184 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2185 TT_URI("http://8000/");
michael@0 2186 evhttp_uri_free(uri);
michael@0 2187
michael@0 2188 uri = URI_PARSE("http://:8000/");
michael@0 2189 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
michael@0 2190 tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0);
michael@0 2191 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
michael@0 2192 tt_want(evhttp_uri_get_query(uri) == NULL);
michael@0 2193 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2194 tt_want(evhttp_uri_get_port(uri) == 8000);
michael@0 2195 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2196 TT_URI("http://:8000/");
michael@0 2197 evhttp_uri_free(uri);
michael@0 2198
michael@0 2199 uri = URI_PARSE("http://www.test.com:/"); /* empty port */
michael@0 2200 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
michael@0 2201 tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
michael@0 2202 tt_want_str_op(evhttp_uri_get_path(uri), ==, "/");
michael@0 2203 tt_want(evhttp_uri_get_query(uri) == NULL);
michael@0 2204 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2205 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2206 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2207 TT_URI("http://www.test.com/");
michael@0 2208 evhttp_uri_free(uri);
michael@0 2209
michael@0 2210 uri = URI_PARSE("http://www.test.com:"); /* empty port 2 */
michael@0 2211 tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
michael@0 2212 tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
michael@0 2213 tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
michael@0 2214 tt_want(evhttp_uri_get_query(uri) == NULL);
michael@0 2215 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2216 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2217 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2218 TT_URI("http://www.test.com");
michael@0 2219 evhttp_uri_free(uri);
michael@0 2220
michael@0 2221 uri = URI_PARSE("ftp://www.test.com/?q=test");
michael@0 2222 tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
michael@0 2223 tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
michael@0 2224 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
michael@0 2225 tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
michael@0 2226 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2227 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2228 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2229 TT_URI("ftp://www.test.com/?q=test");
michael@0 2230 evhttp_uri_free(uri);
michael@0 2231
michael@0 2232 uri = URI_PARSE("ftp://[::1]:999/?q=test");
michael@0 2233 tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
michael@0 2234 tt_want(strcmp(evhttp_uri_get_host(uri), "[::1]") == 0);
michael@0 2235 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
michael@0 2236 tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
michael@0 2237 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2238 tt_want(evhttp_uri_get_port(uri) == 999);
michael@0 2239 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2240 TT_URI("ftp://[::1]:999/?q=test");
michael@0 2241 evhttp_uri_free(uri);
michael@0 2242
michael@0 2243 uri = URI_PARSE("ftp://[ff00::127.0.0.1]/?q=test");
michael@0 2244 tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
michael@0 2245 tt_want(strcmp(evhttp_uri_get_host(uri), "[ff00::127.0.0.1]") == 0);
michael@0 2246 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
michael@0 2247 tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
michael@0 2248 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2249 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2250 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2251 TT_URI("ftp://[ff00::127.0.0.1]/?q=test");
michael@0 2252 evhttp_uri_free(uri);
michael@0 2253
michael@0 2254 uri = URI_PARSE("ftp://[v99.not_(any:time)_soon]/?q=test");
michael@0 2255 tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
michael@0 2256 tt_want(strcmp(evhttp_uri_get_host(uri), "[v99.not_(any:time)_soon]") == 0);
michael@0 2257 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
michael@0 2258 tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0);
michael@0 2259 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2260 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2261 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2262 TT_URI("ftp://[v99.not_(any:time)_soon]/?q=test");
michael@0 2263 evhttp_uri_free(uri);
michael@0 2264
michael@0 2265 uri = URI_PARSE("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment");
michael@0 2266 tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0);
michael@0 2267 tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user:pass") == 0);
michael@0 2268 tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0);
michael@0 2269 tt_want(evhttp_uri_get_port(uri) == 42);
michael@0 2270 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
michael@0 2271 tt_want(strcmp(evhttp_uri_get_query(uri), "q=test&s=some+thing") == 0);
michael@0 2272 tt_want(strcmp(evhttp_uri_get_fragment(uri), "fragment") == 0);
michael@0 2273 TT_URI("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment");
michael@0 2274 evhttp_uri_free(uri);
michael@0 2275
michael@0 2276 uri = URI_PARSE("scheme://user@foo.com/#fragment");
michael@0 2277 tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0);
michael@0 2278 tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user") == 0);
michael@0 2279 tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0);
michael@0 2280 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2281 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
michael@0 2282 tt_want(evhttp_uri_get_query(uri) == NULL);
michael@0 2283 tt_want(strcmp(evhttp_uri_get_fragment(uri), "fragment") == 0);
michael@0 2284 TT_URI("scheme://user@foo.com/#fragment");
michael@0 2285 evhttp_uri_free(uri);
michael@0 2286
michael@0 2287 uri = URI_PARSE("scheme://%75ser@foo.com/#frag@ment");
michael@0 2288 tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0);
michael@0 2289 tt_want(strcmp(evhttp_uri_get_userinfo(uri), "%75ser") == 0);
michael@0 2290 tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0);
michael@0 2291 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2292 tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
michael@0 2293 tt_want(evhttp_uri_get_query(uri) == NULL);
michael@0 2294 tt_want(strcmp(evhttp_uri_get_fragment(uri), "frag@ment") == 0);
michael@0 2295 TT_URI("scheme://%75ser@foo.com/#frag@ment");
michael@0 2296 evhttp_uri_free(uri);
michael@0 2297
michael@0 2298 uri = URI_PARSE("file:///some/path/to/the/file");
michael@0 2299 tt_want(strcmp(evhttp_uri_get_scheme(uri), "file") == 0);
michael@0 2300 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2301 tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0);
michael@0 2302 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2303 tt_want(strcmp(evhttp_uri_get_path(uri), "/some/path/to/the/file") == 0);
michael@0 2304 tt_want(evhttp_uri_get_query(uri) == NULL);
michael@0 2305 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2306 TT_URI("file:///some/path/to/the/file");
michael@0 2307 evhttp_uri_free(uri);
michael@0 2308
michael@0 2309 uri = URI_PARSE("///some/path/to/the-file");
michael@0 2310 tt_want(uri != NULL);
michael@0 2311 tt_want(evhttp_uri_get_scheme(uri) == NULL);
michael@0 2312 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2313 tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0);
michael@0 2314 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2315 tt_want(strcmp(evhttp_uri_get_path(uri), "/some/path/to/the-file") == 0);
michael@0 2316 tt_want(evhttp_uri_get_query(uri) == NULL);
michael@0 2317 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2318 TT_URI("///some/path/to/the-file");
michael@0 2319 evhttp_uri_free(uri);
michael@0 2320
michael@0 2321 uri = URI_PARSE("/s:ome/path/to/the-file?q=99#fred");
michael@0 2322 tt_want(uri != NULL);
michael@0 2323 tt_want(evhttp_uri_get_scheme(uri) == NULL);
michael@0 2324 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2325 tt_want(evhttp_uri_get_host(uri) == NULL);
michael@0 2326 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2327 tt_want(strcmp(evhttp_uri_get_path(uri), "/s:ome/path/to/the-file") == 0);
michael@0 2328 tt_want(strcmp(evhttp_uri_get_query(uri), "q=99") == 0);
michael@0 2329 tt_want(strcmp(evhttp_uri_get_fragment(uri), "fred") == 0);
michael@0 2330 TT_URI("/s:ome/path/to/the-file?q=99#fred");
michael@0 2331 evhttp_uri_free(uri);
michael@0 2332
michael@0 2333 uri = URI_PARSE("relative/path/with/co:lon");
michael@0 2334 tt_want(uri != NULL);
michael@0 2335 tt_want(evhttp_uri_get_scheme(uri) == NULL);
michael@0 2336 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2337 tt_want(evhttp_uri_get_host(uri) == NULL);
michael@0 2338 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2339 tt_want(strcmp(evhttp_uri_get_path(uri), "relative/path/with/co:lon") == 0);
michael@0 2340 tt_want(evhttp_uri_get_query(uri) == NULL);
michael@0 2341 tt_want(evhttp_uri_get_fragment(uri) == NULL);
michael@0 2342 TT_URI("relative/path/with/co:lon");
michael@0 2343 evhttp_uri_free(uri);
michael@0 2344
michael@0 2345 uri = URI_PARSE("bob?q=99&q2=q?33#fr?ed");
michael@0 2346 tt_want(uri != NULL);
michael@0 2347 tt_want(evhttp_uri_get_scheme(uri) == NULL);
michael@0 2348 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2349 tt_want(evhttp_uri_get_host(uri) == NULL);
michael@0 2350 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2351 tt_want(strcmp(evhttp_uri_get_path(uri), "bob") == 0);
michael@0 2352 tt_want(strcmp(evhttp_uri_get_query(uri), "q=99&q2=q?33") == 0);
michael@0 2353 tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0);
michael@0 2354 TT_URI("bob?q=99&q2=q?33#fr?ed");
michael@0 2355 evhttp_uri_free(uri);
michael@0 2356
michael@0 2357 uri = URI_PARSE("#fr?ed");
michael@0 2358 tt_want(uri != NULL);
michael@0 2359 tt_want(evhttp_uri_get_scheme(uri) == NULL);
michael@0 2360 tt_want(evhttp_uri_get_userinfo(uri) == NULL);
michael@0 2361 tt_want(evhttp_uri_get_host(uri) == NULL);
michael@0 2362 tt_want(evhttp_uri_get_port(uri) == -1);
michael@0 2363 tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
michael@0 2364 tt_want(evhttp_uri_get_query(uri) == NULL);
michael@0 2365 tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0);
michael@0 2366 TT_URI("#fr?ed");
michael@0 2367 evhttp_uri_free(uri);
michael@0 2368 #undef URI_PARSE
michael@0 2369 #undef TT_URI
michael@0 2370 #undef BAD
michael@0 2371 }
michael@0 2372
michael@0 2373 static void
michael@0 2374 http_uriencode_test(void *ptr)
michael@0 2375 {
michael@0 2376 char *s=NULL, *s2=NULL;
michael@0 2377 size_t sz;
michael@0 2378
michael@0 2379 #define ENC(from,want,plus) do { \
michael@0 2380 s = evhttp_uriencode((from), -1, (plus)); \
michael@0 2381 tt_assert(s); \
michael@0 2382 tt_str_op(s,==,(want)); \
michael@0 2383 sz = -1; \
michael@0 2384 s2 = evhttp_uridecode((s), (plus), &sz); \
michael@0 2385 tt_assert(s2); \
michael@0 2386 tt_str_op(s2,==,(from)); \
michael@0 2387 tt_int_op(sz,==,strlen(from)); \
michael@0 2388 free(s); \
michael@0 2389 free(s2); \
michael@0 2390 s = s2 = NULL; \
michael@0 2391 } while (0)
michael@0 2392
michael@0 2393 #define DEC(from,want,dp) do { \
michael@0 2394 s = evhttp_uridecode((from),(dp),&sz); \
michael@0 2395 tt_assert(s); \
michael@0 2396 tt_str_op(s,==,(want)); \
michael@0 2397 tt_int_op(sz,==,strlen(want)); \
michael@0 2398 free(s); \
michael@0 2399 s = NULL; \
michael@0 2400 } while (0)
michael@0 2401
michael@0 2402 #define OLD_DEC(from,want) do { \
michael@0 2403 s = evhttp_decode_uri((from)); \
michael@0 2404 tt_assert(s); \
michael@0 2405 tt_str_op(s,==,(want)); \
michael@0 2406 free(s); \
michael@0 2407 s = NULL; \
michael@0 2408 } while (0)
michael@0 2409
michael@0 2410
michael@0 2411 ENC("Hello", "Hello",0);
michael@0 2412 ENC("99", "99",0);
michael@0 2413 ENC("", "",0);
michael@0 2414 ENC(
michael@0 2415 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_",
michael@0 2416 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_",0);
michael@0 2417 ENC(" ", "%20",0);
michael@0 2418 ENC(" ", "+",1);
michael@0 2419 ENC("\xff\xf0\xe0", "%FF%F0%E0",0);
michael@0 2420 ENC("\x01\x19", "%01%19",1);
michael@0 2421 ENC("http://www.ietf.org/rfc/rfc3986.txt",
michael@0 2422 "http%3A%2F%2Fwww.ietf.org%2Frfc%2Frfc3986.txt",1);
michael@0 2423
michael@0 2424 ENC("1+2=3", "1%2B2%3D3",1);
michael@0 2425 ENC("1+2=3", "1%2B2%3D3",0);
michael@0 2426
michael@0 2427 /* Now try encoding with internal NULs. */
michael@0 2428 s = evhttp_uriencode("hello\0world", 11, 0);
michael@0 2429 tt_assert(s);
michael@0 2430 tt_str_op(s,==,"hello%00world");
michael@0 2431 free(s);
michael@0 2432 s = NULL;
michael@0 2433
michael@0 2434 /* Now try out some decoding cases that we don't generate with
michael@0 2435 * encode_uri: Make sure that malformed stuff doesn't crash... */
michael@0 2436 DEC("%%xhello th+ere \xff",
michael@0 2437 "%%xhello th+ere \xff", 0);
michael@0 2438 /* Make sure plus decoding works */
michael@0 2439 DEC("plus+should%20work+", "plus should work ",1);
michael@0 2440 /* Try some lowercase hex */
michael@0 2441 DEC("%f0%a0%b0", "\xf0\xa0\xb0",1);
michael@0 2442
michael@0 2443 /* Try an internal NUL. */
michael@0 2444 sz = 0;
michael@0 2445 s = evhttp_uridecode("%00%00x%00%00", 1, &sz);
michael@0 2446 tt_int_op(sz,==,5);
michael@0 2447 tt_assert(!memcmp(s, "\0\0x\0\0", 5));
michael@0 2448 free(s);
michael@0 2449 s = NULL;
michael@0 2450
michael@0 2451 /* Try with size == NULL */
michael@0 2452 sz = 0;
michael@0 2453 s = evhttp_uridecode("%00%00x%00%00", 1, NULL);
michael@0 2454 tt_assert(!memcmp(s, "\0\0x\0\0", 5));
michael@0 2455 free(s);
michael@0 2456 s = NULL;
michael@0 2457
michael@0 2458 /* Test out the crazy old behavior of the deprecated
michael@0 2459 * evhttp_decode_uri */
michael@0 2460 OLD_DEC("http://example.com/normal+path/?key=val+with+spaces",
michael@0 2461 "http://example.com/normal+path/?key=val with spaces");
michael@0 2462
michael@0 2463 end:
michael@0 2464 if (s)
michael@0 2465 free(s);
michael@0 2466 if (s2)
michael@0 2467 free(s2);
michael@0 2468 #undef ENC
michael@0 2469 #undef DEC
michael@0 2470 #undef OLD_DEC
michael@0 2471 }
michael@0 2472
michael@0 2473 static void
michael@0 2474 http_base_test(void *ptr)
michael@0 2475 {
michael@0 2476 struct event_base *base = NULL;
michael@0 2477 struct bufferevent *bev;
michael@0 2478 evutil_socket_t fd;
michael@0 2479 const char *http_request;
michael@0 2480 ev_uint16_t port = 0;
michael@0 2481
michael@0 2482 test_ok = 0;
michael@0 2483 base = event_base_new();
michael@0 2484 http = http_setup(&port, base);
michael@0 2485
michael@0 2486 fd = http_connect("127.0.0.1", port);
michael@0 2487
michael@0 2488 /* Stupid thing to send a request */
michael@0 2489 bev = bufferevent_socket_new(base, fd, 0);
michael@0 2490 bufferevent_setcb(bev, http_readcb, http_writecb,
michael@0 2491 http_errorcb, base);
michael@0 2492 bufferevent_base_set(base, bev);
michael@0 2493
michael@0 2494 http_request =
michael@0 2495 "GET /test HTTP/1.1\r\n"
michael@0 2496 "Host: somehost\r\n"
michael@0 2497 "Connection: close\r\n"
michael@0 2498 "\r\n";
michael@0 2499
michael@0 2500 bufferevent_write(bev, http_request, strlen(http_request));
michael@0 2501
michael@0 2502 event_base_dispatch(base);
michael@0 2503
michael@0 2504 bufferevent_free(bev);
michael@0 2505 evutil_closesocket(fd);
michael@0 2506
michael@0 2507 evhttp_free(http);
michael@0 2508
michael@0 2509 tt_int_op(test_ok, ==, 2);
michael@0 2510
michael@0 2511 end:
michael@0 2512 if (base)
michael@0 2513 event_base_free(base);
michael@0 2514 }
michael@0 2515
michael@0 2516 /*
michael@0 2517 * the server is just going to close the connection if it times out during
michael@0 2518 * reading the headers.
michael@0 2519 */
michael@0 2520
michael@0 2521 static void
michael@0 2522 http_incomplete_readcb(struct bufferevent *bev, void *arg)
michael@0 2523 {
michael@0 2524 test_ok = -1;
michael@0 2525 event_base_loopexit(exit_base,NULL);
michael@0 2526 }
michael@0 2527
michael@0 2528 static void
michael@0 2529 http_incomplete_errorcb(struct bufferevent *bev, short what, void *arg)
michael@0 2530 {
michael@0 2531 if (what == (BEV_EVENT_READING|BEV_EVENT_EOF))
michael@0 2532 test_ok++;
michael@0 2533 else
michael@0 2534 test_ok = -2;
michael@0 2535 event_base_loopexit(exit_base,NULL);
michael@0 2536 }
michael@0 2537
michael@0 2538 static void
michael@0 2539 http_incomplete_writecb(struct bufferevent *bev, void *arg)
michael@0 2540 {
michael@0 2541 if (arg != NULL) {
michael@0 2542 evutil_socket_t fd = *(evutil_socket_t *)arg;
michael@0 2543 /* terminate the write side to simulate EOF */
michael@0 2544 shutdown(fd, SHUT_WR);
michael@0 2545 }
michael@0 2546 if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) {
michael@0 2547 /* enable reading of the reply */
michael@0 2548 bufferevent_enable(bev, EV_READ);
michael@0 2549 test_ok++;
michael@0 2550 }
michael@0 2551 }
michael@0 2552
michael@0 2553 static void
michael@0 2554 _http_incomplete_test(struct basic_test_data *data, int use_timeout)
michael@0 2555 {
michael@0 2556 struct bufferevent *bev;
michael@0 2557 evutil_socket_t fd;
michael@0 2558 const char *http_request;
michael@0 2559 ev_uint16_t port = 0;
michael@0 2560 struct timeval tv_start, tv_end;
michael@0 2561
michael@0 2562 exit_base = data->base;
michael@0 2563
michael@0 2564 test_ok = 0;
michael@0 2565
michael@0 2566 http = http_setup(&port, data->base);
michael@0 2567 evhttp_set_timeout(http, 1);
michael@0 2568
michael@0 2569 fd = http_connect("127.0.0.1", port);
michael@0 2570
michael@0 2571 /* Stupid thing to send a request */
michael@0 2572 bev = bufferevent_socket_new(data->base, fd, 0);
michael@0 2573 bufferevent_setcb(bev,
michael@0 2574 http_incomplete_readcb, http_incomplete_writecb,
michael@0 2575 http_incomplete_errorcb, use_timeout ? NULL : &fd);
michael@0 2576
michael@0 2577 http_request =
michael@0 2578 "GET /test HTTP/1.1\r\n"
michael@0 2579 "Host: somehost\r\n";
michael@0 2580
michael@0 2581 bufferevent_write(bev, http_request, strlen(http_request));
michael@0 2582
michael@0 2583 evutil_gettimeofday(&tv_start, NULL);
michael@0 2584
michael@0 2585 event_base_dispatch(data->base);
michael@0 2586
michael@0 2587 evutil_gettimeofday(&tv_end, NULL);
michael@0 2588 evutil_timersub(&tv_end, &tv_start, &tv_end);
michael@0 2589
michael@0 2590 bufferevent_free(bev);
michael@0 2591 if (use_timeout) {
michael@0 2592 evutil_closesocket(fd);
michael@0 2593 }
michael@0 2594
michael@0 2595 evhttp_free(http);
michael@0 2596
michael@0 2597 if (use_timeout && tv_end.tv_sec >= 3) {
michael@0 2598 tt_abort_msg("time");
michael@0 2599 } else if (!use_timeout && tv_end.tv_sec >= 1) {
michael@0 2600 /* we should be done immediately */
michael@0 2601 tt_abort_msg("time");
michael@0 2602 }
michael@0 2603
michael@0 2604 tt_int_op(test_ok, ==, 2);
michael@0 2605 end:
michael@0 2606 ;
michael@0 2607 }
michael@0 2608 static void
michael@0 2609 http_incomplete_test(void *arg)
michael@0 2610 {
michael@0 2611 _http_incomplete_test(arg, 0);
michael@0 2612 }
michael@0 2613 static void
michael@0 2614 http_incomplete_timeout_test(void *arg)
michael@0 2615 {
michael@0 2616 _http_incomplete_test(arg, 1);
michael@0 2617 }
michael@0 2618
michael@0 2619 /*
michael@0 2620 * the server is going to reply with chunked data.
michael@0 2621 */
michael@0 2622
michael@0 2623 static void
michael@0 2624 http_chunked_readcb(struct bufferevent *bev, void *arg)
michael@0 2625 {
michael@0 2626 /* nothing here */
michael@0 2627 }
michael@0 2628
michael@0 2629 static void
michael@0 2630 http_chunked_errorcb(struct bufferevent *bev, short what, void *arg)
michael@0 2631 {
michael@0 2632 if (!test_ok)
michael@0 2633 goto out;
michael@0 2634
michael@0 2635 test_ok = -1;
michael@0 2636
michael@0 2637 if ((what & BEV_EVENT_EOF) != 0) {
michael@0 2638 struct evhttp_request *req = evhttp_request_new(NULL, NULL);
michael@0 2639 const char *header;
michael@0 2640 enum message_read_status done;
michael@0 2641
michael@0 2642 /* req->kind = EVHTTP_RESPONSE; */
michael@0 2643 done = evhttp_parse_firstline(req, bufferevent_get_input(bev));
michael@0 2644 if (done != ALL_DATA_READ)
michael@0 2645 goto out;
michael@0 2646
michael@0 2647 done = evhttp_parse_headers(req, bufferevent_get_input(bev));
michael@0 2648 if (done != ALL_DATA_READ)
michael@0 2649 goto out;
michael@0 2650
michael@0 2651 header = evhttp_find_header(evhttp_request_get_input_headers(req), "Transfer-Encoding");
michael@0 2652 if (header == NULL || strcmp(header, "chunked"))
michael@0 2653 goto out;
michael@0 2654
michael@0 2655 header = evhttp_find_header(evhttp_request_get_input_headers(req), "Connection");
michael@0 2656 if (header == NULL || strcmp(header, "close"))
michael@0 2657 goto out;
michael@0 2658
michael@0 2659 header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF);
michael@0 2660 if (header == NULL)
michael@0 2661 goto out;
michael@0 2662 /* 13 chars */
michael@0 2663 if (strcmp(header, "d")) {
michael@0 2664 free((void*)header);
michael@0 2665 goto out;
michael@0 2666 }
michael@0 2667 free((void*)header);
michael@0 2668
michael@0 2669 if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 13),
michael@0 2670 "This is funny", 13))
michael@0 2671 goto out;
michael@0 2672
michael@0 2673 evbuffer_drain(bufferevent_get_input(bev), 13 + 2);
michael@0 2674
michael@0 2675 header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF);
michael@0 2676 if (header == NULL)
michael@0 2677 goto out;
michael@0 2678 /* 18 chars */
michael@0 2679 if (strcmp(header, "12"))
michael@0 2680 goto out;
michael@0 2681 free((char *)header);
michael@0 2682
michael@0 2683 if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 18),
michael@0 2684 "but not hilarious.", 18))
michael@0 2685 goto out;
michael@0 2686
michael@0 2687 evbuffer_drain(bufferevent_get_input(bev), 18 + 2);
michael@0 2688
michael@0 2689 header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF);
michael@0 2690 if (header == NULL)
michael@0 2691 goto out;
michael@0 2692 /* 8 chars */
michael@0 2693 if (strcmp(header, "8")) {
michael@0 2694 free((void*)header);
michael@0 2695 goto out;
michael@0 2696 }
michael@0 2697 free((char *)header);
michael@0 2698
michael@0 2699 if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 8),
michael@0 2700 "bwv 1052.", 8))
michael@0 2701 goto out;
michael@0 2702
michael@0 2703 evbuffer_drain(bufferevent_get_input(bev), 8 + 2);
michael@0 2704
michael@0 2705 header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF);
michael@0 2706 if (header == NULL)
michael@0 2707 goto out;
michael@0 2708 /* 0 chars */
michael@0 2709 if (strcmp(header, "0")) {
michael@0 2710 free((void*)header);
michael@0 2711 goto out;
michael@0 2712 }
michael@0 2713 free((void *)header);
michael@0 2714
michael@0 2715 test_ok = 2;
michael@0 2716
michael@0 2717 evhttp_request_free(req);
michael@0 2718 }
michael@0 2719
michael@0 2720 out:
michael@0 2721 event_base_loopexit(arg, NULL);
michael@0 2722 }
michael@0 2723
michael@0 2724 static void
michael@0 2725 http_chunked_writecb(struct bufferevent *bev, void *arg)
michael@0 2726 {
michael@0 2727 if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) {
michael@0 2728 /* enable reading of the reply */
michael@0 2729 bufferevent_enable(bev, EV_READ);
michael@0 2730 test_ok++;
michael@0 2731 }
michael@0 2732 }
michael@0 2733
michael@0 2734 static void
michael@0 2735 http_chunked_request_done(struct evhttp_request *req, void *arg)
michael@0 2736 {
michael@0 2737 if (evhttp_request_get_response_code(req) != HTTP_OK) {
michael@0 2738 fprintf(stderr, "FAILED\n");
michael@0 2739 exit(1);
michael@0 2740 }
michael@0 2741
michael@0 2742 if (evhttp_find_header(evhttp_request_get_input_headers(req),
michael@0 2743 "Transfer-Encoding") == NULL) {
michael@0 2744 fprintf(stderr, "FAILED\n");
michael@0 2745 exit(1);
michael@0 2746 }
michael@0 2747
michael@0 2748 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 13 + 18 + 8) {
michael@0 2749 fprintf(stderr, "FAILED\n");
michael@0 2750 exit(1);
michael@0 2751 }
michael@0 2752
michael@0 2753 if (strncmp((char *)evbuffer_pullup(evhttp_request_get_input_buffer(req), 13 + 18 + 8),
michael@0 2754 "This is funnybut not hilarious.bwv 1052",
michael@0 2755 13 + 18 + 8)) {
michael@0 2756 fprintf(stderr, "FAILED\n");
michael@0 2757 exit(1);
michael@0 2758 }
michael@0 2759
michael@0 2760 test_ok = 1;
michael@0 2761 event_base_loopexit(arg, NULL);
michael@0 2762 }
michael@0 2763
michael@0 2764 static void
michael@0 2765 http_chunk_out_test(void *arg)
michael@0 2766 {
michael@0 2767 struct basic_test_data *data = arg;
michael@0 2768 struct bufferevent *bev;
michael@0 2769 evutil_socket_t fd;
michael@0 2770 const char *http_request;
michael@0 2771 ev_uint16_t port = 0;
michael@0 2772 struct timeval tv_start, tv_end;
michael@0 2773 struct evhttp_connection *evcon = NULL;
michael@0 2774 struct evhttp_request *req = NULL;
michael@0 2775 int i;
michael@0 2776
michael@0 2777 exit_base = data->base;
michael@0 2778 test_ok = 0;
michael@0 2779
michael@0 2780 http = http_setup(&port, data->base);
michael@0 2781
michael@0 2782 fd = http_connect("127.0.0.1", port);
michael@0 2783
michael@0 2784 /* Stupid thing to send a request */
michael@0 2785 bev = bufferevent_socket_new(data->base, fd, 0);
michael@0 2786 bufferevent_setcb(bev,
michael@0 2787 http_chunked_readcb, http_chunked_writecb,
michael@0 2788 http_chunked_errorcb, data->base);
michael@0 2789
michael@0 2790 http_request =
michael@0 2791 "GET /chunked HTTP/1.1\r\n"
michael@0 2792 "Host: somehost\r\n"
michael@0 2793 "Connection: close\r\n"
michael@0 2794 "\r\n";
michael@0 2795
michael@0 2796 bufferevent_write(bev, http_request, strlen(http_request));
michael@0 2797
michael@0 2798 evutil_gettimeofday(&tv_start, NULL);
michael@0 2799
michael@0 2800 event_base_dispatch(data->base);
michael@0 2801
michael@0 2802 bufferevent_free(bev);
michael@0 2803
michael@0 2804 evutil_gettimeofday(&tv_end, NULL);
michael@0 2805 evutil_timersub(&tv_end, &tv_start, &tv_end);
michael@0 2806
michael@0 2807 tt_int_op(tv_end.tv_sec, <, 1);
michael@0 2808
michael@0 2809 tt_int_op(test_ok, ==, 2);
michael@0 2810
michael@0 2811 /* now try again with the regular connection object */
michael@0 2812 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
michael@0 2813 tt_assert(evcon);
michael@0 2814
michael@0 2815 /* make two requests to check the keepalive behavior */
michael@0 2816 for (i = 0; i < 2; i++) {
michael@0 2817 test_ok = 0;
michael@0 2818 req = evhttp_request_new(http_chunked_request_done,data->base);
michael@0 2819
michael@0 2820 /* Add the information that we care about */
michael@0 2821 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 2822
michael@0 2823 /* We give ownership of the request to the connection */
michael@0 2824 if (evhttp_make_request(evcon, req,
michael@0 2825 EVHTTP_REQ_GET, "/chunked") == -1) {
michael@0 2826 tt_abort_msg("Couldn't make request");
michael@0 2827 }
michael@0 2828
michael@0 2829 event_base_dispatch(data->base);
michael@0 2830
michael@0 2831 tt_assert(test_ok == 1);
michael@0 2832 }
michael@0 2833
michael@0 2834 end:
michael@0 2835 if (evcon)
michael@0 2836 evhttp_connection_free(evcon);
michael@0 2837 if (http)
michael@0 2838 evhttp_free(http);
michael@0 2839 }
michael@0 2840
michael@0 2841 static void
michael@0 2842 http_stream_out_test(void *arg)
michael@0 2843 {
michael@0 2844 struct basic_test_data *data = arg;
michael@0 2845 ev_uint16_t port = 0;
michael@0 2846 struct evhttp_connection *evcon = NULL;
michael@0 2847 struct evhttp_request *req = NULL;
michael@0 2848
michael@0 2849 test_ok = 0;
michael@0 2850 exit_base = data->base;
michael@0 2851
michael@0 2852 http = http_setup(&port, data->base);
michael@0 2853
michael@0 2854 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
michael@0 2855 tt_assert(evcon);
michael@0 2856
michael@0 2857 /*
michael@0 2858 * At this point, we want to schedule a request to the HTTP
michael@0 2859 * server using our make request method.
michael@0 2860 */
michael@0 2861
michael@0 2862 req = evhttp_request_new(http_request_done,
michael@0 2863 (void *)"This is funnybut not hilarious.bwv 1052");
michael@0 2864
michael@0 2865 /* Add the information that we care about */
michael@0 2866 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 2867
michael@0 2868 /* We give ownership of the request to the connection */
michael@0 2869 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/streamed")
michael@0 2870 == -1) {
michael@0 2871 tt_abort_msg("Couldn't make request");
michael@0 2872 }
michael@0 2873
michael@0 2874 event_base_dispatch(data->base);
michael@0 2875
michael@0 2876 end:
michael@0 2877 if (evcon)
michael@0 2878 evhttp_connection_free(evcon);
michael@0 2879 if (http)
michael@0 2880 evhttp_free(http);
michael@0 2881 }
michael@0 2882
michael@0 2883 static void
michael@0 2884 http_stream_in_chunk(struct evhttp_request *req, void *arg)
michael@0 2885 {
michael@0 2886 struct evbuffer *reply = arg;
michael@0 2887
michael@0 2888 if (evhttp_request_get_response_code(req) != HTTP_OK) {
michael@0 2889 fprintf(stderr, "FAILED\n");
michael@0 2890 exit(1);
michael@0 2891 }
michael@0 2892
michael@0 2893 evbuffer_add_buffer(reply, evhttp_request_get_input_buffer(req));
michael@0 2894 }
michael@0 2895
michael@0 2896 static void
michael@0 2897 http_stream_in_done(struct evhttp_request *req, void *arg)
michael@0 2898 {
michael@0 2899 if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 0) {
michael@0 2900 fprintf(stderr, "FAILED\n");
michael@0 2901 exit(1);
michael@0 2902 }
michael@0 2903
michael@0 2904 event_base_loopexit(exit_base, NULL);
michael@0 2905 }
michael@0 2906
michael@0 2907 /**
michael@0 2908 * Makes a request and reads the response in chunks.
michael@0 2909 */
michael@0 2910 static void
michael@0 2911 _http_stream_in_test(struct basic_test_data *data, char const *url,
michael@0 2912 size_t expected_len, char const *expected)
michael@0 2913 {
michael@0 2914 struct evhttp_connection *evcon;
michael@0 2915 struct evbuffer *reply = evbuffer_new();
michael@0 2916 struct evhttp_request *req = NULL;
michael@0 2917 ev_uint16_t port = 0;
michael@0 2918
michael@0 2919 exit_base = data->base;
michael@0 2920 http = http_setup(&port, data->base);
michael@0 2921
michael@0 2922 evcon = evhttp_connection_base_new(data->base, NULL,"127.0.0.1", port);
michael@0 2923 tt_assert(evcon);
michael@0 2924
michael@0 2925 req = evhttp_request_new(http_stream_in_done, reply);
michael@0 2926 evhttp_request_set_chunked_cb(req, http_stream_in_chunk);
michael@0 2927
michael@0 2928 /* We give ownership of the request to the connection */
michael@0 2929 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, url) == -1) {
michael@0 2930 tt_abort_msg("Couldn't make request");
michael@0 2931 }
michael@0 2932
michael@0 2933 event_base_dispatch(data->base);
michael@0 2934
michael@0 2935 if (evbuffer_get_length(reply) != expected_len) {
michael@0 2936 TT_DIE(("reply length %lu; expected %lu; FAILED (%s)\n",
michael@0 2937 (unsigned long)evbuffer_get_length(reply),
michael@0 2938 (unsigned long)expected_len,
michael@0 2939 (char*)evbuffer_pullup(reply, -1)));
michael@0 2940 }
michael@0 2941
michael@0 2942 if (memcmp(evbuffer_pullup(reply, -1), expected, expected_len) != 0) {
michael@0 2943 tt_abort_msg("Memory mismatch");
michael@0 2944 }
michael@0 2945
michael@0 2946 test_ok = 1;
michael@0 2947 end:
michael@0 2948 if (reply)
michael@0 2949 evbuffer_free(reply);
michael@0 2950 if (evcon)
michael@0 2951 evhttp_connection_free(evcon);
michael@0 2952 if (http)
michael@0 2953 evhttp_free(http);
michael@0 2954 }
michael@0 2955
michael@0 2956 static void
michael@0 2957 http_stream_in_test(void *arg)
michael@0 2958 {
michael@0 2959 _http_stream_in_test(arg, "/chunked", 13 + 18 + 8,
michael@0 2960 "This is funnybut not hilarious.bwv 1052");
michael@0 2961
michael@0 2962 _http_stream_in_test(arg, "/test", strlen(BASIC_REQUEST_BODY),
michael@0 2963 BASIC_REQUEST_BODY);
michael@0 2964 }
michael@0 2965
michael@0 2966 static void
michael@0 2967 http_stream_in_cancel_chunk(struct evhttp_request *req, void *arg)
michael@0 2968 {
michael@0 2969 tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_OK);
michael@0 2970
michael@0 2971 end:
michael@0 2972 evhttp_cancel_request(req);
michael@0 2973 event_base_loopexit(arg, NULL);
michael@0 2974 }
michael@0 2975
michael@0 2976 static void
michael@0 2977 http_stream_in_cancel_done(struct evhttp_request *req, void *arg)
michael@0 2978 {
michael@0 2979 /* should never be called */
michael@0 2980 tt_fail_msg("In cancel done");
michael@0 2981 }
michael@0 2982
michael@0 2983 static void
michael@0 2984 http_stream_in_cancel_test(void *arg)
michael@0 2985 {
michael@0 2986 struct basic_test_data *data = arg;
michael@0 2987 struct evhttp_connection *evcon;
michael@0 2988 struct evhttp_request *req = NULL;
michael@0 2989 ev_uint16_t port = 0;
michael@0 2990
michael@0 2991 http = http_setup(&port, data->base);
michael@0 2992
michael@0 2993 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
michael@0 2994 tt_assert(evcon);
michael@0 2995
michael@0 2996 req = evhttp_request_new(http_stream_in_cancel_done, data->base);
michael@0 2997 evhttp_request_set_chunked_cb(req, http_stream_in_cancel_chunk);
michael@0 2998
michael@0 2999 /* We give ownership of the request to the connection */
michael@0 3000 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/chunked") == -1) {
michael@0 3001 tt_abort_msg("Couldn't make request");
michael@0 3002 }
michael@0 3003
michael@0 3004 event_base_dispatch(data->base);
michael@0 3005
michael@0 3006 test_ok = 1;
michael@0 3007 end:
michael@0 3008 evhttp_connection_free(evcon);
michael@0 3009 evhttp_free(http);
michael@0 3010
michael@0 3011 }
michael@0 3012
michael@0 3013 static void
michael@0 3014 http_connection_fail_done(struct evhttp_request *req, void *arg)
michael@0 3015 {
michael@0 3016 /* An ENETUNREACH error results in an unrecoverable
michael@0 3017 * evhttp_connection error (see evhttp_connection_fail()). The
michael@0 3018 * connection will be reset, and the user will be notified with a NULL
michael@0 3019 * req parameter. */
michael@0 3020 tt_assert(!req);
michael@0 3021
michael@0 3022 test_ok = 1;
michael@0 3023
michael@0 3024 end:
michael@0 3025 event_base_loopexit(arg, NULL);
michael@0 3026 }
michael@0 3027
michael@0 3028 /* Test unrecoverable evhttp_connection errors by generating an ENETUNREACH
michael@0 3029 * error on connection. */
michael@0 3030 static void
michael@0 3031 http_connection_fail_test(void *arg)
michael@0 3032 {
michael@0 3033 struct basic_test_data *data = arg;
michael@0 3034 ev_uint16_t port = 0;
michael@0 3035 struct evhttp_connection *evcon = NULL;
michael@0 3036 struct evhttp_request *req = NULL;
michael@0 3037
michael@0 3038 exit_base = data->base;
michael@0 3039 test_ok = 0;
michael@0 3040
michael@0 3041 /* auto detect a port */
michael@0 3042 http = http_setup(&port, data->base);
michael@0 3043 evhttp_free(http);
michael@0 3044 http = NULL;
michael@0 3045
michael@0 3046 /* Pick an unroutable address. This administratively scoped multicast
michael@0 3047 * address should do when working with TCP. */
michael@0 3048 evcon = evhttp_connection_base_new(data->base, NULL, "239.10.20.30", 80);
michael@0 3049 tt_assert(evcon);
michael@0 3050
michael@0 3051 /*
michael@0 3052 * At this point, we want to schedule an HTTP GET request
michael@0 3053 * server using our make request method.
michael@0 3054 */
michael@0 3055
michael@0 3056 req = evhttp_request_new(http_connection_fail_done, data->base);
michael@0 3057 tt_assert(req);
michael@0 3058
michael@0 3059 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/") == -1) {
michael@0 3060 tt_abort_msg("Couldn't make request");
michael@0 3061 }
michael@0 3062
michael@0 3063 event_base_dispatch(data->base);
michael@0 3064
michael@0 3065 tt_int_op(test_ok, ==, 1);
michael@0 3066
michael@0 3067 end:
michael@0 3068 if (evcon)
michael@0 3069 evhttp_connection_free(evcon);
michael@0 3070 }
michael@0 3071
michael@0 3072 static void
michael@0 3073 http_connection_retry_done(struct evhttp_request *req, void *arg)
michael@0 3074 {
michael@0 3075 tt_assert(req);
michael@0 3076 tt_int_op(evhttp_request_get_response_code(req), !=, HTTP_OK);
michael@0 3077 if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") != NULL) {
michael@0 3078 tt_abort_msg("(content type)\n");
michael@0 3079 }
michael@0 3080
michael@0 3081 tt_uint_op(evbuffer_get_length(evhttp_request_get_input_buffer(req)), ==, 0);
michael@0 3082
michael@0 3083 test_ok = 1;
michael@0 3084 end:
michael@0 3085 event_base_loopexit(arg,NULL);
michael@0 3086 }
michael@0 3087
michael@0 3088 static struct event_base *http_make_web_server_base=NULL;
michael@0 3089 static void
michael@0 3090 http_make_web_server(evutil_socket_t fd, short what, void *arg)
michael@0 3091 {
michael@0 3092 ev_uint16_t port = *(ev_uint16_t*)arg;
michael@0 3093 http = http_setup(&port, http_make_web_server_base);
michael@0 3094 }
michael@0 3095
michael@0 3096 static void
michael@0 3097 http_connection_retry_test(void *arg)
michael@0 3098 {
michael@0 3099 struct basic_test_data *data = arg;
michael@0 3100 ev_uint16_t port = 0;
michael@0 3101 struct evhttp_connection *evcon = NULL;
michael@0 3102 struct evhttp_request *req = NULL;
michael@0 3103 struct timeval tv, tv_start, tv_end;
michael@0 3104
michael@0 3105 exit_base = data->base;
michael@0 3106 test_ok = 0;
michael@0 3107
michael@0 3108 /* auto detect a port */
michael@0 3109 http = http_setup(&port, data->base);
michael@0 3110 evhttp_free(http);
michael@0 3111 http = NULL;
michael@0 3112
michael@0 3113 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
michael@0 3114 tt_assert(evcon);
michael@0 3115
michael@0 3116 evhttp_connection_set_timeout(evcon, 1);
michael@0 3117 /* also bind to local host */
michael@0 3118 evhttp_connection_set_local_address(evcon, "127.0.0.1");
michael@0 3119
michael@0 3120 /*
michael@0 3121 * At this point, we want to schedule an HTTP GET request
michael@0 3122 * server using our make request method.
michael@0 3123 */
michael@0 3124
michael@0 3125 req = evhttp_request_new(http_connection_retry_done, data->base);
michael@0 3126 tt_assert(req);
michael@0 3127
michael@0 3128 /* Add the information that we care about */
michael@0 3129 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 3130
michael@0 3131 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
michael@0 3132 "/?arg=val") == -1) {
michael@0 3133 tt_abort_msg("Couldn't make request");
michael@0 3134 }
michael@0 3135
michael@0 3136 evutil_gettimeofday(&tv_start, NULL);
michael@0 3137 event_base_dispatch(data->base);
michael@0 3138 evutil_gettimeofday(&tv_end, NULL);
michael@0 3139 evutil_timersub(&tv_end, &tv_start, &tv_end);
michael@0 3140 tt_int_op(tv_end.tv_sec, <, 1);
michael@0 3141
michael@0 3142 tt_int_op(test_ok, ==, 1);
michael@0 3143
michael@0 3144 /*
michael@0 3145 * now test the same but with retries
michael@0 3146 */
michael@0 3147 test_ok = 0;
michael@0 3148
michael@0 3149 evhttp_connection_set_timeout(evcon, 1);
michael@0 3150 evhttp_connection_set_retries(evcon, 1);
michael@0 3151
michael@0 3152 req = evhttp_request_new(http_connection_retry_done, data->base);
michael@0 3153 tt_assert(req);
michael@0 3154
michael@0 3155 /* Add the information that we care about */
michael@0 3156 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 3157
michael@0 3158 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
michael@0 3159 "/?arg=val") == -1) {
michael@0 3160 tt_abort_msg("Couldn't make request");
michael@0 3161 }
michael@0 3162
michael@0 3163 evutil_gettimeofday(&tv_start, NULL);
michael@0 3164 event_base_dispatch(data->base);
michael@0 3165 evutil_gettimeofday(&tv_end, NULL);
michael@0 3166 evutil_timersub(&tv_end, &tv_start, &tv_end);
michael@0 3167 tt_int_op(tv_end.tv_sec, >, 1);
michael@0 3168 tt_int_op(tv_end.tv_sec, <, 6);
michael@0 3169
michael@0 3170 tt_assert(test_ok == 1);
michael@0 3171
michael@0 3172 /*
michael@0 3173 * now test the same but with retries and give it a web server
michael@0 3174 * at the end
michael@0 3175 */
michael@0 3176 test_ok = 0;
michael@0 3177
michael@0 3178 evhttp_connection_set_timeout(evcon, 1);
michael@0 3179 evhttp_connection_set_retries(evcon, 3);
michael@0 3180
michael@0 3181 req = evhttp_request_new(http_dispatcher_test_done, data->base);
michael@0 3182 tt_assert(req);
michael@0 3183
michael@0 3184 /* Add the information that we care about */
michael@0 3185 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 3186
michael@0 3187 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET,
michael@0 3188 "/?arg=val") == -1) {
michael@0 3189 tt_abort_msg("Couldn't make request");
michael@0 3190 }
michael@0 3191
michael@0 3192 /* start up a web server one second after the connection tried
michael@0 3193 * to send a request
michael@0 3194 */
michael@0 3195 evutil_timerclear(&tv);
michael@0 3196 tv.tv_sec = 1;
michael@0 3197 http_make_web_server_base = data->base;
michael@0 3198 event_base_once(data->base, -1, EV_TIMEOUT, http_make_web_server, &port, &tv);
michael@0 3199
michael@0 3200 evutil_gettimeofday(&tv_start, NULL);
michael@0 3201 event_base_dispatch(data->base);
michael@0 3202 evutil_gettimeofday(&tv_end, NULL);
michael@0 3203
michael@0 3204 evutil_timersub(&tv_end, &tv_start, &tv_end);
michael@0 3205
michael@0 3206 tt_int_op(tv_end.tv_sec, >, 1);
michael@0 3207 tt_int_op(tv_end.tv_sec, <, 6);
michael@0 3208
michael@0 3209 tt_int_op(test_ok, ==, 1);
michael@0 3210
michael@0 3211 end:
michael@0 3212 if (evcon)
michael@0 3213 evhttp_connection_free(evcon);
michael@0 3214 if (http)
michael@0 3215 evhttp_free(http);
michael@0 3216 }
michael@0 3217
michael@0 3218 static void
michael@0 3219 http_primitives(void *ptr)
michael@0 3220 {
michael@0 3221 char *escaped = NULL;
michael@0 3222 struct evhttp *http = NULL;
michael@0 3223
michael@0 3224 escaped = evhttp_htmlescape("<script>");
michael@0 3225 tt_assert(escaped);
michael@0 3226 tt_str_op(escaped, ==, "&lt;script&gt;");
michael@0 3227 free(escaped);
michael@0 3228
michael@0 3229 escaped = evhttp_htmlescape("\"\'&");
michael@0 3230 tt_assert(escaped);
michael@0 3231 tt_str_op(escaped, ==, "&quot;&#039;&amp;");
michael@0 3232
michael@0 3233 http = evhttp_new(NULL);
michael@0 3234 tt_assert(http);
michael@0 3235 tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, NULL), ==, 0);
michael@0 3236 tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, NULL), ==, -1);
michael@0 3237 tt_int_op(evhttp_del_cb(http, "/test"), ==, 0);
michael@0 3238 tt_int_op(evhttp_del_cb(http, "/test"), ==, -1);
michael@0 3239 tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, NULL), ==, 0);
michael@0 3240
michael@0 3241 end:
michael@0 3242 if (escaped)
michael@0 3243 free(escaped);
michael@0 3244 if (http)
michael@0 3245 evhttp_free(http);
michael@0 3246 }
michael@0 3247
michael@0 3248 static void
michael@0 3249 http_multi_line_header_test(void *arg)
michael@0 3250 {
michael@0 3251 struct basic_test_data *data = arg;
michael@0 3252 struct bufferevent *bev= NULL;
michael@0 3253 evutil_socket_t fd = -1;
michael@0 3254 const char *http_start_request;
michael@0 3255 ev_uint16_t port = 0;
michael@0 3256
michael@0 3257 test_ok = 0;
michael@0 3258
michael@0 3259 http = http_setup(&port, data->base);
michael@0 3260
michael@0 3261 fd = http_connect("127.0.0.1", port);
michael@0 3262
michael@0 3263 /* Stupid thing to send a request */
michael@0 3264 bev = bufferevent_socket_new(data->base, fd, 0);
michael@0 3265 bufferevent_setcb(bev, http_readcb, http_writecb,
michael@0 3266 http_errorcb, data->base);
michael@0 3267
michael@0 3268 http_start_request =
michael@0 3269 "GET /test HTTP/1.1\r\n"
michael@0 3270 "Host: somehost\r\n"
michael@0 3271 "Connection: close\r\n"
michael@0 3272 "X-Multi: aaaaaaaa\r\n"
michael@0 3273 " a\r\n"
michael@0 3274 "\tEND\r\n"
michael@0 3275 "X-Last: last\r\n"
michael@0 3276 "\r\n";
michael@0 3277
michael@0 3278 bufferevent_write(bev, http_start_request, strlen(http_start_request));
michael@0 3279
michael@0 3280 event_base_dispatch(data->base);
michael@0 3281
michael@0 3282 tt_int_op(test_ok, ==, 4);
michael@0 3283 end:
michael@0 3284 if (bev)
michael@0 3285 bufferevent_free(bev);
michael@0 3286 if (fd >= 0)
michael@0 3287 evutil_closesocket(fd);
michael@0 3288 if (http)
michael@0 3289 evhttp_free(http);
michael@0 3290 }
michael@0 3291
michael@0 3292 static void
michael@0 3293 http_request_bad(struct evhttp_request *req, void *arg)
michael@0 3294 {
michael@0 3295 if (req != NULL) {
michael@0 3296 fprintf(stderr, "FAILED\n");
michael@0 3297 exit(1);
michael@0 3298 }
michael@0 3299
michael@0 3300 test_ok = 1;
michael@0 3301 event_base_loopexit(arg, NULL);
michael@0 3302 }
michael@0 3303
michael@0 3304 static void
michael@0 3305 http_negative_content_length_test(void *arg)
michael@0 3306 {
michael@0 3307 struct basic_test_data *data = arg;
michael@0 3308 ev_uint16_t port = 0;
michael@0 3309 struct evhttp_connection *evcon = NULL;
michael@0 3310 struct evhttp_request *req = NULL;
michael@0 3311
michael@0 3312 test_ok = 0;
michael@0 3313
michael@0 3314 http = http_setup(&port, data->base);
michael@0 3315
michael@0 3316 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
michael@0 3317 tt_assert(evcon);
michael@0 3318
michael@0 3319 /*
michael@0 3320 * At this point, we want to schedule a request to the HTTP
michael@0 3321 * server using our make request method.
michael@0 3322 */
michael@0 3323
michael@0 3324 req = evhttp_request_new(http_request_bad, data->base);
michael@0 3325
michael@0 3326 /* Cause the response to have a negative content-length */
michael@0 3327 evhttp_add_header(evhttp_request_get_output_headers(req), "X-Negative", "makeitso");
michael@0 3328
michael@0 3329 /* We give ownership of the request to the connection */
michael@0 3330 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
michael@0 3331 tt_abort_msg("Couldn't make request");
michael@0 3332 }
michael@0 3333
michael@0 3334 event_base_dispatch(data->base);
michael@0 3335
michael@0 3336 end:
michael@0 3337 if (evcon)
michael@0 3338 evhttp_connection_free(evcon);
michael@0 3339 if (http)
michael@0 3340 evhttp_free(http);
michael@0 3341 }
michael@0 3342
michael@0 3343
michael@0 3344 static void
michael@0 3345 http_data_length_constraints_test_done(struct evhttp_request *req, void *arg)
michael@0 3346 {
michael@0 3347 tt_assert(req);
michael@0 3348 tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_BADREQUEST);
michael@0 3349 end:
michael@0 3350 event_base_loopexit(arg, NULL);
michael@0 3351 }
michael@0 3352
michael@0 3353 static void
michael@0 3354 http_large_entity_test_done(struct evhttp_request *req, void *arg)
michael@0 3355 {
michael@0 3356 tt_assert(req);
michael@0 3357 tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_ENTITYTOOLARGE);
michael@0 3358 end:
michael@0 3359 event_base_loopexit(arg, NULL);
michael@0 3360 }
michael@0 3361
michael@0 3362 static void
michael@0 3363 http_data_length_constraints_test(void *arg)
michael@0 3364 {
michael@0 3365 struct basic_test_data *data = arg;
michael@0 3366 ev_uint16_t port = 0;
michael@0 3367 struct evhttp_connection *evcon = NULL;
michael@0 3368 struct evhttp_request *req = NULL;
michael@0 3369 char long_str[8192];
michael@0 3370
michael@0 3371 test_ok = 0;
michael@0 3372
michael@0 3373 http = http_setup(&port, data->base);
michael@0 3374
michael@0 3375 evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
michael@0 3376 tt_assert(evcon);
michael@0 3377
michael@0 3378 /* also bind to local host */
michael@0 3379 evhttp_connection_set_local_address(evcon, "127.0.0.1");
michael@0 3380
michael@0 3381 /*
michael@0 3382 * At this point, we want to schedule an HTTP GET request
michael@0 3383 * server using our make request method.
michael@0 3384 */
michael@0 3385
michael@0 3386 req = evhttp_request_new(http_data_length_constraints_test_done, data->base);
michael@0 3387 tt_assert(req);
michael@0 3388
michael@0 3389 memset(long_str, 'a', 8192);
michael@0 3390 long_str[8191] = '\0';
michael@0 3391 /* Add the information that we care about */
michael@0 3392 evhttp_set_max_headers_size(http, 8191);
michael@0 3393 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 3394 evhttp_add_header(evhttp_request_get_output_headers(req), "Longheader", long_str);
michael@0 3395
michael@0 3396 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) {
michael@0 3397 tt_abort_msg("Couldn't make request");
michael@0 3398 }
michael@0 3399 event_base_dispatch(data->base);
michael@0 3400
michael@0 3401 req = evhttp_request_new(http_data_length_constraints_test_done, data->base);
michael@0 3402 tt_assert(req);
michael@0 3403 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 3404
michael@0 3405 /* GET /?arg=verylongvalue HTTP/1.1 */
michael@0 3406 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, long_str) == -1) {
michael@0 3407 tt_abort_msg("Couldn't make request");
michael@0 3408 }
michael@0 3409 event_base_dispatch(data->base);
michael@0 3410
michael@0 3411 evhttp_set_max_body_size(http, 8190);
michael@0 3412 req = evhttp_request_new(http_data_length_constraints_test_done, data->base);
michael@0 3413 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 3414 evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str);
michael@0 3415 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) {
michael@0 3416 tt_abort_msg("Couldn't make request");
michael@0 3417 }
michael@0 3418 event_base_dispatch(data->base);
michael@0 3419
michael@0 3420 req = evhttp_request_new(http_large_entity_test_done, data->base);
michael@0 3421 evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
michael@0 3422 evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "100-continue");
michael@0 3423 evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str);
michael@0 3424 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) {
michael@0 3425 tt_abort_msg("Couldn't make request");
michael@0 3426 }
michael@0 3427 event_base_dispatch(data->base);
michael@0 3428
michael@0 3429 test_ok = 1;
michael@0 3430 end:
michael@0 3431 if (evcon)
michael@0 3432 evhttp_connection_free(evcon);
michael@0 3433 if (http)
michael@0 3434 evhttp_free(http);
michael@0 3435 }
michael@0 3436
michael@0 3437 /*
michael@0 3438 * Testing client reset of server chunked connections
michael@0 3439 */
michael@0 3440
michael@0 3441 struct terminate_state {
michael@0 3442 struct event_base *base;
michael@0 3443 struct evhttp_request *req;
michael@0 3444 struct bufferevent *bev;
michael@0 3445 evutil_socket_t fd;
michael@0 3446 int gotclosecb: 1;
michael@0 3447 };
michael@0 3448
michael@0 3449 static void
michael@0 3450 terminate_chunked_trickle_cb(evutil_socket_t fd, short events, void *arg)
michael@0 3451 {
michael@0 3452 struct terminate_state *state = arg;
michael@0 3453 struct evbuffer *evb;
michael@0 3454 struct timeval tv;
michael@0 3455
michael@0 3456 if (evhttp_request_get_connection(state->req) == NULL) {
michael@0 3457 test_ok = 1;
michael@0 3458 evhttp_request_free(state->req);
michael@0 3459 event_base_loopexit(state->base,NULL);
michael@0 3460 return;
michael@0 3461 }
michael@0 3462
michael@0 3463 evb = evbuffer_new();
michael@0 3464 evbuffer_add_printf(evb, "%p", evb);
michael@0 3465 evhttp_send_reply_chunk(state->req, evb);
michael@0 3466 evbuffer_free(evb);
michael@0 3467
michael@0 3468 tv.tv_sec = 0;
michael@0 3469 tv.tv_usec = 3000;
michael@0 3470 EVUTIL_ASSERT(state);
michael@0 3471 EVUTIL_ASSERT(state->base);
michael@0 3472 event_base_once(state->base, -1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv);
michael@0 3473 }
michael@0 3474
michael@0 3475 static void
michael@0 3476 terminate_chunked_close_cb(struct evhttp_connection *evcon, void *arg)
michael@0 3477 {
michael@0 3478 struct terminate_state *state = arg;
michael@0 3479 state->gotclosecb = 1;
michael@0 3480 }
michael@0 3481
michael@0 3482 static void
michael@0 3483 terminate_chunked_cb(struct evhttp_request *req, void *arg)
michael@0 3484 {
michael@0 3485 struct terminate_state *state = arg;
michael@0 3486 struct timeval tv;
michael@0 3487
michael@0 3488 /* we want to know if this connection closes on us */
michael@0 3489 evhttp_connection_set_closecb(
michael@0 3490 evhttp_request_get_connection(req),
michael@0 3491 terminate_chunked_close_cb, arg);
michael@0 3492
michael@0 3493 state->req = req;
michael@0 3494
michael@0 3495 evhttp_send_reply_start(req, HTTP_OK, "OK");
michael@0 3496
michael@0 3497 tv.tv_sec = 0;
michael@0 3498 tv.tv_usec = 3000;
michael@0 3499 event_base_once(state->base, -1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv);
michael@0 3500 }
michael@0 3501
michael@0 3502 static void
michael@0 3503 terminate_chunked_client(evutil_socket_t fd, short event, void *arg)
michael@0 3504 {
michael@0 3505 struct terminate_state *state = arg;
michael@0 3506 bufferevent_free(state->bev);
michael@0 3507 evutil_closesocket(state->fd);
michael@0 3508 }
michael@0 3509
michael@0 3510 static void
michael@0 3511 terminate_readcb(struct bufferevent *bev, void *arg)
michael@0 3512 {
michael@0 3513 /* just drop the data */
michael@0 3514 evbuffer_drain(bufferevent_get_input(bev), -1);
michael@0 3515 }
michael@0 3516
michael@0 3517
michael@0 3518 static void
michael@0 3519 http_terminate_chunked_test(void *arg)
michael@0 3520 {
michael@0 3521 struct basic_test_data *data = arg;
michael@0 3522 struct bufferevent *bev = NULL;
michael@0 3523 struct timeval tv;
michael@0 3524 const char *http_request;
michael@0 3525 ev_uint16_t port = 0;
michael@0 3526 evutil_socket_t fd = -1;
michael@0 3527 struct terminate_state terminate_state;
michael@0 3528
michael@0 3529 test_ok = 0;
michael@0 3530
michael@0 3531 http = http_setup(&port, data->base);
michael@0 3532 evhttp_del_cb(http, "/test");
michael@0 3533 tt_assert(evhttp_set_cb(http, "/test",
michael@0 3534 terminate_chunked_cb, &terminate_state) == 0);
michael@0 3535
michael@0 3536 fd = http_connect("127.0.0.1", port);
michael@0 3537
michael@0 3538 /* Stupid thing to send a request */
michael@0 3539 bev = bufferevent_socket_new(data->base, fd, 0);
michael@0 3540 bufferevent_setcb(bev, terminate_readcb, http_writecb,
michael@0 3541 http_errorcb, data->base);
michael@0 3542
michael@0 3543 memset(&terminate_state, 0, sizeof(terminate_state));
michael@0 3544 terminate_state.base = data->base;
michael@0 3545 terminate_state.fd = fd;
michael@0 3546 terminate_state.bev = bev;
michael@0 3547 terminate_state.gotclosecb = 0;
michael@0 3548
michael@0 3549 /* first half of the http request */
michael@0 3550 http_request =
michael@0 3551 "GET /test HTTP/1.1\r\n"
michael@0 3552 "Host: some\r\n\r\n";
michael@0 3553
michael@0 3554 bufferevent_write(bev, http_request, strlen(http_request));
michael@0 3555 evutil_timerclear(&tv);
michael@0 3556 tv.tv_usec = 10000;
michael@0 3557 event_base_once(data->base, -1, EV_TIMEOUT, terminate_chunked_client, &terminate_state,
michael@0 3558 &tv);
michael@0 3559
michael@0 3560 event_base_dispatch(data->base);
michael@0 3561
michael@0 3562 if (terminate_state.gotclosecb == 0)
michael@0 3563 test_ok = 0;
michael@0 3564
michael@0 3565 end:
michael@0 3566 if (fd >= 0)
michael@0 3567 evutil_closesocket(fd);
michael@0 3568 if (http)
michael@0 3569 evhttp_free(http);
michael@0 3570 }
michael@0 3571
michael@0 3572 #define HTTP_LEGACY(name) \
michael@0 3573 { #name, run_legacy_test_fn, TT_ISOLATED|TT_LEGACY, &legacy_setup, \
michael@0 3574 http_##name##_test }
michael@0 3575
michael@0 3576 #define HTTP(name) \
michael@0 3577 { #name, http_##name##_test, TT_ISOLATED, &basic_setup, NULL }
michael@0 3578
michael@0 3579 struct testcase_t http_testcases[] = {
michael@0 3580 { "primitives", http_primitives, 0, NULL, NULL },
michael@0 3581 { "base", http_base_test, TT_FORK, NULL, NULL },
michael@0 3582 { "bad_headers", http_bad_header_test, 0, NULL, NULL },
michael@0 3583 { "parse_query", http_parse_query_test, 0, NULL, NULL },
michael@0 3584 { "parse_uri", http_parse_uri_test, 0, NULL, NULL },
michael@0 3585 { "parse_uri_nc", http_parse_uri_test, 0, &basic_setup, (void*)"nc" },
michael@0 3586 { "uriencode", http_uriencode_test, 0, NULL, NULL },
michael@0 3587 HTTP(basic),
michael@0 3588 HTTP(cancel),
michael@0 3589 HTTP(virtual_host),
michael@0 3590 HTTP(post),
michael@0 3591 HTTP(put),
michael@0 3592 HTTP(delete),
michael@0 3593 HTTP(allowed_methods),
michael@0 3594 HTTP(failure),
michael@0 3595 HTTP(connection),
michael@0 3596 HTTP(persist_connection),
michael@0 3597 HTTP(connection_async),
michael@0 3598 HTTP(close_detection),
michael@0 3599 HTTP(close_detection_delay),
michael@0 3600 HTTP(bad_request),
michael@0 3601 HTTP(incomplete),
michael@0 3602 HTTP(incomplete_timeout),
michael@0 3603 HTTP(terminate_chunked),
michael@0 3604
michael@0 3605 HTTP(highport),
michael@0 3606 HTTP(dispatcher),
michael@0 3607 HTTP(multi_line_header),
michael@0 3608 HTTP(negative_content_length),
michael@0 3609 HTTP(chunk_out),
michael@0 3610 HTTP(stream_out),
michael@0 3611
michael@0 3612 HTTP(stream_in),
michael@0 3613 HTTP(stream_in_cancel),
michael@0 3614
michael@0 3615 HTTP(connection_fail),
michael@0 3616 HTTP(connection_retry),
michael@0 3617 HTTP(data_length_constraints),
michael@0 3618
michael@0 3619 END_OF_TESTCASES
michael@0 3620 };
michael@0 3621

mercurial