ipc/chromium/src/third_party/libevent/http.c

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

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

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

michael@0 1 /*
michael@0 2 * Copyright (c) 2002-2007 Niels Provos <provos@citi.umich.edu>
michael@0 3 * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
michael@0 4 *
michael@0 5 * Redistribution and use in source and binary forms, with or without
michael@0 6 * modification, are permitted provided that the following conditions
michael@0 7 * are met:
michael@0 8 * 1. Redistributions of source code must retain the above copyright
michael@0 9 * notice, this list of conditions and the following disclaimer.
michael@0 10 * 2. Redistributions in binary form must reproduce the above copyright
michael@0 11 * notice, this list of conditions and the following disclaimer in the
michael@0 12 * documentation and/or other materials provided with the distribution.
michael@0 13 * 3. The name of the author may not be used to endorse or promote products
michael@0 14 * derived from this software without specific prior written permission.
michael@0 15 *
michael@0 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
michael@0 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
michael@0 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
michael@0 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
michael@0 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
michael@0 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
michael@0 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 26 */
michael@0 27
michael@0 28 #include "event2/event-config.h"
michael@0 29
michael@0 30 #ifdef _EVENT_HAVE_SYS_PARAM_H
michael@0 31 #include <sys/param.h>
michael@0 32 #endif
michael@0 33 #ifdef _EVENT_HAVE_SYS_TYPES_H
michael@0 34 #include <sys/types.h>
michael@0 35 #endif
michael@0 36
michael@0 37 #ifdef _EVENT_HAVE_SYS_TIME_H
michael@0 38 #include <sys/time.h>
michael@0 39 #endif
michael@0 40 #ifdef HAVE_SYS_IOCCOM_H
michael@0 41 #include <sys/ioccom.h>
michael@0 42 #endif
michael@0 43
michael@0 44 #ifndef WIN32
michael@0 45 #include <sys/resource.h>
michael@0 46 #include <sys/socket.h>
michael@0 47 #include <sys/stat.h>
michael@0 48 #include <sys/wait.h>
michael@0 49 #else
michael@0 50 #include <winsock2.h>
michael@0 51 #include <ws2tcpip.h>
michael@0 52 #endif
michael@0 53
michael@0 54 #include <sys/queue.h>
michael@0 55
michael@0 56 #ifdef _EVENT_HAVE_NETINET_IN_H
michael@0 57 #include <netinet/in.h>
michael@0 58 #endif
michael@0 59 #ifdef _EVENT_HAVE_ARPA_INET_H
michael@0 60 #include <arpa/inet.h>
michael@0 61 #endif
michael@0 62 #ifdef _EVENT_HAVE_NETDB_H
michael@0 63 #include <netdb.h>
michael@0 64 #endif
michael@0 65
michael@0 66 #ifdef WIN32
michael@0 67 #include <winsock2.h>
michael@0 68 #endif
michael@0 69
michael@0 70 #include <errno.h>
michael@0 71 #include <stdio.h>
michael@0 72 #include <stdlib.h>
michael@0 73 #include <string.h>
michael@0 74 #ifndef WIN32
michael@0 75 #include <syslog.h>
michael@0 76 #endif
michael@0 77 #include <signal.h>
michael@0 78 #include <time.h>
michael@0 79 #ifdef _EVENT_HAVE_UNISTD_H
michael@0 80 #include <unistd.h>
michael@0 81 #endif
michael@0 82 #ifdef _EVENT_HAVE_FCNTL_H
michael@0 83 #include <fcntl.h>
michael@0 84 #endif
michael@0 85
michael@0 86 #undef timeout_pending
michael@0 87 #undef timeout_initialized
michael@0 88
michael@0 89 #include "strlcpy-internal.h"
michael@0 90 #include "event2/http.h"
michael@0 91 #include "event2/event.h"
michael@0 92 #include "event2/buffer.h"
michael@0 93 #include "event2/bufferevent.h"
michael@0 94 #include "event2/bufferevent_compat.h"
michael@0 95 #include "event2/http_struct.h"
michael@0 96 #include "event2/http_compat.h"
michael@0 97 #include "event2/util.h"
michael@0 98 #include "event2/listener.h"
michael@0 99 #include "log-internal.h"
michael@0 100 #include "util-internal.h"
michael@0 101 #include "http-internal.h"
michael@0 102 #include "mm-internal.h"
michael@0 103 #include "bufferevent-internal.h"
michael@0 104
michael@0 105 #ifndef _EVENT_HAVE_GETNAMEINFO
michael@0 106 #define NI_MAXSERV 32
michael@0 107 #define NI_MAXHOST 1025
michael@0 108
michael@0 109 #ifndef NI_NUMERICHOST
michael@0 110 #define NI_NUMERICHOST 1
michael@0 111 #endif
michael@0 112
michael@0 113 #ifndef NI_NUMERICSERV
michael@0 114 #define NI_NUMERICSERV 2
michael@0 115 #endif
michael@0 116
michael@0 117 static int
michael@0 118 fake_getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
michael@0 119 size_t hostlen, char *serv, size_t servlen, int flags)
michael@0 120 {
michael@0 121 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
michael@0 122
michael@0 123 if (serv != NULL) {
michael@0 124 char tmpserv[16];
michael@0 125 evutil_snprintf(tmpserv, sizeof(tmpserv),
michael@0 126 "%d", ntohs(sin->sin_port));
michael@0 127 if (strlcpy(serv, tmpserv, servlen) >= servlen)
michael@0 128 return (-1);
michael@0 129 }
michael@0 130
michael@0 131 if (host != NULL) {
michael@0 132 if (flags & NI_NUMERICHOST) {
michael@0 133 if (strlcpy(host, inet_ntoa(sin->sin_addr),
michael@0 134 hostlen) >= hostlen)
michael@0 135 return (-1);
michael@0 136 else
michael@0 137 return (0);
michael@0 138 } else {
michael@0 139 struct hostent *hp;
michael@0 140 hp = gethostbyaddr((char *)&sin->sin_addr,
michael@0 141 sizeof(struct in_addr), AF_INET);
michael@0 142 if (hp == NULL)
michael@0 143 return (-2);
michael@0 144
michael@0 145 if (strlcpy(host, hp->h_name, hostlen) >= hostlen)
michael@0 146 return (-1);
michael@0 147 else
michael@0 148 return (0);
michael@0 149 }
michael@0 150 }
michael@0 151 return (0);
michael@0 152 }
michael@0 153
michael@0 154 #endif
michael@0 155
michael@0 156 #define REQ_VERSION_BEFORE(req, major_v, minor_v) \
michael@0 157 ((req)->major < (major_v) || \
michael@0 158 ((req)->major == (major_v) && (req)->minor < (minor_v)))
michael@0 159
michael@0 160 #define REQ_VERSION_ATLEAST(req, major_v, minor_v) \
michael@0 161 ((req)->major > (major_v) || \
michael@0 162 ((req)->major == (major_v) && (req)->minor >= (minor_v)))
michael@0 163
michael@0 164 #ifndef MIN
michael@0 165 #define MIN(a,b) (((a)<(b))?(a):(b))
michael@0 166 #endif
michael@0 167
michael@0 168 extern int debug;
michael@0 169
michael@0 170 static evutil_socket_t bind_socket_ai(struct evutil_addrinfo *, int reuse);
michael@0 171 static evutil_socket_t bind_socket(const char *, ev_uint16_t, int reuse);
michael@0 172 static void name_from_addr(struct sockaddr *, ev_socklen_t, char **, char **);
michael@0 173 static int evhttp_associate_new_request_with_connection(
michael@0 174 struct evhttp_connection *evcon);
michael@0 175 static void evhttp_connection_start_detectclose(
michael@0 176 struct evhttp_connection *evcon);
michael@0 177 static void evhttp_connection_stop_detectclose(
michael@0 178 struct evhttp_connection *evcon);
michael@0 179 static void evhttp_request_dispatch(struct evhttp_connection* evcon);
michael@0 180 static void evhttp_read_firstline(struct evhttp_connection *evcon,
michael@0 181 struct evhttp_request *req);
michael@0 182 static void evhttp_read_header(struct evhttp_connection *evcon,
michael@0 183 struct evhttp_request *req);
michael@0 184 static int evhttp_add_header_internal(struct evkeyvalq *headers,
michael@0 185 const char *key, const char *value);
michael@0 186 static const char *evhttp_response_phrase_internal(int code);
michael@0 187 static void evhttp_get_request(struct evhttp *, evutil_socket_t, struct sockaddr *, ev_socklen_t);
michael@0 188 static void evhttp_write_buffer(struct evhttp_connection *,
michael@0 189 void (*)(struct evhttp_connection *, void *), void *);
michael@0 190 static void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *);
michael@0 191
michael@0 192 /* callbacks for bufferevent */
michael@0 193 static void evhttp_read_cb(struct bufferevent *, void *);
michael@0 194 static void evhttp_write_cb(struct bufferevent *, void *);
michael@0 195 static void evhttp_error_cb(struct bufferevent *bufev, short what, void *arg);
michael@0 196 static int evhttp_decode_uri_internal(const char *uri, size_t length,
michael@0 197 char *ret, int decode_plus);
michael@0 198 static int evhttp_find_vhost(struct evhttp *http, struct evhttp **outhttp,
michael@0 199 const char *hostname);
michael@0 200
michael@0 201 #ifndef _EVENT_HAVE_STRSEP
michael@0 202 /* strsep replacement for platforms that lack it. Only works if
michael@0 203 * del is one character long. */
michael@0 204 static char *
michael@0 205 strsep(char **s, const char *del)
michael@0 206 {
michael@0 207 char *d, *tok;
michael@0 208 EVUTIL_ASSERT(strlen(del) == 1);
michael@0 209 if (!s || !*s)
michael@0 210 return NULL;
michael@0 211 tok = *s;
michael@0 212 d = strstr(tok, del);
michael@0 213 if (d) {
michael@0 214 *d = '\0';
michael@0 215 *s = d + 1;
michael@0 216 } else
michael@0 217 *s = NULL;
michael@0 218 return tok;
michael@0 219 }
michael@0 220 #endif
michael@0 221
michael@0 222 static size_t
michael@0 223 html_replace(const char ch, const char **escaped)
michael@0 224 {
michael@0 225 switch (ch) {
michael@0 226 case '<':
michael@0 227 *escaped = "&lt;";
michael@0 228 return 4;
michael@0 229 case '>':
michael@0 230 *escaped = "&gt;";
michael@0 231 return 4;
michael@0 232 case '"':
michael@0 233 *escaped = "&quot;";
michael@0 234 return 6;
michael@0 235 case '\'':
michael@0 236 *escaped = "&#039;";
michael@0 237 return 6;
michael@0 238 case '&':
michael@0 239 *escaped = "&amp;";
michael@0 240 return 5;
michael@0 241 default:
michael@0 242 break;
michael@0 243 }
michael@0 244
michael@0 245 return 1;
michael@0 246 }
michael@0 247
michael@0 248 /*
michael@0 249 * Replaces <, >, ", ' and & with &lt;, &gt;, &quot;,
michael@0 250 * &#039; and &amp; correspondingly.
michael@0 251 *
michael@0 252 * The returned string needs to be freed by the caller.
michael@0 253 */
michael@0 254
michael@0 255 char *
michael@0 256 evhttp_htmlescape(const char *html)
michael@0 257 {
michael@0 258 size_t i;
michael@0 259 size_t new_size = 0, old_size = 0;
michael@0 260 char *escaped_html, *p;
michael@0 261
michael@0 262 if (html == NULL)
michael@0 263 return (NULL);
michael@0 264
michael@0 265 old_size = strlen(html);
michael@0 266 for (i = 0; i < old_size; ++i) {
michael@0 267 const char *replaced = NULL;
michael@0 268 const size_t replace_size = html_replace(html[i], &replaced);
michael@0 269 if (replace_size > EV_SIZE_MAX - new_size) {
michael@0 270 event_warn("%s: html_replace overflow", __func__);
michael@0 271 return (NULL);
michael@0 272 }
michael@0 273 new_size += replace_size;
michael@0 274 }
michael@0 275
michael@0 276 if (new_size == EV_SIZE_MAX)
michael@0 277 return (NULL);
michael@0 278 p = escaped_html = mm_malloc(new_size + 1);
michael@0 279 if (escaped_html == NULL) {
michael@0 280 event_warn("%s: malloc(%lu)", __func__,
michael@0 281 (unsigned long)(new_size + 1));
michael@0 282 return (NULL);
michael@0 283 }
michael@0 284 for (i = 0; i < old_size; ++i) {
michael@0 285 const char *replaced = &html[i];
michael@0 286 const size_t len = html_replace(html[i], &replaced);
michael@0 287 memcpy(p, replaced, len);
michael@0 288 p += len;
michael@0 289 }
michael@0 290
michael@0 291 *p = '\0';
michael@0 292
michael@0 293 return (escaped_html);
michael@0 294 }
michael@0 295
michael@0 296 /** Given an evhttp_cmd_type, returns a constant string containing the
michael@0 297 * equivalent HTTP command, or NULL if the evhttp_command_type is
michael@0 298 * unrecognized. */
michael@0 299 static const char *
michael@0 300 evhttp_method(enum evhttp_cmd_type type)
michael@0 301 {
michael@0 302 const char *method;
michael@0 303
michael@0 304 switch (type) {
michael@0 305 case EVHTTP_REQ_GET:
michael@0 306 method = "GET";
michael@0 307 break;
michael@0 308 case EVHTTP_REQ_POST:
michael@0 309 method = "POST";
michael@0 310 break;
michael@0 311 case EVHTTP_REQ_HEAD:
michael@0 312 method = "HEAD";
michael@0 313 break;
michael@0 314 case EVHTTP_REQ_PUT:
michael@0 315 method = "PUT";
michael@0 316 break;
michael@0 317 case EVHTTP_REQ_DELETE:
michael@0 318 method = "DELETE";
michael@0 319 break;
michael@0 320 case EVHTTP_REQ_OPTIONS:
michael@0 321 method = "OPTIONS";
michael@0 322 break;
michael@0 323 case EVHTTP_REQ_TRACE:
michael@0 324 method = "TRACE";
michael@0 325 break;
michael@0 326 case EVHTTP_REQ_CONNECT:
michael@0 327 method = "CONNECT";
michael@0 328 break;
michael@0 329 case EVHTTP_REQ_PATCH:
michael@0 330 method = "PATCH";
michael@0 331 break;
michael@0 332 default:
michael@0 333 method = NULL;
michael@0 334 break;
michael@0 335 }
michael@0 336
michael@0 337 return (method);
michael@0 338 }
michael@0 339
michael@0 340 /**
michael@0 341 * Determines if a response should have a body.
michael@0 342 * Follows the rules in RFC 2616 section 4.3.
michael@0 343 * @return 1 if the response MUST have a body; 0 if the response MUST NOT have
michael@0 344 * a body.
michael@0 345 */
michael@0 346 static int
michael@0 347 evhttp_response_needs_body(struct evhttp_request *req)
michael@0 348 {
michael@0 349 return (req->response_code != HTTP_NOCONTENT &&
michael@0 350 req->response_code != HTTP_NOTMODIFIED &&
michael@0 351 (req->response_code < 100 || req->response_code >= 200) &&
michael@0 352 req->type != EVHTTP_REQ_HEAD);
michael@0 353 }
michael@0 354
michael@0 355 /** Helper: adds the event 'ev' with the timeout 'timeout', or with
michael@0 356 * default_timeout if timeout is -1.
michael@0 357 */
michael@0 358 static int
michael@0 359 evhttp_add_event(struct event *ev, int timeout, int default_timeout)
michael@0 360 {
michael@0 361 if (timeout != 0) {
michael@0 362 struct timeval tv;
michael@0 363
michael@0 364 evutil_timerclear(&tv);
michael@0 365 tv.tv_sec = timeout != -1 ? timeout : default_timeout;
michael@0 366 return event_add(ev, &tv);
michael@0 367 } else {
michael@0 368 return event_add(ev, NULL);
michael@0 369 }
michael@0 370 }
michael@0 371
michael@0 372 /** Helper: called after we've added some data to an evcon's bufferevent's
michael@0 373 * output buffer. Sets the evconn's writing-is-done callback, and puts
michael@0 374 * the bufferevent into writing mode.
michael@0 375 */
michael@0 376 static void
michael@0 377 evhttp_write_buffer(struct evhttp_connection *evcon,
michael@0 378 void (*cb)(struct evhttp_connection *, void *), void *arg)
michael@0 379 {
michael@0 380 event_debug(("%s: preparing to write buffer\n", __func__));
michael@0 381
michael@0 382 /* Set call back */
michael@0 383 evcon->cb = cb;
michael@0 384 evcon->cb_arg = arg;
michael@0 385
michael@0 386 bufferevent_enable(evcon->bufev, EV_WRITE);
michael@0 387
michael@0 388 /* Disable the read callback: we don't actually care about data;
michael@0 389 * we only care about close detection. (We don't disable reading,
michael@0 390 * since we *do* want to learn about any close events.) */
michael@0 391 bufferevent_setcb(evcon->bufev,
michael@0 392 NULL, /*read*/
michael@0 393 evhttp_write_cb,
michael@0 394 evhttp_error_cb,
michael@0 395 evcon);
michael@0 396 }
michael@0 397
michael@0 398 static void
michael@0 399 evhttp_send_continue_done(struct evhttp_connection *evcon, void *arg)
michael@0 400 {
michael@0 401 bufferevent_disable(evcon->bufev, EV_WRITE);
michael@0 402 }
michael@0 403
michael@0 404 static void
michael@0 405 evhttp_send_continue(struct evhttp_connection *evcon,
michael@0 406 struct evhttp_request *req)
michael@0 407 {
michael@0 408 bufferevent_enable(evcon->bufev, EV_WRITE);
michael@0 409 evbuffer_add_printf(bufferevent_get_output(evcon->bufev),
michael@0 410 "HTTP/%d.%d 100 Continue\r\n\r\n",
michael@0 411 req->major, req->minor);
michael@0 412 evcon->cb = evhttp_send_continue_done;
michael@0 413 evcon->cb_arg = NULL;
michael@0 414 bufferevent_setcb(evcon->bufev,
michael@0 415 evhttp_read_cb,
michael@0 416 evhttp_write_cb,
michael@0 417 evhttp_error_cb,
michael@0 418 evcon);
michael@0 419 }
michael@0 420
michael@0 421 /** Helper: returns true iff evconn is in any connected state. */
michael@0 422 static int
michael@0 423 evhttp_connected(struct evhttp_connection *evcon)
michael@0 424 {
michael@0 425 switch (evcon->state) {
michael@0 426 case EVCON_DISCONNECTED:
michael@0 427 case EVCON_CONNECTING:
michael@0 428 return (0);
michael@0 429 case EVCON_IDLE:
michael@0 430 case EVCON_READING_FIRSTLINE:
michael@0 431 case EVCON_READING_HEADERS:
michael@0 432 case EVCON_READING_BODY:
michael@0 433 case EVCON_READING_TRAILER:
michael@0 434 case EVCON_WRITING:
michael@0 435 default:
michael@0 436 return (1);
michael@0 437 }
michael@0 438 }
michael@0 439
michael@0 440 /* Create the headers needed for an outgoing HTTP request, adds them to
michael@0 441 * the request's header list, and writes the request line to the
michael@0 442 * connection's output buffer.
michael@0 443 */
michael@0 444 static void
michael@0 445 evhttp_make_header_request(struct evhttp_connection *evcon,
michael@0 446 struct evhttp_request *req)
michael@0 447 {
michael@0 448 const char *method;
michael@0 449
michael@0 450 evhttp_remove_header(req->output_headers, "Proxy-Connection");
michael@0 451
michael@0 452 /* Generate request line */
michael@0 453 method = evhttp_method(req->type);
michael@0 454 evbuffer_add_printf(bufferevent_get_output(evcon->bufev),
michael@0 455 "%s %s HTTP/%d.%d\r\n",
michael@0 456 method, req->uri, req->major, req->minor);
michael@0 457
michael@0 458 /* Add the content length on a post or put request if missing */
michael@0 459 if ((req->type == EVHTTP_REQ_POST || req->type == EVHTTP_REQ_PUT) &&
michael@0 460 evhttp_find_header(req->output_headers, "Content-Length") == NULL){
michael@0 461 char size[22];
michael@0 462 evutil_snprintf(size, sizeof(size), EV_SIZE_FMT,
michael@0 463 EV_SIZE_ARG(evbuffer_get_length(req->output_buffer)));
michael@0 464 evhttp_add_header(req->output_headers, "Content-Length", size);
michael@0 465 }
michael@0 466 }
michael@0 467
michael@0 468 /** Return true if the list of headers in 'headers', intepreted with respect
michael@0 469 * to flags, means that we should send a "connection: close" when the request
michael@0 470 * is done. */
michael@0 471 static int
michael@0 472 evhttp_is_connection_close(int flags, struct evkeyvalq* headers)
michael@0 473 {
michael@0 474 if (flags & EVHTTP_PROXY_REQUEST) {
michael@0 475 /* proxy connection */
michael@0 476 const char *connection = evhttp_find_header(headers, "Proxy-Connection");
michael@0 477 return (connection == NULL || evutil_ascii_strcasecmp(connection, "keep-alive") != 0);
michael@0 478 } else {
michael@0 479 const char *connection = evhttp_find_header(headers, "Connection");
michael@0 480 return (connection != NULL && evutil_ascii_strcasecmp(connection, "close") == 0);
michael@0 481 }
michael@0 482 }
michael@0 483
michael@0 484 /* Return true iff 'headers' contains 'Connection: keep-alive' */
michael@0 485 static int
michael@0 486 evhttp_is_connection_keepalive(struct evkeyvalq* headers)
michael@0 487 {
michael@0 488 const char *connection = evhttp_find_header(headers, "Connection");
michael@0 489 return (connection != NULL
michael@0 490 && evutil_ascii_strncasecmp(connection, "keep-alive", 10) == 0);
michael@0 491 }
michael@0 492
michael@0 493 /* Add a correct "Date" header to headers, unless it already has one. */
michael@0 494 static void
michael@0 495 evhttp_maybe_add_date_header(struct evkeyvalq *headers)
michael@0 496 {
michael@0 497 if (evhttp_find_header(headers, "Date") == NULL) {
michael@0 498 char date[50];
michael@0 499 #ifndef WIN32
michael@0 500 struct tm cur;
michael@0 501 #endif
michael@0 502 struct tm *cur_p;
michael@0 503 time_t t = time(NULL);
michael@0 504 #ifdef WIN32
michael@0 505 cur_p = gmtime(&t);
michael@0 506 #else
michael@0 507 gmtime_r(&t, &cur);
michael@0 508 cur_p = &cur;
michael@0 509 #endif
michael@0 510 if (strftime(date, sizeof(date),
michael@0 511 "%a, %d %b %Y %H:%M:%S GMT", cur_p) != 0) {
michael@0 512 evhttp_add_header(headers, "Date", date);
michael@0 513 }
michael@0 514 }
michael@0 515 }
michael@0 516
michael@0 517 /* Add a "Content-Length" header with value 'content_length' to headers,
michael@0 518 * unless it already has a content-length or transfer-encoding header. */
michael@0 519 static void
michael@0 520 evhttp_maybe_add_content_length_header(struct evkeyvalq *headers,
michael@0 521 size_t content_length)
michael@0 522 {
michael@0 523 if (evhttp_find_header(headers, "Transfer-Encoding") == NULL &&
michael@0 524 evhttp_find_header(headers, "Content-Length") == NULL) {
michael@0 525 char len[22];
michael@0 526 evutil_snprintf(len, sizeof(len), EV_SIZE_FMT,
michael@0 527 EV_SIZE_ARG(content_length));
michael@0 528 evhttp_add_header(headers, "Content-Length", len);
michael@0 529 }
michael@0 530 }
michael@0 531
michael@0 532 /*
michael@0 533 * Create the headers needed for an HTTP reply in req->output_headers,
michael@0 534 * and write the first HTTP response for req line to evcon.
michael@0 535 */
michael@0 536 static void
michael@0 537 evhttp_make_header_response(struct evhttp_connection *evcon,
michael@0 538 struct evhttp_request *req)
michael@0 539 {
michael@0 540 int is_keepalive = evhttp_is_connection_keepalive(req->input_headers);
michael@0 541 evbuffer_add_printf(bufferevent_get_output(evcon->bufev),
michael@0 542 "HTTP/%d.%d %d %s\r\n",
michael@0 543 req->major, req->minor, req->response_code,
michael@0 544 req->response_code_line);
michael@0 545
michael@0 546 if (req->major == 1) {
michael@0 547 if (req->minor >= 1)
michael@0 548 evhttp_maybe_add_date_header(req->output_headers);
michael@0 549
michael@0 550 /*
michael@0 551 * if the protocol is 1.0; and the connection was keep-alive
michael@0 552 * we need to add a keep-alive header, too.
michael@0 553 */
michael@0 554 if (req->minor == 0 && is_keepalive)
michael@0 555 evhttp_add_header(req->output_headers,
michael@0 556 "Connection", "keep-alive");
michael@0 557
michael@0 558 if ((req->minor >= 1 || is_keepalive) &&
michael@0 559 evhttp_response_needs_body(req)) {
michael@0 560 /*
michael@0 561 * we need to add the content length if the
michael@0 562 * user did not give it, this is required for
michael@0 563 * persistent connections to work.
michael@0 564 */
michael@0 565 evhttp_maybe_add_content_length_header(
michael@0 566 req->output_headers,
michael@0 567 evbuffer_get_length(req->output_buffer));
michael@0 568 }
michael@0 569 }
michael@0 570
michael@0 571 /* Potentially add headers for unidentified content. */
michael@0 572 if (evhttp_response_needs_body(req)) {
michael@0 573 if (evhttp_find_header(req->output_headers,
michael@0 574 "Content-Type") == NULL) {
michael@0 575 evhttp_add_header(req->output_headers,
michael@0 576 "Content-Type", "text/html; charset=ISO-8859-1");
michael@0 577 }
michael@0 578 }
michael@0 579
michael@0 580 /* if the request asked for a close, we send a close, too */
michael@0 581 if (evhttp_is_connection_close(req->flags, req->input_headers)) {
michael@0 582 evhttp_remove_header(req->output_headers, "Connection");
michael@0 583 if (!(req->flags & EVHTTP_PROXY_REQUEST))
michael@0 584 evhttp_add_header(req->output_headers, "Connection", "close");
michael@0 585 evhttp_remove_header(req->output_headers, "Proxy-Connection");
michael@0 586 }
michael@0 587 }
michael@0 588
michael@0 589 /** Generate all headers appropriate for sending the http request in req (or
michael@0 590 * the response, if we're sending a response), and write them to evcon's
michael@0 591 * bufferevent. Also writes all data from req->output_buffer */
michael@0 592 static void
michael@0 593 evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
michael@0 594 {
michael@0 595 struct evkeyval *header;
michael@0 596 struct evbuffer *output = bufferevent_get_output(evcon->bufev);
michael@0 597
michael@0 598 /*
michael@0 599 * Depending if this is a HTTP request or response, we might need to
michael@0 600 * add some new headers or remove existing headers.
michael@0 601 */
michael@0 602 if (req->kind == EVHTTP_REQUEST) {
michael@0 603 evhttp_make_header_request(evcon, req);
michael@0 604 } else {
michael@0 605 evhttp_make_header_response(evcon, req);
michael@0 606 }
michael@0 607
michael@0 608 TAILQ_FOREACH(header, req->output_headers, next) {
michael@0 609 evbuffer_add_printf(output, "%s: %s\r\n",
michael@0 610 header->key, header->value);
michael@0 611 }
michael@0 612 evbuffer_add(output, "\r\n", 2);
michael@0 613
michael@0 614 if (evbuffer_get_length(req->output_buffer) > 0) {
michael@0 615 /*
michael@0 616 * For a request, we add the POST data, for a reply, this
michael@0 617 * is the regular data.
michael@0 618 */
michael@0 619 /* XXX We might want to support waiting (a limited amount of
michael@0 620 time) for a continue status line from the server before
michael@0 621 sending POST/PUT message bodies. */
michael@0 622 evbuffer_add_buffer(output, req->output_buffer);
michael@0 623 }
michael@0 624 }
michael@0 625
michael@0 626 void
michael@0 627 evhttp_connection_set_max_headers_size(struct evhttp_connection *evcon,
michael@0 628 ev_ssize_t new_max_headers_size)
michael@0 629 {
michael@0 630 if (new_max_headers_size<0)
michael@0 631 evcon->max_headers_size = EV_SIZE_MAX;
michael@0 632 else
michael@0 633 evcon->max_headers_size = new_max_headers_size;
michael@0 634 }
michael@0 635 void
michael@0 636 evhttp_connection_set_max_body_size(struct evhttp_connection* evcon,
michael@0 637 ev_ssize_t new_max_body_size)
michael@0 638 {
michael@0 639 if (new_max_body_size<0)
michael@0 640 evcon->max_body_size = EV_UINT64_MAX;
michael@0 641 else
michael@0 642 evcon->max_body_size = new_max_body_size;
michael@0 643 }
michael@0 644
michael@0 645 static int
michael@0 646 evhttp_connection_incoming_fail(struct evhttp_request *req,
michael@0 647 enum evhttp_connection_error error)
michael@0 648 {
michael@0 649 switch (error) {
michael@0 650 case EVCON_HTTP_TIMEOUT:
michael@0 651 case EVCON_HTTP_EOF:
michael@0 652 /*
michael@0 653 * these are cases in which we probably should just
michael@0 654 * close the connection and not send a reply. this
michael@0 655 * case may happen when a browser keeps a persistent
michael@0 656 * connection open and we timeout on the read. when
michael@0 657 * the request is still being used for sending, we
michael@0 658 * need to disassociated it from the connection here.
michael@0 659 */
michael@0 660 if (!req->userdone) {
michael@0 661 /* remove it so that it will not be freed */
michael@0 662 TAILQ_REMOVE(&req->evcon->requests, req, next);
michael@0 663 /* indicate that this request no longer has a
michael@0 664 * connection object
michael@0 665 */
michael@0 666 req->evcon = NULL;
michael@0 667 }
michael@0 668 return (-1);
michael@0 669 case EVCON_HTTP_INVALID_HEADER:
michael@0 670 case EVCON_HTTP_BUFFER_ERROR:
michael@0 671 case EVCON_HTTP_REQUEST_CANCEL:
michael@0 672 default: /* xxx: probably should just error on default */
michael@0 673 /* the callback looks at the uri to determine errors */
michael@0 674 if (req->uri) {
michael@0 675 mm_free(req->uri);
michael@0 676 req->uri = NULL;
michael@0 677 }
michael@0 678 if (req->uri_elems) {
michael@0 679 evhttp_uri_free(req->uri_elems);
michael@0 680 req->uri_elems = NULL;
michael@0 681 }
michael@0 682
michael@0 683 /*
michael@0 684 * the callback needs to send a reply, once the reply has
michael@0 685 * been send, the connection should get freed.
michael@0 686 */
michael@0 687 (*req->cb)(req, req->cb_arg);
michael@0 688 }
michael@0 689
michael@0 690 return (0);
michael@0 691 }
michael@0 692
michael@0 693 /* Called when evcon has experienced a (non-recoverable? -NM) error, as
michael@0 694 * given in error. If it's an outgoing connection, reset the connection,
michael@0 695 * retry any pending requests, and inform the user. If it's incoming,
michael@0 696 * delegates to evhttp_connection_incoming_fail(). */
michael@0 697 void
michael@0 698 evhttp_connection_fail(struct evhttp_connection *evcon,
michael@0 699 enum evhttp_connection_error error)
michael@0 700 {
michael@0 701 struct evhttp_request* req = TAILQ_FIRST(&evcon->requests);
michael@0 702 void (*cb)(struct evhttp_request *, void *);
michael@0 703 void *cb_arg;
michael@0 704 EVUTIL_ASSERT(req != NULL);
michael@0 705
michael@0 706 bufferevent_disable(evcon->bufev, EV_READ|EV_WRITE);
michael@0 707
michael@0 708 if (evcon->flags & EVHTTP_CON_INCOMING) {
michael@0 709 /*
michael@0 710 * for incoming requests, there are two different
michael@0 711 * failure cases. it's either a network level error
michael@0 712 * or an http layer error. for problems on the network
michael@0 713 * layer like timeouts we just drop the connections.
michael@0 714 * For HTTP problems, we might have to send back a
michael@0 715 * reply before the connection can be freed.
michael@0 716 */
michael@0 717 if (evhttp_connection_incoming_fail(req, error) == -1)
michael@0 718 evhttp_connection_free(evcon);
michael@0 719 return;
michael@0 720 }
michael@0 721
michael@0 722 /* when the request was canceled, the callback is not executed */
michael@0 723 if (error != EVCON_HTTP_REQUEST_CANCEL) {
michael@0 724 /* save the callback for later; the cb might free our object */
michael@0 725 cb = req->cb;
michael@0 726 cb_arg = req->cb_arg;
michael@0 727 } else {
michael@0 728 cb = NULL;
michael@0 729 cb_arg = NULL;
michael@0 730 }
michael@0 731
michael@0 732 /* do not fail all requests; the next request is going to get
michael@0 733 * send over a new connection. when a user cancels a request,
michael@0 734 * all other pending requests should be processed as normal
michael@0 735 */
michael@0 736 TAILQ_REMOVE(&evcon->requests, req, next);
michael@0 737 evhttp_request_free(req);
michael@0 738
michael@0 739 /* reset the connection */
michael@0 740 evhttp_connection_reset(evcon);
michael@0 741
michael@0 742 /* We are trying the next request that was queued on us */
michael@0 743 if (TAILQ_FIRST(&evcon->requests) != NULL)
michael@0 744 evhttp_connection_connect(evcon);
michael@0 745
michael@0 746 /* inform the user */
michael@0 747 if (cb != NULL)
michael@0 748 (*cb)(NULL, cb_arg);
michael@0 749 }
michael@0 750
michael@0 751 /* Bufferevent callback: invoked when any data has been written from an
michael@0 752 * http connection's bufferevent */
michael@0 753 static void
michael@0 754 evhttp_write_cb(struct bufferevent *bufev, void *arg)
michael@0 755 {
michael@0 756 struct evhttp_connection *evcon = arg;
michael@0 757
michael@0 758 /* Activate our call back */
michael@0 759 if (evcon->cb != NULL)
michael@0 760 (*evcon->cb)(evcon, evcon->cb_arg);
michael@0 761 }
michael@0 762
michael@0 763 /**
michael@0 764 * Advance the connection state.
michael@0 765 * - If this is an outgoing connection, we've just processed the response;
michael@0 766 * idle or close the connection.
michael@0 767 * - If this is an incoming connection, we've just processed the request;
michael@0 768 * respond.
michael@0 769 */
michael@0 770 static void
michael@0 771 evhttp_connection_done(struct evhttp_connection *evcon)
michael@0 772 {
michael@0 773 struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
michael@0 774 int con_outgoing = evcon->flags & EVHTTP_CON_OUTGOING;
michael@0 775
michael@0 776 if (con_outgoing) {
michael@0 777 /* idle or close the connection */
michael@0 778 int need_close;
michael@0 779 TAILQ_REMOVE(&evcon->requests, req, next);
michael@0 780 req->evcon = NULL;
michael@0 781
michael@0 782 evcon->state = EVCON_IDLE;
michael@0 783
michael@0 784 need_close =
michael@0 785 evhttp_is_connection_close(req->flags, req->input_headers)||
michael@0 786 evhttp_is_connection_close(req->flags, req->output_headers);
michael@0 787
michael@0 788 /* check if we got asked to close the connection */
michael@0 789 if (need_close)
michael@0 790 evhttp_connection_reset(evcon);
michael@0 791
michael@0 792 if (TAILQ_FIRST(&evcon->requests) != NULL) {
michael@0 793 /*
michael@0 794 * We have more requests; reset the connection
michael@0 795 * and deal with the next request.
michael@0 796 */
michael@0 797 if (!evhttp_connected(evcon))
michael@0 798 evhttp_connection_connect(evcon);
michael@0 799 else
michael@0 800 evhttp_request_dispatch(evcon);
michael@0 801 } else if (!need_close) {
michael@0 802 /*
michael@0 803 * The connection is going to be persistent, but we
michael@0 804 * need to detect if the other side closes it.
michael@0 805 */
michael@0 806 evhttp_connection_start_detectclose(evcon);
michael@0 807 }
michael@0 808 } else {
michael@0 809 /*
michael@0 810 * incoming connection - we need to leave the request on the
michael@0 811 * connection so that we can reply to it.
michael@0 812 */
michael@0 813 evcon->state = EVCON_WRITING;
michael@0 814 }
michael@0 815
michael@0 816 /* notify the user of the request */
michael@0 817 (*req->cb)(req, req->cb_arg);
michael@0 818
michael@0 819 /* if this was an outgoing request, we own and it's done. so free it.
michael@0 820 * unless the callback specifically requested to own the request.
michael@0 821 */
michael@0 822 if (con_outgoing && ((req->flags & EVHTTP_USER_OWNED) == 0)) {
michael@0 823 evhttp_request_free(req);
michael@0 824 }
michael@0 825 }
michael@0 826
michael@0 827 /*
michael@0 828 * Handles reading from a chunked request.
michael@0 829 * return ALL_DATA_READ:
michael@0 830 * all data has been read
michael@0 831 * return MORE_DATA_EXPECTED:
michael@0 832 * more data is expected
michael@0 833 * return DATA_CORRUPTED:
michael@0 834 * data is corrupted
michael@0 835 * return REQUEST_CANCELED:
michael@0 836 * request was canceled by the user calling evhttp_cancel_request
michael@0 837 * return DATA_TOO_LONG:
michael@0 838 * ran over the maximum limit
michael@0 839 */
michael@0 840
michael@0 841 static enum message_read_status
michael@0 842 evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf)
michael@0 843 {
michael@0 844 if (req == NULL || buf == NULL) {
michael@0 845 return DATA_CORRUPTED;
michael@0 846 }
michael@0 847
michael@0 848 while (1) {
michael@0 849 size_t buflen;
michael@0 850
michael@0 851 if ((buflen = evbuffer_get_length(buf)) == 0) {
michael@0 852 break;
michael@0 853 }
michael@0 854
michael@0 855 /* evbuffer_get_length returns size_t, but len variable is ssize_t,
michael@0 856 * check for overflow conditions */
michael@0 857 if (buflen > EV_SSIZE_MAX) {
michael@0 858 return DATA_CORRUPTED;
michael@0 859 }
michael@0 860
michael@0 861 if (req->ntoread < 0) {
michael@0 862 /* Read chunk size */
michael@0 863 ev_int64_t ntoread;
michael@0 864 char *p = evbuffer_readln(buf, NULL, EVBUFFER_EOL_CRLF);
michael@0 865 char *endp;
michael@0 866 int error;
michael@0 867 if (p == NULL)
michael@0 868 break;
michael@0 869 /* the last chunk is on a new line? */
michael@0 870 if (strlen(p) == 0) {
michael@0 871 mm_free(p);
michael@0 872 continue;
michael@0 873 }
michael@0 874 ntoread = evutil_strtoll(p, &endp, 16);
michael@0 875 error = (*p == '\0' ||
michael@0 876 (*endp != '\0' && *endp != ' ') ||
michael@0 877 ntoread < 0);
michael@0 878 mm_free(p);
michael@0 879 if (error) {
michael@0 880 /* could not get chunk size */
michael@0 881 return (DATA_CORRUPTED);
michael@0 882 }
michael@0 883
michael@0 884 /* ntoread is signed int64, body_size is unsigned size_t, check for under/overflow conditions */
michael@0 885 if ((ev_uint64_t)ntoread > EV_SIZE_MAX - req->body_size) {
michael@0 886 return DATA_CORRUPTED;
michael@0 887 }
michael@0 888
michael@0 889 if (req->body_size + (size_t)ntoread > req->evcon->max_body_size) {
michael@0 890 /* failed body length test */
michael@0 891 event_debug(("Request body is too long"));
michael@0 892 return (DATA_TOO_LONG);
michael@0 893 }
michael@0 894
michael@0 895 req->body_size += (size_t)ntoread;
michael@0 896 req->ntoread = ntoread;
michael@0 897 if (req->ntoread == 0) {
michael@0 898 /* Last chunk */
michael@0 899 return (ALL_DATA_READ);
michael@0 900 }
michael@0 901 continue;
michael@0 902 }
michael@0 903
michael@0 904 /* req->ntoread is signed int64, len is ssize_t, based on arch,
michael@0 905 * ssize_t could only be 32b, check for these conditions */
michael@0 906 if (req->ntoread > EV_SSIZE_MAX) {
michael@0 907 return DATA_CORRUPTED;
michael@0 908 }
michael@0 909
michael@0 910 /* don't have enough to complete a chunk; wait for more */
michael@0 911 if (req->ntoread > 0 && buflen < (ev_uint64_t)req->ntoread)
michael@0 912 return (MORE_DATA_EXPECTED);
michael@0 913
michael@0 914 /* Completed chunk */
michael@0 915 evbuffer_remove_buffer(buf, req->input_buffer, (size_t)req->ntoread);
michael@0 916 req->ntoread = -1;
michael@0 917 if (req->chunk_cb != NULL) {
michael@0 918 req->flags |= EVHTTP_REQ_DEFER_FREE;
michael@0 919 (*req->chunk_cb)(req, req->cb_arg);
michael@0 920 evbuffer_drain(req->input_buffer,
michael@0 921 evbuffer_get_length(req->input_buffer));
michael@0 922 req->flags &= ~EVHTTP_REQ_DEFER_FREE;
michael@0 923 if ((req->flags & EVHTTP_REQ_NEEDS_FREE) != 0) {
michael@0 924 return (REQUEST_CANCELED);
michael@0 925 }
michael@0 926 }
michael@0 927 }
michael@0 928
michael@0 929 return (MORE_DATA_EXPECTED);
michael@0 930 }
michael@0 931
michael@0 932 static void
michael@0 933 evhttp_read_trailer(struct evhttp_connection *evcon, struct evhttp_request *req)
michael@0 934 {
michael@0 935 struct evbuffer *buf = bufferevent_get_input(evcon->bufev);
michael@0 936
michael@0 937 switch (evhttp_parse_headers(req, buf)) {
michael@0 938 case DATA_CORRUPTED:
michael@0 939 case DATA_TOO_LONG:
michael@0 940 evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
michael@0 941 break;
michael@0 942 case ALL_DATA_READ:
michael@0 943 bufferevent_disable(evcon->bufev, EV_READ);
michael@0 944 evhttp_connection_done(evcon);
michael@0 945 break;
michael@0 946 case MORE_DATA_EXPECTED:
michael@0 947 case REQUEST_CANCELED: /* ??? */
michael@0 948 default:
michael@0 949 bufferevent_enable(evcon->bufev, EV_READ);
michael@0 950 break;
michael@0 951 }
michael@0 952 }
michael@0 953
michael@0 954 static void
michael@0 955 evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req)
michael@0 956 {
michael@0 957 struct evbuffer *buf = bufferevent_get_input(evcon->bufev);
michael@0 958
michael@0 959 if (req->chunked) {
michael@0 960 switch (evhttp_handle_chunked_read(req, buf)) {
michael@0 961 case ALL_DATA_READ:
michael@0 962 /* finished last chunk */
michael@0 963 evcon->state = EVCON_READING_TRAILER;
michael@0 964 evhttp_read_trailer(evcon, req);
michael@0 965 return;
michael@0 966 case DATA_CORRUPTED:
michael@0 967 case DATA_TOO_LONG:/*separate error for this? XXX */
michael@0 968 /* corrupted data */
michael@0 969 evhttp_connection_fail(evcon,
michael@0 970 EVCON_HTTP_INVALID_HEADER);
michael@0 971 return;
michael@0 972 case REQUEST_CANCELED:
michael@0 973 /* request canceled */
michael@0 974 evhttp_request_free(req);
michael@0 975 return;
michael@0 976 case MORE_DATA_EXPECTED:
michael@0 977 default:
michael@0 978 break;
michael@0 979 }
michael@0 980 } else if (req->ntoread < 0) {
michael@0 981 /* Read until connection close. */
michael@0 982 if ((size_t)(req->body_size + evbuffer_get_length(buf)) < req->body_size) {
michael@0 983 evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
michael@0 984 return;
michael@0 985 }
michael@0 986
michael@0 987 req->body_size += evbuffer_get_length(buf);
michael@0 988 evbuffer_add_buffer(req->input_buffer, buf);
michael@0 989 } else if (req->chunk_cb != NULL || evbuffer_get_length(buf) >= (size_t)req->ntoread) {
michael@0 990 /* XXX: the above get_length comparison has to be fixed for overflow conditions! */
michael@0 991 /* We've postponed moving the data until now, but we're
michael@0 992 * about to use it. */
michael@0 993 size_t n = evbuffer_get_length(buf);
michael@0 994
michael@0 995 if (n > (size_t) req->ntoread)
michael@0 996 n = (size_t) req->ntoread;
michael@0 997 req->ntoread -= n;
michael@0 998 req->body_size += n;
michael@0 999 evbuffer_remove_buffer(buf, req->input_buffer, n);
michael@0 1000 }
michael@0 1001
michael@0 1002 if (req->body_size > req->evcon->max_body_size ||
michael@0 1003 (!req->chunked && req->ntoread >= 0 &&
michael@0 1004 (size_t)req->ntoread > req->evcon->max_body_size)) {
michael@0 1005 /* XXX: The above casted comparison must checked for overflow */
michael@0 1006 /* failed body length test */
michael@0 1007 event_debug(("Request body is too long"));
michael@0 1008 evhttp_connection_fail(evcon,
michael@0 1009 EVCON_HTTP_INVALID_HEADER);
michael@0 1010 return;
michael@0 1011 }
michael@0 1012
michael@0 1013 if (evbuffer_get_length(req->input_buffer) > 0 && req->chunk_cb != NULL) {
michael@0 1014 req->flags |= EVHTTP_REQ_DEFER_FREE;
michael@0 1015 (*req->chunk_cb)(req, req->cb_arg);
michael@0 1016 req->flags &= ~EVHTTP_REQ_DEFER_FREE;
michael@0 1017 evbuffer_drain(req->input_buffer,
michael@0 1018 evbuffer_get_length(req->input_buffer));
michael@0 1019 if ((req->flags & EVHTTP_REQ_NEEDS_FREE) != 0) {
michael@0 1020 evhttp_request_free(req);
michael@0 1021 return;
michael@0 1022 }
michael@0 1023 }
michael@0 1024
michael@0 1025 if (req->ntoread == 0) {
michael@0 1026 bufferevent_disable(evcon->bufev, EV_READ);
michael@0 1027 /* Completed content length */
michael@0 1028 evhttp_connection_done(evcon);
michael@0 1029 return;
michael@0 1030 }
michael@0 1031
michael@0 1032 /* Read more! */
michael@0 1033 bufferevent_enable(evcon->bufev, EV_READ);
michael@0 1034 }
michael@0 1035
michael@0 1036 #define get_deferred_queue(evcon) \
michael@0 1037 (event_base_get_deferred_cb_queue((evcon)->base))
michael@0 1038
michael@0 1039 /*
michael@0 1040 * Gets called when more data becomes available
michael@0 1041 */
michael@0 1042
michael@0 1043 static void
michael@0 1044 evhttp_read_cb(struct bufferevent *bufev, void *arg)
michael@0 1045 {
michael@0 1046 struct evhttp_connection *evcon = arg;
michael@0 1047 struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
michael@0 1048
michael@0 1049 /* Cancel if it's pending. */
michael@0 1050 event_deferred_cb_cancel(get_deferred_queue(evcon),
michael@0 1051 &evcon->read_more_deferred_cb);
michael@0 1052
michael@0 1053 switch (evcon->state) {
michael@0 1054 case EVCON_READING_FIRSTLINE:
michael@0 1055 evhttp_read_firstline(evcon, req);
michael@0 1056 /* note the request may have been freed in
michael@0 1057 * evhttp_read_body */
michael@0 1058 break;
michael@0 1059 case EVCON_READING_HEADERS:
michael@0 1060 evhttp_read_header(evcon, req);
michael@0 1061 /* note the request may have been freed in
michael@0 1062 * evhttp_read_body */
michael@0 1063 break;
michael@0 1064 case EVCON_READING_BODY:
michael@0 1065 evhttp_read_body(evcon, req);
michael@0 1066 /* note the request may have been freed in
michael@0 1067 * evhttp_read_body */
michael@0 1068 break;
michael@0 1069 case EVCON_READING_TRAILER:
michael@0 1070 evhttp_read_trailer(evcon, req);
michael@0 1071 break;
michael@0 1072 case EVCON_IDLE:
michael@0 1073 {
michael@0 1074 #ifdef USE_DEBUG
michael@0 1075 struct evbuffer *input;
michael@0 1076 size_t total_len;
michael@0 1077
michael@0 1078 input = bufferevent_get_input(evcon->bufev);
michael@0 1079 total_len = evbuffer_get_length(input);
michael@0 1080 event_debug(("%s: read "EV_SIZE_FMT
michael@0 1081 " bytes in EVCON_IDLE state,"
michael@0 1082 " resetting connection",
michael@0 1083 __func__, EV_SIZE_ARG(total_len)));
michael@0 1084 #endif
michael@0 1085
michael@0 1086 evhttp_connection_reset(evcon);
michael@0 1087 }
michael@0 1088 break;
michael@0 1089 case EVCON_DISCONNECTED:
michael@0 1090 case EVCON_CONNECTING:
michael@0 1091 case EVCON_WRITING:
michael@0 1092 default:
michael@0 1093 event_errx(1, "%s: illegal connection state %d",
michael@0 1094 __func__, evcon->state);
michael@0 1095 }
michael@0 1096 }
michael@0 1097
michael@0 1098 static void
michael@0 1099 evhttp_deferred_read_cb(struct deferred_cb *cb, void *data)
michael@0 1100 {
michael@0 1101 struct evhttp_connection *evcon = data;
michael@0 1102 evhttp_read_cb(evcon->bufev, evcon);
michael@0 1103 }
michael@0 1104
michael@0 1105 static void
michael@0 1106 evhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg)
michael@0 1107 {
michael@0 1108 /* This is after writing the request to the server */
michael@0 1109 struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
michael@0 1110 EVUTIL_ASSERT(req != NULL);
michael@0 1111
michael@0 1112 EVUTIL_ASSERT(evcon->state == EVCON_WRITING);
michael@0 1113
michael@0 1114 /* We are done writing our header and are now expecting the response */
michael@0 1115 req->kind = EVHTTP_RESPONSE;
michael@0 1116
michael@0 1117 evhttp_start_read(evcon);
michael@0 1118 }
michael@0 1119
michael@0 1120 /*
michael@0 1121 * Clean up a connection object
michael@0 1122 */
michael@0 1123
michael@0 1124 void
michael@0 1125 evhttp_connection_free(struct evhttp_connection *evcon)
michael@0 1126 {
michael@0 1127 struct evhttp_request *req;
michael@0 1128
michael@0 1129 /* notify interested parties that this connection is going down */
michael@0 1130 if (evcon->fd != -1) {
michael@0 1131 if (evhttp_connected(evcon) && evcon->closecb != NULL)
michael@0 1132 (*evcon->closecb)(evcon, evcon->closecb_arg);
michael@0 1133 }
michael@0 1134
michael@0 1135 /* remove all requests that might be queued on this
michael@0 1136 * connection. for server connections, this should be empty.
michael@0 1137 * because it gets dequeued either in evhttp_connection_done or
michael@0 1138 * evhttp_connection_fail.
michael@0 1139 */
michael@0 1140 while ((req = TAILQ_FIRST(&evcon->requests)) != NULL) {
michael@0 1141 TAILQ_REMOVE(&evcon->requests, req, next);
michael@0 1142 evhttp_request_free(req);
michael@0 1143 }
michael@0 1144
michael@0 1145 if (evcon->http_server != NULL) {
michael@0 1146 struct evhttp *http = evcon->http_server;
michael@0 1147 TAILQ_REMOVE(&http->connections, evcon, next);
michael@0 1148 }
michael@0 1149
michael@0 1150 if (event_initialized(&evcon->retry_ev)) {
michael@0 1151 event_del(&evcon->retry_ev);
michael@0 1152 event_debug_unassign(&evcon->retry_ev);
michael@0 1153 }
michael@0 1154
michael@0 1155 if (evcon->bufev != NULL)
michael@0 1156 bufferevent_free(evcon->bufev);
michael@0 1157
michael@0 1158 event_deferred_cb_cancel(get_deferred_queue(evcon),
michael@0 1159 &evcon->read_more_deferred_cb);
michael@0 1160
michael@0 1161 if (evcon->fd != -1) {
michael@0 1162 shutdown(evcon->fd, EVUTIL_SHUT_WR);
michael@0 1163 evutil_closesocket(evcon->fd);
michael@0 1164 }
michael@0 1165
michael@0 1166 if (evcon->bind_address != NULL)
michael@0 1167 mm_free(evcon->bind_address);
michael@0 1168
michael@0 1169 if (evcon->address != NULL)
michael@0 1170 mm_free(evcon->address);
michael@0 1171
michael@0 1172 mm_free(evcon);
michael@0 1173 }
michael@0 1174
michael@0 1175 void
michael@0 1176 evhttp_connection_set_local_address(struct evhttp_connection *evcon,
michael@0 1177 const char *address)
michael@0 1178 {
michael@0 1179 EVUTIL_ASSERT(evcon->state == EVCON_DISCONNECTED);
michael@0 1180 if (evcon->bind_address)
michael@0 1181 mm_free(evcon->bind_address);
michael@0 1182 if ((evcon->bind_address = mm_strdup(address)) == NULL)
michael@0 1183 event_warn("%s: strdup", __func__);
michael@0 1184 }
michael@0 1185
michael@0 1186 void
michael@0 1187 evhttp_connection_set_local_port(struct evhttp_connection *evcon,
michael@0 1188 ev_uint16_t port)
michael@0 1189 {
michael@0 1190 EVUTIL_ASSERT(evcon->state == EVCON_DISCONNECTED);
michael@0 1191 evcon->bind_port = port;
michael@0 1192 }
michael@0 1193
michael@0 1194 static void
michael@0 1195 evhttp_request_dispatch(struct evhttp_connection* evcon)
michael@0 1196 {
michael@0 1197 struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
michael@0 1198
michael@0 1199 /* this should not usually happy but it's possible */
michael@0 1200 if (req == NULL)
michael@0 1201 return;
michael@0 1202
michael@0 1203 /* delete possible close detection events */
michael@0 1204 evhttp_connection_stop_detectclose(evcon);
michael@0 1205
michael@0 1206 /* we assume that the connection is connected already */
michael@0 1207 EVUTIL_ASSERT(evcon->state == EVCON_IDLE);
michael@0 1208
michael@0 1209 evcon->state = EVCON_WRITING;
michael@0 1210
michael@0 1211 /* Create the header from the store arguments */
michael@0 1212 evhttp_make_header(evcon, req);
michael@0 1213
michael@0 1214 evhttp_write_buffer(evcon, evhttp_write_connectioncb, NULL);
michael@0 1215 }
michael@0 1216
michael@0 1217 /* Reset our connection state: disables reading/writing, closes our fd (if
michael@0 1218 * any), clears out buffers, and puts us in state DISCONNECTED. */
michael@0 1219 void
michael@0 1220 evhttp_connection_reset(struct evhttp_connection *evcon)
michael@0 1221 {
michael@0 1222 struct evbuffer *tmp;
michael@0 1223
michael@0 1224 /* XXXX This is not actually an optimal fix. Instead we ought to have
michael@0 1225 an API for "stop connecting", or use bufferevent_setfd to turn off
michael@0 1226 connecting. But for Libevent 2.0, this seems like a minimal change
michael@0 1227 least likely to disrupt the rest of the bufferevent and http code.
michael@0 1228
michael@0 1229 Why is this here? If the fd is set in the bufferevent, and the
michael@0 1230 bufferevent is connecting, then you can't actually stop the
michael@0 1231 bufferevent from trying to connect with bufferevent_disable(). The
michael@0 1232 connect will never trigger, since we close the fd, but the timeout
michael@0 1233 might. That caused an assertion failure in evhttp_connection_fail.
michael@0 1234 */
michael@0 1235 bufferevent_disable_hard(evcon->bufev, EV_READ|EV_WRITE);
michael@0 1236
michael@0 1237 if (evcon->fd != -1) {
michael@0 1238 /* inform interested parties about connection close */
michael@0 1239 if (evhttp_connected(evcon) && evcon->closecb != NULL)
michael@0 1240 (*evcon->closecb)(evcon, evcon->closecb_arg);
michael@0 1241
michael@0 1242 shutdown(evcon->fd, EVUTIL_SHUT_WR);
michael@0 1243 evutil_closesocket(evcon->fd);
michael@0 1244 evcon->fd = -1;
michael@0 1245 }
michael@0 1246
michael@0 1247 /* we need to clean up any buffered data */
michael@0 1248 tmp = bufferevent_get_output(evcon->bufev);
michael@0 1249 evbuffer_drain(tmp, evbuffer_get_length(tmp));
michael@0 1250 tmp = bufferevent_get_input(evcon->bufev);
michael@0 1251 evbuffer_drain(tmp, evbuffer_get_length(tmp));
michael@0 1252
michael@0 1253 evcon->state = EVCON_DISCONNECTED;
michael@0 1254 }
michael@0 1255
michael@0 1256 static void
michael@0 1257 evhttp_connection_start_detectclose(struct evhttp_connection *evcon)
michael@0 1258 {
michael@0 1259 evcon->flags |= EVHTTP_CON_CLOSEDETECT;
michael@0 1260
michael@0 1261 bufferevent_enable(evcon->bufev, EV_READ);
michael@0 1262 }
michael@0 1263
michael@0 1264 static void
michael@0 1265 evhttp_connection_stop_detectclose(struct evhttp_connection *evcon)
michael@0 1266 {
michael@0 1267 evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
michael@0 1268
michael@0 1269 bufferevent_disable(evcon->bufev, EV_READ);
michael@0 1270 }
michael@0 1271
michael@0 1272 static void
michael@0 1273 evhttp_connection_retry(evutil_socket_t fd, short what, void *arg)
michael@0 1274 {
michael@0 1275 struct evhttp_connection *evcon = arg;
michael@0 1276
michael@0 1277 evcon->state = EVCON_DISCONNECTED;
michael@0 1278 evhttp_connection_connect(evcon);
michael@0 1279 }
michael@0 1280
michael@0 1281 static void
michael@0 1282 evhttp_connection_cb_cleanup(struct evhttp_connection *evcon)
michael@0 1283 {
michael@0 1284 struct evcon_requestq requests;
michael@0 1285
michael@0 1286 if (evcon->retry_max < 0 || evcon->retry_cnt < evcon->retry_max) {
michael@0 1287 evtimer_assign(&evcon->retry_ev, evcon->base, evhttp_connection_retry, evcon);
michael@0 1288 /* XXXX handle failure from evhttp_add_event */
michael@0 1289 evhttp_add_event(&evcon->retry_ev,
michael@0 1290 MIN(3600, 2 << evcon->retry_cnt),
michael@0 1291 HTTP_CONNECT_TIMEOUT);
michael@0 1292 evcon->retry_cnt++;
michael@0 1293 return;
michael@0 1294 }
michael@0 1295 evhttp_connection_reset(evcon);
michael@0 1296
michael@0 1297 /*
michael@0 1298 * User callback can do evhttp_make_request() on the same
michael@0 1299 * evcon so new request will be added to evcon->requests. To
michael@0 1300 * avoid freeing it prematurely we iterate over the copy of
michael@0 1301 * the queue.
michael@0 1302 */
michael@0 1303 TAILQ_INIT(&requests);
michael@0 1304 while (TAILQ_FIRST(&evcon->requests) != NULL) {
michael@0 1305 struct evhttp_request *request = TAILQ_FIRST(&evcon->requests);
michael@0 1306 TAILQ_REMOVE(&evcon->requests, request, next);
michael@0 1307 TAILQ_INSERT_TAIL(&requests, request, next);
michael@0 1308 }
michael@0 1309
michael@0 1310 /* for now, we just signal all requests by executing their callbacks */
michael@0 1311 while (TAILQ_FIRST(&requests) != NULL) {
michael@0 1312 struct evhttp_request *request = TAILQ_FIRST(&requests);
michael@0 1313 TAILQ_REMOVE(&requests, request, next);
michael@0 1314 request->evcon = NULL;
michael@0 1315
michael@0 1316 /* we might want to set an error here */
michael@0 1317 request->cb(request, request->cb_arg);
michael@0 1318 evhttp_request_free(request);
michael@0 1319 }
michael@0 1320 }
michael@0 1321
michael@0 1322 static void
michael@0 1323 evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
michael@0 1324 {
michael@0 1325 struct evhttp_connection *evcon = arg;
michael@0 1326 struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
michael@0 1327
michael@0 1328 switch (evcon->state) {
michael@0 1329 case EVCON_CONNECTING:
michael@0 1330 if (what & BEV_EVENT_TIMEOUT) {
michael@0 1331 event_debug(("%s: connection timeout for \"%s:%d\" on "
michael@0 1332 EV_SOCK_FMT,
michael@0 1333 __func__, evcon->address, evcon->port,
michael@0 1334 EV_SOCK_ARG(evcon->fd)));
michael@0 1335 evhttp_connection_cb_cleanup(evcon);
michael@0 1336 return;
michael@0 1337 }
michael@0 1338 break;
michael@0 1339
michael@0 1340 case EVCON_READING_BODY:
michael@0 1341 if (!req->chunked && req->ntoread < 0
michael@0 1342 && what == (BEV_EVENT_READING|BEV_EVENT_EOF)) {
michael@0 1343 /* EOF on read can be benign */
michael@0 1344 evhttp_connection_done(evcon);
michael@0 1345 return;
michael@0 1346 }
michael@0 1347 break;
michael@0 1348
michael@0 1349 case EVCON_DISCONNECTED:
michael@0 1350 case EVCON_IDLE:
michael@0 1351 case EVCON_READING_FIRSTLINE:
michael@0 1352 case EVCON_READING_HEADERS:
michael@0 1353 case EVCON_READING_TRAILER:
michael@0 1354 case EVCON_WRITING:
michael@0 1355 default:
michael@0 1356 break;
michael@0 1357 }
michael@0 1358
michael@0 1359 /* when we are in close detect mode, a read error means that
michael@0 1360 * the other side closed their connection.
michael@0 1361 */
michael@0 1362 if (evcon->flags & EVHTTP_CON_CLOSEDETECT) {
michael@0 1363 evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
michael@0 1364 EVUTIL_ASSERT(evcon->http_server == NULL);
michael@0 1365 /* For connections from the client, we just
michael@0 1366 * reset the connection so that it becomes
michael@0 1367 * disconnected.
michael@0 1368 */
michael@0 1369 EVUTIL_ASSERT(evcon->state == EVCON_IDLE);
michael@0 1370 evhttp_connection_reset(evcon);
michael@0 1371 return;
michael@0 1372 }
michael@0 1373
michael@0 1374 if (what & BEV_EVENT_TIMEOUT) {
michael@0 1375 evhttp_connection_fail(evcon, EVCON_HTTP_TIMEOUT);
michael@0 1376 } else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
michael@0 1377 evhttp_connection_fail(evcon, EVCON_HTTP_EOF);
michael@0 1378 } else {
michael@0 1379 evhttp_connection_fail(evcon, EVCON_HTTP_BUFFER_ERROR);
michael@0 1380 }
michael@0 1381 }
michael@0 1382
michael@0 1383 /*
michael@0 1384 * Event callback for asynchronous connection attempt.
michael@0 1385 */
michael@0 1386 static void
michael@0 1387 evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
michael@0 1388 {
michael@0 1389 struct evhttp_connection *evcon = arg;
michael@0 1390 int error;
michael@0 1391 ev_socklen_t errsz = sizeof(error);
michael@0 1392
michael@0 1393 if (!(what & BEV_EVENT_CONNECTED)) {
michael@0 1394 /* some operating systems return ECONNREFUSED immediately
michael@0 1395 * when connecting to a local address. the cleanup is going
michael@0 1396 * to reschedule this function call.
michael@0 1397 */
michael@0 1398 #ifndef WIN32
michael@0 1399 if (errno == ECONNREFUSED)
michael@0 1400 goto cleanup;
michael@0 1401 #endif
michael@0 1402 evhttp_error_cb(bufev, what, arg);
michael@0 1403 return;
michael@0 1404 }
michael@0 1405
michael@0 1406 /* Check if the connection completed */
michael@0 1407 if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error,
michael@0 1408 &errsz) == -1) {
michael@0 1409 event_debug(("%s: getsockopt for \"%s:%d\" on "EV_SOCK_FMT,
michael@0 1410 __func__, evcon->address, evcon->port,
michael@0 1411 EV_SOCK_ARG(evcon->fd)));
michael@0 1412 goto cleanup;
michael@0 1413 }
michael@0 1414
michael@0 1415 if (error) {
michael@0 1416 event_debug(("%s: connect failed for \"%s:%d\" on "
michael@0 1417 EV_SOCK_FMT": %s",
michael@0 1418 __func__, evcon->address, evcon->port,
michael@0 1419 EV_SOCK_ARG(evcon->fd),
michael@0 1420 evutil_socket_error_to_string(error)));
michael@0 1421 goto cleanup;
michael@0 1422 }
michael@0 1423
michael@0 1424 /* We are connected to the server now */
michael@0 1425 event_debug(("%s: connected to \"%s:%d\" on "EV_SOCK_FMT"\n",
michael@0 1426 __func__, evcon->address, evcon->port,
michael@0 1427 EV_SOCK_ARG(evcon->fd)));
michael@0 1428
michael@0 1429 /* Reset the retry count as we were successful in connecting */
michael@0 1430 evcon->retry_cnt = 0;
michael@0 1431 evcon->state = EVCON_IDLE;
michael@0 1432
michael@0 1433 /* reset the bufferevent cbs */
michael@0 1434 bufferevent_setcb(evcon->bufev,
michael@0 1435 evhttp_read_cb,
michael@0 1436 evhttp_write_cb,
michael@0 1437 evhttp_error_cb,
michael@0 1438 evcon);
michael@0 1439
michael@0 1440 if (evcon->timeout == -1)
michael@0 1441 bufferevent_settimeout(evcon->bufev,
michael@0 1442 HTTP_READ_TIMEOUT, HTTP_WRITE_TIMEOUT);
michael@0 1443 else {
michael@0 1444 struct timeval tv;
michael@0 1445 tv.tv_sec = evcon->timeout;
michael@0 1446 tv.tv_usec = 0;
michael@0 1447 bufferevent_set_timeouts(evcon->bufev, &tv, &tv);
michael@0 1448 }
michael@0 1449
michael@0 1450 /* try to start requests that have queued up on this connection */
michael@0 1451 evhttp_request_dispatch(evcon);
michael@0 1452 return;
michael@0 1453
michael@0 1454 cleanup:
michael@0 1455 evhttp_connection_cb_cleanup(evcon);
michael@0 1456 }
michael@0 1457
michael@0 1458 /*
michael@0 1459 * Check if we got a valid response code.
michael@0 1460 */
michael@0 1461
michael@0 1462 static int
michael@0 1463 evhttp_valid_response_code(int code)
michael@0 1464 {
michael@0 1465 if (code == 0)
michael@0 1466 return (0);
michael@0 1467
michael@0 1468 return (1);
michael@0 1469 }
michael@0 1470
michael@0 1471 static int
michael@0 1472 evhttp_parse_http_version(const char *version, struct evhttp_request *req)
michael@0 1473 {
michael@0 1474 int major, minor;
michael@0 1475 char ch;
michael@0 1476 int n = sscanf(version, "HTTP/%d.%d%c", &major, &minor, &ch);
michael@0 1477 if (n != 2 || major > 1) {
michael@0 1478 event_debug(("%s: bad version %s on message %p from %s",
michael@0 1479 __func__, version, req, req->remote_host));
michael@0 1480 return (-1);
michael@0 1481 }
michael@0 1482 req->major = major;
michael@0 1483 req->minor = minor;
michael@0 1484 return (0);
michael@0 1485 }
michael@0 1486
michael@0 1487 /* Parses the status line of a web server */
michael@0 1488
michael@0 1489 static int
michael@0 1490 evhttp_parse_response_line(struct evhttp_request *req, char *line)
michael@0 1491 {
michael@0 1492 char *protocol;
michael@0 1493 char *number;
michael@0 1494 const char *readable = "";
michael@0 1495
michael@0 1496 protocol = strsep(&line, " ");
michael@0 1497 if (line == NULL)
michael@0 1498 return (-1);
michael@0 1499 number = strsep(&line, " ");
michael@0 1500 if (line != NULL)
michael@0 1501 readable = line;
michael@0 1502
michael@0 1503 if (evhttp_parse_http_version(protocol, req) < 0)
michael@0 1504 return (-1);
michael@0 1505
michael@0 1506 req->response_code = atoi(number);
michael@0 1507 if (!evhttp_valid_response_code(req->response_code)) {
michael@0 1508 event_debug(("%s: bad response code \"%s\"",
michael@0 1509 __func__, number));
michael@0 1510 return (-1);
michael@0 1511 }
michael@0 1512
michael@0 1513 if ((req->response_code_line = mm_strdup(readable)) == NULL) {
michael@0 1514 event_warn("%s: strdup", __func__);
michael@0 1515 return (-1);
michael@0 1516 }
michael@0 1517
michael@0 1518 return (0);
michael@0 1519 }
michael@0 1520
michael@0 1521 /* Parse the first line of a HTTP request */
michael@0 1522
michael@0 1523 static int
michael@0 1524 evhttp_parse_request_line(struct evhttp_request *req, char *line)
michael@0 1525 {
michael@0 1526 char *method;
michael@0 1527 char *uri;
michael@0 1528 char *version;
michael@0 1529 const char *hostname;
michael@0 1530 const char *scheme;
michael@0 1531
michael@0 1532 /* Parse the request line */
michael@0 1533 method = strsep(&line, " ");
michael@0 1534 if (line == NULL)
michael@0 1535 return (-1);
michael@0 1536 uri = strsep(&line, " ");
michael@0 1537 if (line == NULL)
michael@0 1538 return (-1);
michael@0 1539 version = strsep(&line, " ");
michael@0 1540 if (line != NULL)
michael@0 1541 return (-1);
michael@0 1542
michael@0 1543 /* First line */
michael@0 1544 if (strcmp(method, "GET") == 0) {
michael@0 1545 req->type = EVHTTP_REQ_GET;
michael@0 1546 } else if (strcmp(method, "POST") == 0) {
michael@0 1547 req->type = EVHTTP_REQ_POST;
michael@0 1548 } else if (strcmp(method, "HEAD") == 0) {
michael@0 1549 req->type = EVHTTP_REQ_HEAD;
michael@0 1550 } else if (strcmp(method, "PUT") == 0) {
michael@0 1551 req->type = EVHTTP_REQ_PUT;
michael@0 1552 } else if (strcmp(method, "DELETE") == 0) {
michael@0 1553 req->type = EVHTTP_REQ_DELETE;
michael@0 1554 } else if (strcmp(method, "OPTIONS") == 0) {
michael@0 1555 req->type = EVHTTP_REQ_OPTIONS;
michael@0 1556 } else if (strcmp(method, "TRACE") == 0) {
michael@0 1557 req->type = EVHTTP_REQ_TRACE;
michael@0 1558 } else if (strcmp(method, "PATCH") == 0) {
michael@0 1559 req->type = EVHTTP_REQ_PATCH;
michael@0 1560 } else {
michael@0 1561 req->type = _EVHTTP_REQ_UNKNOWN;
michael@0 1562 event_debug(("%s: bad method %s on request %p from %s",
michael@0 1563 __func__, method, req, req->remote_host));
michael@0 1564 /* No error yet; we'll give a better error later when
michael@0 1565 * we see that req->type is unsupported. */
michael@0 1566 }
michael@0 1567
michael@0 1568 if (evhttp_parse_http_version(version, req) < 0)
michael@0 1569 return (-1);
michael@0 1570
michael@0 1571 if ((req->uri = mm_strdup(uri)) == NULL) {
michael@0 1572 event_debug(("%s: mm_strdup", __func__));
michael@0 1573 return (-1);
michael@0 1574 }
michael@0 1575
michael@0 1576 if ((req->uri_elems = evhttp_uri_parse_with_flags(req->uri,
michael@0 1577 EVHTTP_URI_NONCONFORMANT)) == NULL) {
michael@0 1578 return -1;
michael@0 1579 }
michael@0 1580
michael@0 1581 /* If we have an absolute-URI, check to see if it is an http request
michael@0 1582 for a known vhost or server alias. If we don't know about this
michael@0 1583 host, we consider it a proxy request. */
michael@0 1584 scheme = evhttp_uri_get_scheme(req->uri_elems);
michael@0 1585 hostname = evhttp_uri_get_host(req->uri_elems);
michael@0 1586 if (scheme && (!evutil_ascii_strcasecmp(scheme, "http") ||
michael@0 1587 !evutil_ascii_strcasecmp(scheme, "https")) &&
michael@0 1588 hostname &&
michael@0 1589 !evhttp_find_vhost(req->evcon->http_server, NULL, hostname))
michael@0 1590 req->flags |= EVHTTP_PROXY_REQUEST;
michael@0 1591
michael@0 1592 return (0);
michael@0 1593 }
michael@0 1594
michael@0 1595 const char *
michael@0 1596 evhttp_find_header(const struct evkeyvalq *headers, const char *key)
michael@0 1597 {
michael@0 1598 struct evkeyval *header;
michael@0 1599
michael@0 1600 TAILQ_FOREACH(header, headers, next) {
michael@0 1601 if (evutil_ascii_strcasecmp(header->key, key) == 0)
michael@0 1602 return (header->value);
michael@0 1603 }
michael@0 1604
michael@0 1605 return (NULL);
michael@0 1606 }
michael@0 1607
michael@0 1608 void
michael@0 1609 evhttp_clear_headers(struct evkeyvalq *headers)
michael@0 1610 {
michael@0 1611 struct evkeyval *header;
michael@0 1612
michael@0 1613 for (header = TAILQ_FIRST(headers);
michael@0 1614 header != NULL;
michael@0 1615 header = TAILQ_FIRST(headers)) {
michael@0 1616 TAILQ_REMOVE(headers, header, next);
michael@0 1617 mm_free(header->key);
michael@0 1618 mm_free(header->value);
michael@0 1619 mm_free(header);
michael@0 1620 }
michael@0 1621 }
michael@0 1622
michael@0 1623 /*
michael@0 1624 * Returns 0, if the header was successfully removed.
michael@0 1625 * Returns -1, if the header could not be found.
michael@0 1626 */
michael@0 1627
michael@0 1628 int
michael@0 1629 evhttp_remove_header(struct evkeyvalq *headers, const char *key)
michael@0 1630 {
michael@0 1631 struct evkeyval *header;
michael@0 1632
michael@0 1633 TAILQ_FOREACH(header, headers, next) {
michael@0 1634 if (evutil_ascii_strcasecmp(header->key, key) == 0)
michael@0 1635 break;
michael@0 1636 }
michael@0 1637
michael@0 1638 if (header == NULL)
michael@0 1639 return (-1);
michael@0 1640
michael@0 1641 /* Free and remove the header that we found */
michael@0 1642 TAILQ_REMOVE(headers, header, next);
michael@0 1643 mm_free(header->key);
michael@0 1644 mm_free(header->value);
michael@0 1645 mm_free(header);
michael@0 1646
michael@0 1647 return (0);
michael@0 1648 }
michael@0 1649
michael@0 1650 static int
michael@0 1651 evhttp_header_is_valid_value(const char *value)
michael@0 1652 {
michael@0 1653 const char *p = value;
michael@0 1654
michael@0 1655 while ((p = strpbrk(p, "\r\n")) != NULL) {
michael@0 1656 /* we really expect only one new line */
michael@0 1657 p += strspn(p, "\r\n");
michael@0 1658 /* we expect a space or tab for continuation */
michael@0 1659 if (*p != ' ' && *p != '\t')
michael@0 1660 return (0);
michael@0 1661 }
michael@0 1662 return (1);
michael@0 1663 }
michael@0 1664
michael@0 1665 int
michael@0 1666 evhttp_add_header(struct evkeyvalq *headers,
michael@0 1667 const char *key, const char *value)
michael@0 1668 {
michael@0 1669 event_debug(("%s: key: %s val: %s\n", __func__, key, value));
michael@0 1670
michael@0 1671 if (strchr(key, '\r') != NULL || strchr(key, '\n') != NULL) {
michael@0 1672 /* drop illegal headers */
michael@0 1673 event_debug(("%s: dropping illegal header key\n", __func__));
michael@0 1674 return (-1);
michael@0 1675 }
michael@0 1676
michael@0 1677 if (!evhttp_header_is_valid_value(value)) {
michael@0 1678 event_debug(("%s: dropping illegal header value\n", __func__));
michael@0 1679 return (-1);
michael@0 1680 }
michael@0 1681
michael@0 1682 return (evhttp_add_header_internal(headers, key, value));
michael@0 1683 }
michael@0 1684
michael@0 1685 static int
michael@0 1686 evhttp_add_header_internal(struct evkeyvalq *headers,
michael@0 1687 const char *key, const char *value)
michael@0 1688 {
michael@0 1689 struct evkeyval *header = mm_calloc(1, sizeof(struct evkeyval));
michael@0 1690 if (header == NULL) {
michael@0 1691 event_warn("%s: calloc", __func__);
michael@0 1692 return (-1);
michael@0 1693 }
michael@0 1694 if ((header->key = mm_strdup(key)) == NULL) {
michael@0 1695 mm_free(header);
michael@0 1696 event_warn("%s: strdup", __func__);
michael@0 1697 return (-1);
michael@0 1698 }
michael@0 1699 if ((header->value = mm_strdup(value)) == NULL) {
michael@0 1700 mm_free(header->key);
michael@0 1701 mm_free(header);
michael@0 1702 event_warn("%s: strdup", __func__);
michael@0 1703 return (-1);
michael@0 1704 }
michael@0 1705
michael@0 1706 TAILQ_INSERT_TAIL(headers, header, next);
michael@0 1707
michael@0 1708 return (0);
michael@0 1709 }
michael@0 1710
michael@0 1711 /*
michael@0 1712 * Parses header lines from a request or a response into the specified
michael@0 1713 * request object given an event buffer.
michael@0 1714 *
michael@0 1715 * Returns
michael@0 1716 * DATA_CORRUPTED on error
michael@0 1717 * MORE_DATA_EXPECTED when we need to read more headers
michael@0 1718 * ALL_DATA_READ when all headers have been read.
michael@0 1719 */
michael@0 1720
michael@0 1721 enum message_read_status
michael@0 1722 evhttp_parse_firstline(struct evhttp_request *req, struct evbuffer *buffer)
michael@0 1723 {
michael@0 1724 char *line;
michael@0 1725 enum message_read_status status = ALL_DATA_READ;
michael@0 1726
michael@0 1727 size_t line_length;
michael@0 1728 /* XXX try */
michael@0 1729 line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF);
michael@0 1730 if (line == NULL) {
michael@0 1731 if (req->evcon != NULL &&
michael@0 1732 evbuffer_get_length(buffer) > req->evcon->max_headers_size)
michael@0 1733 return (DATA_TOO_LONG);
michael@0 1734 else
michael@0 1735 return (MORE_DATA_EXPECTED);
michael@0 1736 }
michael@0 1737
michael@0 1738 if (req->evcon != NULL &&
michael@0 1739 line_length > req->evcon->max_headers_size) {
michael@0 1740 mm_free(line);
michael@0 1741 return (DATA_TOO_LONG);
michael@0 1742 }
michael@0 1743
michael@0 1744 req->headers_size = line_length;
michael@0 1745
michael@0 1746 switch (req->kind) {
michael@0 1747 case EVHTTP_REQUEST:
michael@0 1748 if (evhttp_parse_request_line(req, line) == -1)
michael@0 1749 status = DATA_CORRUPTED;
michael@0 1750 break;
michael@0 1751 case EVHTTP_RESPONSE:
michael@0 1752 if (evhttp_parse_response_line(req, line) == -1)
michael@0 1753 status = DATA_CORRUPTED;
michael@0 1754 break;
michael@0 1755 default:
michael@0 1756 status = DATA_CORRUPTED;
michael@0 1757 }
michael@0 1758
michael@0 1759 mm_free(line);
michael@0 1760 return (status);
michael@0 1761 }
michael@0 1762
michael@0 1763 static int
michael@0 1764 evhttp_append_to_last_header(struct evkeyvalq *headers, const char *line)
michael@0 1765 {
michael@0 1766 struct evkeyval *header = TAILQ_LAST(headers, evkeyvalq);
michael@0 1767 char *newval;
michael@0 1768 size_t old_len, line_len;
michael@0 1769
michael@0 1770 if (header == NULL)
michael@0 1771 return (-1);
michael@0 1772
michael@0 1773 old_len = strlen(header->value);
michael@0 1774 line_len = strlen(line);
michael@0 1775
michael@0 1776 newval = mm_realloc(header->value, old_len + line_len + 1);
michael@0 1777 if (newval == NULL)
michael@0 1778 return (-1);
michael@0 1779
michael@0 1780 memcpy(newval + old_len, line, line_len + 1);
michael@0 1781 header->value = newval;
michael@0 1782
michael@0 1783 return (0);
michael@0 1784 }
michael@0 1785
michael@0 1786 enum message_read_status
michael@0 1787 evhttp_parse_headers(struct evhttp_request *req, struct evbuffer* buffer)
michael@0 1788 {
michael@0 1789 enum message_read_status errcode = DATA_CORRUPTED;
michael@0 1790 char *line;
michael@0 1791 enum message_read_status status = MORE_DATA_EXPECTED;
michael@0 1792
michael@0 1793 struct evkeyvalq* headers = req->input_headers;
michael@0 1794 size_t line_length;
michael@0 1795 while ((line = evbuffer_readln(buffer, &line_length, EVBUFFER_EOL_CRLF))
michael@0 1796 != NULL) {
michael@0 1797 char *skey, *svalue;
michael@0 1798
michael@0 1799 req->headers_size += line_length;
michael@0 1800
michael@0 1801 if (req->evcon != NULL &&
michael@0 1802 req->headers_size > req->evcon->max_headers_size) {
michael@0 1803 errcode = DATA_TOO_LONG;
michael@0 1804 goto error;
michael@0 1805 }
michael@0 1806
michael@0 1807 if (*line == '\0') { /* Last header - Done */
michael@0 1808 status = ALL_DATA_READ;
michael@0 1809 mm_free(line);
michael@0 1810 break;
michael@0 1811 }
michael@0 1812
michael@0 1813 /* Check if this is a continuation line */
michael@0 1814 if (*line == ' ' || *line == '\t') {
michael@0 1815 if (evhttp_append_to_last_header(headers, line) == -1)
michael@0 1816 goto error;
michael@0 1817 mm_free(line);
michael@0 1818 continue;
michael@0 1819 }
michael@0 1820
michael@0 1821 /* Processing of header lines */
michael@0 1822 svalue = line;
michael@0 1823 skey = strsep(&svalue, ":");
michael@0 1824 if (svalue == NULL)
michael@0 1825 goto error;
michael@0 1826
michael@0 1827 svalue += strspn(svalue, " ");
michael@0 1828
michael@0 1829 if (evhttp_add_header(headers, skey, svalue) == -1)
michael@0 1830 goto error;
michael@0 1831
michael@0 1832 mm_free(line);
michael@0 1833 }
michael@0 1834
michael@0 1835 if (status == MORE_DATA_EXPECTED) {
michael@0 1836 if (req->evcon != NULL &&
michael@0 1837 req->headers_size + evbuffer_get_length(buffer) > req->evcon->max_headers_size)
michael@0 1838 return (DATA_TOO_LONG);
michael@0 1839 }
michael@0 1840
michael@0 1841 return (status);
michael@0 1842
michael@0 1843 error:
michael@0 1844 mm_free(line);
michael@0 1845 return (errcode);
michael@0 1846 }
michael@0 1847
michael@0 1848 static int
michael@0 1849 evhttp_get_body_length(struct evhttp_request *req)
michael@0 1850 {
michael@0 1851 struct evkeyvalq *headers = req->input_headers;
michael@0 1852 const char *content_length;
michael@0 1853 const char *connection;
michael@0 1854
michael@0 1855 content_length = evhttp_find_header(headers, "Content-Length");
michael@0 1856 connection = evhttp_find_header(headers, "Connection");
michael@0 1857
michael@0 1858 if (content_length == NULL && connection == NULL)
michael@0 1859 req->ntoread = -1;
michael@0 1860 else if (content_length == NULL &&
michael@0 1861 evutil_ascii_strcasecmp(connection, "Close") != 0) {
michael@0 1862 /* Bad combination, we don't know when it will end */
michael@0 1863 event_warnx("%s: we got no content length, but the "
michael@0 1864 "server wants to keep the connection open: %s.",
michael@0 1865 __func__, connection);
michael@0 1866 return (-1);
michael@0 1867 } else if (content_length == NULL) {
michael@0 1868 req->ntoread = -1;
michael@0 1869 } else {
michael@0 1870 char *endp;
michael@0 1871 ev_int64_t ntoread = evutil_strtoll(content_length, &endp, 10);
michael@0 1872 if (*content_length == '\0' || *endp != '\0' || ntoread < 0) {
michael@0 1873 event_debug(("%s: illegal content length: %s",
michael@0 1874 __func__, content_length));
michael@0 1875 return (-1);
michael@0 1876 }
michael@0 1877 req->ntoread = ntoread;
michael@0 1878 }
michael@0 1879
michael@0 1880 event_debug(("%s: bytes to read: "EV_I64_FMT" (in buffer "EV_SIZE_FMT")\n",
michael@0 1881 __func__, EV_I64_ARG(req->ntoread),
michael@0 1882 EV_SIZE_ARG(evbuffer_get_length(bufferevent_get_input(req->evcon->bufev)))));
michael@0 1883
michael@0 1884 return (0);
michael@0 1885 }
michael@0 1886
michael@0 1887 static int
michael@0 1888 evhttp_method_may_have_body(enum evhttp_cmd_type type)
michael@0 1889 {
michael@0 1890 switch (type) {
michael@0 1891 case EVHTTP_REQ_POST:
michael@0 1892 case EVHTTP_REQ_PUT:
michael@0 1893 case EVHTTP_REQ_PATCH:
michael@0 1894 return 1;
michael@0 1895 case EVHTTP_REQ_TRACE:
michael@0 1896 return 0;
michael@0 1897 /* XXX May any of the below methods have a body? */
michael@0 1898 case EVHTTP_REQ_GET:
michael@0 1899 case EVHTTP_REQ_HEAD:
michael@0 1900 case EVHTTP_REQ_DELETE:
michael@0 1901 case EVHTTP_REQ_OPTIONS:
michael@0 1902 case EVHTTP_REQ_CONNECT:
michael@0 1903 return 0;
michael@0 1904 default:
michael@0 1905 return 0;
michael@0 1906 }
michael@0 1907 }
michael@0 1908
michael@0 1909 static void
michael@0 1910 evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
michael@0 1911 {
michael@0 1912 const char *xfer_enc;
michael@0 1913
michael@0 1914 /* If this is a request without a body, then we are done */
michael@0 1915 if (req->kind == EVHTTP_REQUEST &&
michael@0 1916 !evhttp_method_may_have_body(req->type)) {
michael@0 1917 evhttp_connection_done(evcon);
michael@0 1918 return;
michael@0 1919 }
michael@0 1920 evcon->state = EVCON_READING_BODY;
michael@0 1921 xfer_enc = evhttp_find_header(req->input_headers, "Transfer-Encoding");
michael@0 1922 if (xfer_enc != NULL && evutil_ascii_strcasecmp(xfer_enc, "chunked") == 0) {
michael@0 1923 req->chunked = 1;
michael@0 1924 req->ntoread = -1;
michael@0 1925 } else {
michael@0 1926 if (evhttp_get_body_length(req) == -1) {
michael@0 1927 evhttp_connection_fail(evcon,
michael@0 1928 EVCON_HTTP_INVALID_HEADER);
michael@0 1929 return;
michael@0 1930 }
michael@0 1931 if (req->kind == EVHTTP_REQUEST && req->ntoread < 1) {
michael@0 1932 /* An incoming request with no content-length and no
michael@0 1933 * transfer-encoding has no body. */
michael@0 1934 evhttp_connection_done(evcon);
michael@0 1935 return;
michael@0 1936 }
michael@0 1937 }
michael@0 1938
michael@0 1939 /* Should we send a 100 Continue status line? */
michael@0 1940 if (req->kind == EVHTTP_REQUEST && REQ_VERSION_ATLEAST(req, 1, 1)) {
michael@0 1941 const char *expect;
michael@0 1942
michael@0 1943 expect = evhttp_find_header(req->input_headers, "Expect");
michael@0 1944 if (expect) {
michael@0 1945 if (!evutil_ascii_strcasecmp(expect, "100-continue")) {
michael@0 1946 /* XXX It would be nice to do some sanity
michael@0 1947 checking here. Does the resource exist?
michael@0 1948 Should the resource accept post requests? If
michael@0 1949 no, we should respond with an error. For
michael@0 1950 now, just optimistically tell the client to
michael@0 1951 send their message body. */
michael@0 1952 if (req->ntoread > 0) {
michael@0 1953 /* ntoread is ev_int64_t, max_body_size is ev_uint64_t */
michael@0 1954 if ((req->evcon->max_body_size <= EV_INT64_MAX) && (ev_uint64_t)req->ntoread > req->evcon->max_body_size) {
michael@0 1955 evhttp_send_error(req, HTTP_ENTITYTOOLARGE, NULL);
michael@0 1956 return;
michael@0 1957 }
michael@0 1958 }
michael@0 1959 if (!evbuffer_get_length(bufferevent_get_input(evcon->bufev)))
michael@0 1960 evhttp_send_continue(evcon, req);
michael@0 1961 } else {
michael@0 1962 evhttp_send_error(req, HTTP_EXPECTATIONFAILED,
michael@0 1963 NULL);
michael@0 1964 return;
michael@0 1965 }
michael@0 1966 }
michael@0 1967 }
michael@0 1968
michael@0 1969 evhttp_read_body(evcon, req);
michael@0 1970 /* note the request may have been freed in evhttp_read_body */
michael@0 1971 }
michael@0 1972
michael@0 1973 static void
michael@0 1974 evhttp_read_firstline(struct evhttp_connection *evcon,
michael@0 1975 struct evhttp_request *req)
michael@0 1976 {
michael@0 1977 enum message_read_status res;
michael@0 1978
michael@0 1979 res = evhttp_parse_firstline(req, bufferevent_get_input(evcon->bufev));
michael@0 1980 if (res == DATA_CORRUPTED || res == DATA_TOO_LONG) {
michael@0 1981 /* Error while reading, terminate */
michael@0 1982 event_debug(("%s: bad header lines on "EV_SOCK_FMT"\n",
michael@0 1983 __func__, EV_SOCK_ARG(evcon->fd)));
michael@0 1984 evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
michael@0 1985 return;
michael@0 1986 } else if (res == MORE_DATA_EXPECTED) {
michael@0 1987 /* Need more header lines */
michael@0 1988 return;
michael@0 1989 }
michael@0 1990
michael@0 1991 evcon->state = EVCON_READING_HEADERS;
michael@0 1992 evhttp_read_header(evcon, req);
michael@0 1993 }
michael@0 1994
michael@0 1995 static void
michael@0 1996 evhttp_read_header(struct evhttp_connection *evcon,
michael@0 1997 struct evhttp_request *req)
michael@0 1998 {
michael@0 1999 enum message_read_status res;
michael@0 2000 evutil_socket_t fd = evcon->fd;
michael@0 2001
michael@0 2002 res = evhttp_parse_headers(req, bufferevent_get_input(evcon->bufev));
michael@0 2003 if (res == DATA_CORRUPTED || res == DATA_TOO_LONG) {
michael@0 2004 /* Error while reading, terminate */
michael@0 2005 event_debug(("%s: bad header lines on "EV_SOCK_FMT"\n",
michael@0 2006 __func__, EV_SOCK_ARG(fd)));
michael@0 2007 evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
michael@0 2008 return;
michael@0 2009 } else if (res == MORE_DATA_EXPECTED) {
michael@0 2010 /* Need more header lines */
michael@0 2011 return;
michael@0 2012 }
michael@0 2013
michael@0 2014 /* Disable reading for now */
michael@0 2015 bufferevent_disable(evcon->bufev, EV_READ);
michael@0 2016
michael@0 2017 /* Done reading headers, do the real work */
michael@0 2018 switch (req->kind) {
michael@0 2019 case EVHTTP_REQUEST:
michael@0 2020 event_debug(("%s: checking for post data on "EV_SOCK_FMT"\n",
michael@0 2021 __func__, EV_SOCK_ARG(fd)));
michael@0 2022 evhttp_get_body(evcon, req);
michael@0 2023 /* note the request may have been freed in evhttp_get_body */
michael@0 2024 break;
michael@0 2025
michael@0 2026 case EVHTTP_RESPONSE:
michael@0 2027 /* Start over if we got a 100 Continue response. */
michael@0 2028 if (req->response_code == 100) {
michael@0 2029 evhttp_start_read(evcon);
michael@0 2030 return;
michael@0 2031 }
michael@0 2032 if (!evhttp_response_needs_body(req)) {
michael@0 2033 event_debug(("%s: skipping body for code %d\n",
michael@0 2034 __func__, req->response_code));
michael@0 2035 evhttp_connection_done(evcon);
michael@0 2036 } else {
michael@0 2037 event_debug(("%s: start of read body for %s on "
michael@0 2038 EV_SOCK_FMT"\n",
michael@0 2039 __func__, req->remote_host, EV_SOCK_ARG(fd)));
michael@0 2040 evhttp_get_body(evcon, req);
michael@0 2041 /* note the request may have been freed in
michael@0 2042 * evhttp_get_body */
michael@0 2043 }
michael@0 2044 break;
michael@0 2045
michael@0 2046 default:
michael@0 2047 event_warnx("%s: bad header on "EV_SOCK_FMT, __func__,
michael@0 2048 EV_SOCK_ARG(fd));
michael@0 2049 evhttp_connection_fail(evcon, EVCON_HTTP_INVALID_HEADER);
michael@0 2050 break;
michael@0 2051 }
michael@0 2052 /* request may have been freed above */
michael@0 2053 }
michael@0 2054
michael@0 2055 /*
michael@0 2056 * Creates a TCP connection to the specified port and executes a callback
michael@0 2057 * when finished. Failure or success is indicate by the passed connection
michael@0 2058 * object.
michael@0 2059 *
michael@0 2060 * Although this interface accepts a hostname, it is intended to take
michael@0 2061 * only numeric hostnames so that non-blocking DNS resolution can
michael@0 2062 * happen elsewhere.
michael@0 2063 */
michael@0 2064
michael@0 2065 struct evhttp_connection *
michael@0 2066 evhttp_connection_new(const char *address, unsigned short port)
michael@0 2067 {
michael@0 2068 return (evhttp_connection_base_new(NULL, NULL, address, port));
michael@0 2069 }
michael@0 2070
michael@0 2071 struct evhttp_connection *
michael@0 2072 evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
michael@0 2073 const char *address, unsigned short port)
michael@0 2074 {
michael@0 2075 struct evhttp_connection *evcon = NULL;
michael@0 2076
michael@0 2077 event_debug(("Attempting connection to %s:%d\n", address, port));
michael@0 2078
michael@0 2079 if ((evcon = mm_calloc(1, sizeof(struct evhttp_connection))) == NULL) {
michael@0 2080 event_warn("%s: calloc failed", __func__);
michael@0 2081 goto error;
michael@0 2082 }
michael@0 2083
michael@0 2084 evcon->fd = -1;
michael@0 2085 evcon->port = port;
michael@0 2086
michael@0 2087 evcon->max_headers_size = EV_SIZE_MAX;
michael@0 2088 evcon->max_body_size = EV_SIZE_MAX;
michael@0 2089
michael@0 2090 evcon->timeout = -1;
michael@0 2091 evcon->retry_cnt = evcon->retry_max = 0;
michael@0 2092
michael@0 2093 if ((evcon->address = mm_strdup(address)) == NULL) {
michael@0 2094 event_warn("%s: strdup failed", __func__);
michael@0 2095 goto error;
michael@0 2096 }
michael@0 2097
michael@0 2098 if ((evcon->bufev = bufferevent_new(-1,
michael@0 2099 evhttp_read_cb,
michael@0 2100 evhttp_write_cb,
michael@0 2101 evhttp_error_cb, evcon)) == NULL) {
michael@0 2102 event_warn("%s: bufferevent_new failed", __func__);
michael@0 2103 goto error;
michael@0 2104 }
michael@0 2105
michael@0 2106 evcon->state = EVCON_DISCONNECTED;
michael@0 2107 TAILQ_INIT(&evcon->requests);
michael@0 2108
michael@0 2109 if (base != NULL) {
michael@0 2110 evcon->base = base;
michael@0 2111 bufferevent_base_set(base, evcon->bufev);
michael@0 2112 }
michael@0 2113
michael@0 2114
michael@0 2115 event_deferred_cb_init(&evcon->read_more_deferred_cb,
michael@0 2116 evhttp_deferred_read_cb, evcon);
michael@0 2117
michael@0 2118 evcon->dns_base = dnsbase;
michael@0 2119
michael@0 2120 return (evcon);
michael@0 2121
michael@0 2122 error:
michael@0 2123 if (evcon != NULL)
michael@0 2124 evhttp_connection_free(evcon);
michael@0 2125 return (NULL);
michael@0 2126 }
michael@0 2127
michael@0 2128 struct bufferevent *
michael@0 2129 evhttp_connection_get_bufferevent(struct evhttp_connection *evcon)
michael@0 2130 {
michael@0 2131 return evcon->bufev;
michael@0 2132 }
michael@0 2133
michael@0 2134 void
michael@0 2135 evhttp_connection_set_base(struct evhttp_connection *evcon,
michael@0 2136 struct event_base *base)
michael@0 2137 {
michael@0 2138 EVUTIL_ASSERT(evcon->base == NULL);
michael@0 2139 EVUTIL_ASSERT(evcon->state == EVCON_DISCONNECTED);
michael@0 2140 evcon->base = base;
michael@0 2141 bufferevent_base_set(base, evcon->bufev);
michael@0 2142 }
michael@0 2143
michael@0 2144 void
michael@0 2145 evhttp_connection_set_timeout(struct evhttp_connection *evcon,
michael@0 2146 int timeout_in_secs)
michael@0 2147 {
michael@0 2148 evcon->timeout = timeout_in_secs;
michael@0 2149
michael@0 2150 if (evcon->timeout == -1)
michael@0 2151 bufferevent_settimeout(evcon->bufev,
michael@0 2152 HTTP_READ_TIMEOUT, HTTP_WRITE_TIMEOUT);
michael@0 2153 else
michael@0 2154 bufferevent_settimeout(evcon->bufev,
michael@0 2155 evcon->timeout, evcon->timeout);
michael@0 2156 }
michael@0 2157
michael@0 2158 void
michael@0 2159 evhttp_connection_set_retries(struct evhttp_connection *evcon,
michael@0 2160 int retry_max)
michael@0 2161 {
michael@0 2162 evcon->retry_max = retry_max;
michael@0 2163 }
michael@0 2164
michael@0 2165 void
michael@0 2166 evhttp_connection_set_closecb(struct evhttp_connection *evcon,
michael@0 2167 void (*cb)(struct evhttp_connection *, void *), void *cbarg)
michael@0 2168 {
michael@0 2169 evcon->closecb = cb;
michael@0 2170 evcon->closecb_arg = cbarg;
michael@0 2171 }
michael@0 2172
michael@0 2173 void
michael@0 2174 evhttp_connection_get_peer(struct evhttp_connection *evcon,
michael@0 2175 char **address, ev_uint16_t *port)
michael@0 2176 {
michael@0 2177 *address = evcon->address;
michael@0 2178 *port = evcon->port;
michael@0 2179 }
michael@0 2180
michael@0 2181 int
michael@0 2182 evhttp_connection_connect(struct evhttp_connection *evcon)
michael@0 2183 {
michael@0 2184 if (evcon->state == EVCON_CONNECTING)
michael@0 2185 return (0);
michael@0 2186
michael@0 2187 evhttp_connection_reset(evcon);
michael@0 2188
michael@0 2189 EVUTIL_ASSERT(!(evcon->flags & EVHTTP_CON_INCOMING));
michael@0 2190 evcon->flags |= EVHTTP_CON_OUTGOING;
michael@0 2191
michael@0 2192 evcon->fd = bind_socket(
michael@0 2193 evcon->bind_address, evcon->bind_port, 0 /*reuse*/);
michael@0 2194 if (evcon->fd == -1) {
michael@0 2195 event_debug(("%s: failed to bind to \"%s\"",
michael@0 2196 __func__, evcon->bind_address));
michael@0 2197 return (-1);
michael@0 2198 }
michael@0 2199
michael@0 2200 /* Set up a callback for successful connection setup */
michael@0 2201 bufferevent_setfd(evcon->bufev, evcon->fd);
michael@0 2202 bufferevent_setcb(evcon->bufev,
michael@0 2203 NULL /* evhttp_read_cb */,
michael@0 2204 NULL /* evhttp_write_cb */,
michael@0 2205 evhttp_connection_cb,
michael@0 2206 evcon);
michael@0 2207 bufferevent_settimeout(evcon->bufev, 0,
michael@0 2208 evcon->timeout != -1 ? evcon->timeout : HTTP_CONNECT_TIMEOUT);
michael@0 2209 /* make sure that we get a write callback */
michael@0 2210 bufferevent_enable(evcon->bufev, EV_WRITE);
michael@0 2211
michael@0 2212 if (bufferevent_socket_connect_hostname(evcon->bufev, evcon->dns_base,
michael@0 2213 AF_UNSPEC, evcon->address, evcon->port) < 0) {
michael@0 2214 event_sock_warn(evcon->fd, "%s: connection to \"%s\" failed",
michael@0 2215 __func__, evcon->address);
michael@0 2216 /* some operating systems return ECONNREFUSED immediately
michael@0 2217 * when connecting to a local address. the cleanup is going
michael@0 2218 * to reschedule this function call.
michael@0 2219 */
michael@0 2220 evhttp_connection_cb_cleanup(evcon);
michael@0 2221 return (0);
michael@0 2222 }
michael@0 2223
michael@0 2224 evcon->state = EVCON_CONNECTING;
michael@0 2225
michael@0 2226 return (0);
michael@0 2227 }
michael@0 2228
michael@0 2229 /*
michael@0 2230 * Starts an HTTP request on the provided evhttp_connection object.
michael@0 2231 * If the connection object is not connected to the web server already,
michael@0 2232 * this will start the connection.
michael@0 2233 */
michael@0 2234
michael@0 2235 int
michael@0 2236 evhttp_make_request(struct evhttp_connection *evcon,
michael@0 2237 struct evhttp_request *req,
michael@0 2238 enum evhttp_cmd_type type, const char *uri)
michael@0 2239 {
michael@0 2240 /* We are making a request */
michael@0 2241 req->kind = EVHTTP_REQUEST;
michael@0 2242 req->type = type;
michael@0 2243 if (req->uri != NULL)
michael@0 2244 mm_free(req->uri);
michael@0 2245 if ((req->uri = mm_strdup(uri)) == NULL) {
michael@0 2246 event_warn("%s: strdup", __func__);
michael@0 2247 evhttp_request_free(req);
michael@0 2248 return (-1);
michael@0 2249 }
michael@0 2250
michael@0 2251 /* Set the protocol version if it is not supplied */
michael@0 2252 if (!req->major && !req->minor) {
michael@0 2253 req->major = 1;
michael@0 2254 req->minor = 1;
michael@0 2255 }
michael@0 2256
michael@0 2257 EVUTIL_ASSERT(req->evcon == NULL);
michael@0 2258 req->evcon = evcon;
michael@0 2259 EVUTIL_ASSERT(!(req->flags & EVHTTP_REQ_OWN_CONNECTION));
michael@0 2260
michael@0 2261 TAILQ_INSERT_TAIL(&evcon->requests, req, next);
michael@0 2262
michael@0 2263 /* If the connection object is not connected; make it so */
michael@0 2264 if (!evhttp_connected(evcon)) {
michael@0 2265 int res = evhttp_connection_connect(evcon);
michael@0 2266 /* evhttp_connection_fail(), which is called through
michael@0 2267 * evhttp_connection_connect(), assumes that req lies in
michael@0 2268 * evcon->requests. Thus, enqueue the request in advance and r
michael@0 2269 * it in the error case. */
michael@0 2270 if (res != 0)
michael@0 2271 TAILQ_REMOVE(&evcon->requests, req, next);
michael@0 2272
michael@0 2273 return res;
michael@0 2274 }
michael@0 2275
michael@0 2276 /*
michael@0 2277 * If it's connected already and we are the first in the queue,
michael@0 2278 * then we can dispatch this request immediately. Otherwise, it
michael@0 2279 * will be dispatched once the pending requests are completed.
michael@0 2280 */
michael@0 2281 if (TAILQ_FIRST(&evcon->requests) == req)
michael@0 2282 evhttp_request_dispatch(evcon);
michael@0 2283
michael@0 2284 return (0);
michael@0 2285 }
michael@0 2286
michael@0 2287 void
michael@0 2288 evhttp_cancel_request(struct evhttp_request *req)
michael@0 2289 {
michael@0 2290 struct evhttp_connection *evcon = req->evcon;
michael@0 2291 if (evcon != NULL) {
michael@0 2292 /* We need to remove it from the connection */
michael@0 2293 if (TAILQ_FIRST(&evcon->requests) == req) {
michael@0 2294 /* it's currently being worked on, so reset
michael@0 2295 * the connection.
michael@0 2296 */
michael@0 2297 evhttp_connection_fail(evcon,
michael@0 2298 EVCON_HTTP_REQUEST_CANCEL);
michael@0 2299
michael@0 2300 /* connection fail freed the request */
michael@0 2301 return;
michael@0 2302 } else {
michael@0 2303 /* otherwise, we can just remove it from the
michael@0 2304 * queue
michael@0 2305 */
michael@0 2306 TAILQ_REMOVE(&evcon->requests, req, next);
michael@0 2307 }
michael@0 2308 }
michael@0 2309
michael@0 2310 evhttp_request_free(req);
michael@0 2311 }
michael@0 2312
michael@0 2313 /*
michael@0 2314 * Reads data from file descriptor into request structure
michael@0 2315 * Request structure needs to be set up correctly.
michael@0 2316 */
michael@0 2317
michael@0 2318 void
michael@0 2319 evhttp_start_read(struct evhttp_connection *evcon)
michael@0 2320 {
michael@0 2321 /* Set up an event to read the headers */
michael@0 2322 bufferevent_disable(evcon->bufev, EV_WRITE);
michael@0 2323 bufferevent_enable(evcon->bufev, EV_READ);
michael@0 2324 evcon->state = EVCON_READING_FIRSTLINE;
michael@0 2325 /* Reset the bufferevent callbacks */
michael@0 2326 bufferevent_setcb(evcon->bufev,
michael@0 2327 evhttp_read_cb,
michael@0 2328 evhttp_write_cb,
michael@0 2329 evhttp_error_cb,
michael@0 2330 evcon);
michael@0 2331
michael@0 2332 /* If there's still data pending, process it next time through the
michael@0 2333 * loop. Don't do it now; that could get recusive. */
michael@0 2334 if (evbuffer_get_length(bufferevent_get_input(evcon->bufev))) {
michael@0 2335 event_deferred_cb_schedule(get_deferred_queue(evcon),
michael@0 2336 &evcon->read_more_deferred_cb);
michael@0 2337 }
michael@0 2338 }
michael@0 2339
michael@0 2340 static void
michael@0 2341 evhttp_send_done(struct evhttp_connection *evcon, void *arg)
michael@0 2342 {
michael@0 2343 int need_close;
michael@0 2344 struct evhttp_request *req = TAILQ_FIRST(&evcon->requests);
michael@0 2345 TAILQ_REMOVE(&evcon->requests, req, next);
michael@0 2346
michael@0 2347 need_close =
michael@0 2348 (REQ_VERSION_BEFORE(req, 1, 1) &&
michael@0 2349 !evhttp_is_connection_keepalive(req->input_headers))||
michael@0 2350 evhttp_is_connection_close(req->flags, req->input_headers) ||
michael@0 2351 evhttp_is_connection_close(req->flags, req->output_headers);
michael@0 2352
michael@0 2353 EVUTIL_ASSERT(req->flags & EVHTTP_REQ_OWN_CONNECTION);
michael@0 2354 evhttp_request_free(req);
michael@0 2355
michael@0 2356 if (need_close) {
michael@0 2357 evhttp_connection_free(evcon);
michael@0 2358 return;
michael@0 2359 }
michael@0 2360
michael@0 2361 /* we have a persistent connection; try to accept another request. */
michael@0 2362 if (evhttp_associate_new_request_with_connection(evcon) == -1) {
michael@0 2363 evhttp_connection_free(evcon);
michael@0 2364 }
michael@0 2365 }
michael@0 2366
michael@0 2367 /*
michael@0 2368 * Returns an error page.
michael@0 2369 */
michael@0 2370
michael@0 2371 void
michael@0 2372 evhttp_send_error(struct evhttp_request *req, int error, const char *reason)
michael@0 2373 {
michael@0 2374
michael@0 2375 #define ERR_FORMAT "<HTML><HEAD>\n" \
michael@0 2376 "<TITLE>%d %s</TITLE>\n" \
michael@0 2377 "</HEAD><BODY>\n" \
michael@0 2378 "<H1>%s</H1>\n" \
michael@0 2379 "</BODY></HTML>\n"
michael@0 2380
michael@0 2381 struct evbuffer *buf = evbuffer_new();
michael@0 2382 if (buf == NULL) {
michael@0 2383 /* if we cannot allocate memory; we just drop the connection */
michael@0 2384 evhttp_connection_free(req->evcon);
michael@0 2385 return;
michael@0 2386 }
michael@0 2387 if (reason == NULL) {
michael@0 2388 reason = evhttp_response_phrase_internal(error);
michael@0 2389 }
michael@0 2390
michael@0 2391 evhttp_response_code(req, error, reason);
michael@0 2392
michael@0 2393 evbuffer_add_printf(buf, ERR_FORMAT, error, reason, reason);
michael@0 2394
michael@0 2395 evhttp_send_page(req, buf);
michael@0 2396
michael@0 2397 evbuffer_free(buf);
michael@0 2398 #undef ERR_FORMAT
michael@0 2399 }
michael@0 2400
michael@0 2401 /* Requires that headers and response code are already set up */
michael@0 2402
michael@0 2403 static inline void
michael@0 2404 evhttp_send(struct evhttp_request *req, struct evbuffer *databuf)
michael@0 2405 {
michael@0 2406 struct evhttp_connection *evcon = req->evcon;
michael@0 2407
michael@0 2408 if (evcon == NULL) {
michael@0 2409 evhttp_request_free(req);
michael@0 2410 return;
michael@0 2411 }
michael@0 2412
michael@0 2413 EVUTIL_ASSERT(TAILQ_FIRST(&evcon->requests) == req);
michael@0 2414
michael@0 2415 /* we expect no more calls form the user on this request */
michael@0 2416 req->userdone = 1;
michael@0 2417
michael@0 2418 /* xxx: not sure if we really should expose the data buffer this way */
michael@0 2419 if (databuf != NULL)
michael@0 2420 evbuffer_add_buffer(req->output_buffer, databuf);
michael@0 2421
michael@0 2422 /* Adds headers to the response */
michael@0 2423 evhttp_make_header(evcon, req);
michael@0 2424
michael@0 2425 evhttp_write_buffer(evcon, evhttp_send_done, NULL);
michael@0 2426 }
michael@0 2427
michael@0 2428 void
michael@0 2429 evhttp_send_reply(struct evhttp_request *req, int code, const char *reason,
michael@0 2430 struct evbuffer *databuf)
michael@0 2431 {
michael@0 2432 evhttp_response_code(req, code, reason);
michael@0 2433
michael@0 2434 evhttp_send(req, databuf);
michael@0 2435 }
michael@0 2436
michael@0 2437 void
michael@0 2438 evhttp_send_reply_start(struct evhttp_request *req, int code,
michael@0 2439 const char *reason)
michael@0 2440 {
michael@0 2441 evhttp_response_code(req, code, reason);
michael@0 2442 if (evhttp_find_header(req->output_headers, "Content-Length") == NULL &&
michael@0 2443 REQ_VERSION_ATLEAST(req, 1, 1) &&
michael@0 2444 evhttp_response_needs_body(req)) {
michael@0 2445 /*
michael@0 2446 * prefer HTTP/1.1 chunked encoding to closing the connection;
michael@0 2447 * note RFC 2616 section 4.4 forbids it with Content-Length:
michael@0 2448 * and it's not necessary then anyway.
michael@0 2449 */
michael@0 2450 evhttp_add_header(req->output_headers, "Transfer-Encoding",
michael@0 2451 "chunked");
michael@0 2452 req->chunked = 1;
michael@0 2453 } else {
michael@0 2454 req->chunked = 0;
michael@0 2455 }
michael@0 2456 evhttp_make_header(req->evcon, req);
michael@0 2457 evhttp_write_buffer(req->evcon, NULL, NULL);
michael@0 2458 }
michael@0 2459
michael@0 2460 void
michael@0 2461 evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf)
michael@0 2462 {
michael@0 2463 struct evhttp_connection *evcon = req->evcon;
michael@0 2464 struct evbuffer *output;
michael@0 2465
michael@0 2466 if (evcon == NULL)
michael@0 2467 return;
michael@0 2468
michael@0 2469 output = bufferevent_get_output(evcon->bufev);
michael@0 2470
michael@0 2471 if (evbuffer_get_length(databuf) == 0)
michael@0 2472 return;
michael@0 2473 if (!evhttp_response_needs_body(req))
michael@0 2474 return;
michael@0 2475 if (req->chunked) {
michael@0 2476 evbuffer_add_printf(output, "%x\r\n",
michael@0 2477 (unsigned)evbuffer_get_length(databuf));
michael@0 2478 }
michael@0 2479 evbuffer_add_buffer(output, databuf);
michael@0 2480 if (req->chunked) {
michael@0 2481 evbuffer_add(output, "\r\n", 2);
michael@0 2482 }
michael@0 2483 evhttp_write_buffer(evcon, NULL, NULL);
michael@0 2484 }
michael@0 2485
michael@0 2486 void
michael@0 2487 evhttp_send_reply_end(struct evhttp_request *req)
michael@0 2488 {
michael@0 2489 struct evhttp_connection *evcon = req->evcon;
michael@0 2490 struct evbuffer *output;
michael@0 2491
michael@0 2492 if (evcon == NULL) {
michael@0 2493 evhttp_request_free(req);
michael@0 2494 return;
michael@0 2495 }
michael@0 2496
michael@0 2497 output = bufferevent_get_output(evcon->bufev);
michael@0 2498
michael@0 2499 /* we expect no more calls form the user on this request */
michael@0 2500 req->userdone = 1;
michael@0 2501
michael@0 2502 if (req->chunked) {
michael@0 2503 evbuffer_add(output, "0\r\n\r\n", 5);
michael@0 2504 evhttp_write_buffer(req->evcon, evhttp_send_done, NULL);
michael@0 2505 req->chunked = 0;
michael@0 2506 } else if (evbuffer_get_length(output) == 0) {
michael@0 2507 /* let the connection know that we are done with the request */
michael@0 2508 evhttp_send_done(evcon, NULL);
michael@0 2509 } else {
michael@0 2510 /* make the callback execute after all data has been written */
michael@0 2511 evcon->cb = evhttp_send_done;
michael@0 2512 evcon->cb_arg = NULL;
michael@0 2513 }
michael@0 2514 }
michael@0 2515
michael@0 2516 static const char *informational_phrases[] = {
michael@0 2517 /* 100 */ "Continue",
michael@0 2518 /* 101 */ "Switching Protocols"
michael@0 2519 };
michael@0 2520
michael@0 2521 static const char *success_phrases[] = {
michael@0 2522 /* 200 */ "OK",
michael@0 2523 /* 201 */ "Created",
michael@0 2524 /* 202 */ "Accepted",
michael@0 2525 /* 203 */ "Non-Authoritative Information",
michael@0 2526 /* 204 */ "No Content",
michael@0 2527 /* 205 */ "Reset Content",
michael@0 2528 /* 206 */ "Partial Content"
michael@0 2529 };
michael@0 2530
michael@0 2531 static const char *redirection_phrases[] = {
michael@0 2532 /* 300 */ "Multiple Choices",
michael@0 2533 /* 301 */ "Moved Permanently",
michael@0 2534 /* 302 */ "Found",
michael@0 2535 /* 303 */ "See Other",
michael@0 2536 /* 304 */ "Not Modified",
michael@0 2537 /* 305 */ "Use Proxy",
michael@0 2538 /* 307 */ "Temporary Redirect"
michael@0 2539 };
michael@0 2540
michael@0 2541 static const char *client_error_phrases[] = {
michael@0 2542 /* 400 */ "Bad Request",
michael@0 2543 /* 401 */ "Unauthorized",
michael@0 2544 /* 402 */ "Payment Required",
michael@0 2545 /* 403 */ "Forbidden",
michael@0 2546 /* 404 */ "Not Found",
michael@0 2547 /* 405 */ "Method Not Allowed",
michael@0 2548 /* 406 */ "Not Acceptable",
michael@0 2549 /* 407 */ "Proxy Authentication Required",
michael@0 2550 /* 408 */ "Request Time-out",
michael@0 2551 /* 409 */ "Conflict",
michael@0 2552 /* 410 */ "Gone",
michael@0 2553 /* 411 */ "Length Required",
michael@0 2554 /* 412 */ "Precondition Failed",
michael@0 2555 /* 413 */ "Request Entity Too Large",
michael@0 2556 /* 414 */ "Request-URI Too Large",
michael@0 2557 /* 415 */ "Unsupported Media Type",
michael@0 2558 /* 416 */ "Requested range not satisfiable",
michael@0 2559 /* 417 */ "Expectation Failed"
michael@0 2560 };
michael@0 2561
michael@0 2562 static const char *server_error_phrases[] = {
michael@0 2563 /* 500 */ "Internal Server Error",
michael@0 2564 /* 501 */ "Not Implemented",
michael@0 2565 /* 502 */ "Bad Gateway",
michael@0 2566 /* 503 */ "Service Unavailable",
michael@0 2567 /* 504 */ "Gateway Time-out",
michael@0 2568 /* 505 */ "HTTP Version not supported"
michael@0 2569 };
michael@0 2570
michael@0 2571 struct response_class {
michael@0 2572 const char *name;
michael@0 2573 size_t num_responses;
michael@0 2574 const char **responses;
michael@0 2575 };
michael@0 2576
michael@0 2577 #ifndef MEMBERSOF
michael@0 2578 #define MEMBERSOF(x) (sizeof(x)/sizeof(x[0]))
michael@0 2579 #endif
michael@0 2580
michael@0 2581 static const struct response_class response_classes[] = {
michael@0 2582 /* 1xx */ { "Informational", MEMBERSOF(informational_phrases), informational_phrases },
michael@0 2583 /* 2xx */ { "Success", MEMBERSOF(success_phrases), success_phrases },
michael@0 2584 /* 3xx */ { "Redirection", MEMBERSOF(redirection_phrases), redirection_phrases },
michael@0 2585 /* 4xx */ { "Client Error", MEMBERSOF(client_error_phrases), client_error_phrases },
michael@0 2586 /* 5xx */ { "Server Error", MEMBERSOF(server_error_phrases), server_error_phrases }
michael@0 2587 };
michael@0 2588
michael@0 2589 static const char *
michael@0 2590 evhttp_response_phrase_internal(int code)
michael@0 2591 {
michael@0 2592 int klass = code / 100 - 1;
michael@0 2593 int subcode = code % 100;
michael@0 2594
michael@0 2595 /* Unknown class - can't do any better here */
michael@0 2596 if (klass < 0 || klass >= (int) MEMBERSOF(response_classes))
michael@0 2597 return "Unknown Status Class";
michael@0 2598
michael@0 2599 /* Unknown sub-code, return class name at least */
michael@0 2600 if (subcode >= (int) response_classes[klass].num_responses)
michael@0 2601 return response_classes[klass].name;
michael@0 2602
michael@0 2603 return response_classes[klass].responses[subcode];
michael@0 2604 }
michael@0 2605
michael@0 2606 void
michael@0 2607 evhttp_response_code(struct evhttp_request *req, int code, const char *reason)
michael@0 2608 {
michael@0 2609 req->kind = EVHTTP_RESPONSE;
michael@0 2610 req->response_code = code;
michael@0 2611 if (req->response_code_line != NULL)
michael@0 2612 mm_free(req->response_code_line);
michael@0 2613 if (reason == NULL)
michael@0 2614 reason = evhttp_response_phrase_internal(code);
michael@0 2615 req->response_code_line = mm_strdup(reason);
michael@0 2616 if (req->response_code_line == NULL) {
michael@0 2617 event_warn("%s: strdup", __func__);
michael@0 2618 /* XXX what else can we do? */
michael@0 2619 }
michael@0 2620 }
michael@0 2621
michael@0 2622 void
michael@0 2623 evhttp_send_page(struct evhttp_request *req, struct evbuffer *databuf)
michael@0 2624 {
michael@0 2625 if (!req->major || !req->minor) {
michael@0 2626 req->major = 1;
michael@0 2627 req->minor = 1;
michael@0 2628 }
michael@0 2629
michael@0 2630 if (req->kind != EVHTTP_RESPONSE)
michael@0 2631 evhttp_response_code(req, 200, "OK");
michael@0 2632
michael@0 2633 evhttp_clear_headers(req->output_headers);
michael@0 2634 evhttp_add_header(req->output_headers, "Content-Type", "text/html");
michael@0 2635 evhttp_add_header(req->output_headers, "Connection", "close");
michael@0 2636
michael@0 2637 evhttp_send(req, databuf);
michael@0 2638 }
michael@0 2639
michael@0 2640 static const char uri_chars[256] = {
michael@0 2641 /* 0 */
michael@0 2642 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
michael@0 2643 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
michael@0 2644 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
michael@0 2645 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
michael@0 2646 /* 64 */
michael@0 2647 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
michael@0 2648 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
michael@0 2649 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
michael@0 2650 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,
michael@0 2651 /* 128 */
michael@0 2652 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
michael@0 2653 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
michael@0 2654 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
michael@0 2655 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
michael@0 2656 /* 192 */
michael@0 2657 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
michael@0 2658 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
michael@0 2659 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
michael@0 2660 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
michael@0 2661 };
michael@0 2662
michael@0 2663 #define CHAR_IS_UNRESERVED(c) \
michael@0 2664 (uri_chars[(unsigned char)(c)])
michael@0 2665
michael@0 2666 /*
michael@0 2667 * Helper functions to encode/decode a string for inclusion in a URI.
michael@0 2668 * The returned string must be freed by the caller.
michael@0 2669 */
michael@0 2670 char *
michael@0 2671 evhttp_uriencode(const char *uri, ev_ssize_t len, int space_as_plus)
michael@0 2672 {
michael@0 2673 struct evbuffer *buf = evbuffer_new();
michael@0 2674 const char *p, *end;
michael@0 2675 char *result;
michael@0 2676
michael@0 2677 if (buf == NULL)
michael@0 2678 return (NULL);
michael@0 2679
michael@0 2680 if (len >= 0)
michael@0 2681 end = uri+len;
michael@0 2682 else
michael@0 2683 end = uri+strlen(uri);
michael@0 2684
michael@0 2685 for (p = uri; p < end; p++) {
michael@0 2686 if (CHAR_IS_UNRESERVED(*p)) {
michael@0 2687 evbuffer_add(buf, p, 1);
michael@0 2688 } else if (*p == ' ' && space_as_plus) {
michael@0 2689 evbuffer_add(buf, "+", 1);
michael@0 2690 } else {
michael@0 2691 evbuffer_add_printf(buf, "%%%02X", (unsigned char)(*p));
michael@0 2692 }
michael@0 2693 }
michael@0 2694 evbuffer_add(buf, "", 1); /* NUL-terminator. */
michael@0 2695 result = mm_malloc(evbuffer_get_length(buf));
michael@0 2696 if (result)
michael@0 2697 evbuffer_remove(buf, result, evbuffer_get_length(buf));
michael@0 2698 evbuffer_free(buf);
michael@0 2699
michael@0 2700 return (result);
michael@0 2701 }
michael@0 2702
michael@0 2703 char *
michael@0 2704 evhttp_encode_uri(const char *str)
michael@0 2705 {
michael@0 2706 return evhttp_uriencode(str, -1, 0);
michael@0 2707 }
michael@0 2708
michael@0 2709 /*
michael@0 2710 * @param decode_plus_ctl: if 1, we decode plus into space. If 0, we don't.
michael@0 2711 * If -1, when true we transform plus to space only after we've seen
michael@0 2712 * a ?. -1 is deprecated.
michael@0 2713 * @return the number of bytes written to 'ret'.
michael@0 2714 */
michael@0 2715 static int
michael@0 2716 evhttp_decode_uri_internal(
michael@0 2717 const char *uri, size_t length, char *ret, int decode_plus_ctl)
michael@0 2718 {
michael@0 2719 char c;
michael@0 2720 int j;
michael@0 2721 int decode_plus = (decode_plus_ctl == 1) ? 1: 0;
michael@0 2722 unsigned i;
michael@0 2723
michael@0 2724 for (i = j = 0; i < length; i++) {
michael@0 2725 c = uri[i];
michael@0 2726 if (c == '?') {
michael@0 2727 if (decode_plus_ctl < 0)
michael@0 2728 decode_plus = 1;
michael@0 2729 } else if (c == '+' && decode_plus) {
michael@0 2730 c = ' ';
michael@0 2731 } else if (c == '%' && EVUTIL_ISXDIGIT(uri[i+1]) &&
michael@0 2732 EVUTIL_ISXDIGIT(uri[i+2])) {
michael@0 2733 char tmp[3];
michael@0 2734 tmp[0] = uri[i+1];
michael@0 2735 tmp[1] = uri[i+2];
michael@0 2736 tmp[2] = '\0';
michael@0 2737 c = (char)strtol(tmp, NULL, 16);
michael@0 2738 i += 2;
michael@0 2739 }
michael@0 2740 ret[j++] = c;
michael@0 2741 }
michael@0 2742 ret[j] = '\0';
michael@0 2743
michael@0 2744 return (j);
michael@0 2745 }
michael@0 2746
michael@0 2747 /* deprecated */
michael@0 2748 char *
michael@0 2749 evhttp_decode_uri(const char *uri)
michael@0 2750 {
michael@0 2751 char *ret;
michael@0 2752
michael@0 2753 if ((ret = mm_malloc(strlen(uri) + 1)) == NULL) {
michael@0 2754 event_warn("%s: malloc(%lu)", __func__,
michael@0 2755 (unsigned long)(strlen(uri) + 1));
michael@0 2756 return (NULL);
michael@0 2757 }
michael@0 2758
michael@0 2759 evhttp_decode_uri_internal(uri, strlen(uri),
michael@0 2760 ret, -1 /*always_decode_plus*/);
michael@0 2761
michael@0 2762 return (ret);
michael@0 2763 }
michael@0 2764
michael@0 2765 char *
michael@0 2766 evhttp_uridecode(const char *uri, int decode_plus, size_t *size_out)
michael@0 2767 {
michael@0 2768 char *ret;
michael@0 2769 int n;
michael@0 2770
michael@0 2771 if ((ret = mm_malloc(strlen(uri) + 1)) == NULL) {
michael@0 2772 event_warn("%s: malloc(%lu)", __func__,
michael@0 2773 (unsigned long)(strlen(uri) + 1));
michael@0 2774 return (NULL);
michael@0 2775 }
michael@0 2776
michael@0 2777 n = evhttp_decode_uri_internal(uri, strlen(uri),
michael@0 2778 ret, !!decode_plus/*always_decode_plus*/);
michael@0 2779
michael@0 2780 if (size_out) {
michael@0 2781 EVUTIL_ASSERT(n >= 0);
michael@0 2782 *size_out = (size_t)n;
michael@0 2783 }
michael@0 2784
michael@0 2785 return (ret);
michael@0 2786 }
michael@0 2787
michael@0 2788 /*
michael@0 2789 * Helper function to parse out arguments in a query.
michael@0 2790 * The arguments are separated by key and value.
michael@0 2791 */
michael@0 2792
michael@0 2793 static int
michael@0 2794 evhttp_parse_query_impl(const char *str, struct evkeyvalq *headers,
michael@0 2795 int is_whole_uri)
michael@0 2796 {
michael@0 2797 char *line=NULL;
michael@0 2798 char *argument;
michael@0 2799 char *p;
michael@0 2800 const char *query_part;
michael@0 2801 int result = -1;
michael@0 2802 struct evhttp_uri *uri=NULL;
michael@0 2803
michael@0 2804 TAILQ_INIT(headers);
michael@0 2805
michael@0 2806 if (is_whole_uri) {
michael@0 2807 uri = evhttp_uri_parse(str);
michael@0 2808 if (!uri)
michael@0 2809 goto error;
michael@0 2810 query_part = evhttp_uri_get_query(uri);
michael@0 2811 } else {
michael@0 2812 query_part = str;
michael@0 2813 }
michael@0 2814
michael@0 2815 /* No arguments - we are done */
michael@0 2816 if (!query_part || !strlen(query_part)) {
michael@0 2817 result = 0;
michael@0 2818 goto done;
michael@0 2819 }
michael@0 2820
michael@0 2821 if ((line = mm_strdup(query_part)) == NULL) {
michael@0 2822 event_warn("%s: strdup", __func__);
michael@0 2823 goto error;
michael@0 2824 }
michael@0 2825
michael@0 2826 p = argument = line;
michael@0 2827 while (p != NULL && *p != '\0') {
michael@0 2828 char *key, *value, *decoded_value;
michael@0 2829 argument = strsep(&p, "&");
michael@0 2830
michael@0 2831 value = argument;
michael@0 2832 key = strsep(&value, "=");
michael@0 2833 if (value == NULL || *key == '\0') {
michael@0 2834 goto error;
michael@0 2835 }
michael@0 2836
michael@0 2837 if ((decoded_value = mm_malloc(strlen(value) + 1)) == NULL) {
michael@0 2838 event_warn("%s: mm_malloc", __func__);
michael@0 2839 goto error;
michael@0 2840 }
michael@0 2841 evhttp_decode_uri_internal(value, strlen(value),
michael@0 2842 decoded_value, 1 /*always_decode_plus*/);
michael@0 2843 event_debug(("Query Param: %s -> %s\n", key, decoded_value));
michael@0 2844 evhttp_add_header_internal(headers, key, decoded_value);
michael@0 2845 mm_free(decoded_value);
michael@0 2846 }
michael@0 2847
michael@0 2848 result = 0;
michael@0 2849 goto done;
michael@0 2850 error:
michael@0 2851 evhttp_clear_headers(headers);
michael@0 2852 done:
michael@0 2853 if (line)
michael@0 2854 mm_free(line);
michael@0 2855 if (uri)
michael@0 2856 evhttp_uri_free(uri);
michael@0 2857 return result;
michael@0 2858 }
michael@0 2859
michael@0 2860 int
michael@0 2861 evhttp_parse_query(const char *uri, struct evkeyvalq *headers)
michael@0 2862 {
michael@0 2863 return evhttp_parse_query_impl(uri, headers, 1);
michael@0 2864 }
michael@0 2865 int
michael@0 2866 evhttp_parse_query_str(const char *uri, struct evkeyvalq *headers)
michael@0 2867 {
michael@0 2868 return evhttp_parse_query_impl(uri, headers, 0);
michael@0 2869 }
michael@0 2870
michael@0 2871 static struct evhttp_cb *
michael@0 2872 evhttp_dispatch_callback(struct httpcbq *callbacks, struct evhttp_request *req)
michael@0 2873 {
michael@0 2874 struct evhttp_cb *cb;
michael@0 2875 size_t offset = 0;
michael@0 2876 char *translated;
michael@0 2877 const char *path;
michael@0 2878
michael@0 2879 /* Test for different URLs */
michael@0 2880 path = evhttp_uri_get_path(req->uri_elems);
michael@0 2881 offset = strlen(path);
michael@0 2882 if ((translated = mm_malloc(offset + 1)) == NULL)
michael@0 2883 return (NULL);
michael@0 2884 evhttp_decode_uri_internal(path, offset, translated,
michael@0 2885 0 /* decode_plus */);
michael@0 2886
michael@0 2887 TAILQ_FOREACH(cb, callbacks, next) {
michael@0 2888 if (!strcmp(cb->what, translated)) {
michael@0 2889 mm_free(translated);
michael@0 2890 return (cb);
michael@0 2891 }
michael@0 2892 }
michael@0 2893
michael@0 2894 mm_free(translated);
michael@0 2895 return (NULL);
michael@0 2896 }
michael@0 2897
michael@0 2898
michael@0 2899 static int
michael@0 2900 prefix_suffix_match(const char *pattern, const char *name, int ignorecase)
michael@0 2901 {
michael@0 2902 char c;
michael@0 2903
michael@0 2904 while (1) {
michael@0 2905 switch (c = *pattern++) {
michael@0 2906 case '\0':
michael@0 2907 return *name == '\0';
michael@0 2908
michael@0 2909 case '*':
michael@0 2910 while (*name != '\0') {
michael@0 2911 if (prefix_suffix_match(pattern, name,
michael@0 2912 ignorecase))
michael@0 2913 return (1);
michael@0 2914 ++name;
michael@0 2915 }
michael@0 2916 return (0);
michael@0 2917 default:
michael@0 2918 if (c != *name) {
michael@0 2919 if (!ignorecase ||
michael@0 2920 EVUTIL_TOLOWER(c) != EVUTIL_TOLOWER(*name))
michael@0 2921 return (0);
michael@0 2922 }
michael@0 2923 ++name;
michael@0 2924 }
michael@0 2925 }
michael@0 2926 /* NOTREACHED */
michael@0 2927 }
michael@0 2928
michael@0 2929 /*
michael@0 2930 Search the vhost hierarchy beginning with http for a server alias
michael@0 2931 matching hostname. If a match is found, and outhttp is non-null,
michael@0 2932 outhttp is set to the matching http object and 1 is returned.
michael@0 2933 */
michael@0 2934
michael@0 2935 static int
michael@0 2936 evhttp_find_alias(struct evhttp *http, struct evhttp **outhttp,
michael@0 2937 const char *hostname)
michael@0 2938 {
michael@0 2939 struct evhttp_server_alias *alias;
michael@0 2940 struct evhttp *vhost;
michael@0 2941
michael@0 2942 TAILQ_FOREACH(alias, &http->aliases, next) {
michael@0 2943 /* XXX Do we need to handle IP addresses? */
michael@0 2944 if (!evutil_ascii_strcasecmp(alias->alias, hostname)) {
michael@0 2945 if (outhttp)
michael@0 2946 *outhttp = http;
michael@0 2947 return 1;
michael@0 2948 }
michael@0 2949 }
michael@0 2950
michael@0 2951 /* XXX It might be good to avoid recursion here, but I don't
michael@0 2952 see a way to do that w/o a list. */
michael@0 2953 TAILQ_FOREACH(vhost, &http->virtualhosts, next_vhost) {
michael@0 2954 if (evhttp_find_alias(vhost, outhttp, hostname))
michael@0 2955 return 1;
michael@0 2956 }
michael@0 2957
michael@0 2958 return 0;
michael@0 2959 }
michael@0 2960
michael@0 2961 /*
michael@0 2962 Attempts to find the best http object to handle a request for a hostname.
michael@0 2963 All aliases for the root http object and vhosts are searched for an exact
michael@0 2964 match. Then, the vhost hierarchy is traversed again for a matching
michael@0 2965 pattern.
michael@0 2966
michael@0 2967 If an alias or vhost is matched, 1 is returned, and outhttp, if non-null,
michael@0 2968 is set with the best matching http object. If there are no matches, the
michael@0 2969 root http object is stored in outhttp and 0 is returned.
michael@0 2970 */
michael@0 2971
michael@0 2972 static int
michael@0 2973 evhttp_find_vhost(struct evhttp *http, struct evhttp **outhttp,
michael@0 2974 const char *hostname)
michael@0 2975 {
michael@0 2976 struct evhttp *vhost;
michael@0 2977 struct evhttp *oldhttp;
michael@0 2978 int match_found = 0;
michael@0 2979
michael@0 2980 if (evhttp_find_alias(http, outhttp, hostname))
michael@0 2981 return 1;
michael@0 2982
michael@0 2983 do {
michael@0 2984 oldhttp = http;
michael@0 2985 TAILQ_FOREACH(vhost, &http->virtualhosts, next_vhost) {
michael@0 2986 if (prefix_suffix_match(vhost->vhost_pattern,
michael@0 2987 hostname, 1 /* ignorecase */)) {
michael@0 2988 http = vhost;
michael@0 2989 match_found = 1;
michael@0 2990 break;
michael@0 2991 }
michael@0 2992 }
michael@0 2993 } while (oldhttp != http);
michael@0 2994
michael@0 2995 if (outhttp)
michael@0 2996 *outhttp = http;
michael@0 2997
michael@0 2998 return match_found;
michael@0 2999 }
michael@0 3000
michael@0 3001 static void
michael@0 3002 evhttp_handle_request(struct evhttp_request *req, void *arg)
michael@0 3003 {
michael@0 3004 struct evhttp *http = arg;
michael@0 3005 struct evhttp_cb *cb = NULL;
michael@0 3006 const char *hostname;
michael@0 3007
michael@0 3008 /* we have a new request on which the user needs to take action */
michael@0 3009 req->userdone = 0;
michael@0 3010
michael@0 3011 if (req->type == 0 || req->uri == NULL) {
michael@0 3012 evhttp_send_error(req, HTTP_BADREQUEST, NULL);
michael@0 3013 return;
michael@0 3014 }
michael@0 3015
michael@0 3016 if ((http->allowed_methods & req->type) == 0) {
michael@0 3017 event_debug(("Rejecting disallowed method %x (allowed: %x)\n",
michael@0 3018 (unsigned)req->type, (unsigned)http->allowed_methods));
michael@0 3019 evhttp_send_error(req, HTTP_NOTIMPLEMENTED, NULL);
michael@0 3020 return;
michael@0 3021 }
michael@0 3022
michael@0 3023 /* handle potential virtual hosts */
michael@0 3024 hostname = evhttp_request_get_host(req);
michael@0 3025 if (hostname != NULL) {
michael@0 3026 evhttp_find_vhost(http, &http, hostname);
michael@0 3027 }
michael@0 3028
michael@0 3029 if ((cb = evhttp_dispatch_callback(&http->callbacks, req)) != NULL) {
michael@0 3030 (*cb->cb)(req, cb->cbarg);
michael@0 3031 return;
michael@0 3032 }
michael@0 3033
michael@0 3034 /* Generic call back */
michael@0 3035 if (http->gencb) {
michael@0 3036 (*http->gencb)(req, http->gencbarg);
michael@0 3037 return;
michael@0 3038 } else {
michael@0 3039 /* We need to send a 404 here */
michael@0 3040 #define ERR_FORMAT "<html><head>" \
michael@0 3041 "<title>404 Not Found</title>" \
michael@0 3042 "</head><body>" \
michael@0 3043 "<h1>Not Found</h1>" \
michael@0 3044 "<p>The requested URL %s was not found on this server.</p>"\
michael@0 3045 "</body></html>\n"
michael@0 3046
michael@0 3047 char *escaped_html;
michael@0 3048 struct evbuffer *buf;
michael@0 3049
michael@0 3050 if ((escaped_html = evhttp_htmlescape(req->uri)) == NULL) {
michael@0 3051 evhttp_connection_free(req->evcon);
michael@0 3052 return;
michael@0 3053 }
michael@0 3054
michael@0 3055 if ((buf = evbuffer_new()) == NULL) {
michael@0 3056 mm_free(escaped_html);
michael@0 3057 evhttp_connection_free(req->evcon);
michael@0 3058 return;
michael@0 3059 }
michael@0 3060
michael@0 3061 evhttp_response_code(req, HTTP_NOTFOUND, "Not Found");
michael@0 3062
michael@0 3063 evbuffer_add_printf(buf, ERR_FORMAT, escaped_html);
michael@0 3064
michael@0 3065 mm_free(escaped_html);
michael@0 3066
michael@0 3067 evhttp_send_page(req, buf);
michael@0 3068
michael@0 3069 evbuffer_free(buf);
michael@0 3070 #undef ERR_FORMAT
michael@0 3071 }
michael@0 3072 }
michael@0 3073
michael@0 3074 /* Listener callback when a connection arrives at a server. */
michael@0 3075 static void
michael@0 3076 accept_socket_cb(struct evconnlistener *listener, evutil_socket_t nfd, struct sockaddr *peer_sa, int peer_socklen, void *arg)
michael@0 3077 {
michael@0 3078 struct evhttp *http = arg;
michael@0 3079
michael@0 3080 evhttp_get_request(http, nfd, peer_sa, peer_socklen);
michael@0 3081 }
michael@0 3082
michael@0 3083 int
michael@0 3084 evhttp_bind_socket(struct evhttp *http, const char *address, ev_uint16_t port)
michael@0 3085 {
michael@0 3086 struct evhttp_bound_socket *bound =
michael@0 3087 evhttp_bind_socket_with_handle(http, address, port);
michael@0 3088 if (bound == NULL)
michael@0 3089 return (-1);
michael@0 3090 return (0);
michael@0 3091 }
michael@0 3092
michael@0 3093 struct evhttp_bound_socket *
michael@0 3094 evhttp_bind_socket_with_handle(struct evhttp *http, const char *address, ev_uint16_t port)
michael@0 3095 {
michael@0 3096 evutil_socket_t fd;
michael@0 3097 struct evhttp_bound_socket *bound;
michael@0 3098
michael@0 3099 if ((fd = bind_socket(address, port, 1 /*reuse*/)) == -1)
michael@0 3100 return (NULL);
michael@0 3101
michael@0 3102 if (listen(fd, 128) == -1) {
michael@0 3103 event_sock_warn(fd, "%s: listen", __func__);
michael@0 3104 evutil_closesocket(fd);
michael@0 3105 return (NULL);
michael@0 3106 }
michael@0 3107
michael@0 3108 bound = evhttp_accept_socket_with_handle(http, fd);
michael@0 3109
michael@0 3110 if (bound != NULL) {
michael@0 3111 event_debug(("Bound to port %d - Awaiting connections ... ",
michael@0 3112 port));
michael@0 3113 return (bound);
michael@0 3114 }
michael@0 3115
michael@0 3116 return (NULL);
michael@0 3117 }
michael@0 3118
michael@0 3119 int
michael@0 3120 evhttp_accept_socket(struct evhttp *http, evutil_socket_t fd)
michael@0 3121 {
michael@0 3122 struct evhttp_bound_socket *bound =
michael@0 3123 evhttp_accept_socket_with_handle(http, fd);
michael@0 3124 if (bound == NULL)
michael@0 3125 return (-1);
michael@0 3126 return (0);
michael@0 3127 }
michael@0 3128
michael@0 3129
michael@0 3130 struct evhttp_bound_socket *
michael@0 3131 evhttp_accept_socket_with_handle(struct evhttp *http, evutil_socket_t fd)
michael@0 3132 {
michael@0 3133 struct evhttp_bound_socket *bound;
michael@0 3134 struct evconnlistener *listener;
michael@0 3135 const int flags =
michael@0 3136 LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_CLOSE_ON_FREE;
michael@0 3137
michael@0 3138 listener = evconnlistener_new(http->base, NULL, NULL,
michael@0 3139 flags,
michael@0 3140 0, /* Backlog is '0' because we already said 'listen' */
michael@0 3141 fd);
michael@0 3142 if (!listener)
michael@0 3143 return (NULL);
michael@0 3144
michael@0 3145 bound = evhttp_bind_listener(http, listener);
michael@0 3146 if (!bound) {
michael@0 3147 evconnlistener_free(listener);
michael@0 3148 return (NULL);
michael@0 3149 }
michael@0 3150 return (bound);
michael@0 3151 }
michael@0 3152
michael@0 3153 struct evhttp_bound_socket *
michael@0 3154 evhttp_bind_listener(struct evhttp *http, struct evconnlistener *listener)
michael@0 3155 {
michael@0 3156 struct evhttp_bound_socket *bound;
michael@0 3157
michael@0 3158 bound = mm_malloc(sizeof(struct evhttp_bound_socket));
michael@0 3159 if (bound == NULL)
michael@0 3160 return (NULL);
michael@0 3161
michael@0 3162 bound->listener = listener;
michael@0 3163 TAILQ_INSERT_TAIL(&http->sockets, bound, next);
michael@0 3164
michael@0 3165 evconnlistener_set_cb(listener, accept_socket_cb, http);
michael@0 3166 return bound;
michael@0 3167 }
michael@0 3168
michael@0 3169 evutil_socket_t
michael@0 3170 evhttp_bound_socket_get_fd(struct evhttp_bound_socket *bound)
michael@0 3171 {
michael@0 3172 return evconnlistener_get_fd(bound->listener);
michael@0 3173 }
michael@0 3174
michael@0 3175 struct evconnlistener *
michael@0 3176 evhttp_bound_socket_get_listener(struct evhttp_bound_socket *bound)
michael@0 3177 {
michael@0 3178 return bound->listener;
michael@0 3179 }
michael@0 3180
michael@0 3181 void
michael@0 3182 evhttp_del_accept_socket(struct evhttp *http, struct evhttp_bound_socket *bound)
michael@0 3183 {
michael@0 3184 TAILQ_REMOVE(&http->sockets, bound, next);
michael@0 3185 evconnlistener_free(bound->listener);
michael@0 3186 mm_free(bound);
michael@0 3187 }
michael@0 3188
michael@0 3189 static struct evhttp*
michael@0 3190 evhttp_new_object(void)
michael@0 3191 {
michael@0 3192 struct evhttp *http = NULL;
michael@0 3193
michael@0 3194 if ((http = mm_calloc(1, sizeof(struct evhttp))) == NULL) {
michael@0 3195 event_warn("%s: calloc", __func__);
michael@0 3196 return (NULL);
michael@0 3197 }
michael@0 3198
michael@0 3199 http->timeout = -1;
michael@0 3200 evhttp_set_max_headers_size(http, EV_SIZE_MAX);
michael@0 3201 evhttp_set_max_body_size(http, EV_SIZE_MAX);
michael@0 3202 evhttp_set_allowed_methods(http,
michael@0 3203 EVHTTP_REQ_GET |
michael@0 3204 EVHTTP_REQ_POST |
michael@0 3205 EVHTTP_REQ_HEAD |
michael@0 3206 EVHTTP_REQ_PUT |
michael@0 3207 EVHTTP_REQ_DELETE);
michael@0 3208
michael@0 3209 TAILQ_INIT(&http->sockets);
michael@0 3210 TAILQ_INIT(&http->callbacks);
michael@0 3211 TAILQ_INIT(&http->connections);
michael@0 3212 TAILQ_INIT(&http->virtualhosts);
michael@0 3213 TAILQ_INIT(&http->aliases);
michael@0 3214
michael@0 3215 return (http);
michael@0 3216 }
michael@0 3217
michael@0 3218 struct evhttp *
michael@0 3219 evhttp_new(struct event_base *base)
michael@0 3220 {
michael@0 3221 struct evhttp *http = NULL;
michael@0 3222
michael@0 3223 http = evhttp_new_object();
michael@0 3224 if (http == NULL)
michael@0 3225 return (NULL);
michael@0 3226 http->base = base;
michael@0 3227
michael@0 3228 return (http);
michael@0 3229 }
michael@0 3230
michael@0 3231 /*
michael@0 3232 * Start a web server on the specified address and port.
michael@0 3233 */
michael@0 3234
michael@0 3235 struct evhttp *
michael@0 3236 evhttp_start(const char *address, unsigned short port)
michael@0 3237 {
michael@0 3238 struct evhttp *http = NULL;
michael@0 3239
michael@0 3240 http = evhttp_new_object();
michael@0 3241 if (http == NULL)
michael@0 3242 return (NULL);
michael@0 3243 if (evhttp_bind_socket(http, address, port) == -1) {
michael@0 3244 mm_free(http);
michael@0 3245 return (NULL);
michael@0 3246 }
michael@0 3247
michael@0 3248 return (http);
michael@0 3249 }
michael@0 3250
michael@0 3251 void
michael@0 3252 evhttp_free(struct evhttp* http)
michael@0 3253 {
michael@0 3254 struct evhttp_cb *http_cb;
michael@0 3255 struct evhttp_connection *evcon;
michael@0 3256 struct evhttp_bound_socket *bound;
michael@0 3257 struct evhttp* vhost;
michael@0 3258 struct evhttp_server_alias *alias;
michael@0 3259
michael@0 3260 /* Remove the accepting part */
michael@0 3261 while ((bound = TAILQ_FIRST(&http->sockets)) != NULL) {
michael@0 3262 TAILQ_REMOVE(&http->sockets, bound, next);
michael@0 3263
michael@0 3264 evconnlistener_free(bound->listener);
michael@0 3265
michael@0 3266 mm_free(bound);
michael@0 3267 }
michael@0 3268
michael@0 3269 while ((evcon = TAILQ_FIRST(&http->connections)) != NULL) {
michael@0 3270 /* evhttp_connection_free removes the connection */
michael@0 3271 evhttp_connection_free(evcon);
michael@0 3272 }
michael@0 3273
michael@0 3274 while ((http_cb = TAILQ_FIRST(&http->callbacks)) != NULL) {
michael@0 3275 TAILQ_REMOVE(&http->callbacks, http_cb, next);
michael@0 3276 mm_free(http_cb->what);
michael@0 3277 mm_free(http_cb);
michael@0 3278 }
michael@0 3279
michael@0 3280 while ((vhost = TAILQ_FIRST(&http->virtualhosts)) != NULL) {
michael@0 3281 TAILQ_REMOVE(&http->virtualhosts, vhost, next_vhost);
michael@0 3282
michael@0 3283 evhttp_free(vhost);
michael@0 3284 }
michael@0 3285
michael@0 3286 if (http->vhost_pattern != NULL)
michael@0 3287 mm_free(http->vhost_pattern);
michael@0 3288
michael@0 3289 while ((alias = TAILQ_FIRST(&http->aliases)) != NULL) {
michael@0 3290 TAILQ_REMOVE(&http->aliases, alias, next);
michael@0 3291 mm_free(alias->alias);
michael@0 3292 mm_free(alias);
michael@0 3293 }
michael@0 3294
michael@0 3295 mm_free(http);
michael@0 3296 }
michael@0 3297
michael@0 3298 int
michael@0 3299 evhttp_add_virtual_host(struct evhttp* http, const char *pattern,
michael@0 3300 struct evhttp* vhost)
michael@0 3301 {
michael@0 3302 /* a vhost can only be a vhost once and should not have bound sockets */
michael@0 3303 if (vhost->vhost_pattern != NULL ||
michael@0 3304 TAILQ_FIRST(&vhost->sockets) != NULL)
michael@0 3305 return (-1);
michael@0 3306
michael@0 3307 vhost->vhost_pattern = mm_strdup(pattern);
michael@0 3308 if (vhost->vhost_pattern == NULL)
michael@0 3309 return (-1);
michael@0 3310
michael@0 3311 TAILQ_INSERT_TAIL(&http->virtualhosts, vhost, next_vhost);
michael@0 3312
michael@0 3313 return (0);
michael@0 3314 }
michael@0 3315
michael@0 3316 int
michael@0 3317 evhttp_remove_virtual_host(struct evhttp* http, struct evhttp* vhost)
michael@0 3318 {
michael@0 3319 if (vhost->vhost_pattern == NULL)
michael@0 3320 return (-1);
michael@0 3321
michael@0 3322 TAILQ_REMOVE(&http->virtualhosts, vhost, next_vhost);
michael@0 3323
michael@0 3324 mm_free(vhost->vhost_pattern);
michael@0 3325 vhost->vhost_pattern = NULL;
michael@0 3326
michael@0 3327 return (0);
michael@0 3328 }
michael@0 3329
michael@0 3330 int
michael@0 3331 evhttp_add_server_alias(struct evhttp *http, const char *alias)
michael@0 3332 {
michael@0 3333 struct evhttp_server_alias *evalias;
michael@0 3334
michael@0 3335 evalias = mm_calloc(1, sizeof(*evalias));
michael@0 3336 if (!evalias)
michael@0 3337 return -1;
michael@0 3338
michael@0 3339 evalias->alias = mm_strdup(alias);
michael@0 3340 if (!evalias->alias) {
michael@0 3341 mm_free(evalias);
michael@0 3342 return -1;
michael@0 3343 }
michael@0 3344
michael@0 3345 TAILQ_INSERT_TAIL(&http->aliases, evalias, next);
michael@0 3346
michael@0 3347 return 0;
michael@0 3348 }
michael@0 3349
michael@0 3350 int
michael@0 3351 evhttp_remove_server_alias(struct evhttp *http, const char *alias)
michael@0 3352 {
michael@0 3353 struct evhttp_server_alias *evalias;
michael@0 3354
michael@0 3355 TAILQ_FOREACH(evalias, &http->aliases, next) {
michael@0 3356 if (evutil_ascii_strcasecmp(evalias->alias, alias) == 0) {
michael@0 3357 TAILQ_REMOVE(&http->aliases, evalias, next);
michael@0 3358 mm_free(evalias->alias);
michael@0 3359 mm_free(evalias);
michael@0 3360 return 0;
michael@0 3361 }
michael@0 3362 }
michael@0 3363
michael@0 3364 return -1;
michael@0 3365 }
michael@0 3366
michael@0 3367 void
michael@0 3368 evhttp_set_timeout(struct evhttp* http, int timeout_in_secs)
michael@0 3369 {
michael@0 3370 http->timeout = timeout_in_secs;
michael@0 3371 }
michael@0 3372
michael@0 3373 void
michael@0 3374 evhttp_set_max_headers_size(struct evhttp* http, ev_ssize_t max_headers_size)
michael@0 3375 {
michael@0 3376 if (max_headers_size < 0)
michael@0 3377 http->default_max_headers_size = EV_SIZE_MAX;
michael@0 3378 else
michael@0 3379 http->default_max_headers_size = max_headers_size;
michael@0 3380 }
michael@0 3381
michael@0 3382 void
michael@0 3383 evhttp_set_max_body_size(struct evhttp* http, ev_ssize_t max_body_size)
michael@0 3384 {
michael@0 3385 if (max_body_size < 0)
michael@0 3386 http->default_max_body_size = EV_UINT64_MAX;
michael@0 3387 else
michael@0 3388 http->default_max_body_size = max_body_size;
michael@0 3389 }
michael@0 3390
michael@0 3391 void
michael@0 3392 evhttp_set_allowed_methods(struct evhttp* http, ev_uint16_t methods)
michael@0 3393 {
michael@0 3394 http->allowed_methods = methods;
michael@0 3395 }
michael@0 3396
michael@0 3397 int
michael@0 3398 evhttp_set_cb(struct evhttp *http, const char *uri,
michael@0 3399 void (*cb)(struct evhttp_request *, void *), void *cbarg)
michael@0 3400 {
michael@0 3401 struct evhttp_cb *http_cb;
michael@0 3402
michael@0 3403 TAILQ_FOREACH(http_cb, &http->callbacks, next) {
michael@0 3404 if (strcmp(http_cb->what, uri) == 0)
michael@0 3405 return (-1);
michael@0 3406 }
michael@0 3407
michael@0 3408 if ((http_cb = mm_calloc(1, sizeof(struct evhttp_cb))) == NULL) {
michael@0 3409 event_warn("%s: calloc", __func__);
michael@0 3410 return (-2);
michael@0 3411 }
michael@0 3412
michael@0 3413 http_cb->what = mm_strdup(uri);
michael@0 3414 if (http_cb->what == NULL) {
michael@0 3415 event_warn("%s: strdup", __func__);
michael@0 3416 mm_free(http_cb);
michael@0 3417 return (-3);
michael@0 3418 }
michael@0 3419 http_cb->cb = cb;
michael@0 3420 http_cb->cbarg = cbarg;
michael@0 3421
michael@0 3422 TAILQ_INSERT_TAIL(&http->callbacks, http_cb, next);
michael@0 3423
michael@0 3424 return (0);
michael@0 3425 }
michael@0 3426
michael@0 3427 int
michael@0 3428 evhttp_del_cb(struct evhttp *http, const char *uri)
michael@0 3429 {
michael@0 3430 struct evhttp_cb *http_cb;
michael@0 3431
michael@0 3432 TAILQ_FOREACH(http_cb, &http->callbacks, next) {
michael@0 3433 if (strcmp(http_cb->what, uri) == 0)
michael@0 3434 break;
michael@0 3435 }
michael@0 3436 if (http_cb == NULL)
michael@0 3437 return (-1);
michael@0 3438
michael@0 3439 TAILQ_REMOVE(&http->callbacks, http_cb, next);
michael@0 3440 mm_free(http_cb->what);
michael@0 3441 mm_free(http_cb);
michael@0 3442
michael@0 3443 return (0);
michael@0 3444 }
michael@0 3445
michael@0 3446 void
michael@0 3447 evhttp_set_gencb(struct evhttp *http,
michael@0 3448 void (*cb)(struct evhttp_request *, void *), void *cbarg)
michael@0 3449 {
michael@0 3450 http->gencb = cb;
michael@0 3451 http->gencbarg = cbarg;
michael@0 3452 }
michael@0 3453
michael@0 3454 /*
michael@0 3455 * Request related functions
michael@0 3456 */
michael@0 3457
michael@0 3458 struct evhttp_request *
michael@0 3459 evhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg)
michael@0 3460 {
michael@0 3461 struct evhttp_request *req = NULL;
michael@0 3462
michael@0 3463 /* Allocate request structure */
michael@0 3464 if ((req = mm_calloc(1, sizeof(struct evhttp_request))) == NULL) {
michael@0 3465 event_warn("%s: calloc", __func__);
michael@0 3466 goto error;
michael@0 3467 }
michael@0 3468
michael@0 3469 req->headers_size = 0;
michael@0 3470 req->body_size = 0;
michael@0 3471
michael@0 3472 req->kind = EVHTTP_RESPONSE;
michael@0 3473 req->input_headers = mm_calloc(1, sizeof(struct evkeyvalq));
michael@0 3474 if (req->input_headers == NULL) {
michael@0 3475 event_warn("%s: calloc", __func__);
michael@0 3476 goto error;
michael@0 3477 }
michael@0 3478 TAILQ_INIT(req->input_headers);
michael@0 3479
michael@0 3480 req->output_headers = mm_calloc(1, sizeof(struct evkeyvalq));
michael@0 3481 if (req->output_headers == NULL) {
michael@0 3482 event_warn("%s: calloc", __func__);
michael@0 3483 goto error;
michael@0 3484 }
michael@0 3485 TAILQ_INIT(req->output_headers);
michael@0 3486
michael@0 3487 if ((req->input_buffer = evbuffer_new()) == NULL) {
michael@0 3488 event_warn("%s: evbuffer_new", __func__);
michael@0 3489 goto error;
michael@0 3490 }
michael@0 3491
michael@0 3492 if ((req->output_buffer = evbuffer_new()) == NULL) {
michael@0 3493 event_warn("%s: evbuffer_new", __func__);
michael@0 3494 goto error;
michael@0 3495 }
michael@0 3496
michael@0 3497 req->cb = cb;
michael@0 3498 req->cb_arg = arg;
michael@0 3499
michael@0 3500 return (req);
michael@0 3501
michael@0 3502 error:
michael@0 3503 if (req != NULL)
michael@0 3504 evhttp_request_free(req);
michael@0 3505 return (NULL);
michael@0 3506 }
michael@0 3507
michael@0 3508 void
michael@0 3509 evhttp_request_free(struct evhttp_request *req)
michael@0 3510 {
michael@0 3511 if ((req->flags & EVHTTP_REQ_DEFER_FREE) != 0) {
michael@0 3512 req->flags |= EVHTTP_REQ_NEEDS_FREE;
michael@0 3513 return;
michael@0 3514 }
michael@0 3515
michael@0 3516 if (req->remote_host != NULL)
michael@0 3517 mm_free(req->remote_host);
michael@0 3518 if (req->uri != NULL)
michael@0 3519 mm_free(req->uri);
michael@0 3520 if (req->uri_elems != NULL)
michael@0 3521 evhttp_uri_free(req->uri_elems);
michael@0 3522 if (req->response_code_line != NULL)
michael@0 3523 mm_free(req->response_code_line);
michael@0 3524 if (req->host_cache != NULL)
michael@0 3525 mm_free(req->host_cache);
michael@0 3526
michael@0 3527 evhttp_clear_headers(req->input_headers);
michael@0 3528 mm_free(req->input_headers);
michael@0 3529
michael@0 3530 evhttp_clear_headers(req->output_headers);
michael@0 3531 mm_free(req->output_headers);
michael@0 3532
michael@0 3533 if (req->input_buffer != NULL)
michael@0 3534 evbuffer_free(req->input_buffer);
michael@0 3535
michael@0 3536 if (req->output_buffer != NULL)
michael@0 3537 evbuffer_free(req->output_buffer);
michael@0 3538
michael@0 3539 mm_free(req);
michael@0 3540 }
michael@0 3541
michael@0 3542 void
michael@0 3543 evhttp_request_own(struct evhttp_request *req)
michael@0 3544 {
michael@0 3545 req->flags |= EVHTTP_USER_OWNED;
michael@0 3546 }
michael@0 3547
michael@0 3548 int
michael@0 3549 evhttp_request_is_owned(struct evhttp_request *req)
michael@0 3550 {
michael@0 3551 return (req->flags & EVHTTP_USER_OWNED) != 0;
michael@0 3552 }
michael@0 3553
michael@0 3554 struct evhttp_connection *
michael@0 3555 evhttp_request_get_connection(struct evhttp_request *req)
michael@0 3556 {
michael@0 3557 return req->evcon;
michael@0 3558 }
michael@0 3559
michael@0 3560 struct event_base *
michael@0 3561 evhttp_connection_get_base(struct evhttp_connection *conn)
michael@0 3562 {
michael@0 3563 return conn->base;
michael@0 3564 }
michael@0 3565
michael@0 3566 void
michael@0 3567 evhttp_request_set_chunked_cb(struct evhttp_request *req,
michael@0 3568 void (*cb)(struct evhttp_request *, void *))
michael@0 3569 {
michael@0 3570 req->chunk_cb = cb;
michael@0 3571 }
michael@0 3572
michael@0 3573 /*
michael@0 3574 * Allows for inspection of the request URI
michael@0 3575 */
michael@0 3576
michael@0 3577 const char *
michael@0 3578 evhttp_request_get_uri(const struct evhttp_request *req) {
michael@0 3579 if (req->uri == NULL)
michael@0 3580 event_debug(("%s: request %p has no uri\n", __func__, req));
michael@0 3581 return (req->uri);
michael@0 3582 }
michael@0 3583
michael@0 3584 const struct evhttp_uri *
michael@0 3585 evhttp_request_get_evhttp_uri(const struct evhttp_request *req) {
michael@0 3586 if (req->uri_elems == NULL)
michael@0 3587 event_debug(("%s: request %p has no uri elems\n",
michael@0 3588 __func__, req));
michael@0 3589 return (req->uri_elems);
michael@0 3590 }
michael@0 3591
michael@0 3592 const char *
michael@0 3593 evhttp_request_get_host(struct evhttp_request *req)
michael@0 3594 {
michael@0 3595 const char *host = NULL;
michael@0 3596
michael@0 3597 if (req->host_cache)
michael@0 3598 return req->host_cache;
michael@0 3599
michael@0 3600 if (req->uri_elems)
michael@0 3601 host = evhttp_uri_get_host(req->uri_elems);
michael@0 3602 if (!host && req->input_headers) {
michael@0 3603 const char *p;
michael@0 3604 size_t len;
michael@0 3605
michael@0 3606 host = evhttp_find_header(req->input_headers, "Host");
michael@0 3607 /* The Host: header may include a port. Remove it here
michael@0 3608 to be consistent with uri_elems case above. */
michael@0 3609 if (host) {
michael@0 3610 p = host + strlen(host) - 1;
michael@0 3611 while (p > host && EVUTIL_ISDIGIT(*p))
michael@0 3612 --p;
michael@0 3613 if (p > host && *p == ':') {
michael@0 3614 len = p - host;
michael@0 3615 req->host_cache = mm_malloc(len + 1);
michael@0 3616 if (!req->host_cache) {
michael@0 3617 event_warn("%s: malloc", __func__);
michael@0 3618 return NULL;
michael@0 3619 }
michael@0 3620 memcpy(req->host_cache, host, len);
michael@0 3621 req->host_cache[len] = '\0';
michael@0 3622 host = req->host_cache;
michael@0 3623 }
michael@0 3624 }
michael@0 3625 }
michael@0 3626
michael@0 3627 return host;
michael@0 3628 }
michael@0 3629
michael@0 3630 enum evhttp_cmd_type
michael@0 3631 evhttp_request_get_command(const struct evhttp_request *req) {
michael@0 3632 return (req->type);
michael@0 3633 }
michael@0 3634
michael@0 3635 int
michael@0 3636 evhttp_request_get_response_code(const struct evhttp_request *req)
michael@0 3637 {
michael@0 3638 return req->response_code;
michael@0 3639 }
michael@0 3640
michael@0 3641 /** Returns the input headers */
michael@0 3642 struct evkeyvalq *evhttp_request_get_input_headers(struct evhttp_request *req)
michael@0 3643 {
michael@0 3644 return (req->input_headers);
michael@0 3645 }
michael@0 3646
michael@0 3647 /** Returns the output headers */
michael@0 3648 struct evkeyvalq *evhttp_request_get_output_headers(struct evhttp_request *req)
michael@0 3649 {
michael@0 3650 return (req->output_headers);
michael@0 3651 }
michael@0 3652
michael@0 3653 /** Returns the input buffer */
michael@0 3654 struct evbuffer *evhttp_request_get_input_buffer(struct evhttp_request *req)
michael@0 3655 {
michael@0 3656 return (req->input_buffer);
michael@0 3657 }
michael@0 3658
michael@0 3659 /** Returns the output buffer */
michael@0 3660 struct evbuffer *evhttp_request_get_output_buffer(struct evhttp_request *req)
michael@0 3661 {
michael@0 3662 return (req->output_buffer);
michael@0 3663 }
michael@0 3664
michael@0 3665
michael@0 3666 /*
michael@0 3667 * Takes a file descriptor to read a request from.
michael@0 3668 * The callback is executed once the whole request has been read.
michael@0 3669 */
michael@0 3670
michael@0 3671 static struct evhttp_connection*
michael@0 3672 evhttp_get_request_connection(
michael@0 3673 struct evhttp* http,
michael@0 3674 evutil_socket_t fd, struct sockaddr *sa, ev_socklen_t salen)
michael@0 3675 {
michael@0 3676 struct evhttp_connection *evcon;
michael@0 3677 char *hostname = NULL, *portname = NULL;
michael@0 3678
michael@0 3679 name_from_addr(sa, salen, &hostname, &portname);
michael@0 3680 if (hostname == NULL || portname == NULL) {
michael@0 3681 if (hostname) mm_free(hostname);
michael@0 3682 if (portname) mm_free(portname);
michael@0 3683 return (NULL);
michael@0 3684 }
michael@0 3685
michael@0 3686 event_debug(("%s: new request from %s:%s on "EV_SOCK_FMT"\n",
michael@0 3687 __func__, hostname, portname, EV_SOCK_ARG(fd)));
michael@0 3688
michael@0 3689 /* we need a connection object to put the http request on */
michael@0 3690 evcon = evhttp_connection_base_new(
michael@0 3691 http->base, NULL, hostname, atoi(portname));
michael@0 3692 mm_free(hostname);
michael@0 3693 mm_free(portname);
michael@0 3694 if (evcon == NULL)
michael@0 3695 return (NULL);
michael@0 3696
michael@0 3697 evcon->max_headers_size = http->default_max_headers_size;
michael@0 3698 evcon->max_body_size = http->default_max_body_size;
michael@0 3699
michael@0 3700 evcon->flags |= EVHTTP_CON_INCOMING;
michael@0 3701 evcon->state = EVCON_READING_FIRSTLINE;
michael@0 3702
michael@0 3703 evcon->fd = fd;
michael@0 3704
michael@0 3705 bufferevent_setfd(evcon->bufev, fd);
michael@0 3706
michael@0 3707 return (evcon);
michael@0 3708 }
michael@0 3709
michael@0 3710 static int
michael@0 3711 evhttp_associate_new_request_with_connection(struct evhttp_connection *evcon)
michael@0 3712 {
michael@0 3713 struct evhttp *http = evcon->http_server;
michael@0 3714 struct evhttp_request *req;
michael@0 3715 if ((req = evhttp_request_new(evhttp_handle_request, http)) == NULL)
michael@0 3716 return (-1);
michael@0 3717
michael@0 3718 if ((req->remote_host = mm_strdup(evcon->address)) == NULL) {
michael@0 3719 event_warn("%s: strdup", __func__);
michael@0 3720 evhttp_request_free(req);
michael@0 3721 return (-1);
michael@0 3722 }
michael@0 3723 req->remote_port = evcon->port;
michael@0 3724
michael@0 3725 req->evcon = evcon; /* the request ends up owning the connection */
michael@0 3726 req->flags |= EVHTTP_REQ_OWN_CONNECTION;
michael@0 3727
michael@0 3728 /* We did not present the request to the user user yet, so treat it as
michael@0 3729 * if the user was done with the request. This allows us to free the
michael@0 3730 * request on a persistent connection if the client drops it without
michael@0 3731 * sending a request.
michael@0 3732 */
michael@0 3733 req->userdone = 1;
michael@0 3734
michael@0 3735 TAILQ_INSERT_TAIL(&evcon->requests, req, next);
michael@0 3736
michael@0 3737 req->kind = EVHTTP_REQUEST;
michael@0 3738
michael@0 3739
michael@0 3740 evhttp_start_read(evcon);
michael@0 3741
michael@0 3742 return (0);
michael@0 3743 }
michael@0 3744
michael@0 3745 static void
michael@0 3746 evhttp_get_request(struct evhttp *http, evutil_socket_t fd,
michael@0 3747 struct sockaddr *sa, ev_socklen_t salen)
michael@0 3748 {
michael@0 3749 struct evhttp_connection *evcon;
michael@0 3750
michael@0 3751 evcon = evhttp_get_request_connection(http, fd, sa, salen);
michael@0 3752 if (evcon == NULL) {
michael@0 3753 event_sock_warn(fd, "%s: cannot get connection on "EV_SOCK_FMT,
michael@0 3754 __func__, EV_SOCK_ARG(fd));
michael@0 3755 evutil_closesocket(fd);
michael@0 3756 return;
michael@0 3757 }
michael@0 3758
michael@0 3759 /* the timeout can be used by the server to close idle connections */
michael@0 3760 if (http->timeout != -1)
michael@0 3761 evhttp_connection_set_timeout(evcon, http->timeout);
michael@0 3762
michael@0 3763 /*
michael@0 3764 * if we want to accept more than one request on a connection,
michael@0 3765 * we need to know which http server it belongs to.
michael@0 3766 */
michael@0 3767 evcon->http_server = http;
michael@0 3768 TAILQ_INSERT_TAIL(&http->connections, evcon, next);
michael@0 3769
michael@0 3770 if (evhttp_associate_new_request_with_connection(evcon) == -1)
michael@0 3771 evhttp_connection_free(evcon);
michael@0 3772 }
michael@0 3773
michael@0 3774
michael@0 3775 /*
michael@0 3776 * Network helper functions that we do not want to export to the rest of
michael@0 3777 * the world.
michael@0 3778 */
michael@0 3779
michael@0 3780 static void
michael@0 3781 name_from_addr(struct sockaddr *sa, ev_socklen_t salen,
michael@0 3782 char **phost, char **pport)
michael@0 3783 {
michael@0 3784 char ntop[NI_MAXHOST];
michael@0 3785 char strport[NI_MAXSERV];
michael@0 3786 int ni_result;
michael@0 3787
michael@0 3788 #ifdef _EVENT_HAVE_GETNAMEINFO
michael@0 3789 ni_result = getnameinfo(sa, salen,
michael@0 3790 ntop, sizeof(ntop), strport, sizeof(strport),
michael@0 3791 NI_NUMERICHOST|NI_NUMERICSERV);
michael@0 3792
michael@0 3793 if (ni_result != 0) {
michael@0 3794 #ifdef EAI_SYSTEM
michael@0 3795 /* Windows doesn't have an EAI_SYSTEM. */
michael@0 3796 if (ni_result == EAI_SYSTEM)
michael@0 3797 event_err(1, "getnameinfo failed");
michael@0 3798 else
michael@0 3799 #endif
michael@0 3800 event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result));
michael@0 3801 return;
michael@0 3802 }
michael@0 3803 #else
michael@0 3804 ni_result = fake_getnameinfo(sa, salen,
michael@0 3805 ntop, sizeof(ntop), strport, sizeof(strport),
michael@0 3806 NI_NUMERICHOST|NI_NUMERICSERV);
michael@0 3807 if (ni_result != 0)
michael@0 3808 return;
michael@0 3809 #endif
michael@0 3810
michael@0 3811 *phost = mm_strdup(ntop);
michael@0 3812 *pport = mm_strdup(strport);
michael@0 3813 }
michael@0 3814
michael@0 3815 /* Create a non-blocking socket and bind it */
michael@0 3816 /* todo: rename this function */
michael@0 3817 static evutil_socket_t
michael@0 3818 bind_socket_ai(struct evutil_addrinfo *ai, int reuse)
michael@0 3819 {
michael@0 3820 evutil_socket_t fd;
michael@0 3821
michael@0 3822 int on = 1, r;
michael@0 3823 int serrno;
michael@0 3824
michael@0 3825 /* Create listen socket */
michael@0 3826 fd = socket(ai ? ai->ai_family : AF_INET, SOCK_STREAM, 0);
michael@0 3827 if (fd == -1) {
michael@0 3828 event_sock_warn(-1, "socket");
michael@0 3829 return (-1);
michael@0 3830 }
michael@0 3831
michael@0 3832 if (evutil_make_socket_nonblocking(fd) < 0)
michael@0 3833 goto out;
michael@0 3834 if (evutil_make_socket_closeonexec(fd) < 0)
michael@0 3835 goto out;
michael@0 3836
michael@0 3837 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on))<0)
michael@0 3838 goto out;
michael@0 3839 if (reuse) {
michael@0 3840 if (evutil_make_listen_socket_reuseable(fd) < 0)
michael@0 3841 goto out;
michael@0 3842 }
michael@0 3843
michael@0 3844 if (ai != NULL) {
michael@0 3845 r = bind(fd, ai->ai_addr, (ev_socklen_t)ai->ai_addrlen);
michael@0 3846 if (r == -1)
michael@0 3847 goto out;
michael@0 3848 }
michael@0 3849
michael@0 3850 return (fd);
michael@0 3851
michael@0 3852 out:
michael@0 3853 serrno = EVUTIL_SOCKET_ERROR();
michael@0 3854 evutil_closesocket(fd);
michael@0 3855 EVUTIL_SET_SOCKET_ERROR(serrno);
michael@0 3856 return (-1);
michael@0 3857 }
michael@0 3858
michael@0 3859 static struct evutil_addrinfo *
michael@0 3860 make_addrinfo(const char *address, ev_uint16_t port)
michael@0 3861 {
michael@0 3862 struct evutil_addrinfo *ai = NULL;
michael@0 3863
michael@0 3864 struct evutil_addrinfo hints;
michael@0 3865 char strport[NI_MAXSERV];
michael@0 3866 int ai_result;
michael@0 3867
michael@0 3868 memset(&hints, 0, sizeof(hints));
michael@0 3869 hints.ai_family = AF_UNSPEC;
michael@0 3870 hints.ai_socktype = SOCK_STREAM;
michael@0 3871 /* turn NULL hostname into INADDR_ANY, and skip looking up any address
michael@0 3872 * types we don't have an interface to connect to. */
michael@0 3873 hints.ai_flags = EVUTIL_AI_PASSIVE|EVUTIL_AI_ADDRCONFIG;
michael@0 3874 evutil_snprintf(strport, sizeof(strport), "%d", port);
michael@0 3875 if ((ai_result = evutil_getaddrinfo(address, strport, &hints, &ai))
michael@0 3876 != 0) {
michael@0 3877 if (ai_result == EVUTIL_EAI_SYSTEM)
michael@0 3878 event_warn("getaddrinfo");
michael@0 3879 else
michael@0 3880 event_warnx("getaddrinfo: %s",
michael@0 3881 evutil_gai_strerror(ai_result));
michael@0 3882 return (NULL);
michael@0 3883 }
michael@0 3884
michael@0 3885 return (ai);
michael@0 3886 }
michael@0 3887
michael@0 3888 static evutil_socket_t
michael@0 3889 bind_socket(const char *address, ev_uint16_t port, int reuse)
michael@0 3890 {
michael@0 3891 evutil_socket_t fd;
michael@0 3892 struct evutil_addrinfo *aitop = NULL;
michael@0 3893
michael@0 3894 /* just create an unbound socket */
michael@0 3895 if (address == NULL && port == 0)
michael@0 3896 return bind_socket_ai(NULL, 0);
michael@0 3897
michael@0 3898 aitop = make_addrinfo(address, port);
michael@0 3899
michael@0 3900 if (aitop == NULL)
michael@0 3901 return (-1);
michael@0 3902
michael@0 3903 fd = bind_socket_ai(aitop, reuse);
michael@0 3904
michael@0 3905 evutil_freeaddrinfo(aitop);
michael@0 3906
michael@0 3907 return (fd);
michael@0 3908 }
michael@0 3909
michael@0 3910 struct evhttp_uri {
michael@0 3911 unsigned flags;
michael@0 3912 char *scheme; /* scheme; e.g http, ftp etc */
michael@0 3913 char *userinfo; /* userinfo (typically username:pass), or NULL */
michael@0 3914 char *host; /* hostname, IP address, or NULL */
michael@0 3915 int port; /* port, or zero */
michael@0 3916 char *path; /* path, or "". */
michael@0 3917 char *query; /* query, or NULL */
michael@0 3918 char *fragment; /* fragment or NULL */
michael@0 3919 };
michael@0 3920
michael@0 3921 struct evhttp_uri *
michael@0 3922 evhttp_uri_new(void)
michael@0 3923 {
michael@0 3924 struct evhttp_uri *uri = mm_calloc(sizeof(struct evhttp_uri), 1);
michael@0 3925 if (uri)
michael@0 3926 uri->port = -1;
michael@0 3927 return uri;
michael@0 3928 }
michael@0 3929
michael@0 3930 void
michael@0 3931 evhttp_uri_set_flags(struct evhttp_uri *uri, unsigned flags)
michael@0 3932 {
michael@0 3933 uri->flags = flags;
michael@0 3934 }
michael@0 3935
michael@0 3936 /* Return true if the string starting at s and ending immediately before eos
michael@0 3937 * is a valid URI scheme according to RFC3986
michael@0 3938 */
michael@0 3939 static int
michael@0 3940 scheme_ok(const char *s, const char *eos)
michael@0 3941 {
michael@0 3942 /* scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
michael@0 3943 EVUTIL_ASSERT(eos >= s);
michael@0 3944 if (s == eos)
michael@0 3945 return 0;
michael@0 3946 if (!EVUTIL_ISALPHA(*s))
michael@0 3947 return 0;
michael@0 3948 while (++s < eos) {
michael@0 3949 if (! EVUTIL_ISALNUM(*s) &&
michael@0 3950 *s != '+' && *s != '-' && *s != '.')
michael@0 3951 return 0;
michael@0 3952 }
michael@0 3953 return 1;
michael@0 3954 }
michael@0 3955
michael@0 3956 #define SUBDELIMS "!$&'()*+,;="
michael@0 3957
michael@0 3958 /* Return true iff [s..eos) is a valid userinfo */
michael@0 3959 static int
michael@0 3960 userinfo_ok(const char *s, const char *eos)
michael@0 3961 {
michael@0 3962 while (s < eos) {
michael@0 3963 if (CHAR_IS_UNRESERVED(*s) ||
michael@0 3964 strchr(SUBDELIMS, *s) ||
michael@0 3965 *s == ':')
michael@0 3966 ++s;
michael@0 3967 else if (*s == '%' && s+2 < eos &&
michael@0 3968 EVUTIL_ISXDIGIT(s[1]) &&
michael@0 3969 EVUTIL_ISXDIGIT(s[2]))
michael@0 3970 s += 3;
michael@0 3971 else
michael@0 3972 return 0;
michael@0 3973 }
michael@0 3974 return 1;
michael@0 3975 }
michael@0 3976
michael@0 3977 static int
michael@0 3978 regname_ok(const char *s, const char *eos)
michael@0 3979 {
michael@0 3980 while (s && s<eos) {
michael@0 3981 if (CHAR_IS_UNRESERVED(*s) ||
michael@0 3982 strchr(SUBDELIMS, *s))
michael@0 3983 ++s;
michael@0 3984 else if (*s == '%' &&
michael@0 3985 EVUTIL_ISXDIGIT(s[1]) &&
michael@0 3986 EVUTIL_ISXDIGIT(s[2]))
michael@0 3987 s += 3;
michael@0 3988 else
michael@0 3989 return 0;
michael@0 3990 }
michael@0 3991 return 1;
michael@0 3992 }
michael@0 3993
michael@0 3994 static int
michael@0 3995 parse_port(const char *s, const char *eos)
michael@0 3996 {
michael@0 3997 int portnum = 0;
michael@0 3998 while (s < eos) {
michael@0 3999 if (! EVUTIL_ISDIGIT(*s))
michael@0 4000 return -1;
michael@0 4001 portnum = (portnum * 10) + (*s - '0');
michael@0 4002 if (portnum < 0)
michael@0 4003 return -1;
michael@0 4004 ++s;
michael@0 4005 }
michael@0 4006 return portnum;
michael@0 4007 }
michael@0 4008
michael@0 4009 /* returns 0 for bad, 1 for ipv6, 2 for IPvFuture */
michael@0 4010 static int
michael@0 4011 bracket_addr_ok(const char *s, const char *eos)
michael@0 4012 {
michael@0 4013 if (s + 3 > eos || *s != '[' || *(eos-1) != ']')
michael@0 4014 return 0;
michael@0 4015 if (s[1] == 'v') {
michael@0 4016 /* IPvFuture, or junk.
michael@0 4017 "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
michael@0 4018 */
michael@0 4019 s += 2; /* skip [v */
michael@0 4020 --eos;
michael@0 4021 if (!EVUTIL_ISXDIGIT(*s)) /*require at least one*/
michael@0 4022 return 0;
michael@0 4023 while (s < eos && *s != '.') {
michael@0 4024 if (EVUTIL_ISXDIGIT(*s))
michael@0 4025 ++s;
michael@0 4026 else
michael@0 4027 return 0;
michael@0 4028 }
michael@0 4029 if (*s != '.')
michael@0 4030 return 0;
michael@0 4031 ++s;
michael@0 4032 while (s < eos) {
michael@0 4033 if (CHAR_IS_UNRESERVED(*s) ||
michael@0 4034 strchr(SUBDELIMS, *s) ||
michael@0 4035 *s == ':')
michael@0 4036 ++s;
michael@0 4037 else
michael@0 4038 return 0;
michael@0 4039 }
michael@0 4040 return 2;
michael@0 4041 } else {
michael@0 4042 /* IPv6, or junk */
michael@0 4043 char buf[64];
michael@0 4044 ev_ssize_t n_chars = eos-s-2;
michael@0 4045 struct in6_addr in6;
michael@0 4046 if (n_chars >= 64) /* way too long */
michael@0 4047 return 0;
michael@0 4048 memcpy(buf, s+1, n_chars);
michael@0 4049 buf[n_chars]='\0';
michael@0 4050 return (evutil_inet_pton(AF_INET6,buf,&in6)==1) ? 1 : 0;
michael@0 4051 }
michael@0 4052 }
michael@0 4053
michael@0 4054 static int
michael@0 4055 parse_authority(struct evhttp_uri *uri, char *s, char *eos)
michael@0 4056 {
michael@0 4057 char *cp, *port;
michael@0 4058 EVUTIL_ASSERT(eos);
michael@0 4059 if (eos == s) {
michael@0 4060 uri->host = mm_strdup("");
michael@0 4061 if (uri->host == NULL) {
michael@0 4062 event_warn("%s: strdup", __func__);
michael@0 4063 return -1;
michael@0 4064 }
michael@0 4065 return 0;
michael@0 4066 }
michael@0 4067
michael@0 4068 /* Optionally, we start with "userinfo@" */
michael@0 4069
michael@0 4070 cp = strchr(s, '@');
michael@0 4071 if (cp && cp < eos) {
michael@0 4072 if (! userinfo_ok(s,cp))
michael@0 4073 return -1;
michael@0 4074 *cp++ = '\0';
michael@0 4075 uri->userinfo = mm_strdup(s);
michael@0 4076 if (uri->userinfo == NULL) {
michael@0 4077 event_warn("%s: strdup", __func__);
michael@0 4078 return -1;
michael@0 4079 }
michael@0 4080 } else {
michael@0 4081 cp = s;
michael@0 4082 }
michael@0 4083 /* Optionally, we end with ":port" */
michael@0 4084 for (port=eos-1; port >= cp && EVUTIL_ISDIGIT(*port); --port)
michael@0 4085 ;
michael@0 4086 if (port >= cp && *port == ':') {
michael@0 4087 if (port+1 == eos) /* Leave port unspecified; the RFC allows a
michael@0 4088 * nil port */
michael@0 4089 uri->port = -1;
michael@0 4090 else if ((uri->port = parse_port(port+1, eos))<0)
michael@0 4091 return -1;
michael@0 4092 eos = port;
michael@0 4093 }
michael@0 4094 /* Now, cp..eos holds the "host" port, which can be an IPv4Address,
michael@0 4095 * an IP-Literal, or a reg-name */
michael@0 4096 EVUTIL_ASSERT(eos >= cp);
michael@0 4097 if (*cp == '[' && eos >= cp+2 && *(eos-1) == ']') {
michael@0 4098 /* IPv6address, IP-Literal, or junk. */
michael@0 4099 if (! bracket_addr_ok(cp, eos))
michael@0 4100 return -1;
michael@0 4101 } else {
michael@0 4102 /* Make sure the host part is ok. */
michael@0 4103 if (! regname_ok(cp,eos)) /* Match IPv4Address or reg-name */
michael@0 4104 return -1;
michael@0 4105 }
michael@0 4106 uri->host = mm_malloc(eos-cp+1);
michael@0 4107 if (uri->host == NULL) {
michael@0 4108 event_warn("%s: malloc", __func__);
michael@0 4109 return -1;
michael@0 4110 }
michael@0 4111 memcpy(uri->host, cp, eos-cp);
michael@0 4112 uri->host[eos-cp] = '\0';
michael@0 4113 return 0;
michael@0 4114
michael@0 4115 }
michael@0 4116
michael@0 4117 static char *
michael@0 4118 end_of_authority(char *cp)
michael@0 4119 {
michael@0 4120 while (*cp) {
michael@0 4121 if (*cp == '?' || *cp == '#' || *cp == '/')
michael@0 4122 return cp;
michael@0 4123 ++cp;
michael@0 4124 }
michael@0 4125 return cp;
michael@0 4126 }
michael@0 4127
michael@0 4128 enum uri_part {
michael@0 4129 PART_PATH,
michael@0 4130 PART_QUERY,
michael@0 4131 PART_FRAGMENT
michael@0 4132 };
michael@0 4133
michael@0 4134 /* Return the character after the longest prefix of 'cp' that matches...
michael@0 4135 * *pchar / "/" if allow_qchars is false, or
michael@0 4136 * *(pchar / "/" / "?") if allow_qchars is true.
michael@0 4137 */
michael@0 4138 static char *
michael@0 4139 end_of_path(char *cp, enum uri_part part, unsigned flags)
michael@0 4140 {
michael@0 4141 if (flags & EVHTTP_URI_NONCONFORMANT) {
michael@0 4142 /* If NONCONFORMANT:
michael@0 4143 * Path is everything up to a # or ? or nul.
michael@0 4144 * Query is everything up a # or nul
michael@0 4145 * Fragment is everything up to a nul.
michael@0 4146 */
michael@0 4147 switch (part) {
michael@0 4148 case PART_PATH:
michael@0 4149 while (*cp && *cp != '#' && *cp != '?')
michael@0 4150 ++cp;
michael@0 4151 break;
michael@0 4152 case PART_QUERY:
michael@0 4153 while (*cp && *cp != '#')
michael@0 4154 ++cp;
michael@0 4155 break;
michael@0 4156 case PART_FRAGMENT:
michael@0 4157 cp += strlen(cp);
michael@0 4158 break;
michael@0 4159 };
michael@0 4160 return cp;
michael@0 4161 }
michael@0 4162
michael@0 4163 while (*cp) {
michael@0 4164 if (CHAR_IS_UNRESERVED(*cp) ||
michael@0 4165 strchr(SUBDELIMS, *cp) ||
michael@0 4166 *cp == ':' || *cp == '@' || *cp == '/')
michael@0 4167 ++cp;
michael@0 4168 else if (*cp == '%' && EVUTIL_ISXDIGIT(cp[1]) &&
michael@0 4169 EVUTIL_ISXDIGIT(cp[2]))
michael@0 4170 cp += 3;
michael@0 4171 else if (*cp == '?' && part != PART_PATH)
michael@0 4172 ++cp;
michael@0 4173 else
michael@0 4174 return cp;
michael@0 4175 }
michael@0 4176 return cp;
michael@0 4177 }
michael@0 4178
michael@0 4179 static int
michael@0 4180 path_matches_noscheme(const char *cp)
michael@0 4181 {
michael@0 4182 while (*cp) {
michael@0 4183 if (*cp == ':')
michael@0 4184 return 0;
michael@0 4185 else if (*cp == '/')
michael@0 4186 return 1;
michael@0 4187 ++cp;
michael@0 4188 }
michael@0 4189 return 1;
michael@0 4190 }
michael@0 4191
michael@0 4192 struct evhttp_uri *
michael@0 4193 evhttp_uri_parse(const char *source_uri)
michael@0 4194 {
michael@0 4195 return evhttp_uri_parse_with_flags(source_uri, 0);
michael@0 4196 }
michael@0 4197
michael@0 4198 struct evhttp_uri *
michael@0 4199 evhttp_uri_parse_with_flags(const char *source_uri, unsigned flags)
michael@0 4200 {
michael@0 4201 char *readbuf = NULL, *readp = NULL, *token = NULL, *query = NULL;
michael@0 4202 char *path = NULL, *fragment = NULL;
michael@0 4203 int got_authority = 0;
michael@0 4204
michael@0 4205 struct evhttp_uri *uri = mm_calloc(1, sizeof(struct evhttp_uri));
michael@0 4206 if (uri == NULL) {
michael@0 4207 event_warn("%s: calloc", __func__);
michael@0 4208 goto err;
michael@0 4209 }
michael@0 4210 uri->port = -1;
michael@0 4211 uri->flags = flags;
michael@0 4212
michael@0 4213 readbuf = mm_strdup(source_uri);
michael@0 4214 if (readbuf == NULL) {
michael@0 4215 event_warn("%s: strdup", __func__);
michael@0 4216 goto err;
michael@0 4217 }
michael@0 4218
michael@0 4219 readp = readbuf;
michael@0 4220 token = NULL;
michael@0 4221
michael@0 4222 /* We try to follow RFC3986 here as much as we can, and match
michael@0 4223 the productions
michael@0 4224
michael@0 4225 URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
michael@0 4226
michael@0 4227 relative-ref = relative-part [ "?" query ] [ "#" fragment ]
michael@0 4228 */
michael@0 4229
michael@0 4230 /* 1. scheme: */
michael@0 4231 token = strchr(readp, ':');
michael@0 4232 if (token && scheme_ok(readp,token)) {
michael@0 4233 *token = '\0';
michael@0 4234 uri->scheme = mm_strdup(readp);
michael@0 4235 if (uri->scheme == NULL) {
michael@0 4236 event_warn("%s: strdup", __func__);
michael@0 4237 goto err;
michael@0 4238 }
michael@0 4239 readp = token+1; /* eat : */
michael@0 4240 }
michael@0 4241
michael@0 4242 /* 2. Optionally, "//" then an 'authority' part. */
michael@0 4243 if (readp[0]=='/' && readp[1] == '/') {
michael@0 4244 char *authority;
michael@0 4245 readp += 2;
michael@0 4246 authority = readp;
michael@0 4247 path = end_of_authority(readp);
michael@0 4248 if (parse_authority(uri, authority, path) < 0)
michael@0 4249 goto err;
michael@0 4250 readp = path;
michael@0 4251 got_authority = 1;
michael@0 4252 }
michael@0 4253
michael@0 4254 /* 3. Query: path-abempty, path-absolute, path-rootless, or path-empty
michael@0 4255 */
michael@0 4256 path = readp;
michael@0 4257 readp = end_of_path(path, PART_PATH, flags);
michael@0 4258
michael@0 4259 /* Query */
michael@0 4260 if (*readp == '?') {
michael@0 4261 *readp = '\0';
michael@0 4262 ++readp;
michael@0 4263 query = readp;
michael@0 4264 readp = end_of_path(readp, PART_QUERY, flags);
michael@0 4265 }
michael@0 4266 /* fragment */
michael@0 4267 if (*readp == '#') {
michael@0 4268 *readp = '\0';
michael@0 4269 ++readp;
michael@0 4270 fragment = readp;
michael@0 4271 readp = end_of_path(readp, PART_FRAGMENT, flags);
michael@0 4272 }
michael@0 4273 if (*readp != '\0') {
michael@0 4274 goto err;
michael@0 4275 }
michael@0 4276
michael@0 4277 /* These next two cases may be unreachable; I'm leaving them
michael@0 4278 * in to be defensive. */
michael@0 4279 /* If you didn't get an authority, the path can't begin with "//" */
michael@0 4280 if (!got_authority && path[0]=='/' && path[1]=='/')
michael@0 4281 goto err;
michael@0 4282 /* If you did get an authority, the path must begin with "/" or be
michael@0 4283 * empty. */
michael@0 4284 if (got_authority && path[0] != '/' && path[0] != '\0')
michael@0 4285 goto err;
michael@0 4286 /* (End of maybe-unreachable cases) */
michael@0 4287
michael@0 4288 /* If there was no scheme, the first part of the path (if any) must
michael@0 4289 * have no colon in it. */
michael@0 4290 if (! uri->scheme && !path_matches_noscheme(path))
michael@0 4291 goto err;
michael@0 4292
michael@0 4293 EVUTIL_ASSERT(path);
michael@0 4294 uri->path = mm_strdup(path);
michael@0 4295 if (uri->path == NULL) {
michael@0 4296 event_warn("%s: strdup", __func__);
michael@0 4297 goto err;
michael@0 4298 }
michael@0 4299
michael@0 4300 if (query) {
michael@0 4301 uri->query = mm_strdup(query);
michael@0 4302 if (uri->query == NULL) {
michael@0 4303 event_warn("%s: strdup", __func__);
michael@0 4304 goto err;
michael@0 4305 }
michael@0 4306 }
michael@0 4307 if (fragment) {
michael@0 4308 uri->fragment = mm_strdup(fragment);
michael@0 4309 if (uri->fragment == NULL) {
michael@0 4310 event_warn("%s: strdup", __func__);
michael@0 4311 goto err;
michael@0 4312 }
michael@0 4313 }
michael@0 4314
michael@0 4315 mm_free(readbuf);
michael@0 4316
michael@0 4317 return uri;
michael@0 4318 err:
michael@0 4319 if (uri)
michael@0 4320 evhttp_uri_free(uri);
michael@0 4321 if (readbuf)
michael@0 4322 mm_free(readbuf);
michael@0 4323 return NULL;
michael@0 4324 }
michael@0 4325
michael@0 4326 void
michael@0 4327 evhttp_uri_free(struct evhttp_uri *uri)
michael@0 4328 {
michael@0 4329 #define _URI_FREE_STR(f) \
michael@0 4330 if (uri->f) { \
michael@0 4331 mm_free(uri->f); \
michael@0 4332 }
michael@0 4333
michael@0 4334 _URI_FREE_STR(scheme);
michael@0 4335 _URI_FREE_STR(userinfo);
michael@0 4336 _URI_FREE_STR(host);
michael@0 4337 _URI_FREE_STR(path);
michael@0 4338 _URI_FREE_STR(query);
michael@0 4339 _URI_FREE_STR(fragment);
michael@0 4340
michael@0 4341 mm_free(uri);
michael@0 4342 #undef _URI_FREE_STR
michael@0 4343 }
michael@0 4344
michael@0 4345 char *
michael@0 4346 evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit)
michael@0 4347 {
michael@0 4348 struct evbuffer *tmp = 0;
michael@0 4349 size_t joined_size = 0;
michael@0 4350 char *output = NULL;
michael@0 4351
michael@0 4352 #define _URI_ADD(f) evbuffer_add(tmp, uri->f, strlen(uri->f))
michael@0 4353
michael@0 4354 if (!uri || !buf || !limit)
michael@0 4355 return NULL;
michael@0 4356
michael@0 4357 tmp = evbuffer_new();
michael@0 4358 if (!tmp)
michael@0 4359 return NULL;
michael@0 4360
michael@0 4361 if (uri->scheme) {
michael@0 4362 _URI_ADD(scheme);
michael@0 4363 evbuffer_add(tmp, ":", 1);
michael@0 4364 }
michael@0 4365 if (uri->host) {
michael@0 4366 evbuffer_add(tmp, "//", 2);
michael@0 4367 if (uri->userinfo)
michael@0 4368 evbuffer_add_printf(tmp,"%s@", uri->userinfo);
michael@0 4369 _URI_ADD(host);
michael@0 4370 if (uri->port >= 0)
michael@0 4371 evbuffer_add_printf(tmp,":%d", uri->port);
michael@0 4372
michael@0 4373 if (uri->path && uri->path[0] != '/' && uri->path[0] != '\0')
michael@0 4374 goto err;
michael@0 4375 }
michael@0 4376
michael@0 4377 if (uri->path)
michael@0 4378 _URI_ADD(path);
michael@0 4379
michael@0 4380 if (uri->query) {
michael@0 4381 evbuffer_add(tmp, "?", 1);
michael@0 4382 _URI_ADD(query);
michael@0 4383 }
michael@0 4384
michael@0 4385 if (uri->fragment) {
michael@0 4386 evbuffer_add(tmp, "#", 1);
michael@0 4387 _URI_ADD(fragment);
michael@0 4388 }
michael@0 4389
michael@0 4390 evbuffer_add(tmp, "\0", 1); /* NUL */
michael@0 4391
michael@0 4392 joined_size = evbuffer_get_length(tmp);
michael@0 4393
michael@0 4394 if (joined_size > limit) {
michael@0 4395 /* It doesn't fit. */
michael@0 4396 evbuffer_free(tmp);
michael@0 4397 return NULL;
michael@0 4398 }
michael@0 4399 evbuffer_remove(tmp, buf, joined_size);
michael@0 4400
michael@0 4401 output = buf;
michael@0 4402 err:
michael@0 4403 evbuffer_free(tmp);
michael@0 4404
michael@0 4405 return output;
michael@0 4406 #undef _URI_ADD
michael@0 4407 }
michael@0 4408
michael@0 4409 const char *
michael@0 4410 evhttp_uri_get_scheme(const struct evhttp_uri *uri)
michael@0 4411 {
michael@0 4412 return uri->scheme;
michael@0 4413 }
michael@0 4414 const char *
michael@0 4415 evhttp_uri_get_userinfo(const struct evhttp_uri *uri)
michael@0 4416 {
michael@0 4417 return uri->userinfo;
michael@0 4418 }
michael@0 4419 const char *
michael@0 4420 evhttp_uri_get_host(const struct evhttp_uri *uri)
michael@0 4421 {
michael@0 4422 return uri->host;
michael@0 4423 }
michael@0 4424 int
michael@0 4425 evhttp_uri_get_port(const struct evhttp_uri *uri)
michael@0 4426 {
michael@0 4427 return uri->port;
michael@0 4428 }
michael@0 4429 const char *
michael@0 4430 evhttp_uri_get_path(const struct evhttp_uri *uri)
michael@0 4431 {
michael@0 4432 return uri->path;
michael@0 4433 }
michael@0 4434 const char *
michael@0 4435 evhttp_uri_get_query(const struct evhttp_uri *uri)
michael@0 4436 {
michael@0 4437 return uri->query;
michael@0 4438 }
michael@0 4439 const char *
michael@0 4440 evhttp_uri_get_fragment(const struct evhttp_uri *uri)
michael@0 4441 {
michael@0 4442 return uri->fragment;
michael@0 4443 }
michael@0 4444
michael@0 4445 #define _URI_SET_STR(f) do { \
michael@0 4446 if (uri->f) \
michael@0 4447 mm_free(uri->f); \
michael@0 4448 if (f) { \
michael@0 4449 if ((uri->f = mm_strdup(f)) == NULL) { \
michael@0 4450 event_warn("%s: strdup()", __func__); \
michael@0 4451 return -1; \
michael@0 4452 } \
michael@0 4453 } else { \
michael@0 4454 uri->f = NULL; \
michael@0 4455 } \
michael@0 4456 } while(0)
michael@0 4457
michael@0 4458 int
michael@0 4459 evhttp_uri_set_scheme(struct evhttp_uri *uri, const char *scheme)
michael@0 4460 {
michael@0 4461 if (scheme && !scheme_ok(scheme, scheme+strlen(scheme)))
michael@0 4462 return -1;
michael@0 4463
michael@0 4464 _URI_SET_STR(scheme);
michael@0 4465 return 0;
michael@0 4466 }
michael@0 4467 int
michael@0 4468 evhttp_uri_set_userinfo(struct evhttp_uri *uri, const char *userinfo)
michael@0 4469 {
michael@0 4470 if (userinfo && !userinfo_ok(userinfo, userinfo+strlen(userinfo)))
michael@0 4471 return -1;
michael@0 4472 _URI_SET_STR(userinfo);
michael@0 4473 return 0;
michael@0 4474 }
michael@0 4475 int
michael@0 4476 evhttp_uri_set_host(struct evhttp_uri *uri, const char *host)
michael@0 4477 {
michael@0 4478 if (host) {
michael@0 4479 if (host[0] == '[') {
michael@0 4480 if (! bracket_addr_ok(host, host+strlen(host)))
michael@0 4481 return -1;
michael@0 4482 } else {
michael@0 4483 if (! regname_ok(host, host+strlen(host)))
michael@0 4484 return -1;
michael@0 4485 }
michael@0 4486 }
michael@0 4487
michael@0 4488 _URI_SET_STR(host);
michael@0 4489 return 0;
michael@0 4490 }
michael@0 4491 int
michael@0 4492 evhttp_uri_set_port(struct evhttp_uri *uri, int port)
michael@0 4493 {
michael@0 4494 if (port < -1)
michael@0 4495 return -1;
michael@0 4496 uri->port = port;
michael@0 4497 return 0;
michael@0 4498 }
michael@0 4499 #define end_of_cpath(cp,p,f) \
michael@0 4500 ((const char*)(end_of_path(((char*)(cp)), (p), (f))))
michael@0 4501
michael@0 4502 int
michael@0 4503 evhttp_uri_set_path(struct evhttp_uri *uri, const char *path)
michael@0 4504 {
michael@0 4505 if (path && end_of_cpath(path, PART_PATH, uri->flags) != path+strlen(path))
michael@0 4506 return -1;
michael@0 4507
michael@0 4508 _URI_SET_STR(path);
michael@0 4509 return 0;
michael@0 4510 }
michael@0 4511 int
michael@0 4512 evhttp_uri_set_query(struct evhttp_uri *uri, const char *query)
michael@0 4513 {
michael@0 4514 if (query && end_of_cpath(query, PART_QUERY, uri->flags) != query+strlen(query))
michael@0 4515 return -1;
michael@0 4516 _URI_SET_STR(query);
michael@0 4517 return 0;
michael@0 4518 }
michael@0 4519 int
michael@0 4520 evhttp_uri_set_fragment(struct evhttp_uri *uri, const char *fragment)
michael@0 4521 {
michael@0 4522 if (fragment && end_of_cpath(fragment, PART_FRAGMENT, uri->flags) != fragment+strlen(fragment))
michael@0 4523 return -1;
michael@0 4524 _URI_SET_STR(fragment);
michael@0 4525 return 0;
michael@0 4526 }

mercurial