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_file.c | 171 +++++++++++++++++++++++----------------------------- 1 file changed, 75 insertions(+), 96 deletions(-) (limited to 'httpd/server_file.c') diff --git a/httpd/server_file.c b/httpd/server_file.c index 48ecbb5..4255119 100644 --- a/httpd/server_file.c +++ b/httpd/server_file.c @@ -1,7 +1,7 @@ -/* $OpenBSD: server_file.c,v 1.62 2016/05/17 03:12:39 deraadt Exp $ */ +/* $OpenBSD: server_file.c,v 1.65 2017/02/02 22:19:59 reyk 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 @@ -36,12 +36,6 @@ #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) -#define MAX_RANGES 4 - -struct range { - off_t start; - off_t end; -}; int server_file_access(struct httpd *, struct client *, char *, size_t); @@ -55,8 +49,7 @@ int server_file_modified_since(struct http_descriptor *, struct stat *); int server_file_method(struct client *); int parse_range_spec(char *, size_t, struct range *); -struct range *parse_range(char *, size_t, int *); -int buffer_add_range(int, struct evbuffer *, struct range *); +int parse_ranges(struct client *, char *, size_t); int server_file_access(struct httpd *env, struct client *clt, @@ -303,11 +296,10 @@ server_partial_file_request(struct httpd *env, struct client *clt, char *path, struct http_descriptor *resp = clt->clt_descresp; struct http_descriptor *desc = clt->clt_descreq; struct media_type *media, multipart_media; + struct range_data *r = &clt->clt_ranges; struct range *range; - struct evbuffer *evb = NULL; - size_t content_length; + size_t content_length = 0; int code = 500, fd = -1, i, nranges, ret; - uint32_t boundary; char content_range[64]; const char *errstr = NULL; @@ -315,7 +307,7 @@ server_partial_file_request(struct httpd *env, struct client *clt, char *path, if (desc->http_method != HTTP_METHOD_GET) return server_file_request(env, clt, path, st); - if ((range = parse_range(range_str, st->st_size, &nranges)) == NULL) { + if ((nranges = parse_ranges(clt, range_str, st->st_size)) < 1) { code = 416; (void)snprintf(content_range, sizeof(content_range), "bytes */%lld", st->st_size); @@ -328,12 +320,10 @@ server_partial_file_request(struct httpd *env, struct client *clt, char *path, goto abort; media = media_find_config(env, srv_conf, path); - if ((evb = evbuffer_new()) == NULL) { - errstr = "failed to allocate file buffer"; - goto abort; - } + r->range_media = media; if (nranges == 1) { + range = &r->range[0]; (void)snprintf(content_range, sizeof(content_range), "bytes %lld-%lld/%lld", range->start, range->end, st->st_size); @@ -341,56 +331,46 @@ server_partial_file_request(struct httpd *env, struct client *clt, char *path, content_range) == NULL) goto abort; - content_length = range->end - range->start + 1; - if (buffer_add_range(fd, evb, range) == 0) - goto abort; - + range = &r->range[0]; + content_length += range->end - range->start + 1; } else { - content_length = 0; - boundary = arc4random(); - /* Generate a multipart payload of byteranges */ - while (nranges--) { - if ((i = evbuffer_add_printf(evb, "\r\n--%ud\r\n", - boundary)) == -1) - goto abort; + /* Add boundary, all parts will be handled by the callback */ + arc4random_buf(&clt->clt_boundary, sizeof(clt->clt_boundary)); - content_length += i; - if ((i = evbuffer_add_printf(evb, - "Content-Type: %s/%s\r\n", - media->media_type, media->media_subtype)) == -1) - goto abort; + /* Calculate Content-Length of the complete multipart body */ + for (i = 0; i < nranges; i++) { + range = &r->range[i]; - content_length += i; - if ((i = evbuffer_add_printf(evb, + /* calculate Content-Length of the complete body */ + if ((ret = snprintf(NULL, 0, + "\r\n--%llu\r\n" + "Content-Type: %s/%s\r\n" "Content-Range: bytes %lld-%lld/%lld\r\n\r\n", - range->start, range->end, st->st_size)) == -1) + clt->clt_boundary, + media->media_type, media->media_subtype, + range->start, range->end, st->st_size)) < 0) goto abort; - content_length += i; - if (buffer_add_range(fd, evb, range) == 0) - goto abort; + /* Add data length */ + content_length += ret + range->end - range->start + 1; - content_length += range->end - range->start + 1; - range++; } - - if ((i = evbuffer_add_printf(evb, "\r\n--%ud--\r\n", - boundary)) == -1) + if ((ret = snprintf(NULL, 0, "\r\n--%llu--\r\n", + clt->clt_boundary)) < 0) goto abort; - - content_length += i; + content_length += ret; /* prepare multipart/byteranges media type */ (void)strlcpy(multipart_media.media_type, "multipart", sizeof(multipart_media.media_type)); (void)snprintf(multipart_media.media_subtype, sizeof(multipart_media.media_subtype), - "byteranges; boundary=%ud", boundary); + "byteranges; boundary=%llu", clt->clt_boundary); media = &multipart_media; } - close(fd); - fd = -1; + /* Start with first range */ + r->range_toread = TOREAD_HTTP_RANGE; ret = server_response_http(clt, 206, media, content_length, MINIMUM(time(NULL), st->st_mtim.tv_sec)); @@ -399,25 +379,32 @@ server_partial_file_request(struct httpd *env, struct client *clt, char *path, goto fail; case 0: /* Connection is already finished */ - evbuffer_free(evb); - evb = NULL; + close(fd); goto done; default: break; } - if (server_bufferevent_write_buffer(clt, evb) == -1) + clt->clt_fd = fd; + if (clt->clt_srvbev != NULL) + bufferevent_free(clt->clt_srvbev); + + clt->clt_srvbev_throttled = 0; + clt->clt_srvbev = bufferevent_new(clt->clt_fd, server_read_httprange, + server_write, server_file_error, clt); + if (clt->clt_srvbev == NULL) { + errstr = "failed to allocate file buffer event"; goto fail; + } - evbuffer_free(evb); - evb = NULL; + /* Adjust read watermark to the socket output buffer size */ + bufferevent_setwatermark(clt->clt_srvbev, EV_READ, 0, + clt->clt_sndbufsiz); - bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE); - if (clt->clt_persist) - clt->clt_toread = TOREAD_HTTP_HEADER; - else - clt->clt_toread = TOREAD_HTTP_NONE; - clt->clt_done = 0; + bufferevent_settimeout(clt->clt_srvbev, + srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec); + bufferevent_enable(clt->clt_srvbev, EV_READ); + bufferevent_disable(clt->clt_bev, EV_READ); done: server_reset_http(clt); @@ -602,7 +589,7 @@ void server_file_error(struct bufferevent *bev, short error, void *arg) { struct client *clt = arg; - struct evbuffer *dst; + struct evbuffer *src, *dst; if (error & EVBUFFER_TIMEOUT) { server_close(clt, "buffer event timeout"); @@ -621,6 +608,12 @@ server_file_error(struct bufferevent *bev, short error, void *arg) clt->clt_done = 1; + src = EVBUFFER_INPUT(clt->clt_bev); + + /* Close the connection if a previous pipeline is empty */ + if (clt->clt_pipelining && EVBUFFER_LENGTH(src) == 0) + clt->clt_persist = 0; + if (clt->clt_persist) { /* Close input file and wait for next HTTP request */ if (clt->clt_fd != -1) @@ -629,6 +622,12 @@ server_file_error(struct bufferevent *bev, short error, void *arg) clt->clt_toread = TOREAD_HTTP_HEADER; server_reset_http(clt); bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE); + + /* Start pipelining if the buffer is not empty */ + if (EVBUFFER_LENGTH(src)) { + clt->clt_pipelining++; + server_read_http(clt->clt_bev, arg); + } return; } @@ -670,41 +669,44 @@ server_file_modified_since(struct http_descriptor *desc, struct stat *st) return (-1); } -struct range * -parse_range(char *str, size_t file_sz, int *nranges) +int +parse_ranges(struct client *clt, char *str, size_t file_sz) { - static struct range ranges[MAX_RANGES]; int i = 0; char *p, *q; + struct range_data *r = &clt->clt_ranges; + + memset(r, 0, sizeof(*r)); /* Extract range unit */ if ((p = strchr(str, '=')) == NULL) - return (NULL); + return (-1); *p++ = '\0'; /* Check if it's a bytes range spec */ if (strcmp(str, "bytes") != 0) - return (NULL); + return (-1); while ((q = strchr(p, ',')) != NULL) { *q++ = '\0'; /* Extract start and end positions */ - if (parse_range_spec(p, file_sz, &ranges[i]) == 0) + if (parse_range_spec(p, file_sz, &r->range[i]) == 0) continue; i++; - if (i == MAX_RANGES) - return (NULL); + if (i == SERVER_MAX_RANGES) + return (-1); p = q; } - if (parse_range_spec(p, file_sz, &ranges[i]) != 0) + if (parse_range_spec(p, file_sz, &r->range[i]) != 0) i++; - *nranges = i; - return (i ? ranges : NULL); + r->range_total = file_sz; + r->range_count = i; + return (i); } int @@ -754,26 +756,3 @@ parse_range_spec(char *str, size_t size, struct range *r) return (1); } - -int -buffer_add_range(int fd, struct evbuffer *evb, struct range *range) -{ - char buf[BUFSIZ]; - size_t n, range_sz; - ssize_t nread; - - if (lseek(fd, range->start, SEEK_SET) == -1) - return (0); - - range_sz = range->end - range->start + 1; - while (range_sz) { - n = MINIMUM(range_sz, sizeof(buf)); - if ((nread = read(fd, buf, n)) == -1) - return (0); - - evbuffer_add(evb, buf, nread); - range_sz -= nread; - } - - return (1); -} -- cgit v1.2.3-54-g00ecf