mirror of
https://github.com/clearlinux/uwsgi.git
synced 2026-09-07 06:06:08 +00:00
implemented offload static file serving
This commit is contained in:
@@ -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);
|
||||
|
||||
+116
-53
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user