diff --git a/protocol.c b/protocol.c index 703f34fd..b726a019 100644 --- a/protocol.c +++ b/protocol.c @@ -16,7 +16,8 @@ static size_t get_content_length(char *buf, uint16_t size) { return val; } -void set_http_date(time_t t, char *dst) { + +int set_http_date(time_t t, char *header, int header_len, char *dst, int last) { static char *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static char *months[] = { @@ -26,12 +27,58 @@ void set_http_date(time_t t, char *dst) { }; struct tm *hdtm = gmtime(&t); - snprintf(dst, 49, "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n\r\n", + + if (last) { + return snprintf(dst, 36+header_len, "%.*s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n\r\n", + header_len, header, + week[hdtm->tm_wday], hdtm->tm_mday, + months[hdtm->tm_mon], hdtm->tm_year+1900, + hdtm->tm_hour, hdtm->tm_min, hdtm->tm_sec); + } + + return snprintf(dst, 34+header_len, "%.*s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", + header_len, header, week[hdtm->tm_wday], hdtm->tm_mday, months[hdtm->tm_mon], hdtm->tm_year+1900, hdtm->tm_hour, hdtm->tm_min, hdtm->tm_sec); } +void uwsgi_add_expires_type(struct wsgi_request *wsgi_req, char *mime_type, int mime_type_len, struct stat *st) { + + struct uwsgi_dyn_dict *udd = uwsgi.static_expires_type; + time_t now = wsgi_req->start_of_request.tv_sec; + // Expires+34+1 + char expires[42]; + + while(udd) { + if (!uwsgi_strncmp(udd->key, udd->keylen, mime_type, mime_type_len)) { + int delta = uwsgi_str_num(udd->value, udd->vallen); + int size = set_http_date(now+delta, "Expires", 7, expires, 0); + if (size > 0) { + wsgi_req->headers_size += wsgi_req->socket->proto_write_header(wsgi_req, expires, size); + wsgi_req->header_cnt++; + } + return; + } + udd = udd->next; + } + + udd = uwsgi.static_expires_type_mtime; + while(udd) { + if (!uwsgi_strncmp(udd->key, udd->keylen, mime_type, mime_type_len)) { + int delta = uwsgi_str_num(udd->value, udd->vallen); + int size = set_http_date(st->st_mtime+delta, "Expires", 7, expires, 0); + if (size > 0) { + wsgi_req->headers_size += wsgi_req->socket->proto_write_header(wsgi_req, expires, size); + wsgi_req->header_cnt++; + } + return; + } + udd = udd->next; + } +} + + // only RFC 1123 is supported time_t parse_http_date(char *date, uint16_t len) { @@ -1723,6 +1770,9 @@ int uwsgi_file_serve(struct wsgi_request *wsgi_req, char *document_root, uint16_ headers_vec[2].iov_len = 2; wsgi_req->headers_size += wsgi_req->socket->proto_writev_header(wsgi_req, headers_vec, 3); wsgi_req->header_cnt++; + + // check for content-type related headers + uwsgi_add_expires_type(wsgi_req, mime_type, mime_type_size, &st); } // nginx @@ -1732,7 +1782,7 @@ int uwsgi_file_serve(struct wsgi_request *wsgi_req, char *document_root, uint16_ headers_vec[2].iov_base = "\r\n"; headers_vec[2].iov_len = 2; wsgi_req->headers_size += wsgi_req->socket->proto_writev_header(wsgi_req, headers_vec, 3); // this is the final header (\r\n added) - set_http_date(st.st_mtime, http_last_modified); + set_http_date(st.st_mtime, "Last-Modified", 13, http_last_modified, 1); wsgi_req->headers_size += wsgi_req->socket->proto_write_header(wsgi_req, http_last_modified, 48); wsgi_req->header_cnt += 2; } @@ -1743,7 +1793,7 @@ int uwsgi_file_serve(struct wsgi_request *wsgi_req, char *document_root, uint16_ headers_vec[2].iov_base = "\r\n"; headers_vec[2].iov_len = 2; wsgi_req->headers_size += wsgi_req->socket->proto_writev_header(wsgi_req, headers_vec, 3); // this is the final header (\r\n added) - set_http_date(st.st_mtime, http_last_modified); + set_http_date(st.st_mtime, "Last-Modified", 13, http_last_modified, 1); wsgi_req->headers_size += wsgi_req->socket->proto_write_header(wsgi_req, http_last_modified, 48); wsgi_req->header_cnt += 2; } @@ -1757,7 +1807,7 @@ int uwsgi_file_serve(struct wsgi_request *wsgi_req, char *document_root, uint16_ headers_vec[2].iov_base = "\r\n"; headers_vec[2].iov_len = 2; // this is the final header (\r\n added) - set_http_date(st.st_mtime, http_last_modified); + set_http_date(st.st_mtime, "Last-Modified", 13, http_last_modified, 1); headers_vec[3].iov_base = http_last_modified; headers_vec[3].iov_len = 48; wsgi_req->headers_size += wsgi_req->socket->proto_writev_header(wsgi_req, headers_vec, 4); diff --git a/uwsgi.c b/uwsgi.c index 69e96372..d39ba5a7 100644 --- a/uwsgi.c +++ b/uwsgi.c @@ -363,17 +363,21 @@ static struct uwsgi_option uwsgi_base_options[] = { {"route-qs", required_argument, 0, "add a route based on QUERY_STRING", uwsgi_opt_add_route, "query_string", 0}, #endif {"add-header", required_argument, 0, "automatically add HTTP headers to response", uwsgi_opt_add_string_list, &uwsgi.additional_headers, 0}, - {"check-static", required_argument, 0, "check for static files in the specified directory", uwsgi_opt_check_static, NULL, 0}, - {"check-static-docroot", no_argument, 0, "check for static files in the requested DOCUMENT_ROOT", uwsgi_opt_true, &uwsgi.check_static_docroot, UWSGI_OPT_MIME}, - {"static-check", required_argument, 0, "check for static files in the specified directory", uwsgi_opt_check_static, NULL, 0}, - {"static-map", required_argument, 0, "map mountpoint to static directory", uwsgi_opt_static_map, NULL, 0}, - {"static-skip-ext", required_argument, 0, "skip specified extension from staticfile checks", uwsgi_opt_add_string_list, &uwsgi.static_skip_ext, 0}, - {"static-index", required_argument, 0, "search for specified file if a directory is requested", uwsgi_opt_add_string_list, &uwsgi.static_index, 0}, - {"mimefile", required_argument, 0, "set mime types file path (default /etc/mime.types)", uwsgi_opt_set_str, &uwsgi.mime_file, 0}, - {"mime-file", required_argument, 0, "set mime types file path (default /etc/mime.types)", uwsgi_opt_set_str, &uwsgi.mime_file, 0}, - {"file-serve-mode", required_argument, 0, "set static file serving mode", uwsgi_opt_fileserve_mode, NULL,0}, - {"fileserve-mode", required_argument, 0, "set static file serving mode", uwsgi_opt_fileserve_mode, NULL,0}, + {"check-static", required_argument, 0, "check for static files in the specified directory", uwsgi_opt_check_static, NULL, UWSGI_OPT_MIME}, + {"check-static-docroot", no_argument, 0, "check for static files in the requested DOCUMENT_ROOT", uwsgi_opt_true, &uwsgi.check_static_docroot, UWSGI_OPT_MIME}, + {"static-check", required_argument, 0, "check for static files in the specified directory", uwsgi_opt_check_static, NULL, UWSGI_OPT_MIME}, + {"static-map", required_argument, 0, "map mountpoint to static directory", uwsgi_opt_static_map, NULL, UWSGI_OPT_MIME}, + {"static-skip-ext", required_argument, 0, "skip specified extension from staticfile checks", uwsgi_opt_add_string_list, &uwsgi.static_skip_ext, UWSGI_OPT_MIME}, + {"static-index", required_argument, 0, "search for specified file if a directory is requested", uwsgi_opt_add_string_list, &uwsgi.static_index, UWSGI_OPT_MIME}, + {"mimefile", required_argument, 0, "set mime types file path (default /etc/mime.types)", uwsgi_opt_set_str, &uwsgi.mime_file, UWSGI_OPT_MIME}, + {"mime-file", required_argument, 0, "set mime types file path (default /etc/mime.types)", uwsgi_opt_set_str, &uwsgi.mime_file, UWSGI_OPT_MIME}, + + {"static-expires-type", required_argument, 0, "set the Expires header base on content type", uwsgi_opt_add_dyn_dict, &uwsgi.static_expires_type, UWSGI_OPT_MIME}, + {"static-expires-type-mtime", required_argument, 0, "set the Expires header base on content type and file mtime", uwsgi_opt_add_dyn_dict, &uwsgi.static_expires_type_mtime, UWSGI_OPT_MIME}, + + {"file-serve-mode", required_argument, 0, "set static file serving mode", uwsgi_opt_fileserve_mode, NULL, UWSGI_OPT_MIME}, + {"fileserve-mode", required_argument, 0, "set static file serving mode", uwsgi_opt_fileserve_mode, NULL, UWSGI_OPT_MIME}, {"check-cache", no_argument, 0, "check for response data in the cache", uwsgi_opt_true, &uwsgi.check_cache, 0}, {"close-on-exec", no_argument, 0, "set close-on-exec on sockets (could be required for spawning processes in requests)", uwsgi_opt_true, &uwsgi.close_on_exec, 0}, @@ -3689,6 +3693,20 @@ void uwsgi_opt_check_static(char *opt, char *value, void *foobar) { } +void uwsgi_opt_add_dyn_dict(char *opt, char *value, void *dict) { + + char *equal = strchr(value, '='); + if (!equal) { + uwsgi_log("invalid dictionary syntax for %s\n", opt); + exit(1); + } + + struct uwsgi_dyn_dict **udd = (struct uwsgi_dyn_dict **) dict; + + uwsgi_dyn_dict_new(udd, value, equal-value, equal+1, strlen(equal+1)); + +} + void uwsgi_opt_fileserve_mode(char *opt, char *value, void *foobar) { if (!strcasecmp("x-sendfile", value)) { diff --git a/uwsgi.h b/uwsgi.h index 67377b6e..30e184c1 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -1274,6 +1274,10 @@ struct uwsgi_server { struct uwsgi_dyn_dict *mimetypes; struct uwsgi_string_list *static_skip_ext; struct uwsgi_string_list *static_index; + + struct uwsgi_dyn_dict *static_expires_type; + struct uwsgi_dyn_dict *static_expires_type_mtime; + int check_static_docroot; char *daemonize; @@ -2637,6 +2641,7 @@ void uwsgi_opt_set_str(char *, char *, void *); void uwsgi_opt_set_logger(char *, char *, void *); void uwsgi_opt_set_str_spaced(char *, char *, void *); void uwsgi_opt_add_string_list(char *, char *, void *); +void uwsgi_opt_add_dyn_dict(char *, char *, void *); void uwsgi_opt_set_int(char *, char *, void *); void uwsgi_opt_set_64bit(char *, char *, void *); void uwsgi_opt_set_megabytes(char *, char *, void *);