#ifndef EVENT__HAVE_STRSEP /* strsep replacement for platforms that lack it. Only works if
* del is one character long. */ staticchar *
strsep(char **s, constchar *del)
{ char *d, *tok;
EVUTIL_ASSERT(strlen(del) == 1); if (!s || !*s) return NULL;
tok = *s;
d = strstr(tok, del); if (d) {
*d = '\0';
*s = d + 1;
} else
*s = NULL; return tok;
} #endif
/** Helper: called after we've added some data to an evcon's bufferevent's *outputbuffer.Setstheevconn'swriting-is-donecallback,andputs *thebuffereventintowritingmode.
*/ staticvoid
evhttp_write_buffer(struct evhttp_connection *evcon, void (*cb)(struct evhttp_connection *, void *), void *arg)
{
event_debug(("%s: preparing to write buffer\n", __func__));
/* Set call back */
evcon->cb = cb;
evcon->cb_arg = arg;
/* Disable the read callback: we don't actually care about data; *weonlycareaboutclosedetection.(Wedon'tdisablereading--
* EV_READ, since we *do* want to learn about any close events.) */
bufferevent_setcb(evcon->bufev,
NULL, /*read*/
evhttp_write_cb,
evhttp_error_cb,
evcon);
/** Helper: returns true iff evconn is in any connected state. */ staticint
evhttp_connected(struct evhttp_connection *evcon)
{ switch (evcon->state) { case EVCON_DISCONNECTED: case EVCON_CONNECTING: return (0); case EVCON_IDLE: case EVCON_READING_FIRSTLINE: case EVCON_READING_HEADERS: case EVCON_READING_BODY: case EVCON_READING_TRAILER: case EVCON_WRITING: default: return (1);
}
}
/* Create the headers needed for an outgoing HTTP request, adds them to *therequest'sheaderlist,andwritestherequestlinetothe *connection'soutputbuffer.
*/ staticvoid
evhttp_make_header_request(struct evhttp_connection *evcon, struct evhttp_request *req)
{ constchar *method;
/* Add the content length on a post or put request if missing */ if ((req->type == EVHTTP_REQ_POST || req->type == EVHTTP_REQ_PUT) &&
evhttp_find_header(req->output_headers, "Content-Length") == NULL){ char size[22];
evutil_snprintf(size, sizeof(size), EV_SIZE_FMT,
EV_SIZE_ARG(evbuffer_get_length(req->output_buffer)));
evhttp_add_header(req->output_headers, "Content-Length", size);
}
}
/** Return true if the list of headers in 'headers', intepreted with respect *toflags,meansthatweshouldsenda"connection:close"whentherequest
* is done. */ staticint
evhttp_is_connection_close(int flags, struct evkeyvalq* headers)
{ if (flags & EVHTTP_PROXY_REQUEST) { /* proxy connection */ constchar *connection = evhttp_find_header(headers, "Proxy-Connection"); return (connection == NULL || evutil_ascii_strcasecmp(connection, "keep-alive") != 0);
} else { constchar *connection = evhttp_find_header(headers, "Connection"); return (connection != NULL && evutil_ascii_strcasecmp(connection, "close") == 0);
}
} staticint
evhttp_is_request_connection_close(struct evhttp_request *req)
{ if (req->type == EVHTTP_REQ_CONNECT) return0;
/* Potentially add headers for unidentified content. */ if (evhttp_response_needs_body(req)) { if (evhttp_find_header(req->output_headers, "Content-Type") == NULL
&& evcon->http_server->default_content_type) {
evhttp_add_header(req->output_headers, "Content-Type",
evcon->http_server->default_content_type);
}
}
/* if the request asked for a close, we send a close, too */ if (evhttp_is_connection_close(req->flags, req->input_headers)) {
evhttp_remove_header(req->output_headers, "Connection"); if (!(req->flags & EVHTTP_PROXY_REQUEST))
evhttp_add_header(req->output_headers, "Connection", "close");
evhttp_remove_header(req->output_headers, "Proxy-Connection");
}
}
enum expect { NO, CONTINUE, OTHER }; staticenum expect evhttp_have_expect(struct evhttp_request *req, int input)
{ constchar *expect; struct evkeyvalq *h = input ? req->input_headers : req->output_headers;
if (!(req->kind == EVHTTP_REQUEST) || !REQ_VERSION_ATLEAST(req, 1, 1)) return NO;
expect = evhttp_find_header(h, "Expect"); if (!expect) return NO;
/** Generate all headers appropriate for sending the http request in req (or *theresponse,ifwe'resendingaresponse),andwritethemtoevcon's
* bufferevent. Also writes all data from req->output_buffer */ staticvoid
evhttp_make_header(struct evhttp_connection *evcon, struct evhttp_request *req)
{ struct evkeyval *header; struct evbuffer *output = bufferevent_get_output(evcon->bufev);
switch (error) { case EVREQ_HTTP_TIMEOUT: case EVREQ_HTTP_EOF: /* *thesearecasesinwhichweprobablyshouldjust *closetheconnectionandnotsendareply.this *casemayhappenwhenabrowserkeepsapersistent *connectionopenandwetimeoutontheread.when *therequestisstillbeingusedforsending,we *needtodisassociateditfromtheconnectionhere.
*/ if (!req->userdone) { /* remove it so that it will not be freed */
TAILQ_REMOVE(&req->evcon->requests, req, next); /* indicate that this request no longer has a *connectionobject
*/
req->evcon = NULL;
} return (-1); case EVREQ_HTTP_INVALID_HEADER: case EVREQ_HTTP_BUFFER_ERROR: case EVREQ_HTTP_REQUEST_CANCEL: case EVREQ_HTTP_DATA_TOO_LONG: default: /* xxx: probably should just error on default */ /* the callback looks at the uri to determine errors */ if (req->uri) {
mm_free(req->uri);
req->uri = NULL;
} if (req->uri_elems) {
evhttp_uri_free(req->uri_elems);
req->uri_elems = NULL;
}
/* Free connection ownership of which can be acquired by user using
* evhttp_request_own(). */ staticinlinevoid
evhttp_request_free_auto(struct evhttp_request *req)
{ if (!(req->flags & EVHTTP_USER_OWNED))
evhttp_request_free(req);
}
error_cb = req->error_cb;
error_cb_arg = req->cb_arg; /* when the request was canceled, the callback is not executed */ if (error != EVREQ_HTTP_REQUEST_CANCEL) { /* save the callback for later; the cb might free our object */
cb = req->cb;
cb_arg = req->cb_arg;
} else {
cb = NULL;
cb_arg = NULL;
}
/* do not fail all requests; the next request is going to get *sendoveranewconnection.whenausercancelsarequest, *allotherpendingrequestsshouldbeprocessedasnormal
*/
evhttp_request_free_(evcon, req);
/* reset the connection */
evhttp_connection_reset_(evcon);
/* We are trying the next request that was queued on us */ if (TAILQ_FIRST(&evcon->requests) != NULL)
evhttp_connection_connect_(evcon); else if ((evcon->flags & EVHTTP_CON_OUTGOING) &&
(evcon->flags & EVHTTP_CON_AUTOFREE)) {
evhttp_connection_free(evcon);
}
/* The call to evhttp_connection_reset_ overwrote errno. *Let'srestoretheoriginalerrno,sothattheuser's *callbackcanhaveabetterideaofwhattheerrorwas.
*/
EVUTIL_SET_SOCKET_ERROR(errsave);
/* inform the user */ if (error_cb != NULL)
error_cb(error, error_cb_arg); if (cb != NULL)
(*cb)(NULL, cb_arg);
}
/* Bufferevent callback: invoked when any data has been written from an
* http connection's bufferevent */ staticvoid
evhttp_write_cb(struct bufferevent *bufev, void *arg)
{ struct evhttp_connection *evcon = arg;
/* Activate our call back */ if (evcon->cb != NULL)
(*evcon->cb)(evcon, evcon->cb_arg);
}
if (con_outgoing) { /* idle or close the connection */ int need_close = evhttp_is_request_connection_close(req);
TAILQ_REMOVE(&evcon->requests, req, next);
req->evcon = NULL;
evcon->state = EVCON_IDLE;
/* check if we got asked to close the connection */ if (need_close)
evhttp_connection_reset_(evcon);
/* notify the user of the request */
(*req->cb)(req, req->cb_arg);
/* if this was an outgoing request, we own and it's done. so free it. */ if (con_outgoing) {
evhttp_request_free_auto(req);
}
/* If this was the last request of an outgoing connection and we're *notwaitingtoreceiveaconnectioncloseeventandwewantto *automaticallyfreetheconnection.Wechecktoensureourrequest *listisemptyonelasttimejustincaseourcallbackaddeda *newrequest.
*/ if (free_evcon && TAILQ_FIRST(&evcon->requests) == NULL) {
evhttp_connection_free(evcon);
}
}
if ((buflen = evbuffer_get_length(buf)) == 0) { break;
}
/* evbuffer_get_length returns size_t, but len variable is ssize_t,
* check for overflow conditions */ if (buflen > EV_SSIZE_MAX) { return DATA_CORRUPTED;
}
if (req->ntoread < 0) { /* Read chunk size */
ev_int64_t ntoread; char *p = evbuffer_readln(buf, NULL, EVBUFFER_EOL_CRLF); char *endp; int error; if (p == NULL) break; /* the last chunk is on a new line? */ if (strlen(p) == 0) {
mm_free(p); continue;
}
ntoread = evutil_strtoll(p, &endp, 16);
error = (*p == '\0' ||
(*endp != '\0' && *endp != ' ') ||
ntoread < 0);
mm_free(p); if (error) { /* could not get chunk size */ return (DATA_CORRUPTED);
}
/* ntoread is signed int64, body_size is unsigned size_t, check for under/overflow conditions */ if ((ev_uint64_t)ntoread > EV_SIZE_MAX - req->body_size) { return DATA_CORRUPTED;
}
if (req->body_size + (size_t)ntoread > req->evcon->max_body_size) { /* failed body length test */
event_debug(("Request body is too long")); return (DATA_TOO_LONG);
}
req->body_size += (size_t)ntoread;
req->ntoread = ntoread; if (req->ntoread == 0) { /* Last chunk */ return (ALL_DATA_READ);
} continue;
}
/* req->ntoread is signed int64, len is ssize_t, based on arch,
* ssize_t could only be 32b, check for these conditions */ if (req->ntoread > EV_SSIZE_MAX) { return DATA_CORRUPTED;
}
/* don't have enough to complete a chunk; wait for more */ if (req->ntoread > 0 && buflen < (ev_uint64_t)req->ntoread) return (MORE_DATA_EXPECTED);
if (req->chunked) { switch (evhttp_handle_chunked_read(req, buf)) { case ALL_DATA_READ: /* finished last chunk */
evcon->state = EVCON_READING_TRAILER;
evhttp_read_trailer(evcon, req); return; case DATA_CORRUPTED: case DATA_TOO_LONG: /* corrupted data */
evhttp_connection_fail_(evcon,
EVREQ_HTTP_DATA_TOO_LONG); return; case REQUEST_CANCELED: /* request canceled */
evhttp_request_free_auto(req); return; case MORE_DATA_EXPECTED: default: break;
}
} elseif (req->ntoread < 0) { /* Read until connection close. */ if ((size_t)(req->body_size + evbuffer_get_length(buf)) < req->body_size) {
evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER); return;
}
req->body_size += evbuffer_get_length(buf);
evbuffer_add_buffer(req->input_buffer, buf);
} elseif (req->chunk_cb != NULL || evbuffer_get_length(buf) >= (size_t)req->ntoread) { /* XXX: the above get_length comparison has to be fixed for overflow conditions! */ /* We've postponed moving the data until now, but we're
* about to use it. */
size_t n = evbuffer_get_length(buf);
if (n > (size_t) req->ntoread)
n = (size_t) req->ntoread;
req->ntoread -= n;
req->body_size += n;
evbuffer_remove_buffer(buf, req->input_buffer, n);
}
if (req->body_size > req->evcon->max_body_size ||
(!req->chunked && req->ntoread >= 0 &&
(size_t)req->ntoread > req->evcon->max_body_size)) { /* XXX: The above casted comparison must checked for overflow */ /* failed body length test */
/* Cancel if it's pending. */
event_deferred_cb_cancel_(get_deferred_queue(evcon),
&evcon->read_more_deferred_cb);
switch (evcon->state) { case EVCON_READING_FIRSTLINE:
evhttp_read_firstline(evcon, req); /* note the request may have been freed in
* evhttp_read_body */ break; case EVCON_READING_HEADERS:
evhttp_read_header(evcon, req); /* note the request may have been freed in
* evhttp_read_body */ break; case EVCON_READING_BODY:
evhttp_read_body(evcon, req); /* note the request may have been freed in
* evhttp_read_body */ break; case EVCON_READING_TRAILER:
evhttp_read_trailer(evcon, req); break; case EVCON_IDLE:
{ #ifdef USE_DEBUG struct evbuffer *input;
size_t total_len;
evhttp_connection_reset_(evcon);
} break; case EVCON_DISCONNECTED: case EVCON_CONNECTING: case EVCON_WRITING: default:
event_errx(1, "%s: illegal connection state %d",
__func__, evcon->state);
}
}
staticvoid
evhttp_write_connectioncb(struct evhttp_connection *evcon, void *arg)
{ /* This is after writing the request to the server */ struct evhttp_request *req = TAILQ_FIRST(&evcon->requests); struct evbuffer *output = bufferevent_get_output(evcon->bufev);
EVUTIL_ASSERT(req != NULL);
EVUTIL_ASSERT(evcon->state == EVCON_WRITING);
/* We need to wait until we've written all of our output data before we can
* continue */ if (evbuffer_get_length(output) > 0) return;
/* We are done writing our header and are now expecting the response */
req->kind = EVHTTP_RESPONSE;
/* notify interested parties that this connection is going down */ if (evcon->fd != -1) { if (evhttp_connected(evcon) && evcon->closecb != NULL)
(*evcon->closecb)(evcon, evcon->closecb_arg);
}
/* remove all requests that might be queued on this *connection.forserverconnections,thisshouldbeempty. *becauseitgetsdequeuedeitherinevhttp_connection_doneor *evhttp_connection_fail_.
*/ while ((req = TAILQ_FIRST(&evcon->requests)) != NULL) {
evhttp_request_free_(evcon, req);
}
/* XXXX This is not actually an optimal fix. Instead we ought to have anAPIfor"stopconnecting",orusebufferevent_setfdtoturnoff connecting.ButforLibevent2.0,thisseemslikeaminimalchange leastlikelytodisrupttherestofthebuffereventandhttpcode.
/* for now, we just signal all requests by executing their callbacks */ while (TAILQ_FIRST(&requests) != NULL) { struct evhttp_request *request = TAILQ_FIRST(&requests);
TAILQ_REMOVE(&requests, request, next);
request->evcon = NULL;
/* we might want to set an error here */
request->cb(request, request->cb_arg);
evhttp_request_free_auto(request);
}
}
/** Second time, we can't read anything */ if (evcon->flags & EVHTTP_CON_READING_ERROR) {
evcon->flags &= ~EVHTTP_CON_READING_ERROR;
evhttp_connection_fail_(evcon, EVREQ_HTTP_EOF); return;
}
if (evcon->fd == -1)
evcon->fd = bufferevent_getfd(bufev);
switch (evcon->state) { case EVCON_CONNECTING: if (what & BEV_EVENT_TIMEOUT) {
event_debug(("%s: connection timeout for \"%s:%d\" on "
EV_SOCK_FMT,
__func__, evcon->address, evcon->port,
EV_SOCK_ARG(evcon->fd)));
evhttp_connection_cb_cleanup(evcon); return;
} break;
case EVCON_READING_BODY: if (!req->chunked && req->ntoread < 0
&& what == (BEV_EVENT_READING|BEV_EVENT_EOF)) { /* EOF on read can be benign */
evhttp_connection_done(evcon); return;
} break;
case EVCON_DISCONNECTED: case EVCON_IDLE: case EVCON_READING_FIRSTLINE: case EVCON_READING_HEADERS: case EVCON_READING_TRAILER: case EVCON_WRITING: default: break;
}
/* when we are in close detect mode, a read error means that *theothersideclosedtheirconnection.
*/ if (evcon->flags & EVHTTP_CON_CLOSEDETECT) {
evcon->flags &= ~EVHTTP_CON_CLOSEDETECT;
EVUTIL_ASSERT(evcon->http_server == NULL); /* For connections from the client, we just *resettheconnectionsothatitbecomes *disconnected.
*/
EVUTIL_ASSERT(evcon->state == EVCON_IDLE);
evhttp_connection_reset_(evcon);
if (evcon->fd == -1)
evcon->fd = bufferevent_getfd(bufev);
if (!(what & BEV_EVENT_CONNECTED)) { /* some operating systems return ECONNREFUSED immediately *whenconnectingtoalocaladdress.thecleanupisgoing *toreschedulethisfunctioncall.
*/ #ifndef _WIN32 if (errno == ECONNREFUSED) goto cleanup; #endif
evhttp_error_cb(bufev, what, arg); return;
}
if (evcon->fd == -1) {
event_debug(("%s: bufferevent_getfd returned -1",
__func__)); goto cleanup;
}
/* Check if the connection completed */ if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error,
&errsz) == -1) {
event_debug(("%s: getsockopt for \"%s:%d\" on "EV_SOCK_FMT,
__func__, evcon->address, evcon->port,
EV_SOCK_ARG(evcon->fd))); goto cleanup;
}
if (error) {
event_debug(("%s: connect failed for \"%s:%d\" on "
EV_SOCK_FMT": %s",
__func__, evcon->address, evcon->port,
EV_SOCK_ARG(evcon->fd),
evutil_socket_error_to_string(error))); goto cleanup;
}
/* We are connected to the server now */
event_debug(("%s: connected to \"%s:%d\" on "EV_SOCK_FMT"\n",
__func__, evcon->address, evcon->port,
EV_SOCK_ARG(evcon->fd)));
/* Reset the retry count as we were successful in connecting */
evcon->retry_cnt = 0;
evcon->state = EVCON_IDLE;
/* reset the bufferevent cbs */
bufferevent_setcb(evcon->bufev,
evhttp_read_cb,
evhttp_write_cb,
evhttp_error_cb,
evcon);
while (eos > line && *(eos-1) == ' ') {
*(eos-1) = '\0';
--eos;
--len;
} if (len < strlen("GET / HTTP/1.0")) return -1;
/* Parse the request line */
method = strsep(&line, " "); if (!line) return -1;
uri = line;
version = strrchr(uri, ' '); if (!version || uri == version) return -1;
*version = '\0';
version++;
method_len = (uri - method) - 1;
type = EVHTTP_REQ_UNKNOWN_;
/* First line */ switch (method_len) { case3: /* The length of the method string is 3, meaning it can only be one of two methods: GET or PUT */
/* Since both GET and PUT share the same character 'T' at the end, *ifthestringdoesn'thave'T',wecanimmediatelydeterminethis
* is an invalid HTTP method */
if (method[2] != 'T') { break;
}
switch (*method) { case'G': /* This first byte is 'G', so make sure the next byte is
* 'E', if it isn't then this isn't a valid method */
if (method[1] == 'E') {
type = EVHTTP_REQ_GET;
}
break; case'P': /* First byte is P, check second byte for 'U', if not,
* we know it's an invalid method */ if (method[1] == 'U') {
type = EVHTTP_REQ_PUT;
} break; default: break;
} break; case4: /* The method length is 4 bytes, leaving only the methods "POST" and "HEAD" */ switch (*method) { case'P': if (method[3] == 'T' && method[2] == 'S' && method[1] == 'O') {
type = EVHTTP_REQ_POST;
} break; case'H': if (method[3] == 'D' && method[2] == 'A' && method[1] == 'E') {
type = EVHTTP_REQ_HEAD;
} break; default: break;
} break; case5: /* Method length is 5 bytes, which can only encompass PATCH and TRACE */ switch (*method) { case'P': if (method[4] == 'H' && method[3] == 'C' && method[2] == 'T' && method[1] == 'A') {
type = EVHTTP_REQ_PATCH;
} break; case'T': if (method[4] == 'E' && method[3] == 'C' && method[2] == 'A' && method[1] == 'R') {
type = EVHTTP_REQ_TRACE;
}
break; default: break;
} break; case6: /* Method length is 6, only valid method 6 bytes in length is DELEte */
/* If the first byte isn't 'D' then it's invalid */ if (*method != 'D') { break;
}
if ((int)type == EVHTTP_REQ_UNKNOWN_) {
event_debug(("%s: bad method %s on request %p from %s",
__func__, method, req, req->remote_host)); /* No error yet; we'll give a better error later when
* we see that req->type is unsupported. */
}
req->type = type;
if (evhttp_parse_http_version(version, req) < 0) return -1;
if (type == EVHTTP_REQ_CONNECT) { if ((req->uri_elems = evhttp_uri_parse_authority(req->uri)) == NULL) { return -1;
}
} else { if ((req->uri_elems = evhttp_uri_parse_with_flags(req->uri,
EVHTTP_URI_NONCONFORMANT)) == NULL) { return -1;
}
}
/* If we have an absolute-URI, check to see if it is an http request foraknownvhostorserveralias.Ifwedon'tknowaboutthis
host, we consider it a proxy request. */
scheme = evhttp_uri_get_scheme(req->uri_elems);
hostname = evhttp_uri_get_host(req->uri_elems); if (scheme && (!evutil_ascii_strcasecmp(scheme, "http") ||
!evutil_ascii_strcasecmp(scheme, "https")) &&
hostname &&
!evhttp_find_vhost(req->evcon->http_server, NULL, hostname))
req->flags |= EVHTTP_PROXY_REQUEST;
while ((p = strpbrk(p, "\r\n")) != NULL) { /* we really expect only one new line */
p += strspn(p, "\r\n"); /* we expect a space or tab for continuation */ if (*p != ' ' && *p != '\t') return (0);
} return (1);
}
size_t len; /* XXX try */
line = evbuffer_readln(buffer, &len, EVBUFFER_EOL_CRLF); if (line == NULL) { if (req->evcon != NULL &&
evbuffer_get_length(buffer) > req->evcon->max_headers_size) return (DATA_TOO_LONG); else return (MORE_DATA_EXPECTED);
}
if (req->evcon != NULL && len > req->evcon->max_headers_size) {
mm_free(line); return (DATA_TOO_LONG);
}
req->headers_size = len;
switch (req->kind) { case EVHTTP_REQUEST: if (evhttp_parse_request_line(req, line, len) == -1)
status = DATA_CORRUPTED; break; case EVHTTP_RESPONSE: if (evhttp_parse_response_line(req, line) == -1)
status = DATA_CORRUPTED; break; default:
status = DATA_CORRUPTED;
}
if (*line == '\0') { /* Last header - Done */
status = ALL_DATA_READ;
mm_free(line); break;
}
/* Check if this is a continuation line */ if (*line == ' ' || *line == '\t') { if (evhttp_append_to_last_header(headers, line) == -1) goto error;
mm_free(line); continue;
}
/* Processing of header lines */
svalue = line;
skey = strsep(&svalue, ":"); if (svalue == NULL) goto error;
/* If this is a request without a body, then we are done */ if (req->kind == EVHTTP_REQUEST &&
!evhttp_method_may_have_body(req->type)) {
evhttp_connection_done(evcon); return;
}
evcon->state = EVCON_READING_BODY;
xfer_enc = evhttp_find_header(req->input_headers, "Transfer-Encoding"); if (xfer_enc != NULL && evutil_ascii_strcasecmp(xfer_enc, "chunked") == 0) {
req->chunked = 1;
req->ntoread = -1;
} else { if (evhttp_get_body_length(req) == -1) {
evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER); return;
} if (req->kind == EVHTTP_REQUEST && req->ntoread < 1) { /* An incoming request with no content-length and no
* transfer-encoding has no body. */
evhttp_connection_done(evcon); return;
}
}
/* Should we send a 100 Continue status line? */ switch (evhttp_have_expect(req, 1)) { caseCONTINUE: /* XXX It would be nice to do some sanity checkinghere.Doestheresourceexist? Shouldtheresourceacceptpostrequests?If no,weshouldrespondwithanerror.For now,justoptimisticallytelltheclientto
send their message body. */ if (req->ntoread > 0) { /* ntoread is ev_int64_t, max_body_size is ev_uint64_t */ if ((req->evcon->max_body_size <= EV_INT64_MAX) &&
(ev_uint64_t)req->ntoread > req->evcon->max_body_size) {
evhttp_lingering_fail(evcon, req); return;
}
} if (!evbuffer_get_length(bufferevent_get_input(evcon->bufev)))
evhttp_send_continue(evcon, req); break; case OTHER:
evhttp_send_error(req, HTTP_EXPECTATIONFAILED, NULL); return; case NO: break;
}
evhttp_read_body(evcon, req); /* note the request may have been freed in evhttp_read_body */
}
res = evhttp_parse_headers_(req, bufferevent_get_input(evcon->bufev)); if (res == DATA_CORRUPTED || res == DATA_TOO_LONG) { /* Error while reading, terminate */
event_debug(("%s: bad header lines on "EV_SOCK_FMT"\n",
__func__, EV_SOCK_ARG(fd)));
evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER); return;
} elseif (res == MORE_DATA_EXPECTED) { /* Need more header lines */ return;
}
/* Callback can shut down connection with negative return value */ if (req->header_cb != NULL) { if ((*req->header_cb)(req, req->cb_arg) < 0) {
evhttp_connection_fail_(evcon, EVREQ_HTTP_EOF); return;
}
}
/* Done reading headers, do the real work */ switch (req->kind) { case EVHTTP_REQUEST:
event_debug(("%s: checking for post data on "EV_SOCK_FMT"\n",
__func__, EV_SOCK_ARG(fd)));
evhttp_get_body(evcon, req); /* note the request may have been freed in evhttp_get_body */ break;
case EVHTTP_RESPONSE: /* Start over if we got a 100 Continue response. */ if (req->response_code == 100) { struct evbuffer *output = bufferevent_get_output(evcon->bufev);
evbuffer_add_buffer(output, req->output_buffer);
evhttp_start_write_(evcon); return;
} if (!evhttp_response_needs_body(req)) {
event_debug(("%s: skipping body for code %d\n",
__func__, req->response_code));
evhttp_connection_done(evcon);
} else {
event_debug(("%s: start of read body for %s on "
EV_SOCK_FMT"\n",
__func__, req->remote_host, EV_SOCK_ARG(fd)));
evhttp_get_body(evcon, req); /* note the request may have been freed in
* evhttp_get_body */
} break;
default:
event_warnx("%s: bad header on "EV_SOCK_FMT, __func__,
EV_SOCK_ARG(fd));
evhttp_connection_fail_(evcon, EVREQ_HTTP_INVALID_HEADER); break;
} /* request may have been freed above */
}
/* We do not want to conflict with retry_ev */ if (evcon->retry_cnt) return (0);
/* If the connection object is not connected; make it so */ if (!evhttp_connected(evcon)) { int res = evhttp_connection_connect_(evcon); /* evhttp_connection_fail_(), which is called through *evhttp_connection_connect_(),assumesthatreqliesin *evcon->requests.Thus,enqueuetherequestinadvanceand
* remove it in the error case. */ if (res != 0)
TAILQ_REMOVE(&evcon->requests, req, next);
return (res);
}
/* *Ifit'sconnectedalreadyandwearethefirstinthequeue, *thenwecandispatchthisrequestimmediately.Otherwise,it *willbedispatchedoncethependingrequestsarecompleted.
*/ if (TAILQ_FIRST(&evcon->requests) == req)
evhttp_request_dispatch(evcon);
return (0);
}
void
evhttp_cancel_request(struct evhttp_request *req)
{ struct evhttp_connection *evcon = req->evcon; if (evcon != NULL) { /* We need to remove it from the connection */ if (TAILQ_FIRST(&evcon->requests) == req) { /* it's currently being worked on, so reset *theconnection.
*/
evhttp_connection_fail_(evcon,
EVREQ_HTTP_REQUEST_CANCEL);
/* connection fail freed the request */ return;
} else { /* otherwise, we can just remove it from the *queue
*/
TAILQ_REMOVE(&evcon->requests, req, next);
}
}
/* If there's still data pending, process it next time through the
* loop. Don't do it now; that could get recusive. */ if (evbuffer_get_length(bufferevent_get_input(evcon->bufev))) {
event_deferred_cb_schedule_(get_deferred_queue(evcon),
&evcon->read_more_deferred_cb);
}
}
if (need_close) {
evhttp_connection_free(evcon); return;
}
/* we have a persistent connection; try to accept another request. */ if (evhttp_associate_new_request_with_connection(evcon) == -1) {
evhttp_connection_free(evcon);
}
}
/* *Returnsanerrorpage.
*/
void
evhttp_send_error(struct evhttp_request *req, int error, constchar *reason)
{
struct evbuffer *buf = evbuffer_new(); if (buf == NULL) { /* if we cannot allocate memory; we just drop the connection */
evhttp_connection_free(req->evcon); return;
} if (reason == NULL) {
reason = evhttp_response_phrase_internal(error);
}
if (evcon == NULL) {
evhttp_request_free(req); return;
}
output = bufferevent_get_output(evcon->bufev);
/* we expect no more calls form the user on this request */
req->userdone = 1;
if (req->chunked) {
evbuffer_add(output, "0\r\n\r\n", 5);
evhttp_write_buffer(req->evcon, evhttp_send_done, NULL);
req->chunked = 0;
} elseif (evbuffer_get_length(output) == 0) { /* let the connection know that we are done with the request */
evhttp_send_done(evcon, NULL);
} else { /* make the callback execute after all data has been written */
evcon->cb = evhttp_send_done;
evcon->cb_arg = NULL;
}
}
TAILQ_FOREACH(alias, &http->aliases, next) { /* XXX Do we need to handle IP addresses? */ if (!evutil_ascii_strcasecmp(alias->alias, hostname)) { if (outhttp)
*outhttp = http; return1;
}
}
/* XXX It might be good to avoid recursion here, but I don't
see a way to do that w/o a list. */
TAILQ_FOREACH(vhost, &http->virtualhosts, next_vhost) { if (evhttp_find_alias(vhost, outhttp, hostname)) return1;
}
/* Generic call back */ if (http->gencb) {
(*http->gencb)(req, http->gencbarg); return;
} else { /* We need to send a 404 here */ #define ERR_FORMAT "<html><head>" \ "<title>404 Not Found</title>" \ "</head><body>" \ "<h1>Not Found</h1>" \ "<p>The requested URL %s was not found on this server.</p>"\ "</body></html>\n"
char *escaped_html; struct evbuffer *buf;
if ((escaped_html = evhttp_htmlescape(req->uri)) == NULL) {
evhttp_connection_free(req->evcon); return;
}
listener = evconnlistener_new(http->base, NULL, NULL,
flags, 0, /* Backlog is '0' because we already said 'listen' */
fd); if (!listener) return (NULL);
int
evhttp_add_virtual_host(struct evhttp* http, constchar *pattern, struct evhttp* vhost)
{ /* a vhost can only be a vhost once and should not have bound sockets */ if (vhost->vhost_pattern != NULL ||
TAILQ_FIRST(&vhost->sockets) != NULL) return (-1);
vhost->vhost_pattern = mm_strdup(pattern); if (vhost->vhost_pattern == NULL) return (-1);
if (req->remote_host != NULL)
mm_free(req->remote_host); if (req->uri != NULL)
mm_free(req->uri); if (req->uri_elems != NULL)
evhttp_uri_free(req->uri_elems); if (req->response_code_line != NULL)
mm_free(req->response_code_line); if (req->host_cache != NULL)
mm_free(req->host_cache);
if (req->uri_elems)
host = evhttp_uri_get_host(req->uri_elems); if (!host && req->input_headers) { constchar *p;
size_t len;
host = evhttp_find_header(req->input_headers, "Host"); /* The Host: header may include a port. Remove it here
to be consistent with uri_elems case above. */ if (host) {
p = host + strlen(host) - 1; while (p > host && EVUTIL_ISDIGIT_(*p))
--p; if (p > host && *p == ':') {
len = p - host;
req->host_cache = mm_malloc(len + 1); if (!req->host_cache) {
event_warn("%s: malloc", __func__); return NULL;
}
memcpy(req->host_cache, host, len);
req->host_cache[len] = '\0';
host = req->host_cache;
}
}
}
name_from_addr(sa, salen, &hostname, &portname); if (hostname == NULL || portname == NULL) { if (hostname) mm_free(hostname); if (portname) mm_free(portname); return (NULL);
}
event_debug(("%s: new request from %s:%s on "EV_SOCK_FMT"\n",
__func__, hostname, portname, EV_SOCK_ARG(fd)));
/* we need a connection object to put the http request on */ if (http->bevcb != NULL) {
bev = (*http->bevcb)(http->base, http->bevcbarg);
}
evcon = evhttp_connection_base_bufferevent_new(
http->base, NULL, bev, hostname, atoi(portname));
mm_free(hostname);
mm_free(portname); if (evcon == NULL) return (NULL);
if (bufferevent_setfd(evcon->bufev, fd)) goto err; if (bufferevent_enable(evcon->bufev, EV_READ)) goto err; if (bufferevent_disable(evcon->bufev, EV_WRITE)) goto err;
bufferevent_socket_set_conn_address_(evcon->bufev, sa, salen);
req->evcon = evcon; /* the request ends up owning the connection */
req->flags |= EVHTTP_REQ_OWN_CONNECTION;
/* We did not present the request to the user user yet, so treat it as *iftheuserwasdonewiththerequest.Thisallowsustofreethe *requestonapersistentconnectioniftheclientdropsitwithout *sendingarequest.
*/
req->userdone = 1;
evcon = evhttp_get_request_connection(http, fd, sa, salen); if (evcon == NULL) {
event_sock_warn(fd, "%s: cannot get connection on "EV_SOCK_FMT,
__func__, EV_SOCK_ARG(fd));
evutil_closesocket(fd); return;
}
/* the timeout can be used by the server to close idle connections */ if (evutil_timerisset(&http->timeout))
evhttp_connection_set_timeout_tv(evcon, &http->timeout);
/* Create a non-blocking socket and bind it */ static evutil_socket_t
create_bind_socket_nonblock(struct evutil_addrinfo *ai, int reuse)
{
evutil_socket_t fd;
/* Query */ if (*readp == '?') {
*readp = '\0';
++readp;
query = readp;
readp = end_of_path(readp, PART_QUERY, flags);
} /* fragment */ if (*readp == '#') {
*readp = '\0';
++readp;
fragment = readp;
readp = end_of_path(readp, PART_FRAGMENT, flags);
} if (*readp != '\0') { goto err;
}
/* These next two cases may be unreachable; I'm leaving them
* in to be defensive. */ /* If you didn't get an authority, the path can't begin with "//" */ if (!got_authority && path[0]=='/' && path[1]=='/') goto err; /* If you did get an authority, the path must begin with "/" or be
* empty. */ if (got_authority && path[0] != '/' && path[0] != '\0') goto err; /* (End of maybe-unreachable cases) */
/* If there was no scheme, the first part of the path (if any) must
* have no colon in it. */ if (! uri->scheme && !path_matches_noscheme(path)) goto err;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.