diff --git a/logging.c b/logging.c index 8c7b0b17..97e2f92f 100644 --- a/logging.c +++ b/logging.c @@ -136,6 +136,7 @@ void log_request(struct wsgi_request *wsgi_req) { char *msg1 = " via sendfile() "; #endif char *msg3 = " via route() "; + char *msg4 = " via offload() "; struct uwsgi_app *wi; @@ -159,6 +160,9 @@ void log_request(struct wsgi_request *wsgi_req) { if (wsgi_req->status == -17) { via = msg3; } + else if (wsgi_req->status == -30) { + via = msg4; + } #ifdef __sun__ ctime_r((const time_t *) &wsgi_req->start_of_request.tv_sec, time_request, 26); diff --git a/protocol.c b/protocol.c index 1858878d..2f0b57fe 100644 --- a/protocol.c +++ b/protocol.c @@ -1656,57 +1656,15 @@ int uwsgi_static_stat(char *filename, struct stat *st) { return -1; } -int uwsgi_file_serve(struct wsgi_request *wsgi_req, char *document_root, uint16_t document_root_len, char *path_info, uint16_t path_info_len) { +int uwsgi_real_file_serve(struct wsgi_request *wsgi_req, char *real_filename, size_t real_filename_len, struct stat *st) { - struct stat st; struct iovec headers_vec[8]; - char real_filename[PATH_MAX+1]; + + int mime_type_size = 0; + char http_last_modified[49]; + char content_length[sizeof(UMAX64_STR)+1]; - size_t real_filename_len = 0; - char *filename = uwsgi_concat3n(document_root, document_root_len, "/", 1, path_info, path_info_len); - -#ifdef UWSGI_DEBUG - uwsgi_log("[uwsgi-fileserve] checking for %s\n", filename); -#endif - if (!realpath(filename, real_filename)) { -#ifdef UWSGI_DEBUG - uwsgi_log("[uwsgi-fileserve] unable to get realpath() of the static file\n"); -#endif - free(filename); - return -1; - } - - free(filename); - - if (uwsgi_starts_with(real_filename, strlen(real_filename), document_root, document_root_len)) { - uwsgi_log("[uwsgi-fileserve] security error: %s is not under %.*s\n", real_filename, document_root_len, document_root); - return -1; - } - - if (!uwsgi_static_stat(real_filename, &st)) { - - real_filename_len = strlen(real_filename); - - // check for skippable ext - struct uwsgi_string_list *sse = uwsgi.static_skip_ext; - while(sse) { - if (real_filename_len >= sse->len) { - if (!uwsgi_strncmp(real_filename+(real_filename_len - sse->len), sse->len, sse->value, sse->len)) { -#ifdef UWSGI_ROUTING - if (uwsgi_apply_routes_fast(wsgi_req, real_filename, real_filename_len) == UWSGI_ROUTE_BREAK) return 0; -#endif - return -1; - } - } - sse = sse->next; - } - - // Ok, the file must be served as static from uWSGI - // we can now offload to a thread or a threadpool - - int mime_type_size = 0; - char http_last_modified[49]; #ifdef UWSGI_THREADING if (uwsgi.threads > 1) pthread_mutex_lock(&uwsgi.lock_static); #endif @@ -1714,9 +1672,10 @@ int uwsgi_file_serve(struct wsgi_request *wsgi_req, char *document_root, uint16_ #ifdef UWSGI_THREADING if (uwsgi.threads > 1) pthread_mutex_unlock(&uwsgi.lock_static); #endif + if (wsgi_req->if_modified_since_len) { time_t ims = parse_http_date(wsgi_req->if_modified_since, wsgi_req->if_modified_since_len); - if (st.st_mtime <= ims) { + if (st->st_mtime <= ims) { wsgi_req->status = 304; headers_vec[0].iov_base = wsgi_req->protocol; headers_vec[0].iov_len = wsgi_req->protocol_len; @@ -1775,7 +1734,7 @@ int uwsgi_file_serve(struct wsgi_request *wsgi_req, char *document_root, uint16_ wsgi_req->header_cnt++; // check for content-type related headers - uwsgi_add_expires_type(wsgi_req, mime_type, mime_type_size, &st); + uwsgi_add_expires_type(wsgi_req, mime_type, mime_type_size, st); } // nginx @@ -1785,7 +1744,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, "Last-Modified", 13, http_last_modified, 1); + 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; } @@ -1796,7 +1755,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, "Last-Modified", 13, http_last_modified, 1); + 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; } @@ -1805,12 +1764,12 @@ int uwsgi_file_serve(struct wsgi_request *wsgi_req, char *document_root, uint16_ // set Content-Length headers_vec[0].iov_base = "Content-Length: "; headers_vec[0].iov_len = 16; - headers_vec[1].iov_len = uwsgi_long2str2n(st.st_size, content_length, sizeof(UMAX64_STR)+1); + headers_vec[1].iov_len = uwsgi_long2str2n(st->st_size, content_length, sizeof(UMAX64_STR)+1); headers_vec[1].iov_base = content_length; 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, "Last-Modified", 13, http_last_modified, 1); + 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); @@ -1824,6 +1783,110 @@ int uwsgi_file_serve(struct wsgi_request *wsgi_req, char *document_root, uint16_ wsgi_req->status = 200; return 0; +} + +void *uwsgi_static_offload_thread(void *req) { + + struct uwsgi_offload_request *uof_req = (struct uwsgi_offload_request *) req; + + uwsgi_real_file_serve(&uof_req->wsgi_req, uof_req->real_filename, uof_req->real_filename_len, &uof_req->st); + + // close the connection with the webserver + if (!uof_req->wsgi_req.fd_closed || !uof_req->wsgi_req.body_as_file) { + // NOTE, if we close the socket before receiving eventually sent data, socket layer will send a RST + uof_req->wsgi_req.socket->proto_close(&uof_req->wsgi_req); + } + + // free buffer + free(uof_req->buffer); + // free hvec + free(uof_req->hvec); + free(uof_req); + + return NULL; +} + + +int uwsgi_file_serve(struct wsgi_request *wsgi_req, char *document_root, uint16_t document_root_len, char *path_info, uint16_t path_info_len) { + + struct stat st; + char real_filename[PATH_MAX+1]; + size_t real_filename_len = 0; + + char *filename = uwsgi_concat3n(document_root, document_root_len, "/", 1, path_info, path_info_len); + +#ifdef UWSGI_DEBUG + uwsgi_log("[uwsgi-fileserve] checking for %s\n", filename); +#endif + if (!realpath(filename, real_filename)) { +#ifdef UWSGI_DEBUG + uwsgi_log("[uwsgi-fileserve] unable to get realpath() of the static file\n"); +#endif + free(filename); + return -1; + } + + free(filename); + + if (uwsgi_starts_with(real_filename, strlen(real_filename), document_root, document_root_len)) { + uwsgi_log("[uwsgi-fileserve] security error: %s is not under %.*s\n", real_filename, document_root_len, document_root); + return -1; + } + + if (!uwsgi_static_stat(real_filename, &st)) { + + real_filename_len = strlen(real_filename); + + // check for skippable ext + struct uwsgi_string_list *sse = uwsgi.static_skip_ext; + while(sse) { + if (real_filename_len >= sse->len) { + if (!uwsgi_strncmp(real_filename+(real_filename_len - sse->len), sse->len, sse->value, sse->len)) { +#ifdef UWSGI_ROUTING + if (uwsgi_apply_routes_fast(wsgi_req, real_filename, real_filename_len) == UWSGI_ROUTE_BREAK) return 0; +#endif + return -1; + } + } + sse = sse->next; + } + + // Ok, the file must be served as static from uWSGI + if (uwsgi.static_offload_to_thread) { + struct uwsgi_offload_request *uor = uwsgi_malloc(sizeof(struct uwsgi_offload_request)); + + // buffer + uor->buffer = uwsgi_malloc(uwsgi.buffer_size); memcpy(uor->buffer, wsgi_req->buffer, uwsgi.buffer_size); + // iovec + uor->hvec = uwsgi_malloc(sizeof(struct iovec) * uwsgi.vec_size); memcpy(uor->hvec, wsgi_req->hvec, sizeof(struct iovec) * uwsgi.vec_size); + + // wsgi_req + memcpy(&uor->wsgi_req, wsgi_req, sizeof(struct wsgi_request)); + + uor->wsgi_req.buffer = uor->buffer; + uor->wsgi_req.hvec = uor->hvec; + + // stat + memcpy(&uor->st, &st, sizeof(struct stat)); + + // filename + memcpy(uor->real_filename, real_filename, real_filename_len); + uor->real_filename_len = real_filename_len; + + // avoid closing the connection + wsgi_req->fd_closed = 1; + + if (pthread_create(&uor->tid, &uwsgi.static_offload_thread_attr, uwsgi_static_offload_thread, (void *) uor)) { + uwsgi_error("pthread_create()"); + // bad condition, better to exit... + exit(1); + } + wsgi_req->status = -30; + return 0; + } + else { + return uwsgi_real_file_serve(wsgi_req, real_filename, real_filename_len, &st); + } } return -1; diff --git a/uwsgi.c b/uwsgi.c index f635dac3..150889fb 100644 --- a/uwsgi.c +++ b/uwsgi.c @@ -376,6 +376,8 @@ static struct uwsgi_option uwsgi_base_options[] = { {"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}, + {"static-offload-to-thread", required_argument, 0, "offload static file serving to a thread (upto the specified number of threads)", uwsgi_opt_set_int, &uwsgi.static_offload_to_thread, 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}, @@ -1864,6 +1866,11 @@ int uwsgi_start(void *v_argv) { umd = umd->next; } + if (uwsgi.static_offload_to_thread) { + pthread_attr_init(&uwsgi.static_offload_thread_attr); + pthread_attr_setdetachstate(&uwsgi.static_offload_thread_attr, PTHREAD_CREATE_DETACHED); + } + // end of generic initialization // start the Emperor if needed diff --git a/uwsgi.h b/uwsgi.h index 1b040262..ee07f8cb 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -1280,6 +1280,8 @@ struct uwsgi_server { struct uwsgi_dyn_dict *static_expires_type_mtime; int check_static_docroot; + int static_offload_to_thread; + pthread_attr_t static_offload_thread_attr; char *daemonize; char *daemonize2; @@ -1639,6 +1641,17 @@ struct uwsgi_server { }; +struct uwsgi_offload_request { + pthread_t tid; + struct wsgi_request wsgi_req; + char *buffer; + struct iovec *hvec; + char real_filename[PATH_MAX+1]; + size_t real_filename_len; + struct stat st; +}; + + struct uwsgi_rpc { char name[0xff]; void *func;