From ce95017bf16f7937013134fc5188900d3e2cd9c7 Mon Sep 17 00:00:00 2001 From: Reyk Floeter Date: Wed, 16 Aug 2017 09:27:36 +0200 Subject: sync --- httpd/server_http.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 141 insertions(+), 16 deletions(-) (limited to 'httpd/server_http.c') diff --git a/httpd/server_http.c b/httpd/server_http.c index b69805a..e64de0d 100644 --- a/httpd/server_http.c +++ b/httpd/server_http.c @@ -1,7 +1,7 @@ -/* $OpenBSD: server_http.c,v 1.109 2016/07/27 11:02:41 reyk Exp $ */ +/* $OpenBSD: server_http.c,v 1.117 2017/05/15 10:40:47 jsg Exp $ */ /* - * Copyright (c) 2006 - 2015 Reyk Floeter + * Copyright (c) 2006 - 2017 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -49,17 +49,12 @@ int server_http_authenticate(struct server_config *, char *server_expand_http(struct client *, const char *, char *, size_t); -static struct httpd *env = NULL; - static struct http_method http_methods[] = HTTP_METHODS; static struct http_error http_errors[] = HTTP_ERRORS; void -server_http(struct httpd *x_env) +server_http(void) { - if (x_env != NULL) - env = x_env; - DPRINTF("%s: sorting lookup tables, pid %d", __func__, getpid()); /* Sort the HTTP lookup arrays */ @@ -221,16 +216,34 @@ server_read_http(struct bufferevent *bev, void *arg) goto done; } - while (!clt->clt_done && (line = - evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT)) != NULL) { - linelen = strlen(line); + while (!clt->clt_headersdone) { + if (!clt->clt_line) { + /* Peek into the buffer to see if it looks like HTTP */ + key = EVBUFFER_DATA(src); + if (!isalpha(*key)) { + server_abort_http(clt, 400, + "invalid request line"); + goto abort; + } + } + + if ((line = evbuffer_readln(src, + &linelen, EVBUFFER_EOL_CRLF_STRICT)) == NULL) { + /* No newline found after too many bytes */ + if (size > SERVER_MAXHEADERLENGTH) { + server_abort_http(clt, 413, + "request line too long"); + goto abort; + } + break; + } /* * An empty line indicates the end of the request. * libevent already stripped the \r\n for us. */ if (!linelen) { - clt->clt_done = 1; + clt->clt_headersdone = 1; free(line); break; } @@ -365,7 +378,7 @@ server_read_http(struct bufferevent *bev, void *arg) free(line); } - if (clt->clt_done) { + if (clt->clt_headersdone) { if (desc->http_method == HTTP_METHOD_NONE) { server_abort_http(clt, 406, "no method"); return; @@ -377,7 +390,6 @@ server_read_http(struct bufferevent *bev, void *arg) clt->clt_toread = TOREAD_UNLIMITED; bev->readcb = server_read; break; - case HTTP_METHOD_DELETE: case HTTP_METHOD_GET: case HTTP_METHOD_HEAD: /* WebDAV methods */ @@ -385,6 +397,7 @@ server_read_http(struct bufferevent *bev, void *arg) case HTTP_METHOD_MOVE: clt->clt_toread = 0; break; + case HTTP_METHOD_DELETE: case HTTP_METHOD_OPTIONS: case HTTP_METHOD_POST: case HTTP_METHOD_PUT: @@ -435,7 +448,7 @@ server_read_http(struct bufferevent *bev, void *arg) done: if (clt->clt_toread != 0) bufferevent_disable(bev, EV_READ); - server_response(env, clt); + server_response(httpd_env, clt); return; } if (clt->clt_done) { @@ -614,6 +627,101 @@ server_read_httpchunks(struct bufferevent *bev, void *arg) server_close(clt, strerror(errno)); } +void +server_read_httprange(struct bufferevent *bev, void *arg) +{ + struct client *clt = arg; + struct evbuffer *src = EVBUFFER_INPUT(bev); + size_t size; + struct media_type *media; + struct range_data *r = &clt->clt_ranges; + struct range *range; + + getmonotime(&clt->clt_tv_last); + + if (r->range_toread > 0) { + size = EVBUFFER_LENGTH(src); + if (!size) + return; + + /* Read chunk data */ + if ((off_t)size > r->range_toread) { + size = r->range_toread; + if (server_bufferevent_write_chunk(clt, src, size) + == -1) + goto fail; + r->range_toread = 0; + } else { + if (server_bufferevent_write_buffer(clt, src) == -1) + goto fail; + r->range_toread -= size; + } + if (r->range_toread < 1) + r->range_toread = TOREAD_HTTP_RANGE; + DPRINTF("%s: done, size %lu, to read %lld", __func__, + size, r->range_toread); + } + + switch (r->range_toread) { + case TOREAD_HTTP_RANGE: + if (r->range_index >= r->range_count) { + if (r->range_count > 1) { + /* Add end marker */ + if (server_bufferevent_printf(clt, + "\r\n--%llu--\r\n", + clt->clt_boundary) == -1) + goto fail; + } + r->range_toread = TOREAD_HTTP_NONE; + break; + } + + range = &r->range[r->range_index]; + + if (r->range_count > 1) { + media = r->range_media; + if (server_bufferevent_printf(clt, + "\r\n--%llu\r\n" + "Content-Type: %s/%s\r\n" + "Content-Range: bytes %lld-%lld/%zu\r\n\r\n", + clt->clt_boundary, + media->media_type, media->media_subtype, + range->start, range->end, r->range_total) == -1) + goto fail; + } + r->range_toread = range->end - range->start + 1; + + if (lseek(clt->clt_fd, range->start, SEEK_SET) == -1) + goto fail; + + /* Throw away bytes that are already in the input buffer */ + evbuffer_drain(src, EVBUFFER_LENGTH(src)); + + /* Increment for the next part */ + r->range_index++; + break; + case TOREAD_HTTP_NONE: + case 0: + break; + } + + if (clt->clt_done) + goto done; + + if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(clt->clt_bev)) > (size_t) + SERVER_MAX_PREFETCH * clt->clt_sndbufsiz) { + bufferevent_disable(clt->clt_srvbev, EV_READ); + clt->clt_srvbev_throttled = 1; + } + + return; + done: + (*bev->errorcb)(bev, EVBUFFER_READ, bev->cbarg); + return; + fail: + server_close(clt, strerror(errno)); +} + void server_reset_http(struct client *clt) { @@ -624,8 +732,9 @@ server_reset_http(struct client *clt) server_httpdesc_free(clt->clt_descreq); server_httpdesc_free(clt->clt_descresp); clt->clt_headerlen = 0; - clt->clt_line = 0; + clt->clt_headersdone = 0; clt->clt_done = 0; + clt->clt_line = 0; clt->clt_chunk = 0; free(clt->clt_remote_user); clt->clt_remote_user = NULL; @@ -778,6 +887,8 @@ server_abort_http(struct client *clt, unsigned int code, const char *msg) msg = buf; break; case 401: + if (msg == NULL) + break; if (stravis(&escapedmsg, msg, VIS_DQ) == -1) { code = 500; extraheader = NULL; @@ -789,6 +900,8 @@ server_abort_http(struct client *clt, unsigned int code, const char *msg) } break; case 416: + if (msg == NULL) + break; if (asprintf(&extraheader, "Content-Range: %s\r\n", msg) == -1) { code = 500; @@ -959,6 +1072,14 @@ server_expand_http(struct client *clt, const char *val, char *buf, if (ret != 0) return (NULL); } + if (strstr(val, "$HTTP_HOST") != NULL) { + if (desc->http_host == NULL) + return (NULL); + if ((str = url_encode(desc->http_host)) == NULL) + return (NULL); + expand_string(buf, len, "$HTTP_HOST", str); + free(str); + } if (strstr(val, "$REMOTE_") != NULL) { if (strstr(val, "$REMOTE_ADDR") != NULL) { if (print_host(&clt->clt_ss, @@ -1095,6 +1216,10 @@ server_response(struct httpd *httpd, struct client *clt) if (clt->clt_persist >= srv_conf->maxrequests) clt->clt_persist = 0; + /* pipelining should end after the first "idempotent" method */ + if (clt->clt_pipelining && clt->clt_toread > 0) + clt->clt_persist = 0; + /* * Do we have a Host header and matching configuration? * XXX the Host can also appear in the URL path. -- cgit v1.2.3-54-g00ecf