diff --git a/core/protocol.c b/core/protocol.c index 9641b70e..3ebfd447 100644 --- a/core/protocol.c +++ b/core/protocol.c @@ -303,8 +303,41 @@ static int uwsgi_proto_check_9(struct wsgi_request *wsgi_req, char *key, char *b return 0; } +static void uwsgi_parse_http_range(char *buf, uint16_t len, size_t *from, size_t *to) { + *from = 0; + *to = 0; + uint16_t rlen = 0; + uint16_t i; + for(i=0;i 0 && *from > *to) { + *from = 0; + *to = 0; + } +} + static int uwsgi_proto_check_10(struct wsgi_request *wsgi_req, char *key, char *buf, uint16_t len) { + if (uwsgi.honour_range && !uwsgi_proto_key("HTTP_RANGE", 10)) { + uwsgi_parse_http_range(buf, len, &wsgi_req->range_from, &wsgi_req->range_to); + return 0; + } + if (!uwsgi_proto_key("UWSGI_FILE", 10)) { wsgi_req->file = buf; wsgi_req->file_len = len; diff --git a/core/static.c b/core/static.c index e518a406..b437eaf7 100644 --- a/core/static.c +++ b/core/static.c @@ -451,8 +451,31 @@ int uwsgi_real_file_serve(struct wsgi_request *wsgi_req, char *real_filename, si uwsgi_log("[uwsgi-fileserve] file %s found\n", real_filename); #endif + size_t fsize = st->st_size; + if (wsgi_req->range_to) { + fsize = wsgi_req->range_to - wsgi_req->range_from; + if (fsize > (size_t)st->st_size) { + fsize = st->st_size; + } + } + else { + // reset in case of inconsistent size + if (wsgi_req->range_from > fsize) { + wsgi_req->range_from = 0; + fsize = 0 ; + } + else { + fsize -= wsgi_req->range_from; + } + } + // HTTP status - if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) return -1; + if (fsize > 0 && (wsgi_req->range_from || wsgi_req->range_to)) { + if (uwsgi_response_prepare_headers(wsgi_req, "206 Partial Content", 19)) return -1; + } + else { + if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) return -1; + } #ifdef UWSGI_PCRE uwsgi_add_expires(wsgi_req, real_filename, real_filename_len, st); @@ -490,8 +513,12 @@ int uwsgi_real_file_serve(struct wsgi_request *wsgi_req, char *real_filename, si if (uwsgi_static_want_gzip(wsgi_req, real_filename, real_filename_len, st)) { if (uwsgi_response_add_header(wsgi_req, "Content-Encoding", 16, "gzip", 4)) return -1; } - // set Content-Length - if (uwsgi_response_add_content_length(wsgi_req, st->st_size)) return -1; + // set Content-Length (to fsize NOT st->st_size) + if (uwsgi_response_add_content_length(wsgi_req, fsize)) return -1; + if (fsize > 0 && (wsgi_req->range_from || wsgi_req->range_to)) { + // here use teh original size !!! + if (uwsgi_response_add_content_range(wsgi_req, wsgi_req->range_from, wsgi_req->range_to, st->st_size)) return -1; + } int size = set_http_date(st->st_mtime, http_last_modified); if (uwsgi_response_add_header(wsgi_req, "Last-Modified", 13, http_last_modified, size)) return -1; @@ -506,7 +533,7 @@ int uwsgi_real_file_serve(struct wsgi_request *wsgi_req, char *real_filename, si int fd = open(real_filename, O_RDONLY); if (fd < 0) return -1; // fd will be closed in the following function - uwsgi_response_sendfile_do(wsgi_req, fd, 0, st->st_size); + uwsgi_response_sendfile_do(wsgi_req, fd, wsgi_req->range_from, fsize); } wsgi_req->status = 200; diff --git a/core/uwsgi.c b/core/uwsgi.c index 038d394a..ffc0b07b 100644 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -572,6 +572,8 @@ static struct uwsgi_option uwsgi_base_options[] = { {"static-gzip-ext", required_argument, 0, "check for a gzip version of all requested static files with the specified ext/suffix", uwsgi_opt_add_string_list, &uwsgi.static_gzip_ext, UWSGI_OPT_MIME}, {"static-gzip-suffix", required_argument, 0, "check for a gzip version of all requested static files with the specified ext/suffix", uwsgi_opt_add_string_list, &uwsgi.static_gzip_ext, UWSGI_OPT_MIME}, + {"honour-range", no_argument, 0, "enable support for the HTTP Range header", uwsgi_opt_true, &uwsgi.honour_range, 0}, + {"offload-threads", required_argument, 0, "set the number of offload threads to spawn (per-worker, default 0)", uwsgi_opt_set_int, &uwsgi.offload_threads, 0}, {"offload-thread", required_argument, 0, "set the number of offload threads to spawn (per-worker, default 0)", uwsgi_opt_set_int, &uwsgi.offload_threads, 0}, diff --git a/core/writer.c b/core/writer.c index ffc6af11..4624f689 100644 --- a/core/writer.c +++ b/core/writer.c @@ -12,6 +12,19 @@ int uwsgi_response_add_content_length(struct wsgi_request *wsgi_req, uint64_t cl return uwsgi_response_add_header(wsgi_req, "Content-Length", 14, buf, ret); } +int uwsgi_response_add_content_range(struct wsgi_request *wsgi_req, uint64_t start, uint64_t end, uint64_t cl) { + char buf[6+(sizeof(UMAX64_STR)*3)+4]; + if (end == 0) { + end = cl-1; + } + int ret = snprintf(buf, 6+(sizeof(UMAX64_STR)*3)+4, "bytes %llu-%llu/%llu", (unsigned long long) start, (unsigned long long) end, (unsigned long long) cl); + if (ret <= 0 || ret > (int) (6+(sizeof(UMAX64_STR)*3)+4)) { + wsgi_req->write_errors++; + return -1; + } + return uwsgi_response_add_header(wsgi_req, "Content-Range", 13, buf, ret); +} + // status could be NNN or NNN message int uwsgi_response_prepare_headers(struct wsgi_request *wsgi_req, char *status, uint16_t status_len) { @@ -225,6 +238,7 @@ sendfile: if (can_close) close(fd); return -1; } + if (pos >= (size_t)st.st_size) return UWSGI_OK; len = st.st_size; } diff --git a/uwsgi.h b/uwsgi.h index da045240..49e165a8 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -1351,6 +1351,9 @@ struct uwsgi_router { // when set, do not send warnings about bad behaviours int post_warning; + size_t range_from; + size_t range_to; + // current socket mapped to request struct uwsgi_socket *socket; @@ -1801,6 +1804,9 @@ struct uwsgi_server { struct termios termios; int restore_tc; + // honour the HTTP Range header + int honour_range; + // route all of the logs to the master process int req_log_master; int log_master; @@ -3824,6 +3830,7 @@ int uwsgi_proto_base_sendfile(struct wsgi_request *, int, size_t, size_t); ssize_t uwsgi_sendfile_do(int, int, size_t, size_t); int uwsgi_proto_base_fix_headers(struct wsgi_request *); int uwsgi_response_add_content_length(struct wsgi_request *, uint64_t); +int uwsgi_response_add_content_range(struct wsgi_request *, uint64_t, uint64_t, uint64_t); const char *uwsgi_http_status_msg(char *, uint16_t *); int uwsgi_stats_dump_vars(struct uwsgi_stats *, struct uwsgi_core *);