| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (c) 2003-2006 Niels Provos <provos@citi.umich.edu> | |
| 3 * All rights reserved. | |
| 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 | |
| 28 #ifdef WIN32 | |
| 29 #include <winsock2.h> | |
| 30 #include <windows.h> | |
| 31 #endif | |
| 32 | |
| 33 #ifdef HAVE_CONFIG_H | |
| 34 #include "config.h" | |
| 35 #endif | |
| 36 | |
| 37 #include <sys/types.h> | |
| 38 #include <sys/stat.h> | |
| 39 #ifdef 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 "event.h" | |
| 56 #include "evhttp.h" | |
| 57 #include "log.h" | |
| 58 #include "http-internal.h" | |
| 59 | |
| 60 extern int pair[]; | |
| 61 extern int test_ok; | |
| 62 | |
| 63 static struct evhttp *http; | |
| 64 /* set if a test needs to call loopexit on a base */ | |
| 65 static struct event_base *base; | |
| 66 | |
| 67 void http_suite(void); | |
| 68 | |
| 69 void http_basic_cb(struct evhttp_request *req, void *arg); | |
| 70 static void http_chunked_cb(struct evhttp_request *req, void *arg); | |
| 71 void http_post_cb(struct evhttp_request *req, void *arg); | |
| 72 void http_dispatcher_cb(struct evhttp_request *req, void *arg); | |
| 73 static void http_large_delay_cb(struct evhttp_request *req, void *arg); | |
| 74 static void http_badreq_cb(struct evhttp_request *req, void *arg); | |
| 75 | |
| 76 static struct evhttp * | |
| 77 http_setup(short *pport, struct event_base *base) | |
| 78 { | |
| 79 int i; | |
| 80 struct evhttp *myhttp; | |
| 81 short port = -1; | |
| 82 | |
| 83 /* Try a few different ports */ | |
| 84 myhttp = evhttp_new(base); | |
| 85 for (i = 0; i < 50; ++i) { | |
| 86 if (evhttp_bind_socket(myhttp, "127.0.0.1", 8080 + i) != -1) { | |
| 87 port = 8080 + i; | |
| 88 break; | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 if (port == -1) | |
| 93 event_errx(1, "Could not start web server"); | |
| 94 | |
| 95 /* Register a callback for certain types of requests */ | |
| 96 evhttp_set_cb(myhttp, "/test", http_basic_cb, NULL); | |
| 97 evhttp_set_cb(myhttp, "/chunked", http_chunked_cb, NULL); | |
| 98 evhttp_set_cb(myhttp, "/postit", http_post_cb, NULL); | |
| 99 evhttp_set_cb(myhttp, "/largedelay", http_large_delay_cb, NULL); | |
| 100 evhttp_set_cb(myhttp, "/badrequest", http_badreq_cb, NULL); | |
| 101 evhttp_set_cb(myhttp, "/", http_dispatcher_cb, NULL); | |
| 102 | |
| 103 *pport = port; | |
| 104 return (myhttp); | |
| 105 } | |
| 106 | |
| 107 #ifndef NI_MAXSERV | |
| 108 #define NI_MAXSERV 1024 | |
| 109 #endif | |
| 110 | |
| 111 static int | |
| 112 http_connect(const char *address, u_short port) | |
| 113 { | |
| 114 /* Stupid code for connecting */ | |
| 115 #ifdef WIN32 | |
| 116 struct hostent *he; | |
| 117 struct sockaddr_in sin; | |
| 118 #else | |
| 119 struct addrinfo ai, *aitop; | |
| 120 char strport[NI_MAXSERV]; | |
| 121 #endif | |
| 122 struct sockaddr *sa; | |
| 123 int slen; | |
| 124 int fd; | |
| 125 | |
| 126 #ifdef WIN32 | |
| 127 if (!(he = gethostbyname(address))) { | |
| 128 event_warn("gethostbyname"); | |
| 129 } | |
| 130 memcpy(&sin.sin_addr, he->h_addr_list[0], he->h_length); | |
| 131 sin.sin_family = AF_INET; | |
| 132 sin.sin_port = htons(port); | |
| 133 slen = sizeof(struct sockaddr_in); | |
| 134 sa = (struct sockaddr*)&sin; | |
| 135 #else | |
| 136 memset(&ai, 0, sizeof (ai)); | |
| 137 ai.ai_family = AF_INET; | |
| 138 ai.ai_socktype = SOCK_STREAM; | |
| 139 snprintf(strport, sizeof (strport), "%d", port); | |
| 140 if (getaddrinfo(address, strport, &ai, &aitop) != 0) { | |
| 141 event_warn("getaddrinfo"); | |
| 142 return (-1); | |
| 143 } | |
| 144 sa = aitop->ai_addr; | |
| 145 slen = aitop->ai_addrlen; | |
| 146 #endif | |
| 147 | |
| 148 fd = socket(AF_INET, SOCK_STREAM, 0); | |
| 149 if (fd == -1) | |
| 150 event_err(1, "socket failed"); | |
| 151 | |
| 152 if (connect(fd, sa, slen) == -1) | |
| 153 event_err(1, "connect failed"); | |
| 154 | |
| 155 #ifndef WIN32 | |
| 156 freeaddrinfo(aitop); | |
| 157 #endif | |
| 158 | |
| 159 return (fd); | |
| 160 } | |
| 161 | |
| 162 static void | |
| 163 http_readcb(struct bufferevent *bev, void *arg) | |
| 164 { | |
| 165 const char *what = "This is funny"; | |
| 166 | |
| 167 event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input))); | |
| 168 | |
| 169 if (evbuffer_find(bev->input, | |
| 170 (const unsigned char*) what, strlen(what)) != NULL) { | |
| 171 struct evhttp_request *req = evhttp_request_new(NULL, NULL); | |
| 172 enum message_read_status done; | |
| 173 | |
| 174 req->kind = EVHTTP_RESPONSE; | |
| 175 done = evhttp_parse_firstline(req, bev->input); | |
| 176 if (done != ALL_DATA_READ) | |
| 177 goto out; | |
| 178 | |
| 179 done = evhttp_parse_headers(req, bev->input); | |
| 180 if (done != ALL_DATA_READ) | |
| 181 goto out; | |
| 182 | |
| 183 if (done == 1 && | |
| 184 evhttp_find_header(req->input_headers, | |
| 185 "Content-Type") != NULL) | |
| 186 test_ok++; | |
| 187 | |
| 188 out: | |
| 189 evhttp_request_free(req); | |
| 190 bufferevent_disable(bev, EV_READ); | |
| 191 if (base) | |
| 192 event_base_loopexit(base, NULL); | |
| 193 else | |
| 194 event_loopexit(NULL); | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 static void | |
| 199 http_writecb(struct bufferevent *bev, void *arg) | |
| 200 { | |
| 201 if (EVBUFFER_LENGTH(bev->output) == 0) { | |
| 202 /* enable reading of the reply */ | |
| 203 bufferevent_enable(bev, EV_READ); | |
| 204 test_ok++; | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 static void | |
| 209 http_errorcb(struct bufferevent *bev, short what, void *arg) | |
| 210 { | |
| 211 test_ok = -2; | |
| 212 event_loopexit(NULL); | |
| 213 } | |
| 214 | |
| 215 void | |
| 216 http_basic_cb(struct evhttp_request *req, void *arg) | |
| 217 { | |
| 218 struct evbuffer *evb = evbuffer_new(); | |
| 219 int empty = evhttp_find_header(req->input_headers, "Empty") != NULL; | |
| 220 event_debug(("%s: called\n", __func__)); | |
| 221 evbuffer_add_printf(evb, "This is funny"); | |
| 222 | |
| 223 /* For multi-line headers test */ | |
| 224 { | |
| 225 const char *multi = | |
| 226 evhttp_find_header(req->input_headers,"X-multi"); | |
| 227 if (multi) { | |
| 228 if (strcmp("END", multi + strlen(multi) - 3) == 0) | |
| 229 test_ok++; | |
| 230 if (evhttp_find_header(req->input_headers, "X-Last")) | |
| 231 test_ok++; | |
| 232 } | |
| 233 } | |
| 234 | |
| 235 /* injecting a bad content-length */ | |
| 236 if (evhttp_find_header(req->input_headers, "X-Negative")) | |
| 237 evhttp_add_header(req->output_headers, | |
| 238 "Content-Length", "-100"); | |
| 239 | |
| 240 /* allow sending of an empty reply */ | |
| 241 evhttp_send_reply(req, HTTP_OK, "Everything is fine", | |
| 242 !empty ? evb : NULL); | |
| 243 | |
| 244 evbuffer_free(evb); | |
| 245 } | |
| 246 | |
| 247 static char const* const CHUNKS[] = { | |
| 248 "This is funny", | |
| 249 "but not hilarious.", | |
| 250 "bwv 1052" | |
| 251 }; | |
| 252 | |
| 253 struct chunk_req_state { | |
| 254 struct evhttp_request *req; | |
| 255 int i; | |
| 256 }; | |
| 257 | |
| 258 static void | |
| 259 http_chunked_trickle_cb(int fd, short events, void *arg) | |
| 260 { | |
| 261 struct evbuffer *evb = evbuffer_new(); | |
| 262 struct chunk_req_state *state = arg; | |
| 263 struct timeval when = { 0, 0 }; | |
| 264 | |
| 265 evbuffer_add_printf(evb, "%s", CHUNKS[state->i]); | |
| 266 evhttp_send_reply_chunk(state->req, evb); | |
| 267 evbuffer_free(evb); | |
| 268 | |
| 269 if (++state->i < sizeof(CHUNKS)/sizeof(CHUNKS[0])) { | |
| 270 event_once(-1, EV_TIMEOUT, | |
| 271 http_chunked_trickle_cb, state, &when); | |
| 272 } else { | |
| 273 evhttp_send_reply_end(state->req); | |
| 274 free(state); | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 static void | |
| 279 http_chunked_cb(struct evhttp_request *req, void *arg) | |
| 280 { | |
| 281 struct timeval when = { 0, 0 }; | |
| 282 struct chunk_req_state *state = malloc(sizeof(struct chunk_req_state)); | |
| 283 event_debug(("%s: called\n", __func__)); | |
| 284 | |
| 285 memset(state, 0, sizeof(struct chunk_req_state)); | |
| 286 state->req = req; | |
| 287 | |
| 288 /* generate a chunked reply */ | |
| 289 evhttp_send_reply_start(req, HTTP_OK, "Everything is fine"); | |
| 290 | |
| 291 /* but trickle it across several iterations to ensure we're not | |
| 292 * assuming it comes all at once */ | |
| 293 event_once(-1, EV_TIMEOUT, http_chunked_trickle_cb, state, &when); | |
| 294 } | |
| 295 | |
| 296 static void | |
| 297 http_complete_write(int fd, short what, void *arg) | |
| 298 { | |
| 299 struct bufferevent *bev = arg; | |
| 300 const char *http_request = "host\r\n" | |
| 301 "Connection: close\r\n" | |
| 302 "\r\n"; | |
| 303 bufferevent_write(bev, http_request, strlen(http_request)); | |
| 304 } | |
| 305 | |
| 306 static void | |
| 307 http_basic_test(void) | |
| 308 { | |
| 309 struct timeval tv; | |
| 310 struct bufferevent *bev; | |
| 311 int fd; | |
| 312 const char *http_request; | |
| 313 short port = -1; | |
| 314 | |
| 315 test_ok = 0; | |
| 316 fprintf(stdout, "Testing Basic HTTP Server: "); | |
| 317 | |
| 318 http = http_setup(&port, NULL); | |
| 319 | |
| 320 /* bind to a second socket */ | |
| 321 if (evhttp_bind_socket(http, "127.0.0.1", port + 1) == -1) { | |
| 322 fprintf(stdout, "FAILED (bind)\n"); | |
| 323 exit(1); | |
| 324 } | |
| 325 | |
| 326 fd = http_connect("127.0.0.1", port); | |
| 327 | |
| 328 /* Stupid thing to send a request */ | |
| 329 bev = bufferevent_new(fd, http_readcb, http_writecb, | |
| 330 http_errorcb, NULL); | |
| 331 | |
| 332 /* first half of the http request */ | |
| 333 http_request = | |
| 334 "GET /test HTTP/1.1\r\n" | |
| 335 "Host: some"; | |
| 336 | |
| 337 bufferevent_write(bev, http_request, strlen(http_request)); | |
| 338 timerclear(&tv); | |
| 339 tv.tv_usec = 10000; | |
| 340 event_once(-1, EV_TIMEOUT, http_complete_write, bev, &tv); | |
| 341 | |
| 342 event_dispatch(); | |
| 343 | |
| 344 if (test_ok != 3) { | |
| 345 fprintf(stdout, "FAILED\n"); | |
| 346 exit(1); | |
| 347 } | |
| 348 | |
| 349 /* connect to the second port */ | |
| 350 bufferevent_free(bev); | |
| 351 EVUTIL_CLOSESOCKET(fd); | |
| 352 | |
| 353 fd = http_connect("127.0.0.1", port + 1); | |
| 354 | |
| 355 /* Stupid thing to send a request */ | |
| 356 bev = bufferevent_new(fd, http_readcb, http_writecb, | |
| 357 http_errorcb, NULL); | |
| 358 | |
| 359 http_request = | |
| 360 "GET /test HTTP/1.1\r\n" | |
| 361 "Host: somehost\r\n" | |
| 362 "Connection: close\r\n" | |
| 363 "\r\n"; | |
| 364 | |
| 365 bufferevent_write(bev, http_request, strlen(http_request)); | |
| 366 | |
| 367 event_dispatch(); | |
| 368 | |
| 369 bufferevent_free(bev); | |
| 370 EVUTIL_CLOSESOCKET(fd); | |
| 371 | |
| 372 evhttp_free(http); | |
| 373 | |
| 374 if (test_ok != 5) { | |
| 375 fprintf(stdout, "FAILED\n"); | |
| 376 exit(1); | |
| 377 } | |
| 378 | |
| 379 fprintf(stdout, "OK\n"); | |
| 380 } | |
| 381 | |
| 382 static void | |
| 383 http_badreq_cb(struct evhttp_request *req, void *arg) | |
| 384 { | |
| 385 struct evbuffer *buf = evbuffer_new(); | |
| 386 | |
| 387 evhttp_add_header(req->output_headers, "Content-Type", "text/xml; charse
t=UTF-8"); | |
| 388 evbuffer_add_printf(buf, "Hello, %s!", "127.0.0.1"); | |
| 389 | |
| 390 evhttp_send_reply(req, HTTP_OK, "OK", buf); | |
| 391 evbuffer_free(buf); | |
| 392 } | |
| 393 | |
| 394 static void | |
| 395 http_badreq_errorcb(struct bufferevent *bev, short what, void *arg) | |
| 396 { | |
| 397 event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); | |
| 398 /* ignore */ | |
| 399 } | |
| 400 | |
| 401 static void | |
| 402 http_badreq_readcb(struct bufferevent *bev, void *arg) | |
| 403 { | |
| 404 const char *what = "Hello, 127.0.0.1"; | |
| 405 const char *bad_request = "400 Bad Request"; | |
| 406 | |
| 407 event_debug(("%s: %s\n", __func__, EVBUFFER_DATA(bev->input))); | |
| 408 | |
| 409 if (evbuffer_find(bev->input, | |
| 410 (const unsigned char *) bad_request, strlen(bad_request)) != NUL
L) { | |
| 411 event_debug(("%s: bad request detected", __func__)); | |
| 412 test_ok = -10; | |
| 413 bufferevent_disable(bev, EV_READ); | |
| 414 event_loopexit(NULL); | |
| 415 return; | |
| 416 } | |
| 417 | |
| 418 if (evbuffer_find(bev->input, | |
| 419 (const unsigned char*) what, strlen(what)) != NULL) { | |
| 420 struct evhttp_request *req = evhttp_request_new(NULL, NULL); | |
| 421 enum message_read_status done; | |
| 422 | |
| 423 req->kind = EVHTTP_RESPONSE; | |
| 424 done = evhttp_parse_firstline(req, bev->input); | |
| 425 if (done != ALL_DATA_READ) | |
| 426 goto out; | |
| 427 | |
| 428 done = evhttp_parse_headers(req, bev->input); | |
| 429 if (done != ALL_DATA_READ) | |
| 430 goto out; | |
| 431 | |
| 432 if (done == 1 && | |
| 433 evhttp_find_header(req->input_headers, | |
| 434 "Content-Type") != NULL) | |
| 435 test_ok++; | |
| 436 | |
| 437 out: | |
| 438 evhttp_request_free(req); | |
| 439 evbuffer_drain(bev->input, EVBUFFER_LENGTH(bev->input)); | |
| 440 } | |
| 441 | |
| 442 shutdown(bev->ev_read.ev_fd, SHUT_WR); | |
| 443 } | |
| 444 | |
| 445 static void | |
| 446 http_badreq_successcb(int fd, short what, void *arg) | |
| 447 { | |
| 448 event_debug(("%s: called (what=%04x, arg=%p)", __func__, what, arg)); | |
| 449 event_loopexit(NULL); | |
| 450 } | |
| 451 | |
| 452 static void | |
| 453 http_bad_request(void) | |
| 454 { | |
| 455 struct timeval tv; | |
| 456 struct bufferevent *bev; | |
| 457 int fd; | |
| 458 const char *http_request; | |
| 459 short port = -1; | |
| 460 | |
| 461 test_ok = 0; | |
| 462 fprintf(stdout, "Testing \"Bad Request\" on connection close: "); | |
| 463 | |
| 464 http = http_setup(&port, NULL); | |
| 465 | |
| 466 /* bind to a second socket */ | |
| 467 if (evhttp_bind_socket(http, "127.0.0.1", port + 1) == -1) { | |
| 468 fprintf(stdout, "FAILED (bind)\n"); | |
| 469 exit(1); | |
| 470 } | |
| 471 | |
| 472 /* NULL request test */ | |
| 473 fd = http_connect("127.0.0.1", port); | |
| 474 | |
| 475 /* Stupid thing to send a request */ | |
| 476 bev = bufferevent_new(fd, http_badreq_readcb, http_writecb, | |
| 477 http_badreq_errorcb, NULL); | |
| 478 bufferevent_enable(bev, EV_READ); | |
| 479 | |
| 480 /* real NULL request */ | |
| 481 http_request = ""; | |
| 482 | |
| 483 shutdown(fd, SHUT_WR); | |
| 484 timerclear(&tv); | |
| 485 tv.tv_usec = 10000; | |
| 486 event_once(-1, EV_TIMEOUT, http_badreq_successcb, bev, &tv); | |
| 487 | |
| 488 event_dispatch(); | |
| 489 | |
| 490 bufferevent_free(bev); | |
| 491 EVUTIL_CLOSESOCKET(fd); | |
| 492 | |
| 493 if (test_ok != 0) { | |
| 494 fprintf(stdout, "FAILED\n"); | |
| 495 exit(1); | |
| 496 } | |
| 497 | |
| 498 /* Second answer (BAD REQUEST) on connection close */ | |
| 499 | |
| 500 /* connect to the second port */ | |
| 501 fd = http_connect("127.0.0.1", port + 1); | |
| 502 | |
| 503 /* Stupid thing to send a request */ | |
| 504 bev = bufferevent_new(fd, http_badreq_readcb, http_writecb, | |
| 505 http_badreq_errorcb, NULL); | |
| 506 bufferevent_enable(bev, EV_READ); | |
| 507 | |
| 508 /* first half of the http request */ | |
| 509 http_request = | |
| 510 "GET /badrequest HTTP/1.0\r\n" \ | |
| 511 "Connection: Keep-Alive\r\n" \ | |
| 512 "\r\n"; | |
| 513 | |
| 514 bufferevent_write(bev, http_request, strlen(http_request)); | |
| 515 | |
| 516 timerclear(&tv); | |
| 517 tv.tv_usec = 10000; | |
| 518 event_once(-1, EV_TIMEOUT, http_badreq_successcb, bev, &tv); | |
| 519 | |
| 520 event_dispatch(); | |
| 521 | |
| 522 evhttp_free(http); | |
| 523 | |
| 524 if (test_ok != 2) { | |
| 525 fprintf(stdout, "FAILED\n"); | |
| 526 exit(1); | |
| 527 } | |
| 528 | |
| 529 fprintf(stdout, "OK\n"); | |
| 530 } | |
| 531 static struct evhttp_connection *delayed_client; | |
| 532 | |
| 533 static void | |
| 534 http_delay_reply(int fd, short what, void *arg) | |
| 535 { | |
| 536 struct evhttp_request *req = arg; | |
| 537 | |
| 538 evhttp_send_reply(req, HTTP_OK, "Everything is fine", NULL); | |
| 539 | |
| 540 ++test_ok; | |
| 541 } | |
| 542 | |
| 543 static void | |
| 544 http_large_delay_cb(struct evhttp_request *req, void *arg) | |
| 545 { | |
| 546 struct timeval tv; | |
| 547 timerclear(&tv); | |
| 548 tv.tv_sec = 3; | |
| 549 | |
| 550 event_once(-1, EV_TIMEOUT, http_delay_reply, req, &tv); | |
| 551 | |
| 552 /* here we close the client connection which will cause an EOF */ | |
| 553 evhttp_connection_fail(delayed_client, EVCON_HTTP_EOF); | |
| 554 } | |
| 555 | |
| 556 void http_request_done(struct evhttp_request *, void *); | |
| 557 void http_request_empty_done(struct evhttp_request *, void *); | |
| 558 | |
| 559 static void | |
| 560 http_connection_test(int persistent) | |
| 561 { | |
| 562 short port = -1; | |
| 563 struct evhttp_connection *evcon = NULL; | |
| 564 struct evhttp_request *req = NULL; | |
| 565 | |
| 566 test_ok = 0; | |
| 567 fprintf(stdout, "Testing Request Connection Pipeline %s: ", | |
| 568 persistent ? "(persistent)" : ""); | |
| 569 | |
| 570 http = http_setup(&port, NULL); | |
| 571 | |
| 572 evcon = evhttp_connection_new("127.0.0.1", port); | |
| 573 if (evcon == NULL) { | |
| 574 fprintf(stdout, "FAILED\n"); | |
| 575 exit(1); | |
| 576 } | |
| 577 | |
| 578 /* | |
| 579 * At this point, we want to schedule a request to the HTTP | |
| 580 * server using our make request method. | |
| 581 */ | |
| 582 | |
| 583 req = evhttp_request_new(http_request_done, NULL); | |
| 584 | |
| 585 /* Add the information that we care about */ | |
| 586 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
| 587 | |
| 588 /* We give ownership of the request to the connection */ | |
| 589 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { | |
| 590 fprintf(stdout, "FAILED\n"); | |
| 591 exit(1); | |
| 592 } | |
| 593 | |
| 594 event_dispatch(); | |
| 595 | |
| 596 if (test_ok != 1) { | |
| 597 fprintf(stdout, "FAILED\n"); | |
| 598 exit(1); | |
| 599 } | |
| 600 | |
| 601 /* try to make another request over the same connection */ | |
| 602 test_ok = 0; | |
| 603 | |
| 604 req = evhttp_request_new(http_request_done, NULL); | |
| 605 | |
| 606 /* Add the information that we care about */ | |
| 607 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
| 608 | |
| 609 /* | |
| 610 * if our connections are not supposed to be persistent; request | |
| 611 * a close from the server. | |
| 612 */ | |
| 613 if (!persistent) | |
| 614 evhttp_add_header(req->output_headers, "Connection", "close"); | |
| 615 | |
| 616 /* We give ownership of the request to the connection */ | |
| 617 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { | |
| 618 fprintf(stdout, "FAILED\n"); | |
| 619 exit(1); | |
| 620 } | |
| 621 | |
| 622 event_dispatch(); | |
| 623 | |
| 624 /* make another request: request empty reply */ | |
| 625 test_ok = 0; | |
| 626 | |
| 627 req = evhttp_request_new(http_request_empty_done, NULL); | |
| 628 | |
| 629 /* Add the information that we care about */ | |
| 630 evhttp_add_header(req->output_headers, "Empty", "itis"); | |
| 631 | |
| 632 /* We give ownership of the request to the connection */ | |
| 633 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { | |
| 634 fprintf(stdout, "FAILED\n"); | |
| 635 exit(1); | |
| 636 } | |
| 637 | |
| 638 event_dispatch(); | |
| 639 | |
| 640 if (test_ok != 1) { | |
| 641 fprintf(stdout, "FAILED\n"); | |
| 642 exit(1); | |
| 643 } | |
| 644 | |
| 645 evhttp_connection_free(evcon); | |
| 646 evhttp_free(http); | |
| 647 | |
| 648 fprintf(stdout, "OK\n"); | |
| 649 } | |
| 650 | |
| 651 void | |
| 652 http_request_done(struct evhttp_request *req, void *arg) | |
| 653 { | |
| 654 const char *what = "This is funny"; | |
| 655 | |
| 656 if (req->response_code != HTTP_OK) { | |
| 657 fprintf(stderr, "FAILED\n"); | |
| 658 exit(1); | |
| 659 } | |
| 660 | |
| 661 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { | |
| 662 fprintf(stderr, "FAILED\n"); | |
| 663 exit(1); | |
| 664 } | |
| 665 | |
| 666 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { | |
| 667 fprintf(stderr, "FAILED\n"); | |
| 668 exit(1); | |
| 669 } | |
| 670 | |
| 671 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { | |
| 672 fprintf(stderr, "FAILED\n"); | |
| 673 exit(1); | |
| 674 } | |
| 675 | |
| 676 test_ok = 1; | |
| 677 event_loopexit(NULL); | |
| 678 } | |
| 679 | |
| 680 /* test date header and content length */ | |
| 681 | |
| 682 void | |
| 683 http_request_empty_done(struct evhttp_request *req, void *arg) | |
| 684 { | |
| 685 if (req->response_code != HTTP_OK) { | |
| 686 fprintf(stderr, "FAILED\n"); | |
| 687 exit(1); | |
| 688 } | |
| 689 | |
| 690 if (evhttp_find_header(req->input_headers, "Date") == NULL) { | |
| 691 fprintf(stderr, "FAILED\n"); | |
| 692 exit(1); | |
| 693 } | |
| 694 | |
| 695 | |
| 696 if (evhttp_find_header(req->input_headers, "Content-Length") == NULL) { | |
| 697 fprintf(stderr, "FAILED\n"); | |
| 698 exit(1); | |
| 699 } | |
| 700 | |
| 701 if (strcmp(evhttp_find_header(req->input_headers, "Content-Length"), | |
| 702 "0")) { | |
| 703 fprintf(stderr, "FAILED\n"); | |
| 704 exit(1); | |
| 705 } | |
| 706 | |
| 707 if (EVBUFFER_LENGTH(req->input_buffer) != 0) { | |
| 708 fprintf(stderr, "FAILED\n"); | |
| 709 exit(1); | |
| 710 } | |
| 711 | |
| 712 test_ok = 1; | |
| 713 event_loopexit(NULL); | |
| 714 } | |
| 715 | |
| 716 /* | |
| 717 * HTTP DISPATCHER test | |
| 718 */ | |
| 719 | |
| 720 void | |
| 721 http_dispatcher_cb(struct evhttp_request *req, void *arg) | |
| 722 { | |
| 723 | |
| 724 struct evbuffer *evb = evbuffer_new(); | |
| 725 event_debug(("%s: called\n", __func__)); | |
| 726 evbuffer_add_printf(evb, "DISPATCHER_TEST"); | |
| 727 | |
| 728 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); | |
| 729 | |
| 730 evbuffer_free(evb); | |
| 731 } | |
| 732 | |
| 733 static void | |
| 734 http_dispatcher_test_done(struct evhttp_request *req, void *arg) | |
| 735 { | |
| 736 const char *what = "DISPATCHER_TEST"; | |
| 737 | |
| 738 if (req->response_code != HTTP_OK) { | |
| 739 fprintf(stderr, "FAILED\n"); | |
| 740 exit(1); | |
| 741 } | |
| 742 | |
| 743 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { | |
| 744 fprintf(stderr, "FAILED (content type)\n"); | |
| 745 exit(1); | |
| 746 } | |
| 747 | |
| 748 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { | |
| 749 fprintf(stderr, "FAILED (length %zu vs %zu)\n", | |
| 750 EVBUFFER_LENGTH(req->input_buffer), strlen(what)); | |
| 751 exit(1); | |
| 752 } | |
| 753 | |
| 754 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { | |
| 755 fprintf(stderr, "FAILED (data)\n"); | |
| 756 exit(1); | |
| 757 } | |
| 758 | |
| 759 test_ok = 1; | |
| 760 event_loopexit(NULL); | |
| 761 } | |
| 762 | |
| 763 static void | |
| 764 http_dispatcher_test(void) | |
| 765 { | |
| 766 short port = -1; | |
| 767 struct evhttp_connection *evcon = NULL; | |
| 768 struct evhttp_request *req = NULL; | |
| 769 | |
| 770 test_ok = 0; | |
| 771 fprintf(stdout, "Testing HTTP Dispatcher: "); | |
| 772 | |
| 773 http = http_setup(&port, NULL); | |
| 774 | |
| 775 evcon = evhttp_connection_new("127.0.0.1", port); | |
| 776 if (evcon == NULL) { | |
| 777 fprintf(stdout, "FAILED\n"); | |
| 778 exit(1); | |
| 779 } | |
| 780 | |
| 781 /* also bind to local host */ | |
| 782 evhttp_connection_set_local_address(evcon, "127.0.0.1"); | |
| 783 | |
| 784 /* | |
| 785 * At this point, we want to schedule an HTTP GET request | |
| 786 * server using our make request method. | |
| 787 */ | |
| 788 | |
| 789 req = evhttp_request_new(http_dispatcher_test_done, NULL); | |
| 790 if (req == NULL) { | |
| 791 fprintf(stdout, "FAILED\n"); | |
| 792 exit(1); | |
| 793 } | |
| 794 | |
| 795 /* Add the information that we care about */ | |
| 796 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
| 797 | |
| 798 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/?arg=val") == -1)
{ | |
| 799 fprintf(stdout, "FAILED\n"); | |
| 800 exit(1); | |
| 801 } | |
| 802 | |
| 803 event_dispatch(); | |
| 804 | |
| 805 evhttp_connection_free(evcon); | |
| 806 evhttp_free(http); | |
| 807 | |
| 808 if (test_ok != 1) { | |
| 809 fprintf(stdout, "FAILED: %d\n", test_ok); | |
| 810 exit(1); | |
| 811 } | |
| 812 | |
| 813 fprintf(stdout, "OK\n"); | |
| 814 } | |
| 815 | |
| 816 /* | |
| 817 * HTTP POST test. | |
| 818 */ | |
| 819 | |
| 820 void http_postrequest_done(struct evhttp_request *, void *); | |
| 821 | |
| 822 #define POST_DATA "Okay. Not really printf" | |
| 823 | |
| 824 static void | |
| 825 http_post_test(void) | |
| 826 { | |
| 827 short port = -1; | |
| 828 struct evhttp_connection *evcon = NULL; | |
| 829 struct evhttp_request *req = NULL; | |
| 830 | |
| 831 test_ok = 0; | |
| 832 fprintf(stdout, "Testing HTTP POST Request: "); | |
| 833 | |
| 834 http = http_setup(&port, NULL); | |
| 835 | |
| 836 evcon = evhttp_connection_new("127.0.0.1", port); | |
| 837 if (evcon == NULL) { | |
| 838 fprintf(stdout, "FAILED\n"); | |
| 839 exit(1); | |
| 840 } | |
| 841 | |
| 842 /* | |
| 843 * At this point, we want to schedule an HTTP POST request | |
| 844 * server using our make request method. | |
| 845 */ | |
| 846 | |
| 847 req = evhttp_request_new(http_postrequest_done, NULL); | |
| 848 if (req == NULL) { | |
| 849 fprintf(stdout, "FAILED\n"); | |
| 850 exit(1); | |
| 851 } | |
| 852 | |
| 853 /* Add the information that we care about */ | |
| 854 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
| 855 evbuffer_add_printf(req->output_buffer, POST_DATA); | |
| 856 | |
| 857 if (evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/postit") == -1) { | |
| 858 fprintf(stdout, "FAILED\n"); | |
| 859 exit(1); | |
| 860 } | |
| 861 | |
| 862 event_dispatch(); | |
| 863 | |
| 864 evhttp_connection_free(evcon); | |
| 865 evhttp_free(http); | |
| 866 | |
| 867 if (test_ok != 1) { | |
| 868 fprintf(stdout, "FAILED: %d\n", test_ok); | |
| 869 exit(1); | |
| 870 } | |
| 871 | |
| 872 fprintf(stdout, "OK\n"); | |
| 873 } | |
| 874 | |
| 875 void | |
| 876 http_post_cb(struct evhttp_request *req, void *arg) | |
| 877 { | |
| 878 struct evbuffer *evb; | |
| 879 event_debug(("%s: called\n", __func__)); | |
| 880 | |
| 881 /* Yes, we are expecting a post request */ | |
| 882 if (req->type != EVHTTP_REQ_POST) { | |
| 883 fprintf(stdout, "FAILED (post type)\n"); | |
| 884 exit(1); | |
| 885 } | |
| 886 | |
| 887 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(POST_DATA)) { | |
| 888 fprintf(stdout, "FAILED (length: %zu vs %zu)\n", | |
| 889 EVBUFFER_LENGTH(req->input_buffer), strlen(POST_DATA)); | |
| 890 exit(1); | |
| 891 } | |
| 892 | |
| 893 if (memcmp(EVBUFFER_DATA(req->input_buffer), POST_DATA, | |
| 894 strlen(POST_DATA))) { | |
| 895 fprintf(stdout, "FAILED (data)\n"); | |
| 896 fprintf(stdout, "Got :%s\n", EVBUFFER_DATA(req->input_buffer)); | |
| 897 fprintf(stdout, "Want:%s\n", POST_DATA); | |
| 898 exit(1); | |
| 899 } | |
| 900 | |
| 901 evb = evbuffer_new(); | |
| 902 evbuffer_add_printf(evb, "This is funny"); | |
| 903 | |
| 904 evhttp_send_reply(req, HTTP_OK, "Everything is fine", evb); | |
| 905 | |
| 906 evbuffer_free(evb); | |
| 907 } | |
| 908 | |
| 909 void | |
| 910 http_postrequest_done(struct evhttp_request *req, void *arg) | |
| 911 { | |
| 912 const char *what = "This is funny"; | |
| 913 | |
| 914 if (req == NULL) { | |
| 915 fprintf(stderr, "FAILED (timeout)\n"); | |
| 916 exit(1); | |
| 917 } | |
| 918 | |
| 919 if (req->response_code != HTTP_OK) { | |
| 920 | |
| 921 fprintf(stderr, "FAILED (response code)\n"); | |
| 922 exit(1); | |
| 923 } | |
| 924 | |
| 925 if (evhttp_find_header(req->input_headers, "Content-Type") == NULL) { | |
| 926 fprintf(stderr, "FAILED (content type)\n"); | |
| 927 exit(1); | |
| 928 } | |
| 929 | |
| 930 if (EVBUFFER_LENGTH(req->input_buffer) != strlen(what)) { | |
| 931 fprintf(stderr, "FAILED (length %zu vs %zu)\n", | |
| 932 EVBUFFER_LENGTH(req->input_buffer), strlen(what)); | |
| 933 exit(1); | |
| 934 } | |
| 935 | |
| 936 if (memcmp(EVBUFFER_DATA(req->input_buffer), what, strlen(what)) != 0) { | |
| 937 fprintf(stderr, "FAILED (data)\n"); | |
| 938 exit(1); | |
| 939 } | |
| 940 | |
| 941 test_ok = 1; | |
| 942 event_loopexit(NULL); | |
| 943 } | |
| 944 | |
| 945 static void | |
| 946 http_failure_readcb(struct bufferevent *bev, void *arg) | |
| 947 { | |
| 948 const char *what = "400 Bad Request"; | |
| 949 if (evbuffer_find(bev->input, (const unsigned char*) what, strlen(what))
!= NULL) { | |
| 950 test_ok = 2; | |
| 951 bufferevent_disable(bev, EV_READ); | |
| 952 event_loopexit(NULL); | |
| 953 } | |
| 954 } | |
| 955 | |
| 956 /* | |
| 957 * Testing that the HTTP server can deal with a malformed request. | |
| 958 */ | |
| 959 static void | |
| 960 http_failure_test(void) | |
| 961 { | |
| 962 struct bufferevent *bev; | |
| 963 int fd; | |
| 964 const char *http_request; | |
| 965 short port = -1; | |
| 966 | |
| 967 test_ok = 0; | |
| 968 fprintf(stdout, "Testing Bad HTTP Request: "); | |
| 969 | |
| 970 http = http_setup(&port, NULL); | |
| 971 | |
| 972 fd = http_connect("127.0.0.1", port); | |
| 973 | |
| 974 /* Stupid thing to send a request */ | |
| 975 bev = bufferevent_new(fd, http_failure_readcb, http_writecb, | |
| 976 http_errorcb, NULL); | |
| 977 | |
| 978 http_request = "illegal request\r\n"; | |
| 979 | |
| 980 bufferevent_write(bev, http_request, strlen(http_request)); | |
| 981 | |
| 982 event_dispatch(); | |
| 983 | |
| 984 bufferevent_free(bev); | |
| 985 EVUTIL_CLOSESOCKET(fd); | |
| 986 | |
| 987 evhttp_free(http); | |
| 988 | |
| 989 if (test_ok != 2) { | |
| 990 fprintf(stdout, "FAILED\n"); | |
| 991 exit(1); | |
| 992 } | |
| 993 | |
| 994 fprintf(stdout, "OK\n"); | |
| 995 } | |
| 996 | |
| 997 static void | |
| 998 close_detect_done(struct evhttp_request *req, void *arg) | |
| 999 { | |
| 1000 struct timeval tv; | |
| 1001 if (req == NULL || req->response_code != HTTP_OK) { | |
| 1002 | |
| 1003 fprintf(stderr, "FAILED\n"); | |
| 1004 exit(1); | |
| 1005 } | |
| 1006 | |
| 1007 test_ok = 1; | |
| 1008 | |
| 1009 timerclear(&tv); | |
| 1010 tv.tv_sec = 3; /* longer than the http time out */ | |
| 1011 | |
| 1012 event_loopexit(&tv); | |
| 1013 } | |
| 1014 | |
| 1015 static void | |
| 1016 close_detect_launch(int fd, short what, void *arg) | |
| 1017 { | |
| 1018 struct evhttp_connection *evcon = arg; | |
| 1019 struct evhttp_request *req; | |
| 1020 | |
| 1021 req = evhttp_request_new(close_detect_done, NULL); | |
| 1022 | |
| 1023 /* Add the information that we care about */ | |
| 1024 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
| 1025 | |
| 1026 /* We give ownership of the request to the connection */ | |
| 1027 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { | |
| 1028 fprintf(stdout, "FAILED\n"); | |
| 1029 exit(1); | |
| 1030 } | |
| 1031 } | |
| 1032 | |
| 1033 static void | |
| 1034 close_detect_cb(struct evhttp_request *req, void *arg) | |
| 1035 { | |
| 1036 struct evhttp_connection *evcon = arg; | |
| 1037 struct timeval tv; | |
| 1038 | |
| 1039 if (req != NULL && req->response_code != HTTP_OK) { | |
| 1040 | |
| 1041 fprintf(stderr, "FAILED\n"); | |
| 1042 exit(1); | |
| 1043 } | |
| 1044 | |
| 1045 timerclear(&tv); | |
| 1046 tv.tv_sec = 3; /* longer than the http time out */ | |
| 1047 | |
| 1048 /* launch a new request on the persistent connection in 6 seconds */ | |
| 1049 event_once(-1, EV_TIMEOUT, close_detect_launch, evcon, &tv); | |
| 1050 } | |
| 1051 | |
| 1052 | |
| 1053 static void | |
| 1054 http_close_detection(int with_delay) | |
| 1055 { | |
| 1056 short port = -1; | |
| 1057 struct evhttp_connection *evcon = NULL; | |
| 1058 struct evhttp_request *req = NULL; | |
| 1059 | |
| 1060 test_ok = 0; | |
| 1061 fprintf(stdout, "Testing Connection Close Detection%s: ", | |
| 1062 with_delay ? " (with delay)" : ""); | |
| 1063 | |
| 1064 http = http_setup(&port, NULL); | |
| 1065 | |
| 1066 /* 2 second timeout */ | |
| 1067 evhttp_set_timeout(http, 2); | |
| 1068 | |
| 1069 evcon = evhttp_connection_new("127.0.0.1", port); | |
| 1070 if (evcon == NULL) { | |
| 1071 fprintf(stdout, "FAILED\n"); | |
| 1072 exit(1); | |
| 1073 } | |
| 1074 | |
| 1075 delayed_client = evcon; | |
| 1076 | |
| 1077 /* | |
| 1078 * At this point, we want to schedule a request to the HTTP | |
| 1079 * server using our make request method. | |
| 1080 */ | |
| 1081 | |
| 1082 req = evhttp_request_new(close_detect_cb, evcon); | |
| 1083 | |
| 1084 /* Add the information that we care about */ | |
| 1085 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
| 1086 | |
| 1087 /* We give ownership of the request to the connection */ | |
| 1088 if (evhttp_make_request(evcon, | |
| 1089 req, EVHTTP_REQ_GET, with_delay ? "/largedelay" : "/test") == -1) { | |
| 1090 fprintf(stdout, "FAILED\n"); | |
| 1091 exit(1); | |
| 1092 } | |
| 1093 | |
| 1094 event_dispatch(); | |
| 1095 | |
| 1096 if (test_ok != 1) { | |
| 1097 fprintf(stdout, "FAILED\n"); | |
| 1098 exit(1); | |
| 1099 } | |
| 1100 | |
| 1101 /* at this point, the http server should have no connection */ | |
| 1102 if (TAILQ_FIRST(&http->connections) != NULL) { | |
| 1103 fprintf(stdout, "FAILED (left connections)\n"); | |
| 1104 exit(1); | |
| 1105 } | |
| 1106 | |
| 1107 evhttp_connection_free(evcon); | |
| 1108 evhttp_free(http); | |
| 1109 | |
| 1110 fprintf(stdout, "OK\n"); | |
| 1111 } | |
| 1112 | |
| 1113 static void | |
| 1114 http_highport_test(void) | |
| 1115 { | |
| 1116 int i = -1; | |
| 1117 struct evhttp *myhttp = NULL; | |
| 1118 | |
| 1119 fprintf(stdout, "Testing HTTP Server with high port: "); | |
| 1120 | |
| 1121 /* Try a few different ports */ | |
| 1122 for (i = 0; i < 50; ++i) { | |
| 1123 myhttp = evhttp_start("127.0.0.1", 65535 - i); | |
| 1124 if (myhttp != NULL) { | |
| 1125 fprintf(stdout, "OK\n"); | |
| 1126 evhttp_free(myhttp); | |
| 1127 return; | |
| 1128 } | |
| 1129 } | |
| 1130 | |
| 1131 fprintf(stdout, "FAILED\n"); | |
| 1132 exit(1); | |
| 1133 } | |
| 1134 | |
| 1135 static void | |
| 1136 http_bad_header_test(void) | |
| 1137 { | |
| 1138 struct evkeyvalq headers; | |
| 1139 | |
| 1140 fprintf(stdout, "Testing HTTP Header filtering: "); | |
| 1141 | |
| 1142 TAILQ_INIT(&headers); | |
| 1143 | |
| 1144 if (evhttp_add_header(&headers, "One", "Two") != 0) | |
| 1145 goto fail; | |
| 1146 | |
| 1147 if (evhttp_add_header(&headers, "One\r", "Two") != -1) | |
| 1148 goto fail; | |
| 1149 if (evhttp_add_header(&headers, "One", "Two") != 0) | |
| 1150 goto fail; | |
| 1151 if (evhttp_add_header(&headers, "One", "Two\r\n Three") != 0) | |
| 1152 goto fail; | |
| 1153 if (evhttp_add_header(&headers, "One\r", "Two") != -1) | |
| 1154 goto fail; | |
| 1155 if (evhttp_add_header(&headers, "One\n", "Two") != -1) | |
| 1156 goto fail; | |
| 1157 if (evhttp_add_header(&headers, "One", "Two\r") != -1) | |
| 1158 goto fail; | |
| 1159 if (evhttp_add_header(&headers, "One", "Two\n") != -1) | |
| 1160 goto fail; | |
| 1161 | |
| 1162 evhttp_clear_headers(&headers); | |
| 1163 | |
| 1164 fprintf(stdout, "OK\n"); | |
| 1165 return; | |
| 1166 fail: | |
| 1167 fprintf(stdout, "FAILED\n"); | |
| 1168 exit(1); | |
| 1169 } | |
| 1170 | |
| 1171 static int validate_header( | |
| 1172 const struct evkeyvalq* headers, | |
| 1173 const char *key, const char *value) | |
| 1174 { | |
| 1175 const char *real_val = evhttp_find_header(headers, key); | |
| 1176 if (real_val == NULL) | |
| 1177 return (-1); | |
| 1178 if (strcmp(real_val, value) != 0) | |
| 1179 return (-1); | |
| 1180 return (0); | |
| 1181 } | |
| 1182 | |
| 1183 static void | |
| 1184 http_parse_query_test(void) | |
| 1185 { | |
| 1186 struct evkeyvalq headers; | |
| 1187 | |
| 1188 fprintf(stdout, "Testing HTTP query parsing: "); | |
| 1189 | |
| 1190 TAILQ_INIT(&headers); | |
| 1191 | |
| 1192 evhttp_parse_query("http://www.test.com/?q=test", &headers); | |
| 1193 if (validate_header(&headers, "q", "test") != 0) | |
| 1194 goto fail; | |
| 1195 evhttp_clear_headers(&headers); | |
| 1196 | |
| 1197 evhttp_parse_query("http://www.test.com/?q=test&foo=bar", &headers); | |
| 1198 if (validate_header(&headers, "q", "test") != 0) | |
| 1199 goto fail; | |
| 1200 if (validate_header(&headers, "foo", "bar") != 0) | |
| 1201 goto fail; | |
| 1202 evhttp_clear_headers(&headers); | |
| 1203 | |
| 1204 evhttp_parse_query("http://www.test.com/?q=test+foo", &headers); | |
| 1205 if (validate_header(&headers, "q", "test foo") != 0) | |
| 1206 goto fail; | |
| 1207 evhttp_clear_headers(&headers); | |
| 1208 | |
| 1209 evhttp_parse_query("http://www.test.com/?q=test%0Afoo", &headers); | |
| 1210 if (validate_header(&headers, "q", "test\nfoo") != 0) | |
| 1211 goto fail; | |
| 1212 evhttp_clear_headers(&headers); | |
| 1213 | |
| 1214 evhttp_parse_query("http://www.test.com/?q=test%0Dfoo", &headers); | |
| 1215 if (validate_header(&headers, "q", "test\rfoo") != 0) | |
| 1216 goto fail; | |
| 1217 evhttp_clear_headers(&headers); | |
| 1218 | |
| 1219 fprintf(stdout, "OK\n"); | |
| 1220 return; | |
| 1221 fail: | |
| 1222 fprintf(stdout, "FAILED\n"); | |
| 1223 exit(1); | |
| 1224 } | |
| 1225 | |
| 1226 static void | |
| 1227 http_base_test(void) | |
| 1228 { | |
| 1229 struct bufferevent *bev; | |
| 1230 int fd; | |
| 1231 const char *http_request; | |
| 1232 short port = -1; | |
| 1233 | |
| 1234 test_ok = 0; | |
| 1235 fprintf(stdout, "Testing HTTP Server Event Base: "); | |
| 1236 | |
| 1237 base = event_init(); | |
| 1238 | |
| 1239 /* | |
| 1240 * create another bogus base - which is being used by all subsequen | |
| 1241 * tests - yuck! | |
| 1242 */ | |
| 1243 event_init(); | |
| 1244 | |
| 1245 http = http_setup(&port, base); | |
| 1246 | |
| 1247 fd = http_connect("127.0.0.1", port); | |
| 1248 | |
| 1249 /* Stupid thing to send a request */ | |
| 1250 bev = bufferevent_new(fd, http_readcb, http_writecb, | |
| 1251 http_errorcb, NULL); | |
| 1252 bufferevent_base_set(base, bev); | |
| 1253 | |
| 1254 http_request = | |
| 1255 "GET /test HTTP/1.1\r\n" | |
| 1256 "Host: somehost\r\n" | |
| 1257 "Connection: close\r\n" | |
| 1258 "\r\n"; | |
| 1259 | |
| 1260 bufferevent_write(bev, http_request, strlen(http_request)); | |
| 1261 | |
| 1262 event_base_dispatch(base); | |
| 1263 | |
| 1264 bufferevent_free(bev); | |
| 1265 EVUTIL_CLOSESOCKET(fd); | |
| 1266 | |
| 1267 evhttp_free(http); | |
| 1268 | |
| 1269 event_base_free(base); | |
| 1270 base = NULL; | |
| 1271 | |
| 1272 if (test_ok != 2) { | |
| 1273 fprintf(stdout, "FAILED\n"); | |
| 1274 exit(1); | |
| 1275 } | |
| 1276 | |
| 1277 fprintf(stdout, "OK\n"); | |
| 1278 } | |
| 1279 | |
| 1280 /* | |
| 1281 * the server is going to reply with chunked data. | |
| 1282 */ | |
| 1283 | |
| 1284 static void | |
| 1285 http_chunked_readcb(struct bufferevent *bev, void *arg) | |
| 1286 { | |
| 1287 /* nothing here */ | |
| 1288 } | |
| 1289 | |
| 1290 static void | |
| 1291 http_chunked_errorcb(struct bufferevent *bev, short what, void *arg) | |
| 1292 { | |
| 1293 if (!test_ok) | |
| 1294 goto out; | |
| 1295 | |
| 1296 test_ok = -1; | |
| 1297 | |
| 1298 if ((what & EVBUFFER_EOF) != 0) { | |
| 1299 struct evhttp_request *req = evhttp_request_new(NULL, NULL); | |
| 1300 const char *header; | |
| 1301 enum message_read_status done; | |
| 1302 | |
| 1303 req->kind = EVHTTP_RESPONSE; | |
| 1304 done = evhttp_parse_firstline(req, EVBUFFER_INPUT(bev)); | |
| 1305 if (done != ALL_DATA_READ) | |
| 1306 goto out; | |
| 1307 | |
| 1308 done = evhttp_parse_headers(req, EVBUFFER_INPUT(bev)); | |
| 1309 if (done != ALL_DATA_READ) | |
| 1310 goto out; | |
| 1311 | |
| 1312 header = evhttp_find_header(req->input_headers, "Transfer-Encodi
ng"); | |
| 1313 if (header == NULL || strcmp(header, "chunked")) | |
| 1314 goto out; | |
| 1315 | |
| 1316 header = evhttp_find_header(req->input_headers, "Connection"); | |
| 1317 if (header == NULL || strcmp(header, "close")) | |
| 1318 goto out; | |
| 1319 | |
| 1320 header = evbuffer_readline(EVBUFFER_INPUT(bev)); | |
| 1321 if (header == NULL) | |
| 1322 goto out; | |
| 1323 /* 13 chars */ | |
| 1324 if (strcmp(header, "d")) | |
| 1325 goto out; | |
| 1326 free((char*)header); | |
| 1327 | |
| 1328 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), | |
| 1329 "This is funny", 13)) | |
| 1330 goto out; | |
| 1331 | |
| 1332 evbuffer_drain(EVBUFFER_INPUT(bev), 13 + 2); | |
| 1333 | |
| 1334 header = evbuffer_readline(EVBUFFER_INPUT(bev)); | |
| 1335 if (header == NULL) | |
| 1336 goto out; | |
| 1337 /* 18 chars */ | |
| 1338 if (strcmp(header, "12")) | |
| 1339 goto out; | |
| 1340 free((char *)header); | |
| 1341 | |
| 1342 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), | |
| 1343 "but not hilarious.", 18)) | |
| 1344 goto out; | |
| 1345 | |
| 1346 evbuffer_drain(EVBUFFER_INPUT(bev), 18 + 2); | |
| 1347 | |
| 1348 header = evbuffer_readline(EVBUFFER_INPUT(bev)); | |
| 1349 if (header == NULL) | |
| 1350 goto out; | |
| 1351 /* 8 chars */ | |
| 1352 if (strcmp(header, "8")) | |
| 1353 goto out; | |
| 1354 free((char *)header); | |
| 1355 | |
| 1356 if (strncmp((char *)EVBUFFER_DATA(EVBUFFER_INPUT(bev)), | |
| 1357 "bwv 1052.", 8)) | |
| 1358 goto out; | |
| 1359 | |
| 1360 evbuffer_drain(EVBUFFER_INPUT(bev), 8 + 2); | |
| 1361 | |
| 1362 header = evbuffer_readline(EVBUFFER_INPUT(bev)); | |
| 1363 if (header == NULL) | |
| 1364 goto out; | |
| 1365 /* 0 chars */ | |
| 1366 if (strcmp(header, "0")) | |
| 1367 goto out; | |
| 1368 free((char *)header); | |
| 1369 | |
| 1370 test_ok = 2; | |
| 1371 } | |
| 1372 | |
| 1373 out: | |
| 1374 event_loopexit(NULL); | |
| 1375 } | |
| 1376 | |
| 1377 static void | |
| 1378 http_chunked_writecb(struct bufferevent *bev, void *arg) | |
| 1379 { | |
| 1380 if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(bev)) == 0) { | |
| 1381 /* enable reading of the reply */ | |
| 1382 bufferevent_enable(bev, EV_READ); | |
| 1383 test_ok++; | |
| 1384 } | |
| 1385 } | |
| 1386 | |
| 1387 static void | |
| 1388 http_chunked_request_done(struct evhttp_request *req, void *arg) | |
| 1389 { | |
| 1390 if (req->response_code != HTTP_OK) { | |
| 1391 fprintf(stderr, "FAILED\n"); | |
| 1392 exit(1); | |
| 1393 } | |
| 1394 | |
| 1395 if (evhttp_find_header(req->input_headers, | |
| 1396 "Transfer-Encoding") == NULL) { | |
| 1397 fprintf(stderr, "FAILED\n"); | |
| 1398 exit(1); | |
| 1399 } | |
| 1400 | |
| 1401 if (EVBUFFER_LENGTH(req->input_buffer) != 13 + 18 + 8) { | |
| 1402 fprintf(stderr, "FAILED\n"); | |
| 1403 exit(1); | |
| 1404 } | |
| 1405 | |
| 1406 if (strncmp((char *)EVBUFFER_DATA(req->input_buffer), | |
| 1407 "This is funnybut not hilarious.bwv 1052", | |
| 1408 13 + 18 + 8)) { | |
| 1409 fprintf(stderr, "FAILED\n"); | |
| 1410 exit(1); | |
| 1411 } | |
| 1412 | |
| 1413 test_ok = 1; | |
| 1414 event_loopexit(NULL); | |
| 1415 } | |
| 1416 | |
| 1417 static void | |
| 1418 http_chunked_test(void) | |
| 1419 { | |
| 1420 struct bufferevent *bev; | |
| 1421 int fd; | |
| 1422 const char *http_request; | |
| 1423 short port = -1; | |
| 1424 struct timeval tv_start, tv_end; | |
| 1425 struct evhttp_connection *evcon = NULL; | |
| 1426 struct evhttp_request *req = NULL; | |
| 1427 int i; | |
| 1428 | |
| 1429 test_ok = 0; | |
| 1430 fprintf(stdout, "Testing Chunked HTTP Reply: "); | |
| 1431 | |
| 1432 http = http_setup(&port, NULL); | |
| 1433 | |
| 1434 fd = http_connect("127.0.0.1", port); | |
| 1435 | |
| 1436 /* Stupid thing to send a request */ | |
| 1437 bev = bufferevent_new(fd, | |
| 1438 http_chunked_readcb, http_chunked_writecb, | |
| 1439 http_chunked_errorcb, NULL); | |
| 1440 | |
| 1441 http_request = | |
| 1442 "GET /chunked HTTP/1.1\r\n" | |
| 1443 "Host: somehost\r\n" | |
| 1444 "Connection: close\r\n" | |
| 1445 "\r\n"; | |
| 1446 | |
| 1447 bufferevent_write(bev, http_request, strlen(http_request)); | |
| 1448 | |
| 1449 evutil_gettimeofday(&tv_start, NULL); | |
| 1450 | |
| 1451 event_dispatch(); | |
| 1452 | |
| 1453 evutil_gettimeofday(&tv_end, NULL); | |
| 1454 evutil_timersub(&tv_end, &tv_start, &tv_end); | |
| 1455 | |
| 1456 if (tv_end.tv_sec >= 1) { | |
| 1457 fprintf(stdout, "FAILED (time)\n"); | |
| 1458 exit (1); | |
| 1459 } | |
| 1460 | |
| 1461 | |
| 1462 if (test_ok != 2) { | |
| 1463 fprintf(stdout, "FAILED\n"); | |
| 1464 exit(1); | |
| 1465 } | |
| 1466 | |
| 1467 /* now try again with the regular connection object */ | |
| 1468 evcon = evhttp_connection_new("127.0.0.1", port); | |
| 1469 if (evcon == NULL) { | |
| 1470 fprintf(stdout, "FAILED\n"); | |
| 1471 exit(1); | |
| 1472 } | |
| 1473 | |
| 1474 /* make two requests to check the keepalive behavior */ | |
| 1475 for (i = 0; i < 2; i++) { | |
| 1476 test_ok = 0; | |
| 1477 req = evhttp_request_new(http_chunked_request_done, NULL); | |
| 1478 | |
| 1479 /* Add the information that we care about */ | |
| 1480 evhttp_add_header(req->output_headers, "Host", "somehost"); | |
| 1481 | |
| 1482 /* We give ownership of the request to the connection */ | |
| 1483 if (evhttp_make_request(evcon, req, | |
| 1484 EVHTTP_REQ_GET, "/chunked") == -1) { | |
| 1485 fprintf(stdout, "FAILED\n"); | |
| 1486 exit(1); | |
| 1487 } | |
| 1488 | |
| 1489 event_dispatch(); | |
| 1490 | |
| 1491 if (test_ok != 1) { | |
| 1492 fprintf(stdout, "FAILED\n"); | |
| 1493 exit(1); | |
| 1494 } | |
| 1495 } | |
| 1496 | |
| 1497 evhttp_connection_free(evcon); | |
| 1498 evhttp_free(http); | |
| 1499 | |
| 1500 fprintf(stdout, "OK\n"); | |
| 1501 } | |
| 1502 | |
| 1503 static void | |
| 1504 http_multi_line_header_test(void) | |
| 1505 { | |
| 1506 struct bufferevent *bev; | |
| 1507 int fd; | |
| 1508 const char *http_start_request; | |
| 1509 short port = -1; | |
| 1510 | |
| 1511 test_ok = 0; | |
| 1512 fprintf(stdout, "Testing HTTP Server with multi line: "); | |
| 1513 | |
| 1514 http = http_setup(&port, NULL); | |
| 1515 | |
| 1516 fd = http_connect("127.0.0.1", port); | |
| 1517 | |
| 1518 /* Stupid thing to send a request */ | |
| 1519 bev = bufferevent_new(fd, http_readcb, http_writecb, | |
| 1520 http_errorcb, NULL); | |
| 1521 | |
| 1522 http_start_request = | |
| 1523 "GET /test HTTP/1.1\r\n" | |
| 1524 "Host: somehost\r\n" | |
| 1525 "Connection: close\r\n" | |
| 1526 "X-Multi: aaaaaaaa\r\n" | |
| 1527 " a\r\n" | |
| 1528 "\tEND\r\n" | |
| 1529 "X-Last: last\r\n" | |
| 1530 "\r\n"; | |
| 1531 | |
| 1532 bufferevent_write(bev, http_start_request, strlen(http_start_request)); | |
| 1533 | |
| 1534 event_dispatch(); | |
| 1535 | |
| 1536 bufferevent_free(bev); | |
| 1537 EVUTIL_CLOSESOCKET(fd); | |
| 1538 | |
| 1539 evhttp_free(http); | |
| 1540 | |
| 1541 if (test_ok != 4) { | |
| 1542 fprintf(stdout, "FAILED\n"); | |
| 1543 exit(1); | |
| 1544 } | |
| 1545 | |
| 1546 fprintf(stdout, "OK\n"); | |
| 1547 } | |
| 1548 | |
| 1549 static void | |
| 1550 http_request_bad(struct evhttp_request *req, void *arg) | |
| 1551 { | |
| 1552 if (req != NULL) { | |
| 1553 fprintf(stderr, "FAILED\n"); | |
| 1554 exit(1); | |
| 1555 } | |
| 1556 | |
| 1557 test_ok = 1; | |
| 1558 event_loopexit(NULL); | |
| 1559 } | |
| 1560 | |
| 1561 static void | |
| 1562 http_negative_content_length_test(void) | |
| 1563 { | |
| 1564 short port = -1; | |
| 1565 struct evhttp_connection *evcon = NULL; | |
| 1566 struct evhttp_request *req = NULL; | |
| 1567 | |
| 1568 test_ok = 0; | |
| 1569 fprintf(stdout, "Testing HTTP Negative Content Length: "); | |
| 1570 | |
| 1571 http = http_setup(&port, NULL); | |
| 1572 | |
| 1573 evcon = evhttp_connection_new("127.0.0.1", port); | |
| 1574 if (evcon == NULL) { | |
| 1575 fprintf(stdout, "FAILED\n"); | |
| 1576 exit(1); | |
| 1577 } | |
| 1578 | |
| 1579 /* | |
| 1580 * At this point, we want to schedule a request to the HTTP | |
| 1581 * server using our make request method. | |
| 1582 */ | |
| 1583 | |
| 1584 req = evhttp_request_new(http_request_bad, NULL); | |
| 1585 | |
| 1586 /* Cause the response to have a negative content-length */ | |
| 1587 evhttp_add_header(req->output_headers, "X-Negative", "makeitso"); | |
| 1588 | |
| 1589 /* We give ownership of the request to the connection */ | |
| 1590 if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) { | |
| 1591 fprintf(stdout, "FAILED\n"); | |
| 1592 exit(1); | |
| 1593 } | |
| 1594 | |
| 1595 event_dispatch(); | |
| 1596 | |
| 1597 evhttp_free(http); | |
| 1598 | |
| 1599 if (test_ok != 1) { | |
| 1600 fprintf(stdout, "FAILED\n"); | |
| 1601 exit(1); | |
| 1602 } | |
| 1603 | |
| 1604 fprintf(stdout, "OK\n"); | |
| 1605 } | |
| 1606 | |
| 1607 /* | |
| 1608 * Testing client reset of server chunked connections | |
| 1609 */ | |
| 1610 | |
| 1611 struct terminate_state { | |
| 1612 struct evhttp_request *req; | |
| 1613 struct bufferevent *bev; | |
| 1614 int fd; | |
| 1615 } terminate_state; | |
| 1616 | |
| 1617 static void | |
| 1618 terminate_chunked_trickle_cb(int fd, short events, void *arg) | |
| 1619 { | |
| 1620 struct terminate_state *state = arg; | |
| 1621 struct evbuffer *evb = evbuffer_new(); | |
| 1622 struct timeval tv; | |
| 1623 | |
| 1624 if (evhttp_request_get_connection(state->req) == NULL) { | |
| 1625 test_ok = 1; | |
| 1626 evhttp_request_free(state->req); | |
| 1627 event_loopexit(NULL); | |
| 1628 return; | |
| 1629 } | |
| 1630 | |
| 1631 evbuffer_add_printf(evb, "%p", evb); | |
| 1632 evhttp_send_reply_chunk(state->req, evb); | |
| 1633 evbuffer_free(evb); | |
| 1634 | |
| 1635 tv.tv_sec = 0; | |
| 1636 tv.tv_usec = 3000; | |
| 1637 event_once(-1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); | |
| 1638 } | |
| 1639 | |
| 1640 static void | |
| 1641 terminate_chunked_cb(struct evhttp_request *req, void *arg) | |
| 1642 { | |
| 1643 struct terminate_state *state = arg; | |
| 1644 struct timeval tv; | |
| 1645 | |
| 1646 state->req = req; | |
| 1647 | |
| 1648 evhttp_send_reply_start(req, HTTP_OK, "OK"); | |
| 1649 | |
| 1650 tv.tv_sec = 0; | |
| 1651 tv.tv_usec = 3000; | |
| 1652 event_once(-1, EV_TIMEOUT, terminate_chunked_trickle_cb, arg, &tv); | |
| 1653 } | |
| 1654 | |
| 1655 static void | |
| 1656 terminate_chunked_client(int fd, short event, void *arg) | |
| 1657 { | |
| 1658 struct terminate_state *state = arg; | |
| 1659 bufferevent_free(state->bev); | |
| 1660 EVUTIL_CLOSESOCKET(state->fd); | |
| 1661 } | |
| 1662 | |
| 1663 static void | |
| 1664 terminate_readcb(struct bufferevent *bev, void *arg) | |
| 1665 { | |
| 1666 /* just drop the data */ | |
| 1667 evbuffer_drain(bev->output, -1); | |
| 1668 } | |
| 1669 | |
| 1670 | |
| 1671 static void | |
| 1672 http_terminate_chunked_test(void) | |
| 1673 { | |
| 1674 struct bufferevent *bev = NULL; | |
| 1675 struct timeval tv; | |
| 1676 const char *http_request; | |
| 1677 short port = -1; | |
| 1678 int fd = -1; | |
| 1679 | |
| 1680 test_ok = 0; | |
| 1681 fprintf(stdout, "Testing Terminated Chunked Connection: "); | |
| 1682 | |
| 1683 http = http_setup(&port, NULL); | |
| 1684 evhttp_del_cb(http, "/test"); | |
| 1685 evhttp_set_cb(http, "/test", terminate_chunked_cb, &terminate_state); | |
| 1686 | |
| 1687 fd = http_connect("127.0.0.1", port); | |
| 1688 | |
| 1689 /* Stupid thing to send a request */ | |
| 1690 bev = bufferevent_new(fd, terminate_readcb, http_writecb, | |
| 1691 http_errorcb, NULL); | |
| 1692 | |
| 1693 terminate_state.fd = fd; | |
| 1694 terminate_state.bev = bev; | |
| 1695 | |
| 1696 /* first half of the http request */ | |
| 1697 http_request = | |
| 1698 "GET /test HTTP/1.1\r\n" | |
| 1699 "Host: some\r\n\r\n"; | |
| 1700 | |
| 1701 bufferevent_write(bev, http_request, strlen(http_request)); | |
| 1702 evutil_timerclear(&tv); | |
| 1703 tv.tv_usec = 10000; | |
| 1704 event_once(-1, EV_TIMEOUT, terminate_chunked_client, &terminate_state, | |
| 1705 &tv); | |
| 1706 | |
| 1707 event_dispatch(); | |
| 1708 | |
| 1709 if (test_ok != 1) { | |
| 1710 fprintf(stdout, "FAILED\n"); | |
| 1711 exit(1); | |
| 1712 } | |
| 1713 | |
| 1714 fprintf(stdout, "OK\n"); | |
| 1715 | |
| 1716 if (fd >= 0) | |
| 1717 EVUTIL_CLOSESOCKET(fd); | |
| 1718 if (http) | |
| 1719 evhttp_free(http); | |
| 1720 } | |
| 1721 | |
| 1722 void | |
| 1723 http_suite(void) | |
| 1724 { | |
| 1725 http_base_test(); | |
| 1726 http_bad_header_test(); | |
| 1727 http_parse_query_test(); | |
| 1728 http_basic_test(); | |
| 1729 http_connection_test(0 /* not-persistent */); | |
| 1730 http_connection_test(1 /* persistent */); | |
| 1731 http_close_detection(0 /* without delay */); | |
| 1732 http_close_detection(1 /* with delay */); | |
| 1733 http_bad_request(); | |
| 1734 http_post_test(); | |
| 1735 http_failure_test(); | |
| 1736 http_highport_test(); | |
| 1737 http_dispatcher_test(); | |
| 1738 | |
| 1739 http_multi_line_header_test(); | |
| 1740 http_negative_content_length_test(); | |
| 1741 | |
| 1742 http_chunked_test(); | |
| 1743 http_terminate_chunked_test(); | |
| 1744 } | |
| OLD | NEW |