Wed, 31 Dec 2014 06:09:35 +0100
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 = "<"; |
michael@0 | 228 | return 4; |
michael@0 | 229 | case '>': |
michael@0 | 230 | *escaped = ">"; |
michael@0 | 231 | return 4; |
michael@0 | 232 | case '"': |
michael@0 | 233 | *escaped = """; |
michael@0 | 234 | return 6; |
michael@0 | 235 | case '\'': |
michael@0 | 236 | *escaped = "'"; |
michael@0 | 237 | return 6; |
michael@0 | 238 | case '&': |
michael@0 | 239 | *escaped = "&"; |
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 <, >, ", |
michael@0 | 250 | * ' and & 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 | } |