Narayan Kamath | fc74cb4 | 2017-09-13 12:53:52 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu> |
| 3 | * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * 3. The name of the author may not be used to endorse or promote products |
| 14 | * derived from this software without specific prior written permission. |
| 15 | * |
| 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | */ |
| 27 | #include "util-internal.h" |
| 28 | |
| 29 | #ifdef _WIN32 |
| 30 | #include <winsock2.h> |
| 31 | #include <ws2tcpip.h> |
| 32 | #include <windows.h> |
| 33 | #endif |
| 34 | |
| 35 | #include "event2/event-config.h" |
| 36 | |
| 37 | #include <sys/types.h> |
| 38 | #include <sys/stat.h> |
| 39 | #ifdef EVENT__HAVE_SYS_TIME_H |
| 40 | #include <sys/time.h> |
| 41 | #endif |
| 42 | #include <sys/queue.h> |
| 43 | #ifndef _WIN32 |
| 44 | #include <sys/socket.h> |
| 45 | #include <signal.h> |
| 46 | #include <unistd.h> |
| 47 | #include <netdb.h> |
| 48 | #endif |
| 49 | #include <fcntl.h> |
| 50 | #include <stdlib.h> |
| 51 | #include <stdio.h> |
| 52 | #include <string.h> |
| 53 | #include <errno.h> |
| 54 | |
| 55 | #include "event2/dns.h" |
| 56 | |
| 57 | #include "event2/event.h" |
| 58 | #include "event2/http.h" |
| 59 | #include "event2/buffer.h" |
| 60 | #include "event2/bufferevent.h" |
| 61 | #include "event2/bufferevent_ssl.h" |
| 62 | #include "event2/util.h" |
| 63 | #include "event2/listener.h" |
| 64 | #include "log-internal.h" |
| 65 | #include "http-internal.h" |
| 66 | #include "regress.h" |
| 67 | #include "regress_testutils.h" |
| 68 | |
| 69 | /* set if a test needs to call loopexit on a base */ |
| 70 | static struct event_base *exit_base; |
| 71 | |
| 72 | static char const BASIC_REQUEST_BODY[] = "This is funny"; |
| 73 | |
| 74 | static void http_basic_cb(struct evhttp_request *req, void *arg); |
| 75 | static void http_large_cb(struct evhttp_request *req, void *arg); |
| 76 | static void http_chunked_cb(struct evhttp_request *req, void *arg); |
| 77 | static void http_post_cb(struct evhttp_request *req, void *arg); |
| 78 | static void http_put_cb(struct evhttp_request *req, void *arg); |
| 79 | static void http_delete_cb(struct evhttp_request *req, void *arg); |
| 80 | static void http_delay_cb(struct evhttp_request *req, void *arg); |
| 81 | static void http_large_delay_cb(struct evhttp_request *req, void *arg); |
| 82 | static void http_badreq_cb(struct evhttp_request *req, void *arg); |
| 83 | static void http_dispatcher_cb(struct evhttp_request *req, void *arg); |
| 84 | static void http_on_complete_cb(struct evhttp_request *req, void *arg); |
| 85 | |
| 86 | #define HTTP_BIND_IPV6 1 |
| 87 | #define HTTP_BIND_SSL 2 |
| 88 | #define HTTP_SSL_FILTER 4 |
| 89 | static int |
| 90 | http_bind(struct evhttp *myhttp, ev_uint16_t *pport, int mask) |
| 91 | { |
| 92 | int port; |
| 93 | struct evhttp_bound_socket *sock; |
| 94 | int ipv6 = mask & HTTP_BIND_IPV6; |
| 95 | |
| 96 | if (ipv6) |
| 97 | sock = evhttp_bind_socket_with_handle(myhttp, "::1", *pport); |
| 98 | else |
| 99 | sock = evhttp_bind_socket_with_handle(myhttp, "127.0.0.1", *pport); |
| 100 | |
| 101 | if (sock == NULL) { |
| 102 | if (ipv6) |
| 103 | return -1; |
| 104 | else |
| 105 | event_errx(1, "Could not start web server"); |
| 106 | } |
| 107 | |
| 108 | port = regress_get_socket_port(evhttp_bound_socket_get_fd(sock)); |
| 109 | if (port < 0) |
| 110 | return -1; |
| 111 | *pport = (ev_uint16_t) port; |
| 112 | |
| 113 | return 0; |
| 114 | } |
| 115 | |
| 116 | #ifdef EVENT__HAVE_OPENSSL |
| 117 | static struct bufferevent * |
| 118 | https_bev(struct event_base *base, void *arg) |
| 119 | { |
| 120 | SSL *ssl = SSL_new(get_ssl_ctx()); |
| 121 | |
| 122 | SSL_use_certificate(ssl, ssl_getcert()); |
| 123 | SSL_use_PrivateKey(ssl, ssl_getkey()); |
| 124 | |
| 125 | return bufferevent_openssl_socket_new( |
| 126 | base, -1, ssl, BUFFEREVENT_SSL_ACCEPTING, |
| 127 | BEV_OPT_CLOSE_ON_FREE); |
| 128 | } |
| 129 | #endif |
| 130 | static struct evhttp * |
| 131 | http_setup(ev_uint16_t *pport, struct event_base *base, int mask) |
| 132 | { |
| 133 | struct evhttp *myhttp; |
| 134 | |
| 135 | /* Try a few different ports */ |
| 136 | myhttp = evhttp_new(base); |
| 137 | |
| 138 | if (http_bind(myhttp, pport, mask) < 0) |
| 139 | return NULL; |
| 140 | #ifdef EVENT__HAVE_OPENSSL |
| 141 | if (mask & HTTP_BIND_SSL) { |
| 142 | init_ssl(); |
| 143 | evhttp_set_bevcb(myhttp, https_bev, NULL); |
| 144 | } |
| 145 | #endif |
| 146 | |
| 147 | /* Register a callback for certain types of requests */ |
| 148 | evhttp_set_cb(myhttp, "/test", http_basic_cb, myhttp); |
| 149 | evhttp_set_cb(myhttp, "/large", http_large_cb, base); |
| 150 | evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, base); |
| 151 | evhttp_set_cb(myhttp, "/streamed", http_chunked_cb, base); |
| 152 | evhttp_set_cb(myhttp, "/postit", http_post_cb, base); |
| 153 | evhttp_set_cb(myhttp, "/putit", http_put_cb, base); |
| 154 | evhttp_set_cb(myhttp, "/deleteit", http_delete_cb, base); |
| 155 | evhttp_set_cb(myhttp, "/delay", http_delay_cb, base); |
| 156 | evhttp_set_cb(myhttp, "/largedelay", http_large_delay_cb, base); |
| 157 | evhttp_set_cb(myhttp, "/badrequest", http_badreq_cb, base); |
| 158 | evhttp_set_cb(myhttp, "/oncomplete", http_on_complete_cb, base); |
| 159 | evhttp_set_cb(myhttp, "/", http_dispatcher_cb, base); |
| 160 | return (myhttp); |
| 161 | } |
| 162 | |
| 163 | #ifndef NI_MAXSERV |
| 164 | #define NI_MAXSERV 1024 |
| 165 | #endif |
| 166 | |
| 167 | static evutil_socket_t |
| 168 | http_connect(const char *address, ev_uint16_t port) |
| 169 | { |
| 170 | /* Stupid code for connecting */ |
| 171 | struct evutil_addrinfo ai, *aitop; |
| 172 | char strport[NI_MAXSERV]; |
| 173 | |
| 174 | struct sockaddr *sa; |
| 175 | int slen; |
| 176 | evutil_socket_t fd; |
| 177 | |
| 178 | memset(&ai, 0, sizeof(ai)); |
| 179 | ai.ai_family = AF_INET; |
| 180 | ai.ai_socktype = SOCK_STREAM; |
| 181 | evutil_snprintf(strport, sizeof(strport), "%d", port); |
| 182 | if (evutil_getaddrinfo(address, strport, &ai, &aitop) != 0) { |
| 183 | event_warn("getaddrinfo"); |
| 184 | return (-1); |
| 185 | } |
| 186 | sa = aitop->ai_addr; |
| 187 | slen = aitop->ai_addrlen; |
| 188 | |
| 189 | fd = socket(AF_INET, SOCK_STREAM, 0); |
| 190 | if (fd == -1) |
| 191 | event_err(1, "socket failed"); |
| 192 | |
| 193 | evutil_make_socket_nonblocking(fd); |
| 194 | if (connect(fd, sa, slen) == -1) { |
| 195 | #ifdef _WIN32 |
| 196 | int tmp_err = WSAGetLastError(); |
| 197 | if (tmp_err != WSAEINPROGRESS && tmp_err != WSAEINVAL && |
| 198 | tmp_err != WSAEWOULDBLOCK) |
| 199 | event_err(1, "connect failed"); |
| 200 | #else |
| 201 | if (errno != EINPROGRESS) |
| 202 | event_err(1, "connect failed"); |
| 203 | #endif |
| 204 | } |
| 205 | |
| 206 | evutil_freeaddrinfo(aitop); |
| 207 | |
| 208 | return (fd); |
| 209 | } |
| 210 | |
| 211 | /* Helper: do a strcmp on the contents of buf and the string s. */ |
| 212 | static int |
| 213 | evbuffer_datacmp(struct evbuffer *buf, const char *s) |
| 214 | { |
| 215 | size_t b_sz = evbuffer_get_length(buf); |
| 216 | size_t s_sz = strlen(s); |
| 217 | unsigned char *d; |
| 218 | int r; |
| 219 | |
| 220 | if (b_sz < s_sz) |
| 221 | return -1; |
| 222 | |
| 223 | d = evbuffer_pullup(buf, s_sz); |
| 224 | if ((r = memcmp(d, s, s_sz))) |
| 225 | return r; |
| 226 | |
| 227 | if (b_sz > s_sz) |
| 228 | return 1; |
| 229 | else |
| 230 | return 0; |
| 231 | } |
| 232 | |
| 233 | /* Helper: Return true iff buf contains s */ |
| 234 | static int |
| 235 | evbuffer_contains(struct evbuffer *buf, const char *s) |
| 236 | { |
| 237 | struct evbuffer_ptr ptr; |
| 238 | ptr = evbuffer_search(buf, s, strlen(s), NULL); |
| 239 | return ptr.pos != -1; |
| 240 | } |
| 241 | |
| 242 | static void |
| 243 | http_readcb(struct bufferevent *bev, void *arg) |
| 244 | { |
| 245 | const char *what = BASIC_REQUEST_BODY; |
| 246 | struct event_base *my_base = arg; |
| 247 | |
| 248 | if (evbuffer_contains(bufferevent_get_input(bev), what)) { |
| 249 | struct evhttp_request *req = evhttp_request_new(NULL, NULL); |
| 250 | enum message_read_status done; |
| 251 | |
| 252 | /* req->kind = EVHTTP_RESPONSE; */ |
| 253 | done = evhttp_parse_firstline_(req, bufferevent_get_input(bev)); |
| 254 | if (done != ALL_DATA_READ) |
| 255 | goto out; |
| 256 | |
| 257 | done = evhttp_parse_headers_(req, bufferevent_get_input(bev)); |
| 258 | if (done != ALL_DATA_READ) |
| 259 | goto out; |
| 260 | |
| 261 | if (done == 1 && |
| 262 | evhttp_find_header(evhttp_request_get_input_headers(req), |
| 263 | "Content-Type") != NULL) |
| 264 | test_ok++; |
| 265 | |
| 266 | out: |
| 267 | evhttp_request_free(req); |
| 268 | bufferevent_disable(bev, EV_READ); |
| 269 | if (exit_base) |
| 270 | event_base_loopexit(exit_base, NULL); |
| 271 | else if (my_base) |
| 272 | event_base_loopexit(my_base, NULL); |
| 273 | else { |
| 274 | fprintf(stderr, "No way to exit loop!\n"); |
| 275 | exit(1); |
| 276 | } |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | static void |
| 281 | http_writecb(struct bufferevent *bev, void *arg) |
| 282 | { |
| 283 | if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) { |
| 284 | /* enable reading of the reply */ |
| 285 | bufferevent_enable(bev, EV_READ); |
| 286 | test_ok++; |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | static void |
| 291 | http_errorcb(struct bufferevent *bev, short what, void *arg) |
| 292 | { |
| 293 | /** For ssl */ |
| 294 | if (what & BEV_EVENT_CONNECTED) |
| 295 | return; |
| 296 | test_ok = -2; |
| 297 | event_base_loopexit(arg, NULL); |
| 298 | } |
| 299 | |
| 300 | static int found_multi = 0; |
| 301 | static int found_multi2 = 0; |
| 302 | |
| 303 | static void |
| 304 | http_basic_cb(struct evhttp_request *req, void *arg) |
| 305 | { |
| 306 | struct evbuffer *evb = evbuffer_new(); |
| 307 | struct evhttp_connection *evcon; |
| 308 | int empty = evhttp_find_header(evhttp_request_get_input_headers(req), "Empty") != NULL; |
| 309 | event_debug(("%s: called\n", __func__)); |
| 310 | evbuffer_add_printf(evb, BASIC_REQUEST_BODY); |
| 311 | |
| 312 | evcon = evhttp_request_get_connection(req); |
| 313 | tt_assert(evhttp_connection_get_server(evcon) == arg); |
| 314 | |
| 315 | /* For multi-line headers test */ |
| 316 | { |
| 317 | const char *multi = |
| 318 | evhttp_find_header(evhttp_request_get_input_headers(req),"X-Multi"); |
| 319 | if (multi) { |
| 320 | found_multi = !strcmp(multi,"aaaaaaaa a END"); |
| 321 | if (strcmp("END", multi + strlen(multi) - 3) == 0) |
| 322 | test_ok++; |
| 323 | if (evhttp_find_header(evhttp_request_get_input_headers(req), "X-Last")) |
| 324 | test_ok++; |
| 325 | } |
| 326 | } |
| 327 | { |
| 328 | const char *multi2 = |
| 329 | evhttp_find_header(evhttp_request_get_input_headers(req),"X-Multi-Extra-WS"); |
| 330 | if (multi2) { |
| 331 | found_multi2 = !strcmp(multi2,"libevent 2.1"); |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | |
| 336 | /* injecting a bad content-length */ |
| 337 | if (evhttp_find_header(evhttp_request_get_input_headers(req), "X-Negative")) |
| 338 | evhttp_add_header(evhttp_request_get_output_headers(req), |
| 339 | "Content-Length", "-100"); |
| 340 | |
| 341 | /* allow sending of an empty reply */ |
| 342 | evhttp_send_reply(req, HTTP_OK, "Everything is fine", |
| 343 | !empty ? evb : NULL); |
| 344 | |
| 345 | end: |
| 346 | evbuffer_free(evb); |
| 347 | } |
| 348 | |
| 349 | static void |
| 350 | http_large_cb(struct evhttp_request *req, void *arg) |
| 351 | { |
| 352 | struct evbuffer *evb = evbuffer_new(); |
| 353 | int i; |
| 354 | |
| 355 | for (i = 0; i < 1<<20; ++i) { |
| 356 | evbuffer_add_printf(evb, BASIC_REQUEST_BODY); |
| 357 | } |
| 358 | evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); |
| 359 | evbuffer_free(evb); |
| 360 | } |
| 361 | |
| 362 | static char const* const CHUNKS[] = { |
| 363 | "This is funny", |
| 364 | "but not hilarious.", |
| 365 | "bwv 1052" |
| 366 | }; |
| 367 | |
| 368 | struct chunk_req_state { |
| 369 | struct event_base *base; |
| 370 | struct evhttp_request *req; |
| 371 | int i; |
| 372 | }; |
| 373 | |
| 374 | static void |
| 375 | http_chunked_trickle_cb(evutil_socket_t fd, short events, void *arg) |
| 376 | { |
| 377 | struct evbuffer *evb = evbuffer_new(); |
| 378 | struct chunk_req_state *state = arg; |
| 379 | struct timeval when = { 0, 0 }; |
| 380 | |
| 381 | evbuffer_add_printf(evb, "%s", CHUNKS[state->i]); |
| 382 | evhttp_send_reply_chunk(state->req, evb); |
| 383 | evbuffer_free(evb); |
| 384 | |
| 385 | if (++state->i < (int) (sizeof(CHUNKS)/sizeof(CHUNKS[0]))) { |
| 386 | event_base_once(state->base, -1, EV_TIMEOUT, |
| 387 | http_chunked_trickle_cb, state, &when); |
| 388 | } else { |
| 389 | evhttp_send_reply_end(state->req); |
| 390 | free(state); |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | static void |
| 395 | http_chunked_cb(struct evhttp_request *req, void *arg) |
| 396 | { |
| 397 | struct timeval when = { 0, 0 }; |
| 398 | struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state)); |
| 399 | event_debug(("%s: called\n", __func__)); |
| 400 | |
| 401 | memset(state, 0, sizeof(struct chunk_req_state)); |
| 402 | state->req = req; |
| 403 | state->base = arg; |
| 404 | |
| 405 | if (strcmp(evhttp_request_get_uri(req), "/streamed") == 0) { |
| 406 | evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Length", "39"); |
| 407 | } |
| 408 | |
| 409 | /* generate a chunked/streamed reply */ |
| 410 | evhttp_send_reply_start(req, HTTP_OK, "Everything is fine"); |
| 411 | |
| 412 | /* but trickle it across several iterations to ensure we're not |
| 413 | * assuming it comes all at once */ |
| 414 | event_base_once(arg, -1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when); |
| 415 | } |
| 416 | |
| 417 | static void |
| 418 | http_complete_write(evutil_socket_t fd, short what, void *arg) |
| 419 | { |
| 420 | struct bufferevent *bev = arg; |
| 421 | const char *http_request = "host\r\n" |
| 422 | "Connection: close\r\n" |
| 423 | "\r\n"; |
| 424 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 425 | } |
| 426 | |
| 427 | static struct bufferevent * |
| 428 | create_bev(struct event_base *base, int fd, int ssl_mask) |
| 429 | { |
| 430 | int flags = BEV_OPT_DEFER_CALLBACKS; |
| 431 | struct bufferevent *bev = NULL; |
| 432 | |
| 433 | if (!ssl_mask) { |
| 434 | bev = bufferevent_socket_new(base, fd, flags); |
| 435 | } else { |
| 436 | #ifdef EVENT__HAVE_OPENSSL |
| 437 | SSL *ssl = SSL_new(get_ssl_ctx()); |
| 438 | if (ssl_mask & HTTP_SSL_FILTER) { |
| 439 | struct bufferevent *underlying = |
| 440 | bufferevent_socket_new(base, fd, flags); |
| 441 | bev = bufferevent_openssl_filter_new( |
| 442 | base, underlying, ssl, BUFFEREVENT_SSL_CONNECTING, flags); |
| 443 | } else { |
| 444 | bev = bufferevent_openssl_socket_new( |
| 445 | base, fd, ssl, BUFFEREVENT_SSL_CONNECTING, flags); |
| 446 | } |
| 447 | bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); |
| 448 | #endif |
| 449 | } |
| 450 | |
| 451 | return bev; |
| 452 | } |
| 453 | |
| 454 | static void |
| 455 | http_basic_test_impl(void *arg, int ssl) |
| 456 | { |
| 457 | struct basic_test_data *data = arg; |
| 458 | struct timeval tv; |
| 459 | struct bufferevent *bev = NULL; |
| 460 | evutil_socket_t fd; |
| 461 | const char *http_request; |
| 462 | ev_uint16_t port = 0, port2 = 0; |
| 463 | int server_flags = ssl ? HTTP_BIND_SSL : 0; |
| 464 | struct evhttp *http = http_setup(&port, data->base, server_flags); |
| 465 | |
| 466 | exit_base = data->base; |
| 467 | test_ok = 0; |
| 468 | |
| 469 | /* bind to a second socket */ |
| 470 | if (http_bind(http, &port2, server_flags) == -1) { |
| 471 | fprintf(stdout, "FAILED (bind)\n"); |
| 472 | exit(1); |
| 473 | } |
| 474 | |
| 475 | fd = http_connect("127.0.0.1", port); |
| 476 | |
| 477 | /* Stupid thing to send a request */ |
| 478 | bev = create_bev(data->base, fd, ssl); |
| 479 | bufferevent_setcb(bev, http_readcb, http_writecb, |
| 480 | http_errorcb, data->base); |
| 481 | |
| 482 | /* first half of the http request */ |
| 483 | http_request = |
| 484 | "GET /test HTTP/1.1\r\n" |
| 485 | "Host: some"; |
| 486 | |
| 487 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 488 | evutil_timerclear(&tv); |
| 489 | tv.tv_usec = 100000; |
| 490 | event_base_once(data->base, |
| 491 | -1, EV_TIMEOUT, http_complete_write, bev, &tv); |
| 492 | |
| 493 | event_base_dispatch(data->base); |
| 494 | |
| 495 | tt_assert(test_ok == 3); |
| 496 | |
| 497 | /* connect to the second port */ |
| 498 | bufferevent_free(bev); |
| 499 | evutil_closesocket(fd); |
| 500 | |
| 501 | fd = http_connect("127.0.0.1", port2); |
| 502 | |
| 503 | /* Stupid thing to send a request */ |
| 504 | bev = create_bev(data->base, fd, ssl); |
| 505 | bufferevent_setcb(bev, http_readcb, http_writecb, |
| 506 | http_errorcb, data->base); |
| 507 | |
| 508 | http_request = |
| 509 | "GET /test HTTP/1.1\r\n" |
| 510 | "Host: somehost\r\n" |
| 511 | "Connection: close\r\n" |
| 512 | "\r\n"; |
| 513 | |
| 514 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 515 | |
| 516 | event_base_dispatch(data->base); |
| 517 | |
| 518 | tt_assert(test_ok == 5); |
| 519 | |
| 520 | /* Connect to the second port again. This time, send an absolute uri. */ |
| 521 | bufferevent_free(bev); |
| 522 | evutil_closesocket(fd); |
| 523 | |
| 524 | fd = http_connect("127.0.0.1", port2); |
| 525 | |
| 526 | /* Stupid thing to send a request */ |
| 527 | bev = create_bev(data->base, fd, ssl); |
| 528 | bufferevent_setcb(bev, http_readcb, http_writecb, |
| 529 | http_errorcb, data->base); |
| 530 | |
| 531 | http_request = |
| 532 | "GET http://somehost.net/test HTTP/1.1\r\n" |
| 533 | "Host: somehost\r\n" |
| 534 | "Connection: close\r\n" |
| 535 | "\r\n"; |
| 536 | |
| 537 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 538 | |
| 539 | event_base_dispatch(data->base); |
| 540 | |
| 541 | tt_assert(test_ok == 7); |
| 542 | |
| 543 | evhttp_free(http); |
| 544 | end: |
| 545 | if (bev) |
| 546 | bufferevent_free(bev); |
| 547 | } |
| 548 | static void http_basic_test(void *arg) |
| 549 | { return http_basic_test_impl(arg, 0); } |
| 550 | |
| 551 | |
| 552 | static void |
| 553 | http_delay_reply(evutil_socket_t fd, short what, void *arg) |
| 554 | { |
| 555 | struct evhttp_request *req = arg; |
| 556 | |
| 557 | evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL); |
| 558 | |
| 559 | ++test_ok; |
| 560 | } |
| 561 | |
| 562 | static void |
| 563 | http_delay_cb(struct evhttp_request *req, void *arg) |
| 564 | { |
| 565 | struct timeval tv; |
| 566 | evutil_timerclear(&tv); |
| 567 | tv.tv_sec = 0; |
| 568 | tv.tv_usec = 200 * 1000; |
| 569 | |
| 570 | event_base_once(arg, -1, EV_TIMEOUT, http_delay_reply, req, &tv); |
| 571 | } |
| 572 | |
| 573 | static void |
| 574 | http_badreq_cb(struct evhttp_request *req, void *arg) |
| 575 | { |
| 576 | struct evbuffer *buf = evbuffer_new(); |
| 577 | |
| 578 | evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/xml; charset=UTF-8"); |
| 579 | evbuffer_add_printf(buf, "Hello, %s!", "127.0.0.1"); |
| 580 | |
| 581 | evhttp_send_reply(req, HTTP_OK, "OK", buf); |
| 582 | evbuffer_free(buf); |
| 583 | } |
| 584 | |
| 585 | static void |
| 586 | http_badreq_errorcb(struct bufferevent *bev, short what, void *arg) |
| 587 | { |
| 588 | event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); |
| 589 | /* ignore */ |
| 590 | } |
| 591 | |
| 592 | static void |
| 593 | http_badreq_readcb(struct bufferevent *bev, void *arg) |
| 594 | { |
| 595 | const char *what = "Hello, 127.0.0.1"; |
| 596 | const char *bad_request = "400 Bad Request"; |
| 597 | |
| 598 | if (evbuffer_contains(bufferevent_get_input(bev), bad_request)) { |
| 599 | TT_FAIL(("%s:bad request detected", __func__)); |
| 600 | bufferevent_disable(bev, EV_READ); |
| 601 | event_base_loopexit(arg, NULL); |
| 602 | return; |
| 603 | } |
| 604 | |
| 605 | if (evbuffer_contains(bufferevent_get_input(bev), what)) { |
| 606 | struct evhttp_request *req = evhttp_request_new(NULL, NULL); |
| 607 | enum message_read_status done; |
| 608 | |
| 609 | /* req->kind = EVHTTP_RESPONSE; */ |
| 610 | done = evhttp_parse_firstline_(req, bufferevent_get_input(bev)); |
| 611 | if (done != ALL_DATA_READ) |
| 612 | goto out; |
| 613 | |
| 614 | done = evhttp_parse_headers_(req, bufferevent_get_input(bev)); |
| 615 | if (done != ALL_DATA_READ) |
| 616 | goto out; |
| 617 | |
| 618 | if (done == 1 && |
| 619 | evhttp_find_header(evhttp_request_get_input_headers(req), |
| 620 | "Content-Type") != NULL) |
| 621 | test_ok++; |
| 622 | |
| 623 | out: |
| 624 | evhttp_request_free(req); |
| 625 | evbuffer_drain(bufferevent_get_input(bev), evbuffer_get_length(bufferevent_get_input(bev))); |
| 626 | } |
| 627 | |
| 628 | shutdown(bufferevent_getfd(bev), EVUTIL_SHUT_WR); |
| 629 | } |
| 630 | |
| 631 | static void |
| 632 | http_badreq_successcb(evutil_socket_t fd, short what, void *arg) |
| 633 | { |
| 634 | event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); |
| 635 | event_base_loopexit(exit_base, NULL); |
| 636 | } |
| 637 | |
| 638 | static void |
| 639 | http_bad_request_test(void *arg) |
| 640 | { |
| 641 | struct basic_test_data *data = arg; |
| 642 | struct timeval tv; |
| 643 | struct bufferevent *bev = NULL; |
| 644 | evutil_socket_t fd = -1; |
| 645 | const char *http_request; |
| 646 | ev_uint16_t port=0, port2=0; |
| 647 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 648 | |
| 649 | test_ok = 0; |
| 650 | exit_base = data->base; |
| 651 | |
| 652 | /* bind to a second socket */ |
| 653 | if (http_bind(http, &port2, 0) == -1) |
| 654 | TT_DIE(("Bind socket failed")); |
| 655 | |
| 656 | /* NULL request test */ |
| 657 | fd = http_connect("127.0.0.1", port); |
| 658 | tt_int_op(fd, >=, 0); |
| 659 | |
| 660 | /* Stupid thing to send a request */ |
| 661 | bev = bufferevent_socket_new(data->base, fd, 0); |
| 662 | bufferevent_setcb(bev, http_badreq_readcb, http_writecb, |
| 663 | http_badreq_errorcb, data->base); |
| 664 | bufferevent_enable(bev, EV_READ); |
| 665 | |
| 666 | /* real NULL request */ |
| 667 | http_request = ""; |
| 668 | |
| 669 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 670 | |
| 671 | shutdown(fd, EVUTIL_SHUT_WR); |
| 672 | timerclear(&tv); |
| 673 | tv.tv_usec = 10000; |
| 674 | event_base_once(data->base, -1, EV_TIMEOUT, http_badreq_successcb, bev, &tv); |
| 675 | |
| 676 | event_base_dispatch(data->base); |
| 677 | |
| 678 | bufferevent_free(bev); |
| 679 | evutil_closesocket(fd); |
| 680 | |
| 681 | if (test_ok != 0) { |
| 682 | fprintf(stdout, "FAILED\n"); |
| 683 | exit(1); |
| 684 | } |
| 685 | |
| 686 | /* Second answer (BAD REQUEST) on connection close */ |
| 687 | |
| 688 | /* connect to the second port */ |
| 689 | fd = http_connect("127.0.0.1", port2); |
| 690 | |
| 691 | /* Stupid thing to send a request */ |
| 692 | bev = bufferevent_socket_new(data->base, fd, 0); |
| 693 | bufferevent_setcb(bev, http_badreq_readcb, http_writecb, |
| 694 | http_badreq_errorcb, data->base); |
| 695 | bufferevent_enable(bev, EV_READ); |
| 696 | |
| 697 | /* first half of the http request */ |
| 698 | http_request = |
| 699 | "GET /badrequest HTTP/1.0\r\n" \ |
| 700 | "Connection: Keep-Alive\r\n" \ |
| 701 | "\r\n"; |
| 702 | |
| 703 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 704 | |
| 705 | timerclear(&tv); |
| 706 | tv.tv_usec = 10000; |
| 707 | event_base_once(data->base, -1, EV_TIMEOUT, http_badreq_successcb, bev, &tv); |
| 708 | |
| 709 | event_base_dispatch(data->base); |
| 710 | |
| 711 | tt_int_op(test_ok, ==, 2); |
| 712 | |
| 713 | end: |
| 714 | evhttp_free(http); |
| 715 | if (bev) |
| 716 | bufferevent_free(bev); |
| 717 | if (fd >= 0) |
| 718 | evutil_closesocket(fd); |
| 719 | } |
| 720 | |
| 721 | static struct evhttp_connection *delayed_client; |
| 722 | |
| 723 | static void |
| 724 | http_large_delay_cb(struct evhttp_request *req, void *arg) |
| 725 | { |
| 726 | struct timeval tv; |
| 727 | evutil_timerclear(&tv); |
| 728 | tv.tv_usec = 500000; |
| 729 | |
| 730 | event_base_once(arg, -1, EV_TIMEOUT, http_delay_reply, req, &tv); |
| 731 | evhttp_connection_fail_(delayed_client, EVREQ_HTTP_EOF); |
| 732 | } |
| 733 | |
| 734 | /* |
| 735 | * HTTP DELETE test, just piggyback on the basic test |
| 736 | */ |
| 737 | |
| 738 | static void |
| 739 | http_delete_cb(struct evhttp_request *req, void *arg) |
| 740 | { |
| 741 | struct evbuffer *evb = evbuffer_new(); |
| 742 | int empty = evhttp_find_header(evhttp_request_get_input_headers(req), "Empty") != NULL; |
| 743 | |
| 744 | /* Expecting a DELETE request */ |
| 745 | if (evhttp_request_get_command(req) != EVHTTP_REQ_DELETE) { |
| 746 | fprintf(stdout, "FAILED (delete type)\n"); |
| 747 | exit(1); |
| 748 | } |
| 749 | |
| 750 | event_debug(("%s: called\n", __func__)); |
| 751 | evbuffer_add_printf(evb, BASIC_REQUEST_BODY); |
| 752 | |
| 753 | /* allow sending of an empty reply */ |
| 754 | evhttp_send_reply(req, HTTP_OK, "Everything is fine", |
| 755 | !empty ? evb : NULL); |
| 756 | |
| 757 | evbuffer_free(evb); |
| 758 | } |
| 759 | |
| 760 | static void |
| 761 | http_delete_test(void *arg) |
| 762 | { |
| 763 | struct basic_test_data *data = arg; |
| 764 | struct bufferevent *bev; |
| 765 | evutil_socket_t fd = -1; |
| 766 | const char *http_request; |
| 767 | ev_uint16_t port = 0; |
| 768 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 769 | |
| 770 | exit_base = data->base; |
| 771 | test_ok = 0; |
| 772 | |
| 773 | tt_assert(http); |
| 774 | fd = http_connect("127.0.0.1", port); |
| 775 | tt_int_op(fd, >=, 0); |
| 776 | |
| 777 | /* Stupid thing to send a request */ |
| 778 | bev = bufferevent_socket_new(data->base, fd, 0); |
| 779 | bufferevent_setcb(bev, http_readcb, http_writecb, |
| 780 | http_errorcb, data->base); |
| 781 | |
| 782 | http_request = |
| 783 | "DELETE /deleteit HTTP/1.1\r\n" |
| 784 | "Host: somehost\r\n" |
| 785 | "Connection: close\r\n" |
| 786 | "\r\n"; |
| 787 | |
| 788 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 789 | |
| 790 | event_base_dispatch(data->base); |
| 791 | |
| 792 | bufferevent_free(bev); |
| 793 | evutil_closesocket(fd); |
| 794 | fd = -1; |
| 795 | |
| 796 | evhttp_free(http); |
| 797 | |
| 798 | tt_int_op(test_ok, ==, 2); |
| 799 | end: |
| 800 | if (fd >= 0) |
| 801 | evutil_closesocket(fd); |
| 802 | } |
| 803 | |
| 804 | static void |
| 805 | http_sent_cb(struct evhttp_request *req, void *arg) |
| 806 | { |
| 807 | ev_uintptr_t val = (ev_uintptr_t)arg; |
| 808 | struct evbuffer *b; |
| 809 | |
| 810 | if (val != 0xDEADBEEF) { |
| 811 | fprintf(stdout, "FAILED on_complete_cb argument\n"); |
| 812 | exit(1); |
| 813 | } |
| 814 | |
| 815 | b = evhttp_request_get_output_buffer(req); |
| 816 | if (evbuffer_get_length(b) != 0) { |
| 817 | fprintf(stdout, "FAILED on_complete_cb output buffer not written\n"); |
| 818 | exit(1); |
| 819 | } |
| 820 | |
| 821 | event_debug(("%s: called\n", __func__)); |
| 822 | |
| 823 | ++test_ok; |
| 824 | } |
| 825 | |
| 826 | static void |
| 827 | http_on_complete_cb(struct evhttp_request *req, void *arg) |
| 828 | { |
| 829 | struct evbuffer *evb = evbuffer_new(); |
| 830 | |
| 831 | evhttp_request_set_on_complete_cb(req, http_sent_cb, (void *)0xDEADBEEF); |
| 832 | |
| 833 | event_debug(("%s: called\n", __func__)); |
| 834 | evbuffer_add_printf(evb, BASIC_REQUEST_BODY); |
| 835 | |
| 836 | /* allow sending of an empty reply */ |
| 837 | evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); |
| 838 | |
| 839 | evbuffer_free(evb); |
| 840 | |
| 841 | ++test_ok; |
| 842 | } |
| 843 | |
| 844 | static void |
| 845 | http_on_complete_test(void *arg) |
| 846 | { |
| 847 | struct basic_test_data *data = arg; |
| 848 | struct bufferevent *bev; |
| 849 | evutil_socket_t fd = -1; |
| 850 | const char *http_request; |
| 851 | ev_uint16_t port = 0; |
| 852 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 853 | |
| 854 | exit_base = data->base; |
| 855 | test_ok = 0; |
| 856 | |
| 857 | fd = http_connect("127.0.0.1", port); |
| 858 | tt_int_op(fd, >=, 0); |
| 859 | |
| 860 | /* Stupid thing to send a request */ |
| 861 | bev = bufferevent_socket_new(data->base, fd, 0); |
| 862 | bufferevent_setcb(bev, http_readcb, http_writecb, |
| 863 | http_errorcb, data->base); |
| 864 | |
| 865 | http_request = |
| 866 | "GET /oncomplete HTTP/1.1\r\n" |
| 867 | "Host: somehost\r\n" |
| 868 | "Connection: close\r\n" |
| 869 | "\r\n"; |
| 870 | |
| 871 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 872 | |
| 873 | event_base_dispatch(data->base); |
| 874 | |
| 875 | bufferevent_free(bev); |
| 876 | |
| 877 | evhttp_free(http); |
| 878 | |
| 879 | tt_int_op(test_ok, ==, 4); |
| 880 | end: |
| 881 | if (fd >= 0) |
| 882 | evutil_closesocket(fd); |
| 883 | } |
| 884 | |
| 885 | static void |
| 886 | http_allowed_methods_eventcb(struct bufferevent *bev, short what, void *arg) |
| 887 | { |
| 888 | char **output = arg; |
| 889 | if ((what & (BEV_EVENT_ERROR|BEV_EVENT_EOF))) { |
| 890 | char buf[4096]; |
| 891 | int n; |
| 892 | n = evbuffer_remove(bufferevent_get_input(bev), buf, |
| 893 | sizeof(buf)-1); |
| 894 | if (n >= 0) { |
| 895 | buf[n]='\0'; |
| 896 | if (*output) |
| 897 | free(*output); |
| 898 | *output = strdup(buf); |
| 899 | } |
| 900 | event_base_loopexit(exit_base, NULL); |
| 901 | } |
| 902 | } |
| 903 | |
| 904 | static void |
| 905 | http_allowed_methods_test(void *arg) |
| 906 | { |
| 907 | struct basic_test_data *data = arg; |
| 908 | struct bufferevent *bev1, *bev2, *bev3; |
| 909 | evutil_socket_t fd1=-1, fd2=-1, fd3=-1; |
| 910 | const char *http_request; |
| 911 | char *result1=NULL, *result2=NULL, *result3=NULL; |
| 912 | ev_uint16_t port = 0; |
| 913 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 914 | |
| 915 | exit_base = data->base; |
| 916 | test_ok = 0; |
| 917 | |
| 918 | fd1 = http_connect("127.0.0.1", port); |
| 919 | tt_int_op(fd1, >=, 0); |
| 920 | |
| 921 | /* GET is out; PATCH is in. */ |
| 922 | evhttp_set_allowed_methods(http, EVHTTP_REQ_PATCH); |
| 923 | |
| 924 | /* Stupid thing to send a request */ |
| 925 | bev1 = bufferevent_socket_new(data->base, fd1, 0); |
| 926 | bufferevent_enable(bev1, EV_READ|EV_WRITE); |
| 927 | bufferevent_setcb(bev1, NULL, NULL, |
| 928 | http_allowed_methods_eventcb, &result1); |
| 929 | |
| 930 | http_request = |
| 931 | "GET /index.html HTTP/1.1\r\n" |
| 932 | "Host: somehost\r\n" |
| 933 | "Connection: close\r\n" |
| 934 | "\r\n"; |
| 935 | |
| 936 | bufferevent_write(bev1, http_request, strlen(http_request)); |
| 937 | |
| 938 | event_base_dispatch(data->base); |
| 939 | |
| 940 | fd2 = http_connect("127.0.0.1", port); |
| 941 | tt_int_op(fd2, >=, 0); |
| 942 | |
| 943 | bev2 = bufferevent_socket_new(data->base, fd2, 0); |
| 944 | bufferevent_enable(bev2, EV_READ|EV_WRITE); |
| 945 | bufferevent_setcb(bev2, NULL, NULL, |
| 946 | http_allowed_methods_eventcb, &result2); |
| 947 | |
| 948 | http_request = |
| 949 | "PATCH /test HTTP/1.1\r\n" |
| 950 | "Host: somehost\r\n" |
| 951 | "Connection: close\r\n" |
| 952 | "\r\n"; |
| 953 | |
| 954 | bufferevent_write(bev2, http_request, strlen(http_request)); |
| 955 | |
| 956 | event_base_dispatch(data->base); |
| 957 | |
| 958 | fd3 = http_connect("127.0.0.1", port); |
| 959 | tt_int_op(fd3, >=, 0); |
| 960 | |
| 961 | bev3 = bufferevent_socket_new(data->base, fd3, 0); |
| 962 | bufferevent_enable(bev3, EV_READ|EV_WRITE); |
| 963 | bufferevent_setcb(bev3, NULL, NULL, |
| 964 | http_allowed_methods_eventcb, &result3); |
| 965 | |
| 966 | http_request = |
| 967 | "FLOOP /test HTTP/1.1\r\n" |
| 968 | "Host: somehost\r\n" |
| 969 | "Connection: close\r\n" |
| 970 | "\r\n"; |
| 971 | |
| 972 | bufferevent_write(bev3, http_request, strlen(http_request)); |
| 973 | |
| 974 | event_base_dispatch(data->base); |
| 975 | |
| 976 | bufferevent_free(bev1); |
| 977 | bufferevent_free(bev2); |
| 978 | bufferevent_free(bev3); |
| 979 | |
| 980 | evhttp_free(http); |
| 981 | |
| 982 | /* Method known but disallowed */ |
| 983 | tt_assert(result1); |
| 984 | tt_assert(!strncmp(result1, "HTTP/1.1 501 ", strlen("HTTP/1.1 501 "))); |
| 985 | |
| 986 | /* Method known and allowed */ |
| 987 | tt_assert(result2); |
| 988 | tt_assert(!strncmp(result2, "HTTP/1.1 200 ", strlen("HTTP/1.1 200 "))); |
| 989 | |
| 990 | /* Method unknown */ |
| 991 | tt_assert(result3); |
| 992 | tt_assert(!strncmp(result3, "HTTP/1.1 501 ", strlen("HTTP/1.1 501 "))); |
| 993 | |
| 994 | end: |
| 995 | if (result1) |
| 996 | free(result1); |
| 997 | if (result2) |
| 998 | free(result2); |
| 999 | if (result3) |
| 1000 | free(result3); |
| 1001 | if (fd1 >= 0) |
| 1002 | evutil_closesocket(fd1); |
| 1003 | if (fd2 >= 0) |
| 1004 | evutil_closesocket(fd2); |
| 1005 | if (fd3 >= 0) |
| 1006 | evutil_closesocket(fd3); |
| 1007 | } |
| 1008 | |
| 1009 | static void http_request_no_action_done(struct evhttp_request *, void *); |
| 1010 | static void http_request_done(struct evhttp_request *, void *); |
| 1011 | static void http_request_empty_done(struct evhttp_request *, void *); |
| 1012 | |
| 1013 | static void |
| 1014 | http_connection_test_(struct basic_test_data *data, int persistent, |
| 1015 | const char *address, struct evdns_base *dnsbase, int ipv6, int family, |
| 1016 | int ssl) |
| 1017 | { |
| 1018 | ev_uint16_t port = 0; |
| 1019 | struct evhttp_connection *evcon = NULL; |
| 1020 | struct evhttp_request *req = NULL; |
| 1021 | struct evhttp *http; |
| 1022 | |
| 1023 | int mask = 0; |
| 1024 | if (ipv6) |
| 1025 | mask |= HTTP_BIND_IPV6; |
| 1026 | if (ssl) |
| 1027 | mask |= HTTP_BIND_SSL; |
| 1028 | |
| 1029 | http = http_setup(&port, data->base, mask); |
| 1030 | |
| 1031 | test_ok = 0; |
| 1032 | if (!http && ipv6) { |
| 1033 | tt_skip(); |
| 1034 | } |
| 1035 | tt_assert(http); |
| 1036 | |
| 1037 | if (ssl) { |
| 1038 | #ifdef EVENT__HAVE_OPENSSL |
| 1039 | SSL *ssl = SSL_new(get_ssl_ctx()); |
| 1040 | struct bufferevent *bev = bufferevent_openssl_socket_new( |
| 1041 | data->base, -1, ssl, |
| 1042 | BUFFEREVENT_SSL_CONNECTING, BEV_OPT_DEFER_CALLBACKS); |
| 1043 | bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); |
| 1044 | |
| 1045 | evcon = evhttp_connection_base_bufferevent_new(data->base, dnsbase, bev, address, port); |
| 1046 | #else |
| 1047 | tt_skip(); |
| 1048 | #endif |
| 1049 | } else { |
| 1050 | evcon = evhttp_connection_base_new(data->base, dnsbase, address, port); |
| 1051 | } |
| 1052 | tt_assert(evcon); |
| 1053 | evhttp_connection_set_family(evcon, family); |
| 1054 | |
| 1055 | tt_assert(evhttp_connection_get_base(evcon) == data->base); |
| 1056 | |
| 1057 | exit_base = data->base; |
| 1058 | |
| 1059 | tt_assert(evhttp_connection_get_server(evcon) == NULL); |
| 1060 | |
| 1061 | /* |
| 1062 | * At this point, we want to schedule a request to the HTTP |
| 1063 | * server using our make request method. |
| 1064 | */ |
| 1065 | req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
| 1066 | |
| 1067 | /* Add the information that we care about */ |
| 1068 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 1069 | |
| 1070 | /* We give ownership of the request to the connection */ |
| 1071 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
| 1072 | fprintf(stdout, "FAILED\n"); |
| 1073 | exit(1); |
| 1074 | } |
| 1075 | |
| 1076 | event_base_dispatch(data->base); |
| 1077 | |
| 1078 | tt_assert(test_ok); |
| 1079 | |
| 1080 | /* try to make another request over the same connection */ |
| 1081 | test_ok = 0; |
| 1082 | |
| 1083 | req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
| 1084 | |
| 1085 | /* Add the information that we care about */ |
| 1086 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 1087 | |
| 1088 | /* |
| 1089 | * if our connections are not supposed to be persistent; request |
| 1090 | * a close from the server. |
| 1091 | */ |
| 1092 | if (!persistent) |
| 1093 | evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close"); |
| 1094 | |
| 1095 | /* We give ownership of the request to the connection */ |
| 1096 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
| 1097 | tt_abort_msg("couldn't make request"); |
| 1098 | } |
| 1099 | |
| 1100 | event_base_dispatch(data->base); |
| 1101 | |
| 1102 | /* make another request: request empty reply */ |
| 1103 | test_ok = 0; |
| 1104 | |
| 1105 | req = evhttp_request_new(http_request_empty_done, data->base); |
| 1106 | |
| 1107 | /* Add the information that we care about */ |
| 1108 | evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis"); |
| 1109 | |
| 1110 | /* We give ownership of the request to the connection */ |
| 1111 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
| 1112 | tt_abort_msg("Couldn't make request"); |
| 1113 | } |
| 1114 | |
| 1115 | event_base_dispatch(data->base); |
| 1116 | |
| 1117 | end: |
| 1118 | if (evcon) |
| 1119 | evhttp_connection_free(evcon); |
| 1120 | if (http) |
| 1121 | evhttp_free(http); |
| 1122 | } |
| 1123 | |
| 1124 | static void |
| 1125 | http_connection_test(void *arg) |
| 1126 | { |
| 1127 | http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_UNSPEC, 0); |
| 1128 | } |
| 1129 | static void |
| 1130 | http_persist_connection_test(void *arg) |
| 1131 | { |
| 1132 | http_connection_test_(arg, 1, "127.0.0.1", NULL, 0, AF_UNSPEC, 0); |
| 1133 | } |
| 1134 | |
| 1135 | static struct regress_dns_server_table search_table[] = { |
| 1136 | { "localhost", "A", "127.0.0.1", 0, 0 }, |
| 1137 | { NULL, NULL, NULL, 0, 0 } |
| 1138 | }; |
| 1139 | |
| 1140 | static void |
| 1141 | http_connection_async_test(void *arg) |
| 1142 | { |
| 1143 | struct basic_test_data *data = arg; |
| 1144 | ev_uint16_t port = 0; |
| 1145 | struct evhttp_connection *evcon = NULL; |
| 1146 | struct evhttp_request *req = NULL; |
| 1147 | struct evdns_base *dns_base = NULL; |
| 1148 | ev_uint16_t portnum = 0; |
| 1149 | char address[64]; |
| 1150 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 1151 | |
| 1152 | exit_base = data->base; |
| 1153 | tt_assert(regress_dnsserver(data->base, &portnum, search_table)); |
| 1154 | |
| 1155 | dns_base = evdns_base_new(data->base, 0/* init name servers */); |
| 1156 | tt_assert(dns_base); |
| 1157 | |
| 1158 | /* Add ourself as the only nameserver, and make sure we really are |
| 1159 | * the only nameserver. */ |
| 1160 | evutil_snprintf(address, sizeof(address), "127.0.0.1:%d", portnum); |
| 1161 | evdns_base_nameserver_ip_add(dns_base, address); |
| 1162 | |
| 1163 | test_ok = 0; |
| 1164 | |
| 1165 | evcon = evhttp_connection_base_new(data->base, dns_base, "127.0.0.1", port); |
| 1166 | tt_assert(evcon); |
| 1167 | |
| 1168 | /* |
| 1169 | * At this point, we want to schedule a request to the HTTP |
| 1170 | * server using our make request method. |
| 1171 | */ |
| 1172 | |
| 1173 | req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
| 1174 | |
| 1175 | /* Add the information that we care about */ |
| 1176 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 1177 | |
| 1178 | /* We give ownership of the request to the connection */ |
| 1179 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
| 1180 | fprintf(stdout, "FAILED\n"); |
| 1181 | exit(1); |
| 1182 | } |
| 1183 | |
| 1184 | event_base_dispatch(data->base); |
| 1185 | |
| 1186 | tt_assert(test_ok); |
| 1187 | |
| 1188 | /* try to make another request over the same connection */ |
| 1189 | test_ok = 0; |
| 1190 | |
| 1191 | req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
| 1192 | |
| 1193 | /* Add the information that we care about */ |
| 1194 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 1195 | |
| 1196 | /* |
| 1197 | * if our connections are not supposed to be persistent; request |
| 1198 | * a close from the server. |
| 1199 | */ |
| 1200 | evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close"); |
| 1201 | |
| 1202 | /* We give ownership of the request to the connection */ |
| 1203 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
| 1204 | tt_abort_msg("couldn't make request"); |
| 1205 | } |
| 1206 | |
| 1207 | event_base_dispatch(data->base); |
| 1208 | |
| 1209 | /* make another request: request empty reply */ |
| 1210 | test_ok = 0; |
| 1211 | |
| 1212 | req = evhttp_request_new(http_request_empty_done, data->base); |
| 1213 | |
| 1214 | /* Add the information that we care about */ |
| 1215 | evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis"); |
| 1216 | |
| 1217 | /* We give ownership of the request to the connection */ |
| 1218 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
| 1219 | tt_abort_msg("Couldn't make request"); |
| 1220 | } |
| 1221 | |
| 1222 | event_base_dispatch(data->base); |
| 1223 | |
| 1224 | end: |
| 1225 | if (evcon) |
| 1226 | evhttp_connection_free(evcon); |
| 1227 | if (http) |
| 1228 | evhttp_free(http); |
| 1229 | if (dns_base) |
| 1230 | evdns_base_free(dns_base, 0); |
| 1231 | regress_clean_dnsserver(); |
| 1232 | } |
| 1233 | |
| 1234 | static void |
| 1235 | http_autofree_connection_test(void *arg) |
| 1236 | { |
| 1237 | struct basic_test_data *data = arg; |
| 1238 | ev_uint16_t port = 0; |
| 1239 | struct evhttp_connection *evcon = NULL; |
| 1240 | struct evhttp_request *req[2] = { NULL }; |
| 1241 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 1242 | |
| 1243 | test_ok = 0; |
| 1244 | |
| 1245 | evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
| 1246 | tt_assert(evcon); |
| 1247 | |
| 1248 | /* |
| 1249 | * At this point, we want to schedule two request to the HTTP |
| 1250 | * server using our make request method. |
| 1251 | */ |
| 1252 | req[0] = evhttp_request_new(http_request_empty_done, data->base); |
| 1253 | req[1] = evhttp_request_new(http_request_empty_done, data->base); |
| 1254 | |
| 1255 | /* Add the information that we care about */ |
| 1256 | evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Host", "somehost"); |
| 1257 | evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Connection", "close"); |
| 1258 | evhttp_add_header(evhttp_request_get_output_headers(req[0]), "Empty", "itis"); |
| 1259 | evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Host", "somehost"); |
| 1260 | evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Connection", "close"); |
| 1261 | evhttp_add_header(evhttp_request_get_output_headers(req[1]), "Empty", "itis"); |
| 1262 | |
| 1263 | /* We give ownership of the request to the connection */ |
| 1264 | if (evhttp_make_request(evcon, req[0], EVHTTP_REQ_GET, "/test") == -1) { |
| 1265 | tt_abort_msg("couldn't make request"); |
| 1266 | } |
| 1267 | if (evhttp_make_request(evcon, req[1], EVHTTP_REQ_GET, "/test") == -1) { |
| 1268 | tt_abort_msg("couldn't make request"); |
| 1269 | } |
| 1270 | |
| 1271 | /* |
| 1272 | * Tell libevent to free the connection when the request completes |
| 1273 | * We then set the evcon pointer to NULL since we don't want to free it |
| 1274 | * when this function ends. |
| 1275 | */ |
| 1276 | evhttp_connection_free_on_completion(evcon); |
| 1277 | evcon = NULL; |
| 1278 | |
| 1279 | event_base_dispatch(data->base); |
| 1280 | |
| 1281 | /* at this point, the http server should have no connection */ |
| 1282 | tt_assert(TAILQ_FIRST(&http->connections) == NULL); |
| 1283 | |
| 1284 | end: |
| 1285 | if (evcon) |
| 1286 | evhttp_connection_free(evcon); |
| 1287 | if (http) |
| 1288 | evhttp_free(http); |
| 1289 | } |
| 1290 | |
| 1291 | static void |
| 1292 | http_request_never_call(struct evhttp_request *req, void *arg) |
| 1293 | { |
| 1294 | fprintf(stdout, "FAILED\n"); |
| 1295 | exit(1); |
| 1296 | } |
| 1297 | static void |
| 1298 | http_failed_request_done(struct evhttp_request *req, void *arg) |
| 1299 | { |
| 1300 | tt_assert(!req); |
| 1301 | end: |
| 1302 | event_base_loopexit(arg, NULL); |
| 1303 | } |
| 1304 | #ifndef _WIN32 |
| 1305 | static void |
| 1306 | http_timed_out_request_done(struct evhttp_request *req, void *arg) |
| 1307 | { |
| 1308 | tt_assert(req); |
| 1309 | tt_int_op(evhttp_request_get_response_code(req), !=, HTTP_OK); |
| 1310 | end: |
| 1311 | event_base_loopexit(arg, NULL); |
| 1312 | } |
| 1313 | #endif |
| 1314 | |
| 1315 | static void |
| 1316 | http_request_error_cb_with_cancel(enum evhttp_request_error error, void *arg) |
| 1317 | { |
| 1318 | if (error != EVREQ_HTTP_REQUEST_CANCEL) { |
| 1319 | fprintf(stderr, "FAILED\n"); |
| 1320 | exit(1); |
| 1321 | } |
| 1322 | test_ok = 1; |
| 1323 | |
| 1324 | { |
| 1325 | struct timeval tv; |
| 1326 | evutil_timerclear(&tv); |
| 1327 | tv.tv_sec = 0; |
| 1328 | tv.tv_usec = 500 * 1000; |
| 1329 | event_base_loopexit(exit_base, &tv); |
| 1330 | } |
| 1331 | } |
| 1332 | static void |
| 1333 | http_do_cancel(evutil_socket_t fd, short what, void *arg) |
| 1334 | { |
| 1335 | struct evhttp_request *req = arg; |
| 1336 | evhttp_cancel_request(req); |
| 1337 | ++test_ok; |
| 1338 | } |
| 1339 | static void |
| 1340 | http_no_write(struct evbuffer *buffer, const struct evbuffer_cb_info *info, void *arg) |
| 1341 | { |
| 1342 | fprintf(stdout, "FAILED\n"); |
| 1343 | exit(1); |
| 1344 | } |
| 1345 | static void |
| 1346 | http_free_evcons(struct evhttp_connection **evcons) |
| 1347 | { |
| 1348 | struct evhttp_connection *evcon, **orig = evcons; |
| 1349 | |
| 1350 | if (!evcons) |
| 1351 | return; |
| 1352 | |
| 1353 | while ((evcon = *evcons++)) { |
| 1354 | evhttp_connection_free(evcon); |
| 1355 | } |
| 1356 | free(orig); |
| 1357 | } |
| 1358 | /** fill the backlog to force server drop packages for timeouts */ |
| 1359 | static struct evhttp_connection ** |
| 1360 | http_fill_backlog(struct event_base *base, int port) |
| 1361 | { |
| 1362 | #define BACKLOG_SIZE 256 |
| 1363 | struct evhttp_connection **evcon = malloc(sizeof(*evcon) * (BACKLOG_SIZE + 1)); |
| 1364 | int i; |
| 1365 | |
| 1366 | for (i = 0; i < BACKLOG_SIZE; ++i) { |
| 1367 | struct evhttp_request *req; |
| 1368 | |
| 1369 | evcon[i] = evhttp_connection_base_new(base, NULL, "127.0.0.1", port); |
| 1370 | tt_assert(evcon[i]); |
| 1371 | evhttp_connection_set_timeout(evcon[i], 5); |
| 1372 | |
| 1373 | req = evhttp_request_new(http_request_never_call, NULL); |
| 1374 | tt_assert(req); |
| 1375 | tt_int_op(evhttp_make_request(evcon[i], req, EVHTTP_REQ_GET, "/delay"), !=, -1); |
| 1376 | } |
| 1377 | evcon[i] = NULL; |
| 1378 | |
| 1379 | return evcon; |
| 1380 | end: |
| 1381 | fprintf(stderr, "Couldn't fill the backlog"); |
| 1382 | return NULL; |
| 1383 | } |
| 1384 | |
| 1385 | enum http_cancel_test_type { |
| 1386 | BASIC = 1, |
| 1387 | BY_HOST = 2, |
| 1388 | NO_NS = 4, |
| 1389 | INACTIVE_SERVER = 8, |
| 1390 | SERVER_TIMEOUT = 16, |
| 1391 | NS_TIMEOUT = 32, |
| 1392 | }; |
| 1393 | static struct evhttp_request * |
| 1394 | http_cancel_test_bad_request_new(enum http_cancel_test_type type, |
| 1395 | struct event_base *base) |
| 1396 | { |
| 1397 | #ifndef _WIN32 |
| 1398 | if (!(type & NO_NS) && (type & SERVER_TIMEOUT)) |
| 1399 | return evhttp_request_new(http_timed_out_request_done, base); |
| 1400 | else |
| 1401 | #endif |
| 1402 | if ((type & INACTIVE_SERVER) || (type & NO_NS)) |
| 1403 | return evhttp_request_new(http_failed_request_done, base); |
| 1404 | else |
| 1405 | return NULL; |
| 1406 | } |
| 1407 | static void |
| 1408 | http_cancel_test(void *arg) |
| 1409 | { |
| 1410 | struct basic_test_data *data = arg; |
| 1411 | ev_uint16_t port = 0; |
| 1412 | struct evhttp_connection *evcon = NULL; |
| 1413 | struct evhttp_request *req = NULL; |
| 1414 | struct bufferevent *bufev = NULL; |
| 1415 | struct timeval tv; |
| 1416 | struct evdns_base *dns_base = NULL; |
| 1417 | ev_uint16_t portnum = 0; |
| 1418 | char address[64]; |
| 1419 | struct evhttp *inactive_http = NULL; |
| 1420 | struct event_base *inactive_base = NULL; |
| 1421 | struct evhttp_connection **evcons = NULL; |
| 1422 | struct event_base *base_to_fill = data->base; |
| 1423 | |
| 1424 | enum http_cancel_test_type type = |
| 1425 | (enum http_cancel_test_type)data->setup_data; |
| 1426 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 1427 | |
| 1428 | if (type & BY_HOST) { |
| 1429 | const char *timeout = (type & NS_TIMEOUT) ? "6" : "3"; |
| 1430 | |
| 1431 | tt_assert(regress_dnsserver(data->base, &portnum, search_table)); |
| 1432 | |
| 1433 | dns_base = evdns_base_new(data->base, 0/* init name servers */); |
| 1434 | tt_assert(dns_base); |
| 1435 | |
| 1436 | /** XXX: Hack the port to make timeout after resolving */ |
| 1437 | if (type & NO_NS) |
| 1438 | ++portnum; |
| 1439 | |
| 1440 | evutil_snprintf(address, sizeof(address), "127.0.0.1:%d", portnum); |
| 1441 | evdns_base_nameserver_ip_add(dns_base, address); |
| 1442 | |
| 1443 | evdns_base_set_option(dns_base, "timeout:", timeout); |
| 1444 | evdns_base_set_option(dns_base, "initial-probe-timeout:", timeout); |
| 1445 | evdns_base_set_option(dns_base, "attempts:", "1"); |
| 1446 | } |
| 1447 | |
| 1448 | exit_base = data->base; |
| 1449 | |
| 1450 | test_ok = 0; |
| 1451 | |
| 1452 | if (type & INACTIVE_SERVER) { |
| 1453 | port = 0; |
| 1454 | inactive_base = event_base_new(); |
| 1455 | inactive_http = http_setup(&port, inactive_base, 0); |
| 1456 | |
| 1457 | base_to_fill = inactive_base; |
| 1458 | } |
| 1459 | |
| 1460 | if (type & SERVER_TIMEOUT) |
| 1461 | evcons = http_fill_backlog(base_to_fill, port); |
| 1462 | |
| 1463 | evcon = evhttp_connection_base_new( |
| 1464 | data->base, dns_base, |
| 1465 | type & BY_HOST ? "localhost" : "127.0.0.1", |
| 1466 | port); |
| 1467 | if (type & INACTIVE_SERVER) |
| 1468 | evhttp_connection_set_timeout(evcon, 5); |
| 1469 | tt_assert(evcon); |
| 1470 | |
| 1471 | bufev = evhttp_connection_get_bufferevent(evcon); |
| 1472 | /* Guarantee that we stack in connect() not after waiting EV_READ after |
| 1473 | * write() */ |
| 1474 | if (type & SERVER_TIMEOUT) |
| 1475 | evbuffer_add_cb(bufferevent_get_output(bufev), http_no_write, NULL); |
| 1476 | |
| 1477 | /* |
| 1478 | * At this point, we want to schedule a request to the HTTP |
| 1479 | * server using our make request method. |
| 1480 | */ |
| 1481 | |
| 1482 | req = evhttp_request_new(http_request_never_call, NULL); |
| 1483 | evhttp_request_set_error_cb(req, http_request_error_cb_with_cancel); |
| 1484 | |
| 1485 | /* Add the information that we care about */ |
| 1486 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 1487 | |
| 1488 | /* We give ownership of the request to the connection */ |
| 1489 | tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/delay"), |
| 1490 | !=, -1); |
| 1491 | |
| 1492 | evutil_timerclear(&tv); |
| 1493 | tv.tv_sec = 0; |
| 1494 | tv.tv_usec = 100 * 1000; |
| 1495 | |
| 1496 | event_base_once(data->base, -1, EV_TIMEOUT, http_do_cancel, req, &tv); |
| 1497 | |
| 1498 | event_base_dispatch(data->base); |
| 1499 | |
| 1500 | if (type & NO_NS || type & INACTIVE_SERVER) |
| 1501 | tt_int_op(test_ok, ==, 2); /** no servers responses */ |
| 1502 | else |
| 1503 | tt_int_op(test_ok, ==, 3); |
| 1504 | |
| 1505 | /* try to make another request over the same connection */ |
| 1506 | test_ok = 0; |
| 1507 | |
| 1508 | http_free_evcons(evcons); |
| 1509 | if (type & SERVER_TIMEOUT) |
| 1510 | evcons = http_fill_backlog(base_to_fill, port); |
| 1511 | |
| 1512 | req = http_cancel_test_bad_request_new(type, data->base); |
| 1513 | if (!req) |
| 1514 | req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
| 1515 | |
| 1516 | /* Add the information that we care about */ |
| 1517 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 1518 | |
| 1519 | /* We give ownership of the request to the connection */ |
| 1520 | tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test"), |
| 1521 | !=, -1); |
| 1522 | |
| 1523 | event_base_dispatch(data->base); |
| 1524 | |
| 1525 | /* make another request: request empty reply */ |
| 1526 | test_ok = 0; |
| 1527 | |
| 1528 | http_free_evcons(evcons); |
| 1529 | if (type & SERVER_TIMEOUT) |
| 1530 | evcons = http_fill_backlog(base_to_fill, port); |
| 1531 | |
| 1532 | req = http_cancel_test_bad_request_new(type, data->base); |
| 1533 | if (!req) |
| 1534 | req = evhttp_request_new(http_request_empty_done, data->base); |
| 1535 | |
| 1536 | /* Add the information that we care about */ |
| 1537 | evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis"); |
| 1538 | |
| 1539 | /* We give ownership of the request to the connection */ |
| 1540 | tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test"), |
| 1541 | !=, -1); |
| 1542 | |
| 1543 | event_base_dispatch(data->base); |
| 1544 | |
| 1545 | end: |
| 1546 | http_free_evcons(evcons); |
| 1547 | if (bufev) |
| 1548 | evbuffer_remove_cb(bufferevent_get_output(bufev), http_no_write, NULL); |
| 1549 | if (evcon) |
| 1550 | evhttp_connection_free(evcon); |
| 1551 | if (http) |
| 1552 | evhttp_free(http); |
| 1553 | if (dns_base) |
| 1554 | evdns_base_free(dns_base, 0); |
| 1555 | regress_clean_dnsserver(); |
| 1556 | if (inactive_http) |
| 1557 | evhttp_free(inactive_http); |
| 1558 | if (inactive_base) |
| 1559 | event_base_free(inactive_base); |
| 1560 | } |
| 1561 | |
| 1562 | static void |
| 1563 | http_request_no_action_done(struct evhttp_request *req, void *arg) |
| 1564 | { |
| 1565 | EVUTIL_ASSERT(exit_base); |
| 1566 | event_base_loopexit(exit_base, NULL); |
| 1567 | } |
| 1568 | |
| 1569 | static void |
| 1570 | http_request_done(struct evhttp_request *req, void *arg) |
| 1571 | { |
| 1572 | const char *what = arg; |
| 1573 | |
| 1574 | if (!req) { |
| 1575 | fprintf(stderr, "FAILED\n"); |
| 1576 | exit(1); |
| 1577 | } |
| 1578 | |
| 1579 | if (evhttp_request_get_response_code(req) != HTTP_OK) { |
| 1580 | fprintf(stderr, "FAILED\n"); |
| 1581 | exit(1); |
| 1582 | } |
| 1583 | |
| 1584 | if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) { |
| 1585 | fprintf(stderr, "FAILED\n"); |
| 1586 | exit(1); |
| 1587 | } |
| 1588 | |
| 1589 | if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) { |
| 1590 | fprintf(stderr, "FAILED\n"); |
| 1591 | exit(1); |
| 1592 | } |
| 1593 | |
| 1594 | if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) { |
| 1595 | fprintf(stderr, "FAILED\n"); |
| 1596 | exit(1); |
| 1597 | } |
| 1598 | |
| 1599 | test_ok = 1; |
| 1600 | EVUTIL_ASSERT(exit_base); |
| 1601 | event_base_loopexit(exit_base, NULL); |
| 1602 | } |
| 1603 | |
| 1604 | static void |
| 1605 | http_request_expect_error(struct evhttp_request *req, void *arg) |
| 1606 | { |
| 1607 | if (evhttp_request_get_response_code(req) == HTTP_OK) { |
| 1608 | fprintf(stderr, "FAILED\n"); |
| 1609 | exit(1); |
| 1610 | } |
| 1611 | |
| 1612 | test_ok = 1; |
| 1613 | EVUTIL_ASSERT(arg); |
| 1614 | event_base_loopexit(arg, NULL); |
| 1615 | } |
| 1616 | |
| 1617 | /* test virtual hosts */ |
| 1618 | static void |
| 1619 | http_virtual_host_test(void *arg) |
| 1620 | { |
| 1621 | struct basic_test_data *data = arg; |
| 1622 | ev_uint16_t port = 0; |
| 1623 | struct evhttp_connection *evcon = NULL; |
| 1624 | struct evhttp_request *req = NULL; |
| 1625 | struct evhttp *second = NULL, *third = NULL; |
| 1626 | evutil_socket_t fd; |
| 1627 | struct bufferevent *bev; |
| 1628 | const char *http_request; |
| 1629 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 1630 | |
| 1631 | exit_base = data->base; |
| 1632 | |
| 1633 | /* virtual host */ |
| 1634 | second = evhttp_new(NULL); |
| 1635 | evhttp_set_cb(second, "/funnybunny", http_basic_cb, http); |
| 1636 | third = evhttp_new(NULL); |
| 1637 | evhttp_set_cb(third, "/blackcoffee", http_basic_cb, http); |
| 1638 | |
| 1639 | if (evhttp_add_virtual_host(http, "foo.com", second) == -1) { |
| 1640 | tt_abort_msg("Couldn't add vhost"); |
| 1641 | } |
| 1642 | |
| 1643 | if (evhttp_add_virtual_host(http, "bar.*.foo.com", third) == -1) { |
| 1644 | tt_abort_msg("Couldn't add wildcarded vhost"); |
| 1645 | } |
| 1646 | |
| 1647 | /* add some aliases to the vhosts */ |
| 1648 | tt_assert(evhttp_add_server_alias(second, "manolito.info") == 0); |
| 1649 | tt_assert(evhttp_add_server_alias(third, "bonkers.org") == 0); |
| 1650 | |
| 1651 | evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
| 1652 | tt_assert(evcon); |
| 1653 | |
| 1654 | /* make a request with a different host and expect an error */ |
| 1655 | req = evhttp_request_new(http_request_expect_error, data->base); |
| 1656 | |
| 1657 | /* Add the information that we care about */ |
| 1658 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 1659 | |
| 1660 | /* We give ownership of the request to the connection */ |
| 1661 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
| 1662 | "/funnybunny") == -1) { |
| 1663 | tt_abort_msg("Couldn't make request"); |
| 1664 | } |
| 1665 | |
| 1666 | event_base_dispatch(data->base); |
| 1667 | |
| 1668 | tt_assert(test_ok == 1); |
| 1669 | |
| 1670 | test_ok = 0; |
| 1671 | |
| 1672 | /* make a request with the right host and expect a response */ |
| 1673 | req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
| 1674 | |
| 1675 | /* Add the information that we care about */ |
| 1676 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "foo.com"); |
| 1677 | |
| 1678 | /* We give ownership of the request to the connection */ |
| 1679 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
| 1680 | "/funnybunny") == -1) { |
| 1681 | fprintf(stdout, "FAILED\n"); |
| 1682 | exit(1); |
| 1683 | } |
| 1684 | |
| 1685 | event_base_dispatch(data->base); |
| 1686 | |
| 1687 | tt_assert(test_ok == 1); |
| 1688 | |
| 1689 | test_ok = 0; |
| 1690 | |
| 1691 | /* make a request with the right host and expect a response */ |
| 1692 | req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
| 1693 | |
| 1694 | /* Add the information that we care about */ |
| 1695 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "bar.magic.foo.com"); |
| 1696 | |
| 1697 | /* We give ownership of the request to the connection */ |
| 1698 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
| 1699 | "/blackcoffee") == -1) { |
| 1700 | tt_abort_msg("Couldn't make request"); |
| 1701 | } |
| 1702 | |
| 1703 | event_base_dispatch(data->base); |
| 1704 | |
| 1705 | tt_assert(test_ok == 1) |
| 1706 | |
| 1707 | test_ok = 0; |
| 1708 | |
| 1709 | /* make a request with the right host and expect a response */ |
| 1710 | req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
| 1711 | |
| 1712 | /* Add the information that we care about */ |
| 1713 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "manolito.info"); |
| 1714 | |
| 1715 | /* We give ownership of the request to the connection */ |
| 1716 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
| 1717 | "/funnybunny") == -1) { |
| 1718 | tt_abort_msg("Couldn't make request"); |
| 1719 | } |
| 1720 | |
| 1721 | event_base_dispatch(data->base); |
| 1722 | |
| 1723 | tt_assert(test_ok == 1) |
| 1724 | |
| 1725 | test_ok = 0; |
| 1726 | |
| 1727 | /* make a request with the right host and expect a response */ |
| 1728 | req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
| 1729 | |
| 1730 | /* Add the Host header. This time with the optional port. */ |
| 1731 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "bonkers.org:8000"); |
| 1732 | |
| 1733 | /* We give ownership of the request to the connection */ |
| 1734 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
| 1735 | "/blackcoffee") == -1) { |
| 1736 | tt_abort_msg("Couldn't make request"); |
| 1737 | } |
| 1738 | |
| 1739 | event_base_dispatch(data->base); |
| 1740 | |
| 1741 | tt_assert(test_ok == 1) |
| 1742 | |
| 1743 | test_ok = 0; |
| 1744 | |
| 1745 | /* Now make a raw request with an absolute URI. */ |
| 1746 | fd = http_connect("127.0.0.1", port); |
| 1747 | |
| 1748 | /* Stupid thing to send a request */ |
| 1749 | bev = bufferevent_socket_new(data->base, fd, 0); |
| 1750 | bufferevent_setcb(bev, http_readcb, http_writecb, |
| 1751 | http_errorcb, NULL); |
| 1752 | |
| 1753 | /* The host in the URI should override the Host: header */ |
| 1754 | http_request = |
| 1755 | "GET http://manolito.info/funnybunny HTTP/1.1\r\n" |
| 1756 | "Host: somehost\r\n" |
| 1757 | "Connection: close\r\n" |
| 1758 | "\r\n"; |
| 1759 | |
| 1760 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 1761 | |
| 1762 | event_base_dispatch(data->base); |
| 1763 | |
| 1764 | tt_int_op(test_ok, ==, 2); |
| 1765 | |
| 1766 | bufferevent_free(bev); |
| 1767 | evutil_closesocket(fd); |
| 1768 | |
| 1769 | end: |
| 1770 | if (evcon) |
| 1771 | evhttp_connection_free(evcon); |
| 1772 | if (http) |
| 1773 | evhttp_free(http); |
| 1774 | } |
| 1775 | |
| 1776 | |
| 1777 | /* test date header and content length */ |
| 1778 | |
| 1779 | static void |
| 1780 | http_request_empty_done(struct evhttp_request *req, void *arg) |
| 1781 | { |
| 1782 | if (!req) { |
| 1783 | fprintf(stderr, "FAILED\n"); |
| 1784 | exit(1); |
| 1785 | } |
| 1786 | |
| 1787 | if (evhttp_request_get_response_code(req) != HTTP_OK) { |
| 1788 | fprintf(stderr, "FAILED\n"); |
| 1789 | exit(1); |
| 1790 | } |
| 1791 | |
| 1792 | if (evhttp_find_header(evhttp_request_get_input_headers(req), "Date") == NULL) { |
| 1793 | fprintf(stderr, "FAILED\n"); |
| 1794 | exit(1); |
| 1795 | } |
| 1796 | |
| 1797 | |
| 1798 | if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Length") == NULL) { |
| 1799 | fprintf(stderr, "FAILED\n"); |
| 1800 | exit(1); |
| 1801 | } |
| 1802 | |
| 1803 | if (strcmp(evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Length"), |
| 1804 | "0")) { |
| 1805 | fprintf(stderr, "FAILED\n"); |
| 1806 | exit(1); |
| 1807 | } |
| 1808 | |
| 1809 | if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 0) { |
| 1810 | fprintf(stderr, "FAILED\n"); |
| 1811 | exit(1); |
| 1812 | } |
| 1813 | |
| 1814 | test_ok = 1; |
| 1815 | EVUTIL_ASSERT(arg); |
| 1816 | event_base_loopexit(arg, NULL); |
| 1817 | } |
| 1818 | |
| 1819 | /* |
| 1820 | * HTTP DISPATCHER test |
| 1821 | */ |
| 1822 | |
| 1823 | void |
| 1824 | http_dispatcher_cb(struct evhttp_request *req, void *arg) |
| 1825 | { |
| 1826 | |
| 1827 | struct evbuffer *evb = evbuffer_new(); |
| 1828 | event_debug(("%s: called\n", __func__)); |
| 1829 | evbuffer_add_printf(evb, "DISPATCHER_TEST"); |
| 1830 | |
| 1831 | evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); |
| 1832 | |
| 1833 | evbuffer_free(evb); |
| 1834 | } |
| 1835 | |
| 1836 | static void |
| 1837 | http_dispatcher_test_done(struct evhttp_request *req, void *arg) |
| 1838 | { |
| 1839 | struct event_base *base = arg; |
| 1840 | const char *what = "DISPATCHER_TEST"; |
| 1841 | |
| 1842 | if (!req) { |
| 1843 | fprintf(stderr, "FAILED\n"); |
| 1844 | exit(1); |
| 1845 | } |
| 1846 | |
| 1847 | if (evhttp_request_get_response_code(req) != HTTP_OK) { |
| 1848 | fprintf(stderr, "FAILED\n"); |
| 1849 | exit(1); |
| 1850 | } |
| 1851 | |
| 1852 | if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) { |
| 1853 | fprintf(stderr, "FAILED (content type)\n"); |
| 1854 | exit(1); |
| 1855 | } |
| 1856 | |
| 1857 | if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) { |
| 1858 | fprintf(stderr, "FAILED (length %lu vs %lu)\n", |
| 1859 | (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what)); |
| 1860 | exit(1); |
| 1861 | } |
| 1862 | |
| 1863 | if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) { |
| 1864 | fprintf(stderr, "FAILED (data)\n"); |
| 1865 | exit(1); |
| 1866 | } |
| 1867 | |
| 1868 | test_ok = 1; |
| 1869 | event_base_loopexit(base, NULL); |
| 1870 | } |
| 1871 | |
| 1872 | static void |
| 1873 | http_dispatcher_test(void *arg) |
| 1874 | { |
| 1875 | struct basic_test_data *data = arg; |
| 1876 | ev_uint16_t port = 0; |
| 1877 | struct evhttp_connection *evcon = NULL; |
| 1878 | struct evhttp_request *req = NULL; |
| 1879 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 1880 | |
| 1881 | test_ok = 0; |
| 1882 | |
| 1883 | evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
| 1884 | tt_assert(evcon); |
| 1885 | |
| 1886 | /* also bind to local host */ |
| 1887 | evhttp_connection_set_local_address(evcon, "127.0.0.1"); |
| 1888 | |
| 1889 | /* |
| 1890 | * At this point, we want to schedule an HTTP GET request |
| 1891 | * server using our make request method. |
| 1892 | */ |
| 1893 | |
| 1894 | req = evhttp_request_new(http_dispatcher_test_done, data->base); |
| 1895 | tt_assert(req); |
| 1896 | |
| 1897 | /* Add the information that we care about */ |
| 1898 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 1899 | |
| 1900 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) { |
| 1901 | tt_abort_msg("Couldn't make request"); |
| 1902 | } |
| 1903 | |
| 1904 | event_base_dispatch(data->base); |
| 1905 | |
| 1906 | end: |
| 1907 | if (evcon) |
| 1908 | evhttp_connection_free(evcon); |
| 1909 | if (http) |
| 1910 | evhttp_free(http); |
| 1911 | } |
| 1912 | |
| 1913 | /* |
| 1914 | * HTTP POST test. |
| 1915 | */ |
| 1916 | |
| 1917 | void http_postrequest_done(struct evhttp_request *, void *); |
| 1918 | |
| 1919 | #define POST_DATA "Okay. Not really printf" |
| 1920 | |
| 1921 | static void |
| 1922 | http_post_test(void *arg) |
| 1923 | { |
| 1924 | struct basic_test_data *data = arg; |
| 1925 | ev_uint16_t port = 0; |
| 1926 | struct evhttp_connection *evcon = NULL; |
| 1927 | struct evhttp_request *req = NULL; |
| 1928 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 1929 | |
| 1930 | test_ok = 0; |
| 1931 | |
| 1932 | evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
| 1933 | tt_assert(evcon); |
| 1934 | |
| 1935 | /* |
| 1936 | * At this point, we want to schedule an HTTP POST request |
| 1937 | * server using our make request method. |
| 1938 | */ |
| 1939 | |
| 1940 | req = evhttp_request_new(http_postrequest_done, data->base); |
| 1941 | tt_assert(req); |
| 1942 | |
| 1943 | /* Add the information that we care about */ |
| 1944 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 1945 | evbuffer_add_printf(evhttp_request_get_output_buffer(req), POST_DATA); |
| 1946 | |
| 1947 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) { |
| 1948 | tt_abort_msg("Couldn't make request"); |
| 1949 | } |
| 1950 | |
| 1951 | event_base_dispatch(data->base); |
| 1952 | |
| 1953 | tt_int_op(test_ok, ==, 1); |
| 1954 | |
| 1955 | test_ok = 0; |
| 1956 | |
| 1957 | req = evhttp_request_new(http_postrequest_done, data->base); |
| 1958 | tt_assert(req); |
| 1959 | |
| 1960 | /* Now try with 100-continue. */ |
| 1961 | |
| 1962 | /* Add the information that we care about */ |
| 1963 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 1964 | evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "100-continue"); |
| 1965 | evbuffer_add_printf(evhttp_request_get_output_buffer(req), POST_DATA); |
| 1966 | |
| 1967 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) { |
| 1968 | tt_abort_msg("Couldn't make request"); |
| 1969 | } |
| 1970 | |
| 1971 | event_base_dispatch(data->base); |
| 1972 | |
| 1973 | tt_int_op(test_ok, ==, 1); |
| 1974 | |
| 1975 | evhttp_connection_free(evcon); |
| 1976 | evhttp_free(http); |
| 1977 | |
| 1978 | end: |
| 1979 | ; |
| 1980 | } |
| 1981 | |
| 1982 | void |
| 1983 | http_post_cb(struct evhttp_request *req, void *arg) |
| 1984 | { |
| 1985 | struct evbuffer *evb; |
| 1986 | event_debug(("%s: called\n", __func__)); |
| 1987 | |
| 1988 | /* Yes, we are expecting a post request */ |
| 1989 | if (evhttp_request_get_command(req) != EVHTTP_REQ_POST) { |
| 1990 | fprintf(stdout, "FAILED (post type)\n"); |
| 1991 | exit(1); |
| 1992 | } |
| 1993 | |
| 1994 | if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(POST_DATA)) { |
| 1995 | fprintf(stdout, "FAILED (length: %lu vs %lu)\n", |
| 1996 | (unsigned long) evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long) strlen(POST_DATA)); |
| 1997 | exit(1); |
| 1998 | } |
| 1999 | |
| 2000 | if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), POST_DATA) != 0) { |
| 2001 | fprintf(stdout, "FAILED (data)\n"); |
| 2002 | fprintf(stdout, "Got :%s\n", evbuffer_pullup(evhttp_request_get_input_buffer(req),-1)); |
| 2003 | fprintf(stdout, "Want:%s\n", POST_DATA); |
| 2004 | exit(1); |
| 2005 | } |
| 2006 | |
| 2007 | evb = evbuffer_new(); |
| 2008 | evbuffer_add_printf(evb, BASIC_REQUEST_BODY); |
| 2009 | |
| 2010 | evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); |
| 2011 | |
| 2012 | evbuffer_free(evb); |
| 2013 | } |
| 2014 | |
| 2015 | void |
| 2016 | http_postrequest_done(struct evhttp_request *req, void *arg) |
| 2017 | { |
| 2018 | const char *what = BASIC_REQUEST_BODY; |
| 2019 | struct event_base *base = arg; |
| 2020 | |
| 2021 | if (req == NULL) { |
| 2022 | fprintf(stderr, "FAILED (timeout)\n"); |
| 2023 | exit(1); |
| 2024 | } |
| 2025 | |
| 2026 | if (evhttp_request_get_response_code(req) != HTTP_OK) { |
| 2027 | |
| 2028 | fprintf(stderr, "FAILED (response code)\n"); |
| 2029 | exit(1); |
| 2030 | } |
| 2031 | |
| 2032 | if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) { |
| 2033 | fprintf(stderr, "FAILED (content type)\n"); |
| 2034 | exit(1); |
| 2035 | } |
| 2036 | |
| 2037 | if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) { |
| 2038 | fprintf(stderr, "FAILED (length %lu vs %lu)\n", |
| 2039 | (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what)); |
| 2040 | exit(1); |
| 2041 | } |
| 2042 | |
| 2043 | if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) { |
| 2044 | fprintf(stderr, "FAILED (data)\n"); |
| 2045 | exit(1); |
| 2046 | } |
| 2047 | |
| 2048 | test_ok = 1; |
| 2049 | event_base_loopexit(base, NULL); |
| 2050 | } |
| 2051 | |
| 2052 | /* |
| 2053 | * HTTP PUT test, basically just like POST, but ... |
| 2054 | */ |
| 2055 | |
| 2056 | void http_putrequest_done(struct evhttp_request *, void *); |
| 2057 | |
| 2058 | #define PUT_DATA "Hi, I'm some PUT data" |
| 2059 | |
| 2060 | static void |
| 2061 | http_put_test(void *arg) |
| 2062 | { |
| 2063 | struct basic_test_data *data = arg; |
| 2064 | ev_uint16_t port = 0; |
| 2065 | struct evhttp_connection *evcon = NULL; |
| 2066 | struct evhttp_request *req = NULL; |
| 2067 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 2068 | |
| 2069 | test_ok = 0; |
| 2070 | |
| 2071 | evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
| 2072 | tt_assert(evcon); |
| 2073 | |
| 2074 | /* |
| 2075 | * Schedule the HTTP PUT request |
| 2076 | */ |
| 2077 | |
| 2078 | req = evhttp_request_new(http_putrequest_done, data->base); |
| 2079 | tt_assert(req); |
| 2080 | |
| 2081 | /* Add the information that we care about */ |
| 2082 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "someotherhost"); |
| 2083 | evbuffer_add_printf(evhttp_request_get_output_buffer(req), PUT_DATA); |
| 2084 | |
| 2085 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_PUT, "/putit") == -1) { |
| 2086 | tt_abort_msg("Couldn't make request"); |
| 2087 | } |
| 2088 | |
| 2089 | event_base_dispatch(data->base); |
| 2090 | |
| 2091 | evhttp_connection_free(evcon); |
| 2092 | evhttp_free(http); |
| 2093 | |
| 2094 | tt_int_op(test_ok, ==, 1); |
| 2095 | end: |
| 2096 | ; |
| 2097 | } |
| 2098 | |
| 2099 | void |
| 2100 | http_put_cb(struct evhttp_request *req, void *arg) |
| 2101 | { |
| 2102 | struct evbuffer *evb; |
| 2103 | event_debug(("%s: called\n", __func__)); |
| 2104 | |
| 2105 | /* Expecting a PUT request */ |
| 2106 | if (evhttp_request_get_command(req) != EVHTTP_REQ_PUT) { |
| 2107 | fprintf(stdout, "FAILED (put type)\n"); |
| 2108 | exit(1); |
| 2109 | } |
| 2110 | |
| 2111 | if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(PUT_DATA)) { |
| 2112 | fprintf(stdout, "FAILED (length: %lu vs %lu)\n", |
| 2113 | (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(PUT_DATA)); |
| 2114 | exit(1); |
| 2115 | } |
| 2116 | |
| 2117 | if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), PUT_DATA) != 0) { |
| 2118 | fprintf(stdout, "FAILED (data)\n"); |
| 2119 | fprintf(stdout, "Got :%s\n", evbuffer_pullup(evhttp_request_get_input_buffer(req),-1)); |
| 2120 | fprintf(stdout, "Want:%s\n", PUT_DATA); |
| 2121 | exit(1); |
| 2122 | } |
| 2123 | |
| 2124 | evb = evbuffer_new(); |
| 2125 | evbuffer_add_printf(evb, "That ain't funny"); |
| 2126 | |
| 2127 | evhttp_send_reply(req, HTTP_OK, "Everything is great", evb); |
| 2128 | |
| 2129 | evbuffer_free(evb); |
| 2130 | } |
| 2131 | |
| 2132 | void |
| 2133 | http_putrequest_done(struct evhttp_request *req, void *arg) |
| 2134 | { |
| 2135 | struct event_base *base = arg; |
| 2136 | const char *what = "That ain't funny"; |
| 2137 | |
| 2138 | if (req == NULL) { |
| 2139 | fprintf(stderr, "FAILED (timeout)\n"); |
| 2140 | exit(1); |
| 2141 | } |
| 2142 | |
| 2143 | if (evhttp_request_get_response_code(req) != HTTP_OK) { |
| 2144 | |
| 2145 | fprintf(stderr, "FAILED (response code)\n"); |
| 2146 | exit(1); |
| 2147 | } |
| 2148 | |
| 2149 | if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") == NULL) { |
| 2150 | fprintf(stderr, "FAILED (content type)\n"); |
| 2151 | exit(1); |
| 2152 | } |
| 2153 | |
| 2154 | if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != strlen(what)) { |
| 2155 | fprintf(stderr, "FAILED (length %lu vs %lu)\n", |
| 2156 | (unsigned long)evbuffer_get_length(evhttp_request_get_input_buffer(req)), (unsigned long)strlen(what)); |
| 2157 | exit(1); |
| 2158 | } |
| 2159 | |
| 2160 | |
| 2161 | if (evbuffer_datacmp(evhttp_request_get_input_buffer(req), what) != 0) { |
| 2162 | fprintf(stderr, "FAILED (data)\n"); |
| 2163 | exit(1); |
| 2164 | } |
| 2165 | |
| 2166 | test_ok = 1; |
| 2167 | event_base_loopexit(base, NULL); |
| 2168 | } |
| 2169 | |
| 2170 | static void |
| 2171 | http_failure_readcb(struct bufferevent *bev, void *arg) |
| 2172 | { |
| 2173 | const char *what = "400 Bad Request"; |
| 2174 | if (evbuffer_contains(bufferevent_get_input(bev), what)) { |
| 2175 | test_ok = 2; |
| 2176 | bufferevent_disable(bev, EV_READ); |
| 2177 | event_base_loopexit(arg, NULL); |
| 2178 | } |
| 2179 | } |
| 2180 | |
| 2181 | /* |
| 2182 | * Testing that the HTTP server can deal with a malformed request. |
| 2183 | */ |
| 2184 | static void |
| 2185 | http_failure_test(void *arg) |
| 2186 | { |
| 2187 | struct basic_test_data *data = arg; |
| 2188 | struct bufferevent *bev; |
| 2189 | evutil_socket_t fd = -1; |
| 2190 | const char *http_request; |
| 2191 | ev_uint16_t port = 0; |
| 2192 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 2193 | |
| 2194 | test_ok = 0; |
| 2195 | |
| 2196 | fd = http_connect("127.0.0.1", port); |
| 2197 | tt_int_op(fd, >=, 0); |
| 2198 | |
| 2199 | /* Stupid thing to send a request */ |
| 2200 | bev = bufferevent_socket_new(data->base, fd, 0); |
| 2201 | bufferevent_setcb(bev, http_failure_readcb, http_writecb, |
| 2202 | http_errorcb, data->base); |
| 2203 | |
| 2204 | http_request = "illegal request\r\n"; |
| 2205 | |
| 2206 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 2207 | |
| 2208 | event_base_dispatch(data->base); |
| 2209 | |
| 2210 | bufferevent_free(bev); |
| 2211 | |
| 2212 | evhttp_free(http); |
| 2213 | |
| 2214 | tt_int_op(test_ok, ==, 2); |
| 2215 | end: |
| 2216 | if (fd >= 0) |
| 2217 | evutil_closesocket(fd); |
| 2218 | } |
| 2219 | |
| 2220 | static void |
| 2221 | close_detect_done(struct evhttp_request *req, void *arg) |
| 2222 | { |
| 2223 | struct timeval tv; |
| 2224 | tt_assert(req); |
| 2225 | tt_assert(evhttp_request_get_response_code(req) == HTTP_OK); |
| 2226 | |
| 2227 | test_ok = 1; |
| 2228 | |
| 2229 | end: |
| 2230 | evutil_timerclear(&tv); |
| 2231 | tv.tv_usec = 150000; |
| 2232 | event_base_loopexit(arg, &tv); |
| 2233 | } |
| 2234 | |
| 2235 | static void |
| 2236 | close_detect_launch(evutil_socket_t fd, short what, void *arg) |
| 2237 | { |
| 2238 | struct evhttp_connection *evcon = arg; |
| 2239 | struct event_base *base = evhttp_connection_get_base(evcon); |
| 2240 | struct evhttp_request *req; |
| 2241 | |
| 2242 | req = evhttp_request_new(close_detect_done, base); |
| 2243 | |
| 2244 | /* Add the information that we care about */ |
| 2245 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 2246 | |
| 2247 | /* We give ownership of the request to the connection */ |
| 2248 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
| 2249 | tt_fail_msg("Couldn't make request"); |
| 2250 | } |
| 2251 | } |
| 2252 | |
| 2253 | static void |
| 2254 | close_detect_cb(struct evhttp_request *req, void *arg) |
| 2255 | { |
| 2256 | struct evhttp_connection *evcon = arg; |
| 2257 | struct event_base *base = evhttp_connection_get_base(evcon); |
| 2258 | struct timeval tv; |
| 2259 | |
| 2260 | if (req != NULL && evhttp_request_get_response_code(req) != HTTP_OK) { |
| 2261 | tt_abort_msg("Failed"); |
| 2262 | } |
| 2263 | |
| 2264 | evutil_timerclear(&tv); |
| 2265 | tv.tv_sec = 0; /* longer than the http time out */ |
| 2266 | tv.tv_usec = 600000; /* longer than the http time out */ |
| 2267 | |
| 2268 | /* launch a new request on the persistent connection in .3 seconds */ |
| 2269 | event_base_once(base, -1, EV_TIMEOUT, close_detect_launch, evcon, &tv); |
| 2270 | end: |
| 2271 | ; |
| 2272 | } |
| 2273 | |
| 2274 | |
| 2275 | static void |
| 2276 | http_close_detection_(struct basic_test_data *data, int with_delay) |
| 2277 | { |
| 2278 | ev_uint16_t port = 0; |
| 2279 | struct evhttp_connection *evcon = NULL; |
| 2280 | struct evhttp_request *req = NULL; |
| 2281 | const struct timeval sec_tenth = { 0, 100000 }; |
| 2282 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 2283 | |
| 2284 | test_ok = 0; |
| 2285 | |
| 2286 | /* .1 second timeout */ |
| 2287 | evhttp_set_timeout_tv(http, &sec_tenth); |
| 2288 | |
| 2289 | evcon = evhttp_connection_base_new(data->base, NULL, |
| 2290 | "127.0.0.1", port); |
| 2291 | tt_assert(evcon); |
| 2292 | evhttp_connection_set_timeout_tv(evcon, &sec_tenth); |
| 2293 | |
| 2294 | |
| 2295 | tt_assert(evcon); |
| 2296 | delayed_client = evcon; |
| 2297 | |
| 2298 | /* |
| 2299 | * At this point, we want to schedule a request to the HTTP |
| 2300 | * server using our make request method. |
| 2301 | */ |
| 2302 | |
| 2303 | req = evhttp_request_new(close_detect_cb, evcon); |
| 2304 | |
| 2305 | /* Add the information that we care about */ |
| 2306 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 2307 | |
| 2308 | /* We give ownership of the request to the connection */ |
| 2309 | if (evhttp_make_request(evcon, |
| 2310 | req, EVHTTP_REQ_GET, with_delay ? "/largedelay" : "/test") == -1) { |
| 2311 | tt_abort_msg("couldn't make request"); |
| 2312 | } |
| 2313 | |
| 2314 | event_base_dispatch(data->base); |
| 2315 | |
| 2316 | /* at this point, the http server should have no connection */ |
| 2317 | tt_assert(TAILQ_FIRST(&http->connections) == NULL); |
| 2318 | |
| 2319 | end: |
| 2320 | if (evcon) |
| 2321 | evhttp_connection_free(evcon); |
| 2322 | if (http) |
| 2323 | evhttp_free(http); |
| 2324 | } |
| 2325 | static void |
| 2326 | http_close_detection_test(void *arg) |
| 2327 | { |
| 2328 | http_close_detection_(arg, 0); |
| 2329 | } |
| 2330 | static void |
| 2331 | http_close_detection_delay_test(void *arg) |
| 2332 | { |
| 2333 | http_close_detection_(arg, 1); |
| 2334 | } |
| 2335 | |
| 2336 | static void |
| 2337 | http_highport_test(void *arg) |
| 2338 | { |
| 2339 | struct basic_test_data *data = arg; |
| 2340 | int i = -1; |
| 2341 | struct evhttp *myhttp = NULL; |
| 2342 | |
| 2343 | /* Try a few different ports */ |
| 2344 | for (i = 0; i < 50; ++i) { |
| 2345 | myhttp = evhttp_new(data->base); |
| 2346 | if (evhttp_bind_socket(myhttp, "127.0.0.1", 65535 - i) == 0) { |
| 2347 | test_ok = 1; |
| 2348 | evhttp_free(myhttp); |
| 2349 | return; |
| 2350 | } |
| 2351 | evhttp_free(myhttp); |
| 2352 | } |
| 2353 | |
| 2354 | tt_fail_msg("Couldn't get a high port"); |
| 2355 | } |
| 2356 | |
| 2357 | static void |
| 2358 | http_bad_header_test(void *ptr) |
| 2359 | { |
| 2360 | struct evkeyvalq headers; |
| 2361 | |
| 2362 | TAILQ_INIT(&headers); |
| 2363 | |
| 2364 | tt_want(evhttp_add_header(&headers, "One", "Two") == 0); |
| 2365 | tt_want(evhttp_add_header(&headers, "One", "Two\r\n Three") == 0); |
| 2366 | tt_want(evhttp_add_header(&headers, "One\r", "Two") == -1); |
| 2367 | tt_want(evhttp_add_header(&headers, "One\n", "Two") == -1); |
| 2368 | tt_want(evhttp_add_header(&headers, "One", "Two\r") == -1); |
| 2369 | tt_want(evhttp_add_header(&headers, "One", "Two\n") == -1); |
| 2370 | |
| 2371 | evhttp_clear_headers(&headers); |
| 2372 | } |
| 2373 | |
| 2374 | static int validate_header( |
| 2375 | const struct evkeyvalq* headers, |
| 2376 | const char *key, const char *value) |
| 2377 | { |
| 2378 | const char *real_val = evhttp_find_header(headers, key); |
| 2379 | tt_assert(real_val != NULL); |
| 2380 | tt_want(strcmp(real_val, value) == 0); |
| 2381 | end: |
| 2382 | return (0); |
| 2383 | } |
| 2384 | |
| 2385 | static void |
| 2386 | http_parse_query_test(void *ptr) |
| 2387 | { |
| 2388 | struct evkeyvalq headers; |
| 2389 | int r; |
| 2390 | |
| 2391 | TAILQ_INIT(&headers); |
| 2392 | |
| 2393 | r = evhttp_parse_query("http://www.test.com/?q=test", &headers); |
| 2394 | tt_want(validate_header(&headers, "q", "test") == 0); |
| 2395 | tt_int_op(r, ==, 0); |
| 2396 | evhttp_clear_headers(&headers); |
| 2397 | |
| 2398 | r = evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers); |
| 2399 | tt_want(validate_header(&headers, "q", "test") == 0); |
| 2400 | tt_want(validate_header(&headers, "foo", "bar") == 0); |
| 2401 | tt_int_op(r, ==, 0); |
| 2402 | evhttp_clear_headers(&headers); |
| 2403 | |
| 2404 | r = evhttp_parse_query("http://www.test.com/?q=test+foo", &headers); |
| 2405 | tt_want(validate_header(&headers, "q", "test foo") == 0); |
| 2406 | tt_int_op(r, ==, 0); |
| 2407 | evhttp_clear_headers(&headers); |
| 2408 | |
| 2409 | r = evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers); |
| 2410 | tt_want(validate_header(&headers, "q", "test\nfoo") == 0); |
| 2411 | tt_int_op(r, ==, 0); |
| 2412 | evhttp_clear_headers(&headers); |
| 2413 | |
| 2414 | r = evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers); |
| 2415 | tt_want(validate_header(&headers, "q", "test\rfoo") == 0); |
| 2416 | tt_int_op(r, ==, 0); |
| 2417 | evhttp_clear_headers(&headers); |
| 2418 | |
| 2419 | r = evhttp_parse_query("http://www.test.com/?q=test&&q2", &headers); |
| 2420 | tt_int_op(r, ==, -1); |
| 2421 | evhttp_clear_headers(&headers); |
| 2422 | |
| 2423 | r = evhttp_parse_query("http://www.test.com/?q=test+this", &headers); |
| 2424 | tt_want(validate_header(&headers, "q", "test this") == 0); |
| 2425 | tt_int_op(r, ==, 0); |
| 2426 | evhttp_clear_headers(&headers); |
| 2427 | |
| 2428 | r = evhttp_parse_query("http://www.test.com/?q=test&q2=foo", &headers); |
| 2429 | tt_int_op(r, ==, 0); |
| 2430 | tt_want(validate_header(&headers, "q", "test") == 0); |
| 2431 | tt_want(validate_header(&headers, "q2", "foo") == 0); |
| 2432 | evhttp_clear_headers(&headers); |
| 2433 | |
| 2434 | r = evhttp_parse_query("http://www.test.com/?q&q2=foo", &headers); |
| 2435 | tt_int_op(r, ==, -1); |
| 2436 | evhttp_clear_headers(&headers); |
| 2437 | |
| 2438 | r = evhttp_parse_query("http://www.test.com/?q=foo&q2", &headers); |
| 2439 | tt_int_op(r, ==, -1); |
| 2440 | evhttp_clear_headers(&headers); |
| 2441 | |
| 2442 | r = evhttp_parse_query("http://www.test.com/?q=foo&q2&q3=x", &headers); |
| 2443 | tt_int_op(r, ==, -1); |
| 2444 | evhttp_clear_headers(&headers); |
| 2445 | |
| 2446 | r = evhttp_parse_query("http://www.test.com/?q=&q2=&q3=", &headers); |
| 2447 | tt_int_op(r, ==, 0); |
| 2448 | tt_want(validate_header(&headers, "q", "") == 0); |
| 2449 | tt_want(validate_header(&headers, "q2", "") == 0); |
| 2450 | tt_want(validate_header(&headers, "q3", "") == 0); |
| 2451 | evhttp_clear_headers(&headers); |
| 2452 | |
| 2453 | end: |
| 2454 | evhttp_clear_headers(&headers); |
| 2455 | } |
| 2456 | |
| 2457 | static void |
| 2458 | http_parse_uri_test(void *ptr) |
| 2459 | { |
| 2460 | const int nonconform = (ptr != NULL); |
| 2461 | const unsigned parse_flags = |
| 2462 | nonconform ? EVHTTP_URI_NONCONFORMANT : 0; |
| 2463 | struct evhttp_uri *uri = NULL; |
| 2464 | char url_tmp[4096]; |
| 2465 | #define URI_PARSE(uri) \ |
| 2466 | evhttp_uri_parse_with_flags((uri), parse_flags) |
| 2467 | |
| 2468 | #define TT_URI(want) do { \ |
| 2469 | char *ret = evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)); \ |
| 2470 | tt_want(ret != NULL); \ |
| 2471 | tt_want(ret == url_tmp); \ |
| 2472 | if (strcmp(ret,want) != 0) \ |
| 2473 | TT_FAIL(("\"%s\" != \"%s\"",ret,want)); \ |
| 2474 | } while(0) |
| 2475 | |
| 2476 | tt_want(evhttp_uri_join(NULL, 0, 0) == NULL); |
| 2477 | tt_want(evhttp_uri_join(NULL, url_tmp, 0) == NULL); |
| 2478 | tt_want(evhttp_uri_join(NULL, url_tmp, sizeof(url_tmp)) == NULL); |
| 2479 | |
| 2480 | /* bad URIs: parsing */ |
| 2481 | #define BAD(s) do { \ |
| 2482 | if (URI_PARSE(s) != NULL) \ |
| 2483 | TT_FAIL(("Expected error parsing \"%s\"",s)); \ |
| 2484 | } while(0) |
| 2485 | /* Nonconformant URIs we can parse: parsing */ |
| 2486 | #define NCF(s) do { \ |
| 2487 | uri = URI_PARSE(s); \ |
| 2488 | if (uri != NULL && !nonconform) { \ |
| 2489 | TT_FAIL(("Expected error parsing \"%s\"",s)); \ |
| 2490 | } else if (uri == NULL && nonconform) { \ |
| 2491 | TT_FAIL(("Couldn't parse nonconformant URI \"%s\"", \ |
| 2492 | s)); \ |
| 2493 | } \ |
| 2494 | if (uri) { \ |
| 2495 | tt_want(evhttp_uri_join(uri, url_tmp, \ |
| 2496 | sizeof(url_tmp))); \ |
| 2497 | evhttp_uri_free(uri); \ |
| 2498 | } \ |
| 2499 | } while(0) |
| 2500 | |
| 2501 | NCF("http://www.test.com/ why hello"); |
| 2502 | NCF("http://www.test.com/why-hello\x01"); |
| 2503 | NCF("http://www.test.com/why-hello?\x01"); |
| 2504 | NCF("http://www.test.com/why-hello#\x01"); |
| 2505 | BAD("http://www.\x01.test.com/why-hello"); |
| 2506 | BAD("http://www.%7test.com/why-hello"); |
| 2507 | NCF("http://www.test.com/why-hell%7o"); |
| 2508 | BAD("h%3ttp://www.test.com/why-hello"); |
| 2509 | NCF("http://www.test.com/why-hello%7"); |
| 2510 | NCF("http://www.test.com/why-hell%7o"); |
| 2511 | NCF("http://www.test.com/foo?ba%r"); |
| 2512 | NCF("http://www.test.com/foo#ba%r"); |
| 2513 | BAD("99:99/foo"); |
| 2514 | BAD("http://www.test.com:999x/"); |
| 2515 | BAD("http://www.test.com:x/"); |
| 2516 | BAD("http://[hello-there]/"); |
| 2517 | BAD("http://[::1]]/"); |
| 2518 | BAD("http://[::1/"); |
| 2519 | BAD("http://[foob/"); |
| 2520 | BAD("http://[/"); |
| 2521 | BAD("http://[ffff:ffff:ffff:ffff:Ffff:ffff:ffff:" |
| 2522 | "ffff:ffff:ffff:ffff:ffff:ffff:ffff]/"); |
| 2523 | BAD("http://[vX.foo]/"); |
| 2524 | BAD("http://[vX.foo]/"); |
| 2525 | BAD("http://[v.foo]/"); |
| 2526 | BAD("http://[v5.fo%o]/"); |
| 2527 | BAD("http://[v5X]/"); |
| 2528 | BAD("http://[v5]/"); |
| 2529 | BAD("http://[]/"); |
| 2530 | BAD("http://f\x01red@www.example.com/"); |
| 2531 | BAD("http://f%0red@www.example.com/"); |
| 2532 | BAD("http://www.example.com:9999999999999999999999999999999999999/"); |
| 2533 | BAD("http://www.example.com:hihi/"); |
| 2534 | BAD("://www.example.com/"); |
| 2535 | |
| 2536 | /* bad URIs: joining */ |
| 2537 | uri = evhttp_uri_new(); |
| 2538 | tt_want(0==evhttp_uri_set_host(uri, "www.example.com")); |
| 2539 | tt_want(evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)) != NULL); |
| 2540 | /* not enough space: */ |
| 2541 | tt_want(evhttp_uri_join(uri, url_tmp, 3) == NULL); |
| 2542 | /* host is set, but path doesn't start with "/": */ |
| 2543 | tt_want(0==evhttp_uri_set_path(uri, "hi_mom")); |
| 2544 | tt_want(evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)) == NULL); |
| 2545 | tt_want(evhttp_uri_join(uri, NULL, sizeof(url_tmp))==NULL); |
| 2546 | tt_want(evhttp_uri_join(uri, url_tmp, 0)==NULL); |
| 2547 | evhttp_uri_free(uri); |
| 2548 | uri = URI_PARSE("mailto:foo@bar"); |
| 2549 | tt_want(uri != NULL); |
| 2550 | tt_want(evhttp_uri_get_host(uri) == NULL); |
| 2551 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2552 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2553 | tt_want(!strcmp(evhttp_uri_get_scheme(uri), "mailto")); |
| 2554 | tt_want(!strcmp(evhttp_uri_get_path(uri), "foo@bar")); |
| 2555 | tt_want(evhttp_uri_get_query(uri) == NULL); |
| 2556 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2557 | TT_URI("mailto:foo@bar"); |
| 2558 | evhttp_uri_free(uri); |
| 2559 | |
| 2560 | uri = evhttp_uri_new(); |
| 2561 | /* Bad URI usage: setting invalid values */ |
| 2562 | tt_want(-1 == evhttp_uri_set_scheme(uri,"")); |
| 2563 | tt_want(-1 == evhttp_uri_set_scheme(uri,"33")); |
| 2564 | tt_want(-1 == evhttp_uri_set_scheme(uri,"hi!")); |
| 2565 | tt_want(-1 == evhttp_uri_set_userinfo(uri,"hello@")); |
| 2566 | tt_want(-1 == evhttp_uri_set_host(uri,"[1.2.3.4]")); |
| 2567 | tt_want(-1 == evhttp_uri_set_host(uri,"[")); |
| 2568 | tt_want(-1 == evhttp_uri_set_host(uri,"www.[foo].com")); |
| 2569 | tt_want(-1 == evhttp_uri_set_port(uri,-3)); |
| 2570 | tt_want(-1 == evhttp_uri_set_path(uri,"hello?world")); |
| 2571 | tt_want(-1 == evhttp_uri_set_query(uri,"hello#world")); |
| 2572 | tt_want(-1 == evhttp_uri_set_fragment(uri,"hello#world")); |
| 2573 | /* Valid URI usage: setting valid values */ |
| 2574 | tt_want(0 == evhttp_uri_set_scheme(uri,"http")); |
| 2575 | tt_want(0 == evhttp_uri_set_scheme(uri,NULL)); |
| 2576 | tt_want(0 == evhttp_uri_set_userinfo(uri,"username:pass")); |
| 2577 | tt_want(0 == evhttp_uri_set_userinfo(uri,NULL)); |
| 2578 | tt_want(0 == evhttp_uri_set_host(uri,"www.example.com")); |
| 2579 | tt_want(0 == evhttp_uri_set_host(uri,"1.2.3.4")); |
| 2580 | tt_want(0 == evhttp_uri_set_host(uri,"[1:2:3:4::]")); |
| 2581 | tt_want(0 == evhttp_uri_set_host(uri,"[v7.wobblewobble]")); |
| 2582 | tt_want(0 == evhttp_uri_set_host(uri,NULL)); |
| 2583 | tt_want(0 == evhttp_uri_set_host(uri,"")); |
| 2584 | tt_want(0 == evhttp_uri_set_port(uri, -1)); |
| 2585 | tt_want(0 == evhttp_uri_set_port(uri, 80)); |
| 2586 | tt_want(0 == evhttp_uri_set_port(uri, 65535)); |
| 2587 | tt_want(0 == evhttp_uri_set_path(uri, "")); |
| 2588 | tt_want(0 == evhttp_uri_set_path(uri, "/documents/public/index.html")); |
| 2589 | tt_want(0 == evhttp_uri_set_path(uri, NULL)); |
| 2590 | tt_want(0 == evhttp_uri_set_query(uri, "key=val&key2=val2")); |
| 2591 | tt_want(0 == evhttp_uri_set_query(uri, "keyvalblarg")); |
| 2592 | tt_want(0 == evhttp_uri_set_query(uri, "")); |
| 2593 | tt_want(0 == evhttp_uri_set_query(uri, NULL)); |
| 2594 | tt_want(0 == evhttp_uri_set_fragment(uri, "")); |
| 2595 | tt_want(0 == evhttp_uri_set_fragment(uri, "here?i?am")); |
| 2596 | tt_want(0 == evhttp_uri_set_fragment(uri, NULL)); |
| 2597 | evhttp_uri_free(uri); |
| 2598 | |
| 2599 | /* Valid parsing */ |
| 2600 | uri = URI_PARSE("http://www.test.com/?q=t%33est"); |
| 2601 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
| 2602 | tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); |
| 2603 | tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
| 2604 | tt_want(strcmp(evhttp_uri_get_query(uri), "q=t%33est") == 0); |
| 2605 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2606 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2607 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2608 | TT_URI("http://www.test.com/?q=t%33est"); |
| 2609 | evhttp_uri_free(uri); |
| 2610 | |
| 2611 | uri = URI_PARSE("http://%77ww.test.com"); |
| 2612 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
| 2613 | tt_want(strcmp(evhttp_uri_get_host(uri), "%77ww.test.com") == 0); |
| 2614 | tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); |
| 2615 | tt_want(evhttp_uri_get_query(uri) == NULL); |
| 2616 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2617 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2618 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2619 | TT_URI("http://%77ww.test.com"); |
| 2620 | evhttp_uri_free(uri); |
| 2621 | |
| 2622 | uri = URI_PARSE("http://www.test.com?q=test"); |
| 2623 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
| 2624 | tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); |
| 2625 | tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); |
| 2626 | tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); |
| 2627 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2628 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2629 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2630 | TT_URI("http://www.test.com?q=test"); |
| 2631 | evhttp_uri_free(uri); |
| 2632 | |
| 2633 | uri = URI_PARSE("http://www.test.com#fragment"); |
| 2634 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
| 2635 | tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); |
| 2636 | tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); |
| 2637 | tt_want(evhttp_uri_get_query(uri) == NULL); |
| 2638 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2639 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2640 | tt_want_str_op(evhttp_uri_get_fragment(uri), ==, "fragment"); |
| 2641 | TT_URI("http://www.test.com#fragment"); |
| 2642 | evhttp_uri_free(uri); |
| 2643 | |
| 2644 | uri = URI_PARSE("http://8000/"); |
| 2645 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
| 2646 | tt_want(strcmp(evhttp_uri_get_host(uri), "8000") == 0); |
| 2647 | tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
| 2648 | tt_want(evhttp_uri_get_query(uri) == NULL); |
| 2649 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2650 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2651 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2652 | TT_URI("http://8000/"); |
| 2653 | evhttp_uri_free(uri); |
| 2654 | |
| 2655 | uri = URI_PARSE("http://:8000/"); |
| 2656 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
| 2657 | tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0); |
| 2658 | tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
| 2659 | tt_want(evhttp_uri_get_query(uri) == NULL); |
| 2660 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2661 | tt_want(evhttp_uri_get_port(uri) == 8000); |
| 2662 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2663 | TT_URI("http://:8000/"); |
| 2664 | evhttp_uri_free(uri); |
| 2665 | |
| 2666 | uri = URI_PARSE("http://www.test.com:/"); /* empty port */ |
| 2667 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
| 2668 | tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); |
| 2669 | tt_want_str_op(evhttp_uri_get_path(uri), ==, "/"); |
| 2670 | tt_want(evhttp_uri_get_query(uri) == NULL); |
| 2671 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2672 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2673 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2674 | TT_URI("http://www.test.com/"); |
| 2675 | evhttp_uri_free(uri); |
| 2676 | |
| 2677 | uri = URI_PARSE("http://www.test.com:"); /* empty port 2 */ |
| 2678 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0); |
| 2679 | tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); |
| 2680 | tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); |
| 2681 | tt_want(evhttp_uri_get_query(uri) == NULL); |
| 2682 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2683 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2684 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2685 | TT_URI("http://www.test.com"); |
| 2686 | evhttp_uri_free(uri); |
| 2687 | |
| 2688 | uri = URI_PARSE("ftp://www.test.com/?q=test"); |
| 2689 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0); |
| 2690 | tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0); |
| 2691 | tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
| 2692 | tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); |
| 2693 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2694 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2695 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2696 | TT_URI("ftp://www.test.com/?q=test"); |
| 2697 | evhttp_uri_free(uri); |
| 2698 | |
| 2699 | uri = URI_PARSE("ftp://[::1]:999/?q=test"); |
| 2700 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0); |
| 2701 | tt_want(strcmp(evhttp_uri_get_host(uri), "[::1]") == 0); |
| 2702 | tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
| 2703 | tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); |
| 2704 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2705 | tt_want(evhttp_uri_get_port(uri) == 999); |
| 2706 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2707 | TT_URI("ftp://[::1]:999/?q=test"); |
| 2708 | evhttp_uri_free(uri); |
| 2709 | |
| 2710 | uri = URI_PARSE("ftp://[ff00::127.0.0.1]/?q=test"); |
| 2711 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0); |
| 2712 | tt_want(strcmp(evhttp_uri_get_host(uri), "[ff00::127.0.0.1]") == 0); |
| 2713 | tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
| 2714 | tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); |
| 2715 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2716 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2717 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2718 | TT_URI("ftp://[ff00::127.0.0.1]/?q=test"); |
| 2719 | evhttp_uri_free(uri); |
| 2720 | |
| 2721 | uri = URI_PARSE("ftp://[v99.not_(any:time)_soon]/?q=test"); |
| 2722 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0); |
| 2723 | tt_want(strcmp(evhttp_uri_get_host(uri), "[v99.not_(any:time)_soon]") == 0); |
| 2724 | tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
| 2725 | tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); |
| 2726 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2727 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2728 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2729 | TT_URI("ftp://[v99.not_(any:time)_soon]/?q=test"); |
| 2730 | evhttp_uri_free(uri); |
| 2731 | |
| 2732 | uri = URI_PARSE("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment"); |
| 2733 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0); |
| 2734 | tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user:pass") == 0); |
| 2735 | tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0); |
| 2736 | tt_want(evhttp_uri_get_port(uri) == 42); |
| 2737 | tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
| 2738 | tt_want(strcmp(evhttp_uri_get_query(uri), "q=test&s=some+thing") == 0); |
| 2739 | tt_want(strcmp(evhttp_uri_get_fragment(uri), "fragment") == 0); |
| 2740 | TT_URI("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment"); |
| 2741 | evhttp_uri_free(uri); |
| 2742 | |
| 2743 | uri = URI_PARSE("scheme://user@foo.com/#fragment"); |
| 2744 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0); |
| 2745 | tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user") == 0); |
| 2746 | tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0); |
| 2747 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2748 | tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
| 2749 | tt_want(evhttp_uri_get_query(uri) == NULL); |
| 2750 | tt_want(strcmp(evhttp_uri_get_fragment(uri), "fragment") == 0); |
| 2751 | TT_URI("scheme://user@foo.com/#fragment"); |
| 2752 | evhttp_uri_free(uri); |
| 2753 | |
| 2754 | uri = URI_PARSE("scheme://%75ser@foo.com/#frag@ment"); |
| 2755 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0); |
| 2756 | tt_want(strcmp(evhttp_uri_get_userinfo(uri), "%75ser") == 0); |
| 2757 | tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0); |
| 2758 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2759 | tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); |
| 2760 | tt_want(evhttp_uri_get_query(uri) == NULL); |
| 2761 | tt_want(strcmp(evhttp_uri_get_fragment(uri), "frag@ment") == 0); |
| 2762 | TT_URI("scheme://%75ser@foo.com/#frag@ment"); |
| 2763 | evhttp_uri_free(uri); |
| 2764 | |
| 2765 | uri = URI_PARSE("file:///some/path/to/the/file"); |
| 2766 | tt_want(strcmp(evhttp_uri_get_scheme(uri), "file") == 0); |
| 2767 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2768 | tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0); |
| 2769 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2770 | tt_want(strcmp(evhttp_uri_get_path(uri), "/some/path/to/the/file") == 0); |
| 2771 | tt_want(evhttp_uri_get_query(uri) == NULL); |
| 2772 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2773 | TT_URI("file:///some/path/to/the/file"); |
| 2774 | evhttp_uri_free(uri); |
| 2775 | |
| 2776 | uri = URI_PARSE("///some/path/to/the-file"); |
| 2777 | tt_want(uri != NULL); |
| 2778 | tt_want(evhttp_uri_get_scheme(uri) == NULL); |
| 2779 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2780 | tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0); |
| 2781 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2782 | tt_want(strcmp(evhttp_uri_get_path(uri), "/some/path/to/the-file") == 0); |
| 2783 | tt_want(evhttp_uri_get_query(uri) == NULL); |
| 2784 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2785 | TT_URI("///some/path/to/the-file"); |
| 2786 | evhttp_uri_free(uri); |
| 2787 | |
| 2788 | uri = URI_PARSE("/s:ome/path/to/the-file?q=99#fred"); |
| 2789 | tt_want(uri != NULL); |
| 2790 | tt_want(evhttp_uri_get_scheme(uri) == NULL); |
| 2791 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2792 | tt_want(evhttp_uri_get_host(uri) == NULL); |
| 2793 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2794 | tt_want(strcmp(evhttp_uri_get_path(uri), "/s:ome/path/to/the-file") == 0); |
| 2795 | tt_want(strcmp(evhttp_uri_get_query(uri), "q=99") == 0); |
| 2796 | tt_want(strcmp(evhttp_uri_get_fragment(uri), "fred") == 0); |
| 2797 | TT_URI("/s:ome/path/to/the-file?q=99#fred"); |
| 2798 | evhttp_uri_free(uri); |
| 2799 | |
| 2800 | uri = URI_PARSE("relative/path/with/co:lon"); |
| 2801 | tt_want(uri != NULL); |
| 2802 | tt_want(evhttp_uri_get_scheme(uri) == NULL); |
| 2803 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2804 | tt_want(evhttp_uri_get_host(uri) == NULL); |
| 2805 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2806 | tt_want(strcmp(evhttp_uri_get_path(uri), "relative/path/with/co:lon") == 0); |
| 2807 | tt_want(evhttp_uri_get_query(uri) == NULL); |
| 2808 | tt_want(evhttp_uri_get_fragment(uri) == NULL); |
| 2809 | TT_URI("relative/path/with/co:lon"); |
| 2810 | evhttp_uri_free(uri); |
| 2811 | |
| 2812 | uri = URI_PARSE("bob?q=99&q2=q?33#fr?ed"); |
| 2813 | tt_want(uri != NULL); |
| 2814 | tt_want(evhttp_uri_get_scheme(uri) == NULL); |
| 2815 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2816 | tt_want(evhttp_uri_get_host(uri) == NULL); |
| 2817 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2818 | tt_want(strcmp(evhttp_uri_get_path(uri), "bob") == 0); |
| 2819 | tt_want(strcmp(evhttp_uri_get_query(uri), "q=99&q2=q?33") == 0); |
| 2820 | tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0); |
| 2821 | TT_URI("bob?q=99&q2=q?33#fr?ed"); |
| 2822 | evhttp_uri_free(uri); |
| 2823 | |
| 2824 | uri = URI_PARSE("#fr?ed"); |
| 2825 | tt_want(uri != NULL); |
| 2826 | tt_want(evhttp_uri_get_scheme(uri) == NULL); |
| 2827 | tt_want(evhttp_uri_get_userinfo(uri) == NULL); |
| 2828 | tt_want(evhttp_uri_get_host(uri) == NULL); |
| 2829 | tt_want(evhttp_uri_get_port(uri) == -1); |
| 2830 | tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0); |
| 2831 | tt_want(evhttp_uri_get_query(uri) == NULL); |
| 2832 | tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0); |
| 2833 | TT_URI("#fr?ed"); |
| 2834 | evhttp_uri_free(uri); |
| 2835 | #undef URI_PARSE |
| 2836 | #undef TT_URI |
| 2837 | #undef BAD |
| 2838 | } |
| 2839 | |
| 2840 | static void |
| 2841 | http_uriencode_test(void *ptr) |
| 2842 | { |
| 2843 | char *s=NULL, *s2=NULL; |
| 2844 | size_t sz; |
| 2845 | int bytes_decoded; |
| 2846 | |
| 2847 | #define ENC(from,want,plus) do { \ |
| 2848 | s = evhttp_uriencode((from), -1, (plus)); \ |
| 2849 | tt_assert(s); \ |
| 2850 | tt_str_op(s,==,(want)); \ |
| 2851 | sz = -1; \ |
| 2852 | s2 = evhttp_uridecode((s), (plus), &sz); \ |
| 2853 | tt_assert(s2); \ |
| 2854 | tt_str_op(s2,==,(from)); \ |
| 2855 | tt_int_op(sz,==,strlen(from)); \ |
| 2856 | free(s); \ |
| 2857 | free(s2); \ |
| 2858 | s = s2 = NULL; \ |
| 2859 | } while (0) |
| 2860 | |
| 2861 | #define DEC(from,want,dp) do { \ |
| 2862 | s = evhttp_uridecode((from),(dp),&sz); \ |
| 2863 | tt_assert(s); \ |
| 2864 | tt_str_op(s,==,(want)); \ |
| 2865 | tt_int_op(sz,==,strlen(want)); \ |
| 2866 | free(s); \ |
| 2867 | s = NULL; \ |
| 2868 | } while (0) |
| 2869 | |
| 2870 | #define OLD_DEC(from,want) do { \ |
| 2871 | s = evhttp_decode_uri((from)); \ |
| 2872 | tt_assert(s); \ |
| 2873 | tt_str_op(s,==,(want)); \ |
| 2874 | free(s); \ |
| 2875 | s = NULL; \ |
| 2876 | } while (0) |
| 2877 | |
| 2878 | |
| 2879 | ENC("Hello", "Hello",0); |
| 2880 | ENC("99", "99",0); |
| 2881 | ENC("", "",0); |
| 2882 | ENC( |
| 2883 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_", |
| 2884 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-.~_",0); |
| 2885 | ENC(" ", "%20",0); |
| 2886 | ENC(" ", "+",1); |
| 2887 | ENC("\xff\xf0\xe0", "%FF%F0%E0",0); |
| 2888 | ENC("\x01\x19", "%01%19",1); |
| 2889 | ENC("http://www.ietf.org/rfc/rfc3986.txt", |
| 2890 | "http%3A%2F%2Fwww.ietf.org%2Frfc%2Frfc3986.txt",1); |
| 2891 | |
| 2892 | ENC("1+2=3", "1%2B2%3D3",1); |
| 2893 | ENC("1+2=3", "1%2B2%3D3",0); |
| 2894 | |
| 2895 | /* Now try encoding with internal NULs. */ |
| 2896 | s = evhttp_uriencode("hello\0world", 11, 0); |
| 2897 | tt_assert(s); |
| 2898 | tt_str_op(s,==,"hello%00world"); |
| 2899 | free(s); |
| 2900 | s = NULL; |
| 2901 | |
| 2902 | /* Now try decoding just part of string. */ |
| 2903 | s = malloc(6 + 1 /* NUL byte */); |
| 2904 | bytes_decoded = evhttp_decode_uri_internal("hello%20%20", 6, s, 0); |
| 2905 | tt_assert(s); |
| 2906 | tt_int_op(bytes_decoded,==,6); |
| 2907 | tt_str_op(s,==,"hello%"); |
| 2908 | free(s); |
| 2909 | s = NULL; |
| 2910 | |
| 2911 | /* Now try out some decoding cases that we don't generate with |
| 2912 | * encode_uri: Make sure that malformed stuff doesn't crash... */ |
| 2913 | DEC("%%xhello th+ere \xff", |
| 2914 | "%%xhello th+ere \xff", 0); |
| 2915 | /* Make sure plus decoding works */ |
| 2916 | DEC("plus+should%20work+", "plus should work ",1); |
| 2917 | /* Try some lowercase hex */ |
| 2918 | DEC("%f0%a0%b0", "\xf0\xa0\xb0",1); |
| 2919 | |
| 2920 | /* Try an internal NUL. */ |
| 2921 | sz = 0; |
| 2922 | s = evhttp_uridecode("%00%00x%00%00", 1, &sz); |
| 2923 | tt_int_op(sz,==,5); |
| 2924 | tt_assert(!memcmp(s, "\0\0x\0\0", 5)); |
| 2925 | free(s); |
| 2926 | s = NULL; |
| 2927 | |
| 2928 | /* Try with size == NULL */ |
| 2929 | sz = 0; |
| 2930 | s = evhttp_uridecode("%00%00x%00%00", 1, NULL); |
| 2931 | tt_assert(!memcmp(s, "\0\0x\0\0", 5)); |
| 2932 | free(s); |
| 2933 | s = NULL; |
| 2934 | |
| 2935 | /* Test out the crazy old behavior of the deprecated |
| 2936 | * evhttp_decode_uri */ |
| 2937 | OLD_DEC("http://example.com/normal+path/?key=val+with+spaces", |
| 2938 | "http://example.com/normal+path/?key=val with spaces"); |
| 2939 | |
| 2940 | end: |
| 2941 | if (s) |
| 2942 | free(s); |
| 2943 | if (s2) |
| 2944 | free(s2); |
| 2945 | #undef ENC |
| 2946 | #undef DEC |
| 2947 | #undef OLD_DEC |
| 2948 | } |
| 2949 | |
| 2950 | static void |
| 2951 | http_base_test(void *ptr) |
| 2952 | { |
| 2953 | struct event_base *base = NULL; |
| 2954 | struct bufferevent *bev; |
| 2955 | evutil_socket_t fd; |
| 2956 | const char *http_request; |
| 2957 | ev_uint16_t port = 0; |
| 2958 | struct evhttp *http; |
| 2959 | |
| 2960 | test_ok = 0; |
| 2961 | base = event_base_new(); |
| 2962 | tt_assert(base); |
| 2963 | http = http_setup(&port, base, 0); |
| 2964 | |
| 2965 | fd = http_connect("127.0.0.1", port); |
| 2966 | tt_int_op(fd, >=, 0); |
| 2967 | |
| 2968 | /* Stupid thing to send a request */ |
| 2969 | bev = bufferevent_socket_new(base, fd, 0); |
| 2970 | bufferevent_setcb(bev, http_readcb, http_writecb, |
| 2971 | http_errorcb, base); |
| 2972 | bufferevent_base_set(base, bev); |
| 2973 | |
| 2974 | http_request = |
| 2975 | "GET /test HTTP/1.1\r\n" |
| 2976 | "Host: somehost\r\n" |
| 2977 | "Connection: close\r\n" |
| 2978 | "\r\n"; |
| 2979 | |
| 2980 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 2981 | |
| 2982 | event_base_dispatch(base); |
| 2983 | |
| 2984 | bufferevent_free(bev); |
| 2985 | evutil_closesocket(fd); |
| 2986 | |
| 2987 | evhttp_free(http); |
| 2988 | |
| 2989 | tt_int_op(test_ok, ==, 2); |
| 2990 | |
| 2991 | end: |
| 2992 | if (base) |
| 2993 | event_base_free(base); |
| 2994 | } |
| 2995 | |
| 2996 | /* |
| 2997 | * the server is just going to close the connection if it times out during |
| 2998 | * reading the headers. |
| 2999 | */ |
| 3000 | |
| 3001 | static void |
| 3002 | http_incomplete_readcb(struct bufferevent *bev, void *arg) |
| 3003 | { |
| 3004 | test_ok = -1; |
| 3005 | event_base_loopexit(exit_base,NULL); |
| 3006 | } |
| 3007 | |
| 3008 | static void |
| 3009 | http_incomplete_errorcb(struct bufferevent *bev, short what, void *arg) |
| 3010 | { |
| 3011 | /** For ssl */ |
| 3012 | if (what & BEV_EVENT_CONNECTED) |
| 3013 | return; |
| 3014 | |
| 3015 | if (what == (BEV_EVENT_READING|BEV_EVENT_EOF)) |
| 3016 | test_ok++; |
| 3017 | else |
| 3018 | test_ok = -2; |
| 3019 | event_base_loopexit(exit_base,NULL); |
| 3020 | } |
| 3021 | |
| 3022 | static void |
| 3023 | http_incomplete_writecb(struct bufferevent *bev, void *arg) |
| 3024 | { |
| 3025 | if (arg != NULL) { |
| 3026 | evutil_socket_t fd = *(evutil_socket_t *)arg; |
| 3027 | /* terminate the write side to simulate EOF */ |
| 3028 | shutdown(fd, EVUTIL_SHUT_WR); |
| 3029 | } |
| 3030 | if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) { |
| 3031 | /* enable reading of the reply */ |
| 3032 | bufferevent_enable(bev, EV_READ); |
| 3033 | test_ok++; |
| 3034 | } |
| 3035 | } |
| 3036 | |
| 3037 | static void |
| 3038 | http_incomplete_test_(struct basic_test_data *data, int use_timeout, int ssl) |
| 3039 | { |
| 3040 | struct bufferevent *bev; |
| 3041 | evutil_socket_t fd; |
| 3042 | const char *http_request; |
| 3043 | ev_uint16_t port = 0; |
| 3044 | struct timeval tv_start, tv_end; |
| 3045 | struct evhttp *http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); |
| 3046 | |
| 3047 | exit_base = data->base; |
| 3048 | test_ok = 0; |
| 3049 | |
| 3050 | evhttp_set_timeout(http, 1); |
| 3051 | |
| 3052 | fd = http_connect("127.0.0.1", port); |
| 3053 | tt_int_op(fd, >=, 0); |
| 3054 | |
| 3055 | /* Stupid thing to send a request */ |
| 3056 | bev = create_bev(data->base, fd, ssl); |
| 3057 | bufferevent_setcb(bev, |
| 3058 | http_incomplete_readcb, http_incomplete_writecb, |
| 3059 | http_incomplete_errorcb, use_timeout ? NULL : &fd); |
| 3060 | |
| 3061 | http_request = |
| 3062 | "GET /test HTTP/1.1\r\n" |
| 3063 | "Host: somehost\r\n"; |
| 3064 | |
| 3065 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 3066 | |
| 3067 | evutil_gettimeofday(&tv_start, NULL); |
| 3068 | |
| 3069 | event_base_dispatch(data->base); |
| 3070 | |
| 3071 | evutil_gettimeofday(&tv_end, NULL); |
| 3072 | evutil_timersub(&tv_end, &tv_start, &tv_end); |
| 3073 | |
| 3074 | bufferevent_free(bev); |
| 3075 | if (use_timeout) { |
| 3076 | evutil_closesocket(fd); |
| 3077 | fd = -1; |
| 3078 | } |
| 3079 | |
| 3080 | evhttp_free(http); |
| 3081 | |
| 3082 | if (use_timeout && tv_end.tv_sec >= 3) { |
| 3083 | tt_abort_msg("time"); |
| 3084 | } else if (!use_timeout && tv_end.tv_sec >= 1) { |
| 3085 | /* we should be done immediately */ |
| 3086 | tt_abort_msg("time"); |
| 3087 | } |
| 3088 | |
| 3089 | tt_int_op(test_ok, ==, 2); |
| 3090 | end: |
| 3091 | if (fd >= 0) |
| 3092 | evutil_closesocket(fd); |
| 3093 | } |
| 3094 | static void http_incomplete_test(void *arg) |
| 3095 | { http_incomplete_test_(arg, 0, 0); } |
| 3096 | static void http_incomplete_timeout_test(void *arg) |
| 3097 | { http_incomplete_test_(arg, 1, 0); } |
| 3098 | |
| 3099 | |
| 3100 | /* |
| 3101 | * the server is going to reply with chunked data. |
| 3102 | */ |
| 3103 | |
| 3104 | static void |
| 3105 | http_chunked_readcb(struct bufferevent *bev, void *arg) |
| 3106 | { |
| 3107 | /* nothing here */ |
| 3108 | } |
| 3109 | |
| 3110 | static void |
| 3111 | http_chunked_errorcb(struct bufferevent *bev, short what, void *arg) |
| 3112 | { |
| 3113 | struct evhttp_request *req = NULL; |
| 3114 | |
| 3115 | /** SSL */ |
| 3116 | if (what & BEV_EVENT_CONNECTED) |
| 3117 | return; |
| 3118 | |
| 3119 | if (!test_ok) |
| 3120 | goto out; |
| 3121 | |
| 3122 | test_ok = -1; |
| 3123 | |
| 3124 | if ((what & BEV_EVENT_EOF) != 0) { |
| 3125 | const char *header; |
| 3126 | enum message_read_status done; |
| 3127 | req = evhttp_request_new(NULL, NULL); |
| 3128 | |
| 3129 | /* req->kind = EVHTTP_RESPONSE; */ |
| 3130 | done = evhttp_parse_firstline_(req, bufferevent_get_input(bev)); |
| 3131 | if (done != ALL_DATA_READ) |
| 3132 | goto out; |
| 3133 | |
| 3134 | done = evhttp_parse_headers_(req, bufferevent_get_input(bev)); |
| 3135 | if (done != ALL_DATA_READ) |
| 3136 | goto out; |
| 3137 | |
| 3138 | header = evhttp_find_header(evhttp_request_get_input_headers(req), "Transfer-Encoding"); |
| 3139 | if (header == NULL || strcmp(header, "chunked")) |
| 3140 | goto out; |
| 3141 | |
| 3142 | header = evhttp_find_header(evhttp_request_get_input_headers(req), "Connection"); |
| 3143 | if (header == NULL || strcmp(header, "close")) |
| 3144 | goto out; |
| 3145 | |
| 3146 | header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF); |
| 3147 | if (header == NULL) |
| 3148 | goto out; |
| 3149 | /* 13 chars */ |
| 3150 | if (strcmp(header, "d")) { |
| 3151 | free((void*)header); |
| 3152 | goto out; |
| 3153 | } |
| 3154 | free((void*)header); |
| 3155 | |
| 3156 | if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 13), |
| 3157 | "This is funny", 13)) |
| 3158 | goto out; |
| 3159 | |
| 3160 | evbuffer_drain(bufferevent_get_input(bev), 13 + 2); |
| 3161 | |
| 3162 | header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF); |
| 3163 | if (header == NULL) |
| 3164 | goto out; |
| 3165 | /* 18 chars */ |
| 3166 | if (strcmp(header, "12")) |
| 3167 | goto out; |
| 3168 | free((char *)header); |
| 3169 | |
| 3170 | if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 18), |
| 3171 | "but not hilarious.", 18)) |
| 3172 | goto out; |
| 3173 | |
| 3174 | evbuffer_drain(bufferevent_get_input(bev), 18 + 2); |
| 3175 | |
| 3176 | header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF); |
| 3177 | if (header == NULL) |
| 3178 | goto out; |
| 3179 | /* 8 chars */ |
| 3180 | if (strcmp(header, "8")) { |
| 3181 | free((void*)header); |
| 3182 | goto out; |
| 3183 | } |
| 3184 | free((char *)header); |
| 3185 | |
| 3186 | if (strncmp((char *)evbuffer_pullup(bufferevent_get_input(bev), 8), |
| 3187 | "bwv 1052.", 8)) |
| 3188 | goto out; |
| 3189 | |
| 3190 | evbuffer_drain(bufferevent_get_input(bev), 8 + 2); |
| 3191 | |
| 3192 | header = evbuffer_readln(bufferevent_get_input(bev), NULL, EVBUFFER_EOL_CRLF); |
| 3193 | if (header == NULL) |
| 3194 | goto out; |
| 3195 | /* 0 chars */ |
| 3196 | if (strcmp(header, "0")) { |
| 3197 | free((void*)header); |
| 3198 | goto out; |
| 3199 | } |
| 3200 | free((void *)header); |
| 3201 | |
| 3202 | test_ok = 2; |
| 3203 | } |
| 3204 | |
| 3205 | out: |
| 3206 | if (req) |
| 3207 | evhttp_request_free(req); |
| 3208 | |
| 3209 | event_base_loopexit(arg, NULL); |
| 3210 | } |
| 3211 | |
| 3212 | static void |
| 3213 | http_chunked_writecb(struct bufferevent *bev, void *arg) |
| 3214 | { |
| 3215 | if (evbuffer_get_length(bufferevent_get_output(bev)) == 0) { |
| 3216 | /* enable reading of the reply */ |
| 3217 | bufferevent_enable(bev, EV_READ); |
| 3218 | test_ok++; |
| 3219 | } |
| 3220 | } |
| 3221 | |
| 3222 | static void |
| 3223 | http_chunked_request_done(struct evhttp_request *req, void *arg) |
| 3224 | { |
| 3225 | if (evhttp_request_get_response_code(req) != HTTP_OK) { |
| 3226 | fprintf(stderr, "FAILED\n"); |
| 3227 | exit(1); |
| 3228 | } |
| 3229 | |
| 3230 | if (evhttp_find_header(evhttp_request_get_input_headers(req), |
| 3231 | "Transfer-Encoding") == NULL) { |
| 3232 | fprintf(stderr, "FAILED\n"); |
| 3233 | exit(1); |
| 3234 | } |
| 3235 | |
| 3236 | if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 13 + 18 + 8) { |
| 3237 | fprintf(stderr, "FAILED\n"); |
| 3238 | exit(1); |
| 3239 | } |
| 3240 | |
| 3241 | if (strncmp((char *)evbuffer_pullup(evhttp_request_get_input_buffer(req), 13 + 18 + 8), |
| 3242 | "This is funnybut not hilarious.bwv 1052", |
| 3243 | 13 + 18 + 8)) { |
| 3244 | fprintf(stderr, "FAILED\n"); |
| 3245 | exit(1); |
| 3246 | } |
| 3247 | |
| 3248 | test_ok = 1; |
| 3249 | event_base_loopexit(arg, NULL); |
| 3250 | } |
| 3251 | |
| 3252 | static void |
| 3253 | http_chunk_out_test_impl(void *arg, int ssl) |
| 3254 | { |
| 3255 | struct basic_test_data *data = arg; |
| 3256 | struct bufferevent *bev; |
| 3257 | evutil_socket_t fd; |
| 3258 | const char *http_request; |
| 3259 | ev_uint16_t port = 0; |
| 3260 | struct timeval tv_start, tv_end; |
| 3261 | struct evhttp_connection *evcon = NULL; |
| 3262 | struct evhttp_request *req = NULL; |
| 3263 | int i; |
| 3264 | struct evhttp *http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); |
| 3265 | |
| 3266 | exit_base = data->base; |
| 3267 | test_ok = 0; |
| 3268 | |
| 3269 | fd = http_connect("127.0.0.1", port); |
| 3270 | |
| 3271 | /* Stupid thing to send a request */ |
| 3272 | bev = create_bev(data->base, fd, ssl); |
| 3273 | bufferevent_setcb(bev, |
| 3274 | http_chunked_readcb, http_chunked_writecb, |
| 3275 | http_chunked_errorcb, data->base); |
| 3276 | |
| 3277 | http_request = |
| 3278 | "GET /chunked HTTP/1.1\r\n" |
| 3279 | "Host: somehost\r\n" |
| 3280 | "Connection: close\r\n" |
| 3281 | "\r\n"; |
| 3282 | |
| 3283 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 3284 | |
| 3285 | evutil_gettimeofday(&tv_start, NULL); |
| 3286 | |
| 3287 | event_base_dispatch(data->base); |
| 3288 | |
| 3289 | bufferevent_free(bev); |
| 3290 | |
| 3291 | evutil_gettimeofday(&tv_end, NULL); |
| 3292 | evutil_timersub(&tv_end, &tv_start, &tv_end); |
| 3293 | |
| 3294 | tt_int_op(tv_end.tv_sec, <, 1); |
| 3295 | |
| 3296 | tt_int_op(test_ok, ==, 2); |
| 3297 | |
| 3298 | /* now try again with the regular connection object */ |
| 3299 | bev = create_bev(data->base, -1, ssl); |
| 3300 | evcon = evhttp_connection_base_bufferevent_new( |
| 3301 | data->base, NULL, bev, "127.0.0.1", port); |
| 3302 | tt_assert(evcon); |
| 3303 | |
| 3304 | /* make two requests to check the keepalive behavior */ |
| 3305 | for (i = 0; i < 2; i++) { |
| 3306 | test_ok = 0; |
| 3307 | req = evhttp_request_new(http_chunked_request_done,data->base); |
| 3308 | |
| 3309 | /* Add the information that we care about */ |
| 3310 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 3311 | |
| 3312 | /* We give ownership of the request to the connection */ |
| 3313 | if (evhttp_make_request(evcon, req, |
| 3314 | EVHTTP_REQ_GET, "/chunked") == -1) { |
| 3315 | tt_abort_msg("Couldn't make request"); |
| 3316 | } |
| 3317 | |
| 3318 | event_base_dispatch(data->base); |
| 3319 | |
| 3320 | tt_assert(test_ok == 1); |
| 3321 | } |
| 3322 | |
| 3323 | end: |
| 3324 | if (evcon) |
| 3325 | evhttp_connection_free(evcon); |
| 3326 | if (http) |
| 3327 | evhttp_free(http); |
| 3328 | } |
| 3329 | static void http_chunk_out_test(void *arg) |
| 3330 | { return http_chunk_out_test_impl(arg, 0); } |
| 3331 | |
| 3332 | static void |
| 3333 | http_stream_out_test_impl(void *arg, int ssl) |
| 3334 | { |
| 3335 | struct basic_test_data *data = arg; |
| 3336 | ev_uint16_t port = 0; |
| 3337 | struct evhttp_connection *evcon = NULL; |
| 3338 | struct evhttp_request *req = NULL; |
| 3339 | struct bufferevent *bev; |
| 3340 | struct evhttp *http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); |
| 3341 | |
| 3342 | test_ok = 0; |
| 3343 | exit_base = data->base; |
| 3344 | |
| 3345 | bev = create_bev(data->base, -1, ssl); |
| 3346 | evcon = evhttp_connection_base_bufferevent_new( |
| 3347 | data->base, NULL, bev, "127.0.0.1", port); |
| 3348 | tt_assert(evcon); |
| 3349 | |
| 3350 | /* |
| 3351 | * At this point, we want to schedule a request to the HTTP |
| 3352 | * server using our make request method. |
| 3353 | */ |
| 3354 | |
| 3355 | req = evhttp_request_new(http_request_done, |
| 3356 | (void *)"This is funnybut not hilarious.bwv 1052"); |
| 3357 | |
| 3358 | /* Add the information that we care about */ |
| 3359 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 3360 | |
| 3361 | /* We give ownership of the request to the connection */ |
| 3362 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/streamed") |
| 3363 | == -1) { |
| 3364 | tt_abort_msg("Couldn't make request"); |
| 3365 | } |
| 3366 | |
| 3367 | event_base_dispatch(data->base); |
| 3368 | |
| 3369 | end: |
| 3370 | if (evcon) |
| 3371 | evhttp_connection_free(evcon); |
| 3372 | if (http) |
| 3373 | evhttp_free(http); |
| 3374 | } |
| 3375 | static void http_stream_out_test(void *arg) |
| 3376 | { return http_stream_out_test_impl(arg, 0); } |
| 3377 | |
| 3378 | static void |
| 3379 | http_stream_in_chunk(struct evhttp_request *req, void *arg) |
| 3380 | { |
| 3381 | struct evbuffer *reply = arg; |
| 3382 | |
| 3383 | if (evhttp_request_get_response_code(req) != HTTP_OK) { |
| 3384 | fprintf(stderr, "FAILED\n"); |
| 3385 | exit(1); |
| 3386 | } |
| 3387 | |
| 3388 | evbuffer_add_buffer(reply, evhttp_request_get_input_buffer(req)); |
| 3389 | } |
| 3390 | |
| 3391 | static void |
| 3392 | http_stream_in_done(struct evhttp_request *req, void *arg) |
| 3393 | { |
| 3394 | if (evbuffer_get_length(evhttp_request_get_input_buffer(req)) != 0) { |
| 3395 | fprintf(stderr, "FAILED\n"); |
| 3396 | exit(1); |
| 3397 | } |
| 3398 | |
| 3399 | event_base_loopexit(exit_base, NULL); |
| 3400 | } |
| 3401 | |
| 3402 | /** |
| 3403 | * Makes a request and reads the response in chunks. |
| 3404 | */ |
| 3405 | static void |
| 3406 | http_stream_in_test_(struct basic_test_data *data, char const *url, |
| 3407 | size_t expected_len, char const *expected) |
| 3408 | { |
| 3409 | struct evhttp_connection *evcon; |
| 3410 | struct evbuffer *reply = evbuffer_new(); |
| 3411 | struct evhttp_request *req = NULL; |
| 3412 | ev_uint16_t port = 0; |
| 3413 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 3414 | |
| 3415 | exit_base = data->base; |
| 3416 | |
| 3417 | evcon = evhttp_connection_base_new(data->base, NULL,"127.0.0.1", port); |
| 3418 | tt_assert(evcon); |
| 3419 | |
| 3420 | req = evhttp_request_new(http_stream_in_done, reply); |
| 3421 | evhttp_request_set_chunked_cb(req, http_stream_in_chunk); |
| 3422 | |
| 3423 | /* We give ownership of the request to the connection */ |
| 3424 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, url) == -1) { |
| 3425 | tt_abort_msg("Couldn't make request"); |
| 3426 | } |
| 3427 | |
| 3428 | event_base_dispatch(data->base); |
| 3429 | |
| 3430 | if (evbuffer_get_length(reply) != expected_len) { |
| 3431 | TT_DIE(("reply length %lu; expected %lu; FAILED (%s)\n", |
| 3432 | (unsigned long)evbuffer_get_length(reply), |
| 3433 | (unsigned long)expected_len, |
| 3434 | (char*)evbuffer_pullup(reply, -1))); |
| 3435 | } |
| 3436 | |
| 3437 | if (memcmp(evbuffer_pullup(reply, -1), expected, expected_len) != 0) { |
| 3438 | tt_abort_msg("Memory mismatch"); |
| 3439 | } |
| 3440 | |
| 3441 | test_ok = 1; |
| 3442 | end: |
| 3443 | if (reply) |
| 3444 | evbuffer_free(reply); |
| 3445 | if (evcon) |
| 3446 | evhttp_connection_free(evcon); |
| 3447 | if (http) |
| 3448 | evhttp_free(http); |
| 3449 | } |
| 3450 | |
| 3451 | static void |
| 3452 | http_stream_in_test(void *arg) |
| 3453 | { |
| 3454 | http_stream_in_test_(arg, "/chunked", 13 + 18 + 8, |
| 3455 | "This is funnybut not hilarious.bwv 1052"); |
| 3456 | |
| 3457 | http_stream_in_test_(arg, "/test", strlen(BASIC_REQUEST_BODY), |
| 3458 | BASIC_REQUEST_BODY); |
| 3459 | } |
| 3460 | |
| 3461 | static void |
| 3462 | http_stream_in_cancel_chunk(struct evhttp_request *req, void *arg) |
| 3463 | { |
| 3464 | tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_OK); |
| 3465 | |
| 3466 | end: |
| 3467 | evhttp_cancel_request(req); |
| 3468 | event_base_loopexit(arg, NULL); |
| 3469 | } |
| 3470 | |
| 3471 | static void |
| 3472 | http_stream_in_cancel_done(struct evhttp_request *req, void *arg) |
| 3473 | { |
| 3474 | /* should never be called */ |
| 3475 | tt_fail_msg("In cancel done"); |
| 3476 | } |
| 3477 | |
| 3478 | static void |
| 3479 | http_stream_in_cancel_test(void *arg) |
| 3480 | { |
| 3481 | struct basic_test_data *data = arg; |
| 3482 | struct evhttp_connection *evcon; |
| 3483 | struct evhttp_request *req = NULL; |
| 3484 | ev_uint16_t port = 0; |
| 3485 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 3486 | |
| 3487 | evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
| 3488 | tt_assert(evcon); |
| 3489 | |
| 3490 | req = evhttp_request_new(http_stream_in_cancel_done, data->base); |
| 3491 | evhttp_request_set_chunked_cb(req, http_stream_in_cancel_chunk); |
| 3492 | |
| 3493 | /* We give ownership of the request to the connection */ |
| 3494 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/chunked") == -1) { |
| 3495 | tt_abort_msg("Couldn't make request"); |
| 3496 | } |
| 3497 | |
| 3498 | event_base_dispatch(data->base); |
| 3499 | |
| 3500 | test_ok = 1; |
| 3501 | end: |
| 3502 | evhttp_connection_free(evcon); |
| 3503 | evhttp_free(http); |
| 3504 | |
| 3505 | } |
| 3506 | |
| 3507 | static void |
| 3508 | http_connection_fail_done(struct evhttp_request *req, void *arg) |
| 3509 | { |
| 3510 | struct evhttp_connection *evcon = arg; |
| 3511 | struct event_base *base = evhttp_connection_get_base(evcon); |
| 3512 | |
| 3513 | /* An ENETUNREACH error results in an unrecoverable |
| 3514 | * evhttp_connection error (see evhttp_connection_fail_()). The |
| 3515 | * connection will be reset, and the user will be notified with a NULL |
| 3516 | * req parameter. */ |
| 3517 | tt_assert(!req); |
| 3518 | |
| 3519 | evhttp_connection_free(evcon); |
| 3520 | |
| 3521 | test_ok = 1; |
| 3522 | |
| 3523 | end: |
| 3524 | event_base_loopexit(base, NULL); |
| 3525 | } |
| 3526 | |
| 3527 | /* Test unrecoverable evhttp_connection errors by generating an ENETUNREACH |
| 3528 | * error on connection. */ |
| 3529 | static void |
| 3530 | http_connection_fail_test_impl(void *arg, int ssl) |
| 3531 | { |
| 3532 | struct basic_test_data *data = arg; |
| 3533 | ev_uint16_t port = 0; |
| 3534 | struct evhttp_connection *evcon = NULL; |
| 3535 | struct evhttp_request *req = NULL; |
| 3536 | struct bufferevent *bev; |
| 3537 | struct evhttp *http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); |
| 3538 | |
| 3539 | exit_base = data->base; |
| 3540 | test_ok = 0; |
| 3541 | |
| 3542 | /* auto detect a port */ |
| 3543 | evhttp_free(http); |
| 3544 | |
| 3545 | bev = create_bev(data->base, -1, ssl); |
| 3546 | /* Pick an unroutable address. This administratively scoped multicast |
| 3547 | * address should do when working with TCP. */ |
| 3548 | evcon = evhttp_connection_base_bufferevent_new( |
| 3549 | data->base, NULL, bev, "239.10.20.30", 80); |
| 3550 | tt_assert(evcon); |
| 3551 | |
| 3552 | /* |
| 3553 | * At this point, we want to schedule an HTTP GET request |
| 3554 | * server using our make request method. |
| 3555 | */ |
| 3556 | |
| 3557 | req = evhttp_request_new(http_connection_fail_done, evcon); |
| 3558 | tt_assert(req); |
| 3559 | |
| 3560 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/") == -1) { |
| 3561 | tt_abort_msg("Couldn't make request"); |
| 3562 | } |
| 3563 | |
| 3564 | event_base_dispatch(data->base); |
| 3565 | |
| 3566 | tt_int_op(test_ok, ==, 1); |
| 3567 | |
| 3568 | end: |
| 3569 | ; |
| 3570 | } |
| 3571 | static void http_connection_fail_test(void *arg) |
| 3572 | { return http_connection_fail_test_impl(arg, 0); } |
| 3573 | |
| 3574 | static void |
| 3575 | http_connection_retry_done(struct evhttp_request *req, void *arg) |
| 3576 | { |
| 3577 | tt_assert(req); |
| 3578 | tt_int_op(evhttp_request_get_response_code(req), !=, HTTP_OK); |
| 3579 | if (evhttp_find_header(evhttp_request_get_input_headers(req), "Content-Type") != NULL) { |
| 3580 | tt_abort_msg("(content type)\n"); |
| 3581 | } |
| 3582 | |
| 3583 | tt_uint_op(evbuffer_get_length(evhttp_request_get_input_buffer(req)), ==, 0); |
| 3584 | |
| 3585 | test_ok = 1; |
| 3586 | end: |
| 3587 | event_base_loopexit(arg,NULL); |
| 3588 | } |
| 3589 | |
| 3590 | struct http_server |
| 3591 | { |
| 3592 | ev_uint16_t port; |
| 3593 | int ssl; |
| 3594 | struct evhttp *http; |
| 3595 | }; |
| 3596 | static struct event_base *http_make_web_server_base=NULL; |
| 3597 | static void |
| 3598 | http_make_web_server(evutil_socket_t fd, short what, void *arg) |
| 3599 | { |
| 3600 | struct http_server *hs = (struct http_server *)arg; |
| 3601 | hs->http = http_setup(&hs->port, http_make_web_server_base, hs->ssl ? HTTP_BIND_SSL : 0); |
| 3602 | } |
| 3603 | |
| 3604 | static void |
| 3605 | http_simple_test_impl(void *arg, int ssl, int dirty) |
| 3606 | { |
| 3607 | struct basic_test_data *data = arg; |
| 3608 | struct evhttp_connection *evcon = NULL; |
| 3609 | struct evhttp_request *req = NULL; |
| 3610 | struct bufferevent *bev; |
| 3611 | struct http_server hs = { .port = 0, .ssl = ssl, }; |
| 3612 | struct evhttp *http = http_setup(&hs.port, data->base, ssl ? HTTP_BIND_SSL : 0); |
| 3613 | |
| 3614 | exit_base = data->base; |
| 3615 | test_ok = 0; |
| 3616 | |
| 3617 | bev = create_bev(data->base, -1, ssl); |
| 3618 | #ifdef EVENT__HAVE_OPENSSL |
| 3619 | bufferevent_openssl_set_allow_dirty_shutdown(bev, dirty); |
| 3620 | #endif |
| 3621 | |
| 3622 | evcon = evhttp_connection_base_bufferevent_new( |
| 3623 | data->base, NULL, bev, "127.0.0.1", hs.port); |
| 3624 | tt_assert(evcon); |
| 3625 | evhttp_connection_set_local_address(evcon, "127.0.0.1"); |
| 3626 | |
| 3627 | req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY); |
| 3628 | tt_assert(req); |
| 3629 | |
| 3630 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
| 3631 | tt_abort_msg("Couldn't make request"); |
| 3632 | } |
| 3633 | |
| 3634 | event_base_dispatch(data->base); |
| 3635 | tt_int_op(test_ok, ==, 1); |
| 3636 | |
| 3637 | end: |
| 3638 | if (evcon) |
| 3639 | evhttp_connection_free(evcon); |
| 3640 | if (http) |
| 3641 | evhttp_free(http); |
| 3642 | } |
| 3643 | static void http_simple_test(void *arg) |
| 3644 | { return http_simple_test_impl(arg, 0, 0); } |
| 3645 | |
| 3646 | static void |
| 3647 | http_connection_retry_test_basic(void *arg, const char *addr, struct evdns_base *dns_base, int ssl) |
| 3648 | { |
| 3649 | struct basic_test_data *data = arg; |
| 3650 | struct evhttp_connection *evcon = NULL; |
| 3651 | struct evhttp_request *req = NULL; |
| 3652 | struct timeval tv, tv_start, tv_end; |
| 3653 | struct bufferevent *bev; |
| 3654 | struct http_server hs = { .port = 0, .ssl = ssl, }; |
| 3655 | struct evhttp *http = http_setup(&hs.port, data->base, ssl ? HTTP_BIND_SSL : 0); |
| 3656 | |
| 3657 | exit_base = data->base; |
| 3658 | test_ok = 0; |
| 3659 | |
| 3660 | /* auto detect a port */ |
| 3661 | evhttp_free(http); |
| 3662 | |
| 3663 | bev = create_bev(data->base, -1, ssl); |
| 3664 | evcon = evhttp_connection_base_bufferevent_new(data->base, dns_base, bev, addr, hs.port); |
| 3665 | tt_assert(evcon); |
| 3666 | if (dns_base) |
| 3667 | tt_assert(!evhttp_connection_set_flags(evcon, EVHTTP_CON_REUSE_CONNECTED_ADDR)); |
| 3668 | |
| 3669 | evhttp_connection_set_timeout(evcon, 1); |
| 3670 | /* also bind to local host */ |
| 3671 | evhttp_connection_set_local_address(evcon, "127.0.0.1"); |
| 3672 | |
| 3673 | /* |
| 3674 | * At this point, we want to schedule an HTTP GET request |
| 3675 | * server using our make request method. |
| 3676 | */ |
| 3677 | |
| 3678 | req = evhttp_request_new(http_connection_retry_done, data->base); |
| 3679 | tt_assert(req); |
| 3680 | |
| 3681 | /* Add the information that we care about */ |
| 3682 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 3683 | |
| 3684 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
| 3685 | "/?arg=val") == -1) { |
| 3686 | tt_abort_msg("Couldn't make request"); |
| 3687 | } |
| 3688 | |
| 3689 | evutil_gettimeofday(&tv_start, NULL); |
| 3690 | event_base_dispatch(data->base); |
| 3691 | evutil_gettimeofday(&tv_end, NULL); |
| 3692 | evutil_timersub(&tv_end, &tv_start, &tv_end); |
| 3693 | tt_int_op(tv_end.tv_sec, <, 1); |
| 3694 | |
| 3695 | tt_int_op(test_ok, ==, 1); |
| 3696 | |
| 3697 | /* |
| 3698 | * now test the same but with retries |
| 3699 | */ |
| 3700 | test_ok = 0; |
| 3701 | /** Shutdown dns server, to test conn_address reusing */ |
| 3702 | if (dns_base) |
| 3703 | regress_clean_dnsserver(); |
| 3704 | |
| 3705 | { |
| 3706 | const struct timeval tv_timeout = { 0, 500000 }; |
| 3707 | const struct timeval tv_retry = { 0, 500000 }; |
| 3708 | evhttp_connection_set_timeout_tv(evcon, &tv_timeout); |
| 3709 | evhttp_connection_set_initial_retry_tv(evcon, &tv_retry); |
| 3710 | } |
| 3711 | evhttp_connection_set_retries(evcon, 1); |
| 3712 | |
| 3713 | req = evhttp_request_new(http_connection_retry_done, data->base); |
| 3714 | tt_assert(req); |
| 3715 | |
| 3716 | /* Add the information that we care about */ |
| 3717 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 3718 | |
| 3719 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
| 3720 | "/?arg=val") == -1) { |
| 3721 | tt_abort_msg("Couldn't make request"); |
| 3722 | } |
| 3723 | |
| 3724 | evutil_gettimeofday(&tv_start, NULL); |
| 3725 | event_base_dispatch(data->base); |
| 3726 | evutil_gettimeofday(&tv_end, NULL); |
| 3727 | |
| 3728 | /* fails fast, .5 sec to wait to retry, fails fast again. */ |
| 3729 | test_timeval_diff_leq(&tv_start, &tv_end, 500, 200); |
| 3730 | |
| 3731 | tt_assert(test_ok == 1); |
| 3732 | |
| 3733 | /* |
| 3734 | * now test the same but with retries and give it a web server |
| 3735 | * at the end |
| 3736 | */ |
| 3737 | test_ok = 0; |
| 3738 | |
| 3739 | evhttp_connection_set_timeout(evcon, 1); |
| 3740 | evhttp_connection_set_retries(evcon, 3); |
| 3741 | |
| 3742 | req = evhttp_request_new(http_dispatcher_test_done, data->base); |
| 3743 | tt_assert(req); |
| 3744 | |
| 3745 | /* Add the information that we care about */ |
| 3746 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 3747 | |
| 3748 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, |
| 3749 | "/?arg=val") == -1) { |
| 3750 | tt_abort_msg("Couldn't make request"); |
| 3751 | } |
| 3752 | |
| 3753 | /* start up a web server .2 seconds after the connection tried |
| 3754 | * to send a request |
| 3755 | */ |
| 3756 | evutil_timerclear(&tv); |
| 3757 | tv.tv_usec = 200000; |
| 3758 | http_make_web_server_base = data->base; |
| 3759 | event_base_once(data->base, -1, EV_TIMEOUT, http_make_web_server, &hs, &tv); |
| 3760 | |
| 3761 | evutil_gettimeofday(&tv_start, NULL); |
| 3762 | event_base_dispatch(data->base); |
| 3763 | evutil_gettimeofday(&tv_end, NULL); |
| 3764 | /* We'll wait twice as long as we did last time. */ |
| 3765 | test_timeval_diff_leq(&tv_start, &tv_end, 1000, 400); |
| 3766 | |
| 3767 | tt_int_op(test_ok, ==, 1); |
| 3768 | |
| 3769 | end: |
| 3770 | if (evcon) |
| 3771 | evhttp_connection_free(evcon); |
| 3772 | if (http) |
| 3773 | evhttp_free(hs.http); |
| 3774 | } |
| 3775 | |
| 3776 | static void |
| 3777 | http_connection_retry_conn_address_test_impl(void *arg, int ssl) |
| 3778 | { |
| 3779 | struct basic_test_data *data = arg; |
| 3780 | ev_uint16_t portnum = 0; |
| 3781 | struct evdns_base *dns_base = NULL; |
| 3782 | char address[64]; |
| 3783 | |
| 3784 | tt_assert(regress_dnsserver(data->base, &portnum, search_table)); |
| 3785 | dns_base = evdns_base_new(data->base, 0/* init name servers */); |
| 3786 | tt_assert(dns_base); |
| 3787 | |
| 3788 | /* Add ourself as the only nameserver, and make sure we really are |
| 3789 | * the only nameserver. */ |
| 3790 | evutil_snprintf(address, sizeof(address), "127.0.0.1:%d", portnum); |
| 3791 | evdns_base_nameserver_ip_add(dns_base, address); |
| 3792 | |
| 3793 | http_connection_retry_test_basic(arg, "localhost", dns_base, ssl); |
| 3794 | |
| 3795 | end: |
| 3796 | if (dns_base) |
| 3797 | evdns_base_free(dns_base, 0); |
| 3798 | /** dnsserver will be cleaned in http_connection_retry_test_basic() */ |
| 3799 | } |
| 3800 | static void http_connection_retry_conn_address_test(void *arg) |
| 3801 | { return http_connection_retry_conn_address_test_impl(arg, 0); } |
| 3802 | |
| 3803 | static void |
| 3804 | http_connection_retry_test_impl(void *arg, int ssl) |
| 3805 | { |
| 3806 | return http_connection_retry_test_basic(arg, "127.0.0.1", NULL, ssl); |
| 3807 | } |
| 3808 | static void |
| 3809 | http_connection_retry_test(void *arg) |
| 3810 | { return http_connection_retry_test_impl(arg, 0); } |
| 3811 | |
| 3812 | static void |
| 3813 | http_primitives(void *ptr) |
| 3814 | { |
| 3815 | char *escaped = NULL; |
| 3816 | struct evhttp *http = NULL; |
| 3817 | |
| 3818 | escaped = evhttp_htmlescape("<script>"); |
| 3819 | tt_assert(escaped); |
| 3820 | tt_str_op(escaped, ==, "<script>"); |
| 3821 | free(escaped); |
| 3822 | |
| 3823 | escaped = evhttp_htmlescape("\"\'&"); |
| 3824 | tt_assert(escaped); |
| 3825 | tt_str_op(escaped, ==, ""'&"); |
| 3826 | |
| 3827 | http = evhttp_new(NULL); |
| 3828 | tt_assert(http); |
| 3829 | tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, http), ==, 0); |
| 3830 | tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, http), ==, -1); |
| 3831 | tt_int_op(evhttp_del_cb(http, "/test"), ==, 0); |
| 3832 | tt_int_op(evhttp_del_cb(http, "/test"), ==, -1); |
| 3833 | tt_int_op(evhttp_set_cb(http, "/test", http_basic_cb, http), ==, 0); |
| 3834 | |
| 3835 | end: |
| 3836 | if (escaped) |
| 3837 | free(escaped); |
| 3838 | if (http) |
| 3839 | evhttp_free(http); |
| 3840 | } |
| 3841 | |
| 3842 | static void |
| 3843 | http_multi_line_header_test(void *arg) |
| 3844 | { |
| 3845 | struct basic_test_data *data = arg; |
| 3846 | struct bufferevent *bev= NULL; |
| 3847 | evutil_socket_t fd = -1; |
| 3848 | const char *http_start_request; |
| 3849 | ev_uint16_t port = 0; |
| 3850 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 3851 | |
| 3852 | exit_base = data->base; |
| 3853 | test_ok = 0; |
| 3854 | |
| 3855 | tt_ptr_op(http, !=, NULL); |
| 3856 | |
| 3857 | fd = http_connect("127.0.0.1", port); |
| 3858 | |
| 3859 | tt_int_op(fd, !=, -1); |
| 3860 | |
| 3861 | /* Stupid thing to send a request */ |
| 3862 | bev = bufferevent_socket_new(data->base, fd, 0); |
| 3863 | tt_ptr_op(bev, !=, NULL); |
| 3864 | bufferevent_setcb(bev, http_readcb, http_writecb, |
| 3865 | http_errorcb, data->base); |
| 3866 | |
| 3867 | http_start_request = |
| 3868 | "GET /test HTTP/1.1\r\n" |
| 3869 | "Host: somehost\r\n" |
| 3870 | "Connection: close\r\n" |
| 3871 | "X-Multi-Extra-WS: libevent \r\n" |
| 3872 | "\t\t\t2.1 \r\n" |
| 3873 | "X-Multi: aaaaaaaa\r\n" |
| 3874 | " a\r\n" |
| 3875 | "\tEND\r\n" |
| 3876 | "X-Last: last\r\n" |
| 3877 | "\r\n"; |
| 3878 | |
| 3879 | bufferevent_write(bev, http_start_request, strlen(http_start_request)); |
| 3880 | found_multi = found_multi2 = 0; |
| 3881 | |
| 3882 | event_base_dispatch(data->base); |
| 3883 | |
| 3884 | tt_int_op(found_multi, ==, 1); |
| 3885 | tt_int_op(found_multi2, ==, 1); |
| 3886 | tt_int_op(test_ok, ==, 4); |
| 3887 | end: |
| 3888 | if (bev) |
| 3889 | bufferevent_free(bev); |
| 3890 | if (fd >= 0) |
| 3891 | evutil_closesocket(fd); |
| 3892 | if (http) |
| 3893 | evhttp_free(http); |
| 3894 | } |
| 3895 | |
| 3896 | static void |
| 3897 | http_request_bad(struct evhttp_request *req, void *arg) |
| 3898 | { |
| 3899 | if (req != NULL) { |
| 3900 | fprintf(stderr, "FAILED\n"); |
| 3901 | exit(1); |
| 3902 | } |
| 3903 | |
| 3904 | test_ok = 1; |
| 3905 | event_base_loopexit(arg, NULL); |
| 3906 | } |
| 3907 | |
| 3908 | static void |
| 3909 | http_negative_content_length_test(void *arg) |
| 3910 | { |
| 3911 | struct basic_test_data *data = arg; |
| 3912 | ev_uint16_t port = 0; |
| 3913 | struct evhttp_connection *evcon = NULL; |
| 3914 | struct evhttp_request *req = NULL; |
| 3915 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 3916 | |
| 3917 | test_ok = 0; |
| 3918 | |
| 3919 | evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
| 3920 | tt_assert(evcon); |
| 3921 | |
| 3922 | /* |
| 3923 | * At this point, we want to schedule a request to the HTTP |
| 3924 | * server using our make request method. |
| 3925 | */ |
| 3926 | |
| 3927 | req = evhttp_request_new(http_request_bad, data->base); |
| 3928 | |
| 3929 | /* Cause the response to have a negative content-length */ |
| 3930 | evhttp_add_header(evhttp_request_get_output_headers(req), "X-Negative", "makeitso"); |
| 3931 | |
| 3932 | /* We give ownership of the request to the connection */ |
| 3933 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
| 3934 | tt_abort_msg("Couldn't make request"); |
| 3935 | } |
| 3936 | |
| 3937 | event_base_dispatch(data->base); |
| 3938 | |
| 3939 | end: |
| 3940 | if (evcon) |
| 3941 | evhttp_connection_free(evcon); |
| 3942 | if (http) |
| 3943 | evhttp_free(http); |
| 3944 | } |
| 3945 | |
| 3946 | |
| 3947 | static void |
| 3948 | http_data_length_constraints_test_done(struct evhttp_request *req, void *arg) |
| 3949 | { |
| 3950 | tt_assert(req); |
| 3951 | tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_BADREQUEST); |
| 3952 | end: |
| 3953 | event_base_loopexit(arg, NULL); |
| 3954 | } |
| 3955 | static void |
| 3956 | http_large_entity_test_done(struct evhttp_request *req, void *arg) |
| 3957 | { |
| 3958 | tt_assert(req); |
| 3959 | tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_ENTITYTOOLARGE); |
| 3960 | end: |
| 3961 | event_base_loopexit(arg, NULL); |
| 3962 | } |
| 3963 | #ifndef WIN32 |
| 3964 | static void |
| 3965 | http_expectation_failed_done(struct evhttp_request *req, void *arg) |
| 3966 | { |
| 3967 | tt_assert(req); |
| 3968 | tt_int_op(evhttp_request_get_response_code(req), ==, HTTP_EXPECTATIONFAILED); |
| 3969 | end: |
| 3970 | event_base_loopexit(arg, NULL); |
| 3971 | } |
| 3972 | #endif |
| 3973 | |
| 3974 | static void |
| 3975 | http_data_length_constraints_test_impl(void *arg, int read_on_write_error) |
| 3976 | { |
| 3977 | struct basic_test_data *data = arg; |
| 3978 | ev_uint16_t port = 0; |
| 3979 | struct evhttp_connection *evcon = NULL; |
| 3980 | struct evhttp_request *req = NULL; |
| 3981 | char *long_str = NULL; |
| 3982 | const size_t continue_size = 1<<20; |
| 3983 | const size_t size = (1<<20) * 3; |
| 3984 | void (*cb)(struct evhttp_request *, void *); |
| 3985 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 3986 | |
| 3987 | test_ok = 0; |
| 3988 | cb = http_failed_request_done; |
| 3989 | #ifndef WIN32 |
| 3990 | if (read_on_write_error) |
| 3991 | cb = http_data_length_constraints_test_done; |
| 3992 | #endif |
| 3993 | |
| 3994 | tt_assert(continue_size < size); |
| 3995 | |
| 3996 | evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
| 3997 | tt_assert(evcon); |
| 3998 | |
| 3999 | if (read_on_write_error) |
| 4000 | tt_assert(!evhttp_connection_set_flags(evcon, EVHTTP_CON_READ_ON_WRITE_ERROR)); |
| 4001 | |
| 4002 | /* also bind to local host */ |
| 4003 | evhttp_connection_set_local_address(evcon, "127.0.0.1"); |
| 4004 | |
| 4005 | /* |
| 4006 | * At this point, we want to schedule an HTTP GET request |
| 4007 | * server using our make request method. |
| 4008 | */ |
| 4009 | |
| 4010 | req = evhttp_request_new(http_data_length_constraints_test_done, data->base); |
| 4011 | tt_assert(req); |
| 4012 | |
| 4013 | long_str = malloc(size); |
| 4014 | memset(long_str, 'a', size); |
| 4015 | long_str[size - 1] = '\0'; |
| 4016 | /* Add the information that we care about */ |
| 4017 | evhttp_set_max_headers_size(http, size - 1); |
| 4018 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 4019 | evhttp_add_header(evhttp_request_get_output_headers(req), "Longheader", long_str); |
| 4020 | |
| 4021 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1) { |
| 4022 | tt_abort_msg("Couldn't make request"); |
| 4023 | } |
| 4024 | event_base_dispatch(data->base); |
| 4025 | |
| 4026 | req = evhttp_request_new(http_data_length_constraints_test_done, data->base); |
| 4027 | tt_assert(req); |
| 4028 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 4029 | |
| 4030 | /* GET /?arg=verylongvalue HTTP/1.1 */ |
| 4031 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, long_str) == -1) { |
| 4032 | tt_abort_msg("Couldn't make request"); |
| 4033 | } |
| 4034 | event_base_dispatch(data->base); |
| 4035 | |
| 4036 | #ifndef WIN32 |
| 4037 | if (read_on_write_error) |
| 4038 | cb = http_large_entity_test_done; |
| 4039 | #endif |
| 4040 | evhttp_set_max_body_size(http, size - 2); |
| 4041 | req = evhttp_request_new(cb, data->base); |
| 4042 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 4043 | evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str); |
| 4044 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) { |
| 4045 | tt_abort_msg("Couldn't make request"); |
| 4046 | } |
| 4047 | event_base_dispatch(data->base); |
| 4048 | |
| 4049 | req = evhttp_request_new(http_large_entity_test_done, data->base); |
| 4050 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 4051 | evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "100-continue"); |
| 4052 | evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str); |
| 4053 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) { |
| 4054 | tt_abort_msg("Couldn't make request"); |
| 4055 | } |
| 4056 | event_base_dispatch(data->base); |
| 4057 | |
| 4058 | req = evhttp_request_new(http_dispatcher_test_done, data->base); |
| 4059 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 4060 | evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "100-continue"); |
| 4061 | long_str[continue_size] = '\0'; |
| 4062 | evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str); |
| 4063 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) { |
| 4064 | tt_abort_msg("Couldn't make request"); |
| 4065 | } |
| 4066 | event_base_dispatch(data->base); |
| 4067 | |
| 4068 | #ifndef WIN32 |
| 4069 | if (read_on_write_error) |
| 4070 | cb = http_expectation_failed_done; |
| 4071 | #endif |
| 4072 | req = evhttp_request_new(cb, data->base); |
| 4073 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 4074 | evhttp_add_header(evhttp_request_get_output_headers(req), "Expect", "101-continue"); |
| 4075 | evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str); |
| 4076 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) { |
| 4077 | tt_abort_msg("Couldn't make request"); |
| 4078 | } |
| 4079 | event_base_dispatch(data->base); |
| 4080 | |
| 4081 | test_ok = 1; |
| 4082 | end: |
| 4083 | if (evcon) |
| 4084 | evhttp_connection_free(evcon); |
| 4085 | if (http) |
| 4086 | evhttp_free(http); |
| 4087 | if (long_str) |
| 4088 | free(long_str); |
| 4089 | } |
| 4090 | static void http_data_length_constraints_test(void *arg) |
| 4091 | { http_data_length_constraints_test_impl(arg, 0); } |
| 4092 | static void http_read_on_write_error_test(void *arg) |
| 4093 | { http_data_length_constraints_test_impl(arg, 1); } |
| 4094 | |
| 4095 | static void |
| 4096 | http_lingering_close_test_impl(void *arg, int lingering) |
| 4097 | { |
| 4098 | struct basic_test_data *data = arg; |
| 4099 | ev_uint16_t port = 0; |
| 4100 | struct evhttp_connection *evcon = NULL; |
| 4101 | struct evhttp_request *req = NULL; |
| 4102 | char *long_str = NULL; |
| 4103 | size_t size = (1<<20) * 3; |
| 4104 | void (*cb)(struct evhttp_request *, void *); |
| 4105 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 4106 | |
| 4107 | test_ok = 0; |
| 4108 | |
| 4109 | if (lingering) |
| 4110 | tt_assert(!evhttp_set_flags(http, EVHTTP_SERVER_LINGERING_CLOSE)); |
| 4111 | evhttp_set_max_body_size(http, size / 2); |
| 4112 | |
| 4113 | evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
| 4114 | tt_assert(evcon); |
| 4115 | evhttp_connection_set_local_address(evcon, "127.0.0.1"); |
| 4116 | |
| 4117 | /* |
| 4118 | * At this point, we want to schedule an HTTP GET request |
| 4119 | * server using our make request method. |
| 4120 | */ |
| 4121 | |
| 4122 | long_str = malloc(size); |
| 4123 | memset(long_str, 'a', size); |
| 4124 | long_str[size - 1] = '\0'; |
| 4125 | |
| 4126 | if (lingering) |
| 4127 | cb = http_large_entity_test_done; |
| 4128 | else |
| 4129 | cb = http_failed_request_done; |
| 4130 | req = evhttp_request_new(cb, data->base); |
| 4131 | tt_assert(req); |
| 4132 | evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost"); |
| 4133 | evbuffer_add_printf(evhttp_request_get_output_buffer(req), "%s", long_str); |
| 4134 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/") == -1) { |
| 4135 | tt_abort_msg("Couldn't make request"); |
| 4136 | } |
| 4137 | event_base_dispatch(data->base); |
| 4138 | |
| 4139 | test_ok = 1; |
| 4140 | end: |
| 4141 | if (evcon) |
| 4142 | evhttp_connection_free(evcon); |
| 4143 | if (http) |
| 4144 | evhttp_free(http); |
| 4145 | if (long_str) |
| 4146 | free(long_str); |
| 4147 | } |
| 4148 | static void http_non_lingering_close_test(void *arg) |
| 4149 | { http_lingering_close_test_impl(arg, 0); } |
| 4150 | static void http_lingering_close_test(void *arg) |
| 4151 | { http_lingering_close_test_impl(arg, 1); } |
| 4152 | |
| 4153 | /* |
| 4154 | * Testing client reset of server chunked connections |
| 4155 | */ |
| 4156 | |
| 4157 | struct terminate_state { |
| 4158 | struct event_base *base; |
| 4159 | struct evhttp_request *req; |
| 4160 | struct bufferevent *bev; |
| 4161 | evutil_socket_t fd; |
| 4162 | int gotclosecb: 1; |
| 4163 | int oneshot: 1; |
| 4164 | }; |
| 4165 | |
| 4166 | static void |
| 4167 | terminate_chunked_trickle_cb(evutil_socket_t fd, short events, void *arg) |
| 4168 | { |
| 4169 | struct terminate_state *state = arg; |
| 4170 | struct evbuffer *evb; |
| 4171 | |
| 4172 | if (!state->req) { |
| 4173 | return; |
| 4174 | } |
| 4175 | |
| 4176 | if (evhttp_request_get_connection(state->req) == NULL) { |
| 4177 | test_ok = 1; |
| 4178 | evhttp_request_free(state->req); |
| 4179 | event_base_loopexit(state->base,NULL); |
| 4180 | return; |
| 4181 | } |
| 4182 | |
| 4183 | evb = evbuffer_new(); |
| 4184 | evbuffer_add_printf(evb, "%p", evb); |
| 4185 | evhttp_send_reply_chunk(state->req, evb); |
| 4186 | evbuffer_free(evb); |
| 4187 | |
| 4188 | if (!state->oneshot) { |
| 4189 | struct timeval tv; |
| 4190 | tv.tv_sec = 0; |
| 4191 | tv.tv_usec = 3000; |
| 4192 | EVUTIL_ASSERT(state); |
| 4193 | EVUTIL_ASSERT(state->base); |
| 4194 | event_base_once(state->base, -1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); |
| 4195 | } |
| 4196 | } |
| 4197 | |
| 4198 | static void |
| 4199 | terminate_chunked_close_cb(struct evhttp_connection *evcon, void *arg) |
| 4200 | { |
| 4201 | struct terminate_state *state = arg; |
| 4202 | state->gotclosecb = 1; |
| 4203 | |
| 4204 | /** TODO: though we can do this unconditionally */ |
| 4205 | if (state->oneshot) { |
| 4206 | evhttp_request_free(state->req); |
| 4207 | state->req = NULL; |
| 4208 | event_base_loopexit(state->base,NULL); |
| 4209 | } |
| 4210 | } |
| 4211 | |
| 4212 | static void |
| 4213 | terminate_chunked_cb(struct evhttp_request *req, void *arg) |
| 4214 | { |
| 4215 | struct terminate_state *state = arg; |
| 4216 | struct timeval tv; |
| 4217 | |
| 4218 | /* we want to know if this connection closes on us */ |
| 4219 | evhttp_connection_set_closecb( |
| 4220 | evhttp_request_get_connection(req), |
| 4221 | terminate_chunked_close_cb, arg); |
| 4222 | |
| 4223 | state->req = req; |
| 4224 | |
| 4225 | evhttp_send_reply_start(req, HTTP_OK, "OK"); |
| 4226 | |
| 4227 | tv.tv_sec = 0; |
| 4228 | tv.tv_usec = 3000; |
| 4229 | event_base_once(state->base, -1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); |
| 4230 | } |
| 4231 | |
| 4232 | static void |
| 4233 | terminate_chunked_client(evutil_socket_t fd, short event, void *arg) |
| 4234 | { |
| 4235 | struct terminate_state *state = arg; |
| 4236 | bufferevent_free(state->bev); |
| 4237 | evutil_closesocket(state->fd); |
| 4238 | } |
| 4239 | |
| 4240 | static void |
| 4241 | terminate_readcb(struct bufferevent *bev, void *arg) |
| 4242 | { |
| 4243 | /* just drop the data */ |
| 4244 | evbuffer_drain(bufferevent_get_input(bev), -1); |
| 4245 | } |
| 4246 | |
| 4247 | |
| 4248 | static void |
| 4249 | http_terminate_chunked_test_impl(void *arg, int oneshot) |
| 4250 | { |
| 4251 | struct basic_test_data *data = arg; |
| 4252 | struct bufferevent *bev = NULL; |
| 4253 | struct timeval tv; |
| 4254 | const char *http_request; |
| 4255 | ev_uint16_t port = 0; |
| 4256 | evutil_socket_t fd = -1; |
| 4257 | struct terminate_state terminate_state; |
| 4258 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 4259 | |
| 4260 | test_ok = 0; |
| 4261 | |
| 4262 | evhttp_del_cb(http, "/test"); |
| 4263 | tt_assert(evhttp_set_cb(http, "/test", |
| 4264 | terminate_chunked_cb, &terminate_state) == 0); |
| 4265 | |
| 4266 | fd = http_connect("127.0.0.1", port); |
| 4267 | |
| 4268 | /* Stupid thing to send a request */ |
| 4269 | bev = bufferevent_socket_new(data->base, fd, 0); |
| 4270 | bufferevent_setcb(bev, terminate_readcb, http_writecb, |
| 4271 | http_errorcb, data->base); |
| 4272 | |
| 4273 | memset(&terminate_state, 0, sizeof(terminate_state)); |
| 4274 | terminate_state.base = data->base; |
| 4275 | terminate_state.fd = fd; |
| 4276 | terminate_state.bev = bev; |
| 4277 | terminate_state.gotclosecb = 0; |
| 4278 | terminate_state.oneshot = oneshot; |
| 4279 | |
| 4280 | /* first half of the http request */ |
| 4281 | http_request = |
| 4282 | "GET /test HTTP/1.1\r\n" |
| 4283 | "Host: some\r\n\r\n"; |
| 4284 | |
| 4285 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 4286 | evutil_timerclear(&tv); |
| 4287 | tv.tv_usec = 10000; |
| 4288 | event_base_once(data->base, -1, EV_TIMEOUT, terminate_chunked_client, &terminate_state, |
| 4289 | &tv); |
| 4290 | |
| 4291 | event_base_dispatch(data->base); |
| 4292 | |
| 4293 | if (terminate_state.gotclosecb == 0) |
| 4294 | test_ok = 0; |
| 4295 | |
| 4296 | end: |
| 4297 | if (fd >= 0) |
| 4298 | evutil_closesocket(fd); |
| 4299 | if (http) |
| 4300 | evhttp_free(http); |
| 4301 | } |
| 4302 | static void |
| 4303 | http_terminate_chunked_test(void *arg) |
| 4304 | { |
| 4305 | http_terminate_chunked_test_impl(arg, 0); |
| 4306 | } |
| 4307 | static void |
| 4308 | http_terminate_chunked_oneshot_test(void *arg) |
| 4309 | { |
| 4310 | http_terminate_chunked_test_impl(arg, 1); |
| 4311 | } |
| 4312 | |
| 4313 | static struct regress_dns_server_table ipv6_search_table[] = { |
| 4314 | { "localhost", "AAAA", "::1", 0, 0 }, |
| 4315 | { NULL, NULL, NULL, 0, 0 } |
| 4316 | }; |
| 4317 | |
| 4318 | static void |
| 4319 | http_ipv6_for_domain_test_impl(void *arg, int family) |
| 4320 | { |
| 4321 | struct basic_test_data *data = arg; |
| 4322 | struct evdns_base *dns_base = NULL; |
| 4323 | ev_uint16_t portnum = 0; |
| 4324 | char address[64]; |
| 4325 | |
| 4326 | tt_assert(regress_dnsserver(data->base, &portnum, ipv6_search_table)); |
| 4327 | |
| 4328 | dns_base = evdns_base_new(data->base, 0/* init name servers */); |
| 4329 | tt_assert(dns_base); |
| 4330 | |
| 4331 | /* Add ourself as the only nameserver, and make sure we really are |
| 4332 | * the only nameserver. */ |
| 4333 | evutil_snprintf(address, sizeof(address), "127.0.0.1:%d", portnum); |
| 4334 | evdns_base_nameserver_ip_add(dns_base, address); |
| 4335 | |
| 4336 | http_connection_test_(arg, 0 /* not persistent */, "localhost", dns_base, |
| 4337 | 1 /* ipv6 */, family, 0); |
| 4338 | |
| 4339 | end: |
| 4340 | if (dns_base) |
| 4341 | evdns_base_free(dns_base, 0); |
| 4342 | regress_clean_dnsserver(); |
| 4343 | } |
| 4344 | static void |
| 4345 | http_ipv6_for_domain_test(void *arg) |
| 4346 | { |
| 4347 | http_ipv6_for_domain_test_impl(arg, AF_UNSPEC); |
| 4348 | } |
| 4349 | |
| 4350 | static void |
| 4351 | http_request_get_addr_on_close(struct evhttp_connection *evcon, void *arg) |
| 4352 | { |
| 4353 | const struct sockaddr *storage; |
| 4354 | char addrbuf[128]; |
| 4355 | char local[] = "127.0.0.1:"; |
| 4356 | |
| 4357 | test_ok = 0; |
| 4358 | tt_assert(evcon); |
| 4359 | |
| 4360 | storage = evhttp_connection_get_addr(evcon); |
| 4361 | tt_assert(storage); |
| 4362 | |
| 4363 | evutil_format_sockaddr_port_((struct sockaddr *)storage, addrbuf, sizeof(addrbuf)); |
| 4364 | tt_assert(!strncmp(addrbuf, local, sizeof(local) - 1)); |
| 4365 | |
| 4366 | test_ok = 1; |
| 4367 | return; |
| 4368 | |
| 4369 | end: |
| 4370 | test_ok = 0; |
| 4371 | } |
| 4372 | |
| 4373 | static void |
| 4374 | http_get_addr_test(void *arg) |
| 4375 | { |
| 4376 | struct basic_test_data *data = arg; |
| 4377 | ev_uint16_t port = 0; |
| 4378 | struct evhttp_connection *evcon = NULL; |
| 4379 | struct evhttp_request *req = NULL; |
| 4380 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 4381 | |
| 4382 | test_ok = 0; |
| 4383 | exit_base = data->base; |
| 4384 | |
| 4385 | evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
| 4386 | tt_assert(evcon); |
| 4387 | evhttp_connection_set_closecb(evcon, http_request_get_addr_on_close, arg); |
| 4388 | |
| 4389 | /* |
| 4390 | * At this point, we want to schedule a request to the HTTP |
| 4391 | * server using our make request method. |
| 4392 | */ |
| 4393 | |
| 4394 | req = evhttp_request_new(http_request_done, (void *)BASIC_REQUEST_BODY); |
| 4395 | |
| 4396 | /* We give ownership of the request to the connection */ |
| 4397 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
| 4398 | tt_abort_msg("Couldn't make request"); |
| 4399 | } |
| 4400 | |
| 4401 | event_base_dispatch(data->base); |
| 4402 | |
| 4403 | http_request_get_addr_on_close(evcon, NULL); |
| 4404 | |
| 4405 | end: |
| 4406 | if (evcon) |
| 4407 | evhttp_connection_free(evcon); |
| 4408 | if (http) |
| 4409 | evhttp_free(http); |
| 4410 | } |
| 4411 | |
| 4412 | static void |
| 4413 | http_set_family_test(void *arg) |
| 4414 | { |
| 4415 | http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_UNSPEC, 0); |
| 4416 | } |
| 4417 | static void |
| 4418 | http_set_family_ipv4_test(void *arg) |
| 4419 | { |
| 4420 | http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_INET, 0); |
| 4421 | } |
| 4422 | static void |
| 4423 | http_set_family_ipv6_test(void *arg) |
| 4424 | { |
| 4425 | http_ipv6_for_domain_test_impl(arg, AF_INET6); |
| 4426 | } |
| 4427 | |
| 4428 | static void |
| 4429 | http_write_during_read(evutil_socket_t fd, short what, void *arg) |
| 4430 | { |
| 4431 | struct bufferevent *bev = arg; |
| 4432 | struct timeval tv; |
| 4433 | |
| 4434 | bufferevent_write(bev, "foobar", strlen("foobar")); |
| 4435 | |
| 4436 | evutil_timerclear(&tv); |
| 4437 | tv.tv_sec = 1; |
| 4438 | event_base_loopexit(exit_base, &tv); |
| 4439 | } |
| 4440 | static void |
| 4441 | http_write_during_read_test_impl(void *arg, int ssl) |
| 4442 | { |
| 4443 | struct basic_test_data *data = arg; |
| 4444 | ev_uint16_t port = 0; |
| 4445 | struct bufferevent *bev = NULL; |
| 4446 | struct timeval tv; |
| 4447 | int fd; |
| 4448 | const char *http_request; |
| 4449 | struct evhttp *http = http_setup(&port, data->base, ssl ? HTTP_BIND_SSL : 0); |
| 4450 | |
| 4451 | test_ok = 0; |
| 4452 | exit_base = data->base; |
| 4453 | |
| 4454 | fd = http_connect("127.0.0.1", port); |
| 4455 | bev = create_bev(data->base, fd, 0); |
| 4456 | bufferevent_setcb(bev, NULL, NULL, NULL, data->base); |
| 4457 | bufferevent_disable(bev, EV_READ); |
| 4458 | |
| 4459 | http_request = |
| 4460 | "GET /large HTTP/1.1\r\n" |
| 4461 | "Host: somehost\r\n" |
| 4462 | "\r\n"; |
| 4463 | |
| 4464 | bufferevent_write(bev, http_request, strlen(http_request)); |
| 4465 | evutil_timerclear(&tv); |
| 4466 | tv.tv_usec = 10000; |
| 4467 | event_base_once(data->base, -1, EV_TIMEOUT, http_write_during_read, bev, &tv); |
| 4468 | |
| 4469 | event_base_dispatch(data->base); |
| 4470 | |
| 4471 | if (bev) |
| 4472 | bufferevent_free(bev); |
| 4473 | if (http) |
| 4474 | evhttp_free(http); |
| 4475 | } |
| 4476 | static void http_write_during_read_test(void *arg) |
| 4477 | { return http_write_during_read_test_impl(arg, 0); } |
| 4478 | |
| 4479 | static void |
| 4480 | http_request_own_test(void *arg) |
| 4481 | { |
| 4482 | struct basic_test_data *data = arg; |
| 4483 | ev_uint16_t port = 0; |
| 4484 | struct evhttp_connection *evcon = NULL; |
| 4485 | struct evhttp_request *req = NULL; |
| 4486 | struct evhttp *http = http_setup(&port, data->base, 0); |
| 4487 | |
| 4488 | test_ok = 0; |
| 4489 | exit_base = data->base; |
| 4490 | |
| 4491 | evhttp_free(http); |
| 4492 | |
| 4493 | evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port); |
| 4494 | tt_assert(evcon); |
| 4495 | |
| 4496 | req = evhttp_request_new(http_request_no_action_done, NULL); |
| 4497 | |
| 4498 | if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { |
| 4499 | tt_abort_msg("Couldn't make request"); |
| 4500 | } |
| 4501 | evhttp_request_own(req); |
| 4502 | |
| 4503 | event_base_dispatch(data->base); |
| 4504 | |
| 4505 | end: |
| 4506 | if (evcon) |
| 4507 | evhttp_connection_free(evcon); |
| 4508 | if (req) |
| 4509 | evhttp_request_free(req); |
| 4510 | |
| 4511 | test_ok = 1; |
| 4512 | } |
| 4513 | |
| 4514 | #define HTTP_LEGACY(name) \ |
| 4515 | { #name, run_legacy_test_fn, TT_ISOLATED|TT_LEGACY, &legacy_setup, \ |
| 4516 | http_##name##_test } |
| 4517 | |
| 4518 | #define HTTP_CAST_ARG(a) ((void *)(a)) |
| 4519 | #define HTTP_OFF_N(title, name, arg) \ |
| 4520 | { #title, http_##name##_test, TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, HTTP_CAST_ARG(arg) } |
| 4521 | #define HTTP_N(title, name, arg) \ |
| 4522 | { #title, http_##name##_test, TT_ISOLATED, &basic_setup, HTTP_CAST_ARG(arg) } |
| 4523 | #define HTTP(name) HTTP_N(name, name, NULL) |
| 4524 | #define HTTPS(name) \ |
| 4525 | { "https_" #name, https_##name##_test, TT_ISOLATED, &basic_setup, NULL } |
| 4526 | |
| 4527 | #ifdef EVENT__HAVE_OPENSSL |
| 4528 | static void https_basic_test(void *arg) |
| 4529 | { return http_basic_test_impl(arg, 1); } |
| 4530 | static void https_filter_basic_test(void *arg) |
| 4531 | { return http_basic_test_impl(arg, 1 | HTTP_SSL_FILTER); } |
| 4532 | static void https_incomplete_test(void *arg) |
| 4533 | { http_incomplete_test_(arg, 0, 1); } |
| 4534 | static void https_incomplete_timeout_test(void *arg) |
| 4535 | { http_incomplete_test_(arg, 1, 1); } |
| 4536 | static void https_simple_test(void *arg) |
| 4537 | { return http_simple_test_impl(arg, 1, 0); } |
| 4538 | static void https_simple_dirty_test(void *arg) |
| 4539 | { return http_simple_test_impl(arg, 1, 1); } |
| 4540 | static void https_connection_retry_conn_address_test(void *arg) |
| 4541 | { return http_connection_retry_conn_address_test_impl(arg, 1); } |
| 4542 | static void https_connection_retry_test(void *arg) |
| 4543 | { return http_connection_retry_test_impl(arg, 1); } |
| 4544 | static void https_chunk_out_test(void *arg) |
| 4545 | { return http_chunk_out_test_impl(arg, 1); } |
| 4546 | static void https_filter_chunk_out_test(void *arg) |
| 4547 | { return http_chunk_out_test_impl(arg, 1 | HTTP_SSL_FILTER); } |
| 4548 | static void https_stream_out_test(void *arg) |
| 4549 | { return http_stream_out_test_impl(arg, 1); } |
| 4550 | static void https_connection_fail_test(void *arg) |
| 4551 | { return http_connection_fail_test_impl(arg, 1); } |
| 4552 | static void https_write_during_read_test(void *arg) |
| 4553 | { return http_write_during_read_test_impl(arg, 1); } |
| 4554 | static void https_connection_test(void *arg) |
| 4555 | { return http_connection_test_(arg, 0, "127.0.0.1", NULL, 0, AF_UNSPEC, 1); } |
| 4556 | static void https_persist_connection_test(void *arg) |
| 4557 | { return http_connection_test_(arg, 1, "127.0.0.1", NULL, 0, AF_UNSPEC, 1); } |
| 4558 | #endif |
| 4559 | |
| 4560 | struct testcase_t http_testcases[] = { |
| 4561 | { "primitives", http_primitives, 0, NULL, NULL }, |
| 4562 | { "base", http_base_test, TT_FORK, NULL, NULL }, |
| 4563 | { "bad_headers", http_bad_header_test, 0, NULL, NULL }, |
| 4564 | { "parse_query", http_parse_query_test, 0, NULL, NULL }, |
| 4565 | { "parse_uri", http_parse_uri_test, 0, NULL, NULL }, |
| 4566 | { "parse_uri_nc", http_parse_uri_test, 0, &basic_setup, (void*)"nc" }, |
| 4567 | { "uriencode", http_uriencode_test, 0, NULL, NULL }, |
| 4568 | HTTP(basic), |
| 4569 | HTTP(simple), |
| 4570 | |
| 4571 | HTTP_N(cancel, cancel, BASIC), |
| 4572 | HTTP_N(cancel_by_host, cancel, BY_HOST), |
| 4573 | HTTP_N(cancel_by_host_no_ns, cancel, BY_HOST | NO_NS), |
| 4574 | HTTP_N(cancel_by_host_inactive_server, cancel, BY_HOST | INACTIVE_SERVER), |
| 4575 | HTTP_N(cancel_inactive_server, cancel, INACTIVE_SERVER), |
| 4576 | HTTP_N(cancel_by_host_no_ns_inactive_server, cancel, BY_HOST | NO_NS | INACTIVE_SERVER), |
| 4577 | HTTP_OFF_N(cancel_by_host_server_timeout, cancel, BY_HOST | INACTIVE_SERVER | SERVER_TIMEOUT), |
| 4578 | HTTP_OFF_N(cancel_server_timeout, cancel, INACTIVE_SERVER | SERVER_TIMEOUT), |
| 4579 | HTTP_OFF_N(cancel_by_host_no_ns_server_timeout, cancel, BY_HOST | NO_NS | INACTIVE_SERVER | SERVER_TIMEOUT), |
| 4580 | HTTP_OFF_N(cancel_by_host_ns_timeout_server_timeout, cancel, BY_HOST | NO_NS | NS_TIMEOUT | INACTIVE_SERVER | SERVER_TIMEOUT), |
| 4581 | HTTP_N(cancel_by_host_ns_timeout, cancel, BY_HOST | NO_NS | NS_TIMEOUT), |
| 4582 | HTTP_N(cancel_by_host_ns_timeout_inactive_server, cancel, BY_HOST | NO_NS | NS_TIMEOUT | INACTIVE_SERVER), |
| 4583 | |
| 4584 | HTTP(virtual_host), |
| 4585 | HTTP(post), |
| 4586 | HTTP(put), |
| 4587 | HTTP(delete), |
| 4588 | HTTP(allowed_methods), |
| 4589 | HTTP(failure), |
| 4590 | HTTP(connection), |
| 4591 | HTTP(persist_connection), |
| 4592 | HTTP(autofree_connection), |
| 4593 | HTTP(connection_async), |
| 4594 | HTTP(close_detection), |
| 4595 | HTTP(close_detection_delay), |
| 4596 | HTTP(bad_request), |
| 4597 | HTTP(incomplete), |
| 4598 | HTTP(incomplete_timeout), |
| 4599 | HTTP(terminate_chunked), |
| 4600 | HTTP(terminate_chunked_oneshot), |
| 4601 | HTTP(on_complete), |
| 4602 | |
| 4603 | HTTP(highport), |
| 4604 | HTTP(dispatcher), |
| 4605 | HTTP(multi_line_header), |
| 4606 | HTTP(negative_content_length), |
| 4607 | HTTP(chunk_out), |
| 4608 | HTTP(stream_out), |
| 4609 | |
| 4610 | HTTP(stream_in), |
| 4611 | HTTP(stream_in_cancel), |
| 4612 | |
| 4613 | HTTP(connection_fail), |
| 4614 | { "connection_retry", http_connection_retry_test, TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, |
| 4615 | { "connection_retry_conn_address", http_connection_retry_conn_address_test, |
| 4616 | TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, |
| 4617 | |
| 4618 | HTTP(data_length_constraints), |
| 4619 | HTTP(read_on_write_error), |
| 4620 | HTTP(non_lingering_close), |
| 4621 | HTTP(lingering_close), |
| 4622 | |
| 4623 | HTTP(ipv6_for_domain), |
| 4624 | HTTP(get_addr), |
| 4625 | |
| 4626 | HTTP(set_family), |
| 4627 | HTTP(set_family_ipv4), |
| 4628 | HTTP(set_family_ipv6), |
| 4629 | |
| 4630 | HTTP(write_during_read), |
| 4631 | HTTP(request_own), |
| 4632 | |
| 4633 | #ifdef EVENT__HAVE_OPENSSL |
| 4634 | HTTPS(basic), |
| 4635 | HTTPS(filter_basic), |
| 4636 | HTTPS(simple), |
| 4637 | HTTPS(simple_dirty), |
| 4638 | HTTPS(incomplete), |
| 4639 | HTTPS(incomplete_timeout), |
| 4640 | { "https_connection_retry", https_connection_retry_test, TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, |
| 4641 | { "https_connection_retry_conn_address", https_connection_retry_conn_address_test, |
| 4642 | TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, |
| 4643 | HTTPS(chunk_out), |
| 4644 | HTTPS(filter_chunk_out), |
| 4645 | HTTPS(stream_out), |
| 4646 | HTTPS(connection_fail), |
| 4647 | HTTPS(write_during_read), |
| 4648 | HTTPS(connection), |
| 4649 | HTTPS(persist_connection), |
| 4650 | #endif |
| 4651 | |
| 4652 | END_OF_TESTCASES |
| 4653 | }; |
| 4654 | |