From b326500913bb27813978a4fae88194a77f324dbe Mon Sep 17 00:00:00 2001 From: "roberto@natty32" Date: Mon, 4 Apr 2011 08:31:17 +0200 Subject: [PATCH] --static-map mountpoint=documentroot option --- protocol.c | 120 +++++++++++++++++++++++++++++++++++++++-------------- utils.c | 7 ++++ uwsgi.c | 52 +++++++++++++++++++++++ uwsgi.h | 19 +++++++++ 4 files changed, 168 insertions(+), 30 deletions(-) diff --git a/protocol.c b/protocol.c index 8943b9bf..9b891df6 100644 --- a/protocol.c +++ b/protocol.c @@ -779,42 +779,25 @@ int uwsgi_parse_vars(struct wsgi_request *wsgi_req) { } } - // check i fa file name uwsgi.check_static+env['PATH_INFO'] exists + // check if a file named uwsgi.check_static+env['PATH_INFO'] exists if (uwsgi.check_static && wsgi_req->path_info_len > 1) { - struct stat st; - char *filename = uwsgi_concat2n(uwsgi.check_static, uwsgi.check_static_len, wsgi_req->path_info, wsgi_req->path_info_len); + if (!uwsgi_file_serve(wsgi_req, uwsgi.check_static, uwsgi.check_static_len, wsgi_req->path_info, wsgi_req->path_info_len)) { + return -1; + } + } + + // check static-map + struct uwsgi_static_map *usm = uwsgi.static_maps; + while(usm) { #ifdef UWSGI_DEBUG - uwsgi_log("checking for %s\n", filename); + uwsgi_log("checking for %.*s <-> %.*s\n", wsgi_req->path_info_len, wsgi_req->path_info, usm->mountpoint_len, usm->mountpoint); #endif - if (!stat(filename, &st)) { - 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) { - wsgi_req->status = 304; - wsgi_req->headers_size = write(wsgi_req->poll.fd, wsgi_req->protocol, wsgi_req->protocol_len); - wsgi_req->headers_size += write(wsgi_req->poll.fd, " 304 Not Modified\r\n\r\n", 21); - return -1; - } - } - if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { - char http_last_modified[49]; -#ifdef UWSGI_DEBUG - uwsgi_log("file %s found\n", filename); -#endif - // no need to set content-type/content-length, they will be fixed by the http server/router - wsgi_req->sendfile_fd = open(filename, O_RDONLY); - wsgi_req->headers_size = write(wsgi_req->poll.fd, wsgi_req->protocol, wsgi_req->protocol_len); - wsgi_req->headers_size += write(wsgi_req->poll.fd, " 200 OK\r\n", 9); - set_http_date(st.st_mtime, http_last_modified); - wsgi_req->headers_size += write(wsgi_req->poll.fd, http_last_modified, 48); - wsgi_req->header_cnt = 1; - wsgi_req->response_size += uwsgi_sendfile(wsgi_req); - wsgi_req->status = 200; - free(filename); + if (!uwsgi_starts_with(wsgi_req->path_info, wsgi_req->path_info_len, usm->mountpoint, usm->mountpoint_len)) { + if (!uwsgi_file_serve(wsgi_req, usm->document_root, usm->document_root_len, wsgi_req->path_info+usm->mountpoint_len, wsgi_req->path_info_len-usm->mountpoint_len)) { return -1; } } - free(filename); + usm = usm->next; } return 0; @@ -1317,3 +1300,80 @@ int uwsgi_simple_send_string(char *socket_name, uint8_t modifier1, uint8_t modif return 0; } + +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]; + char *filename = uwsgi_concat3n(document_root, document_root_len, "/", 1, path_info, path_info_len); +#ifdef UWSGI_DEBUG + uwsgi_log("checking for %s\n", filename); +#endif + if (!realpath(filename, real_filename)) { +#ifdef UWSGI_DEBUG + uwsgi_log("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("security error: %s is not under %.*s\n", real_filename, document_root_len, document_root); + return -1; + } + if (!stat(real_filename, &st)) { + 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) { + wsgi_req->status = 304; + wsgi_req->headers_size = write(wsgi_req->poll.fd, wsgi_req->protocol, wsgi_req->protocol_len); + wsgi_req->headers_size += write(wsgi_req->poll.fd, " 304 Not Modified\r\n\r\n", 21); + return 0; + } + } + if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { + char http_last_modified[49]; +#ifdef UWSGI_DEBUG + uwsgi_log("file %s found\n", real_filename); +#endif + // no need to set content-type/content-length, they will be fixed by the http server/router + + wsgi_req->headers_size = write(wsgi_req->poll.fd, wsgi_req->protocol, wsgi_req->protocol_len); + wsgi_req->headers_size += write(wsgi_req->poll.fd, " 200 OK\r\n", 9); + + if (uwsgi.file_serve_mode == 1) { + wsgi_req->header_cnt = 2; + wsgi_req->headers_size += write(wsgi_req->poll.fd, "X-Accel-Redirect: ", 18); + wsgi_req->headers_size += write(wsgi_req->poll.fd, path_info, path_info_len); + wsgi_req->headers_size += write(wsgi_req->poll.fd, "\r\n", 2); + set_http_date(st.st_mtime, http_last_modified); + wsgi_req->headers_size += write(wsgi_req->poll.fd, http_last_modified, 48); + } + else if (uwsgi.file_serve_mode == 2) { + wsgi_req->header_cnt = 2; + wsgi_req->headers_size += write(wsgi_req->poll.fd, "X-Sendfile: ", 12); + wsgi_req->headers_size += write(wsgi_req->poll.fd, real_filename, strlen(real_filename)); + wsgi_req->headers_size += write(wsgi_req->poll.fd, "\r\n", 2); + set_http_date(st.st_mtime, http_last_modified); + wsgi_req->headers_size += write(wsgi_req->poll.fd, http_last_modified, 48); + } + else { + wsgi_req->header_cnt = 1; + set_http_date(st.st_mtime, http_last_modified); + wsgi_req->headers_size += write(wsgi_req->poll.fd, http_last_modified, 48); + wsgi_req->sendfile_fd = open(real_filename, O_RDONLY); + wsgi_req->response_size += uwsgi_sendfile(wsgi_req); + } + + + wsgi_req->status = 200; + return 0; + } + } + + return -1; + +} + diff --git a/utils.c b/utils.c index 47962ccc..205e7539 100644 --- a/utils.c +++ b/utils.c @@ -718,6 +718,13 @@ inline int uwsgi_strncmp(char *src, int slen, char *dst, int dlen) { } +inline int uwsgi_starts_with(char *src, int slen, char *dst, int dlen) { + + if (slen < dlen) return -1; + + return memcmp(src, dst, dlen); +} + inline int uwsgi_startswith(char *src, char *what, int wlen) { int i; diff --git a/uwsgi.c b/uwsgi.c index a2eefcab..bc7ee447 100644 --- a/uwsgi.c +++ b/uwsgi.c @@ -156,6 +156,8 @@ static struct option long_base_options[] = { {"routing", no_argument, &uwsgi.routing, 1}, #endif {"check-static", required_argument, 0, LONG_ARGS_CHECK_STATIC}, + {"static-map", required_argument, 0, LONG_ARGS_STATIC_MAP}, + {"file-serve-mode", required_argument, 0, LONG_ARGS_FILE_SERVE_MODE}, {"check-cache", no_argument, &uwsgi.check_cache, 1}, {"close-on-exec", no_argument, &uwsgi.close_on_exec, 1}, {"mode", required_argument, 0, LONG_ARGS_MODE}, @@ -2266,6 +2268,7 @@ end: static int manage_base_opt(int i, char *optarg) { char *p; + struct uwsgi_static_map *usm; switch (i) { @@ -2434,6 +2437,55 @@ end: uwsgi.check_static = optarg; uwsgi.check_static_len = strlen(uwsgi.check_static); return 1; + case LONG_ARGS_FILE_SERVE_MODE: + if (!strcasecmp("x-sendfile", optarg)) { + uwsgi.file_serve_mode = 2; + } + else if (!strcasecmp("xsendfile", optarg)) { + uwsgi.file_serve_mode = 2; + } + else if (!strcasecmp("x-accel-redirect", optarg)) { + uwsgi.file_serve_mode = 1; + } + else if (!strcasecmp("xaccelredirect", optarg)) { + uwsgi.file_serve_mode = 1; + } + else if (!strcasecmp("nginx", optarg)) { + uwsgi.file_serve_mode = 1; + } + return 1; + case LONG_ARGS_STATIC_MAP: + usm = uwsgi.static_maps; + if (!usm) { + usm = uwsgi_malloc(sizeof(struct uwsgi_static_map)); + uwsgi.static_maps = usm; + } + else { + while(usm->next) { + if (!usm->next) { + usm->next = uwsgi_malloc(sizeof(struct uwsgi_static_map)); + usm = usm->next; + break; + } + } + } + + char *docroot = strchr(optarg, '='); + if (!docroot) { + uwsgi_log("invalid document root in static map\n"); + exit(1); + } + + usm->mountpoint = optarg; + usm->mountpoint_len = docroot-usm->mountpoint; + + usm->document_root = docroot+1; + usm->document_root_len = strlen(usm->document_root); + + uwsgi_log("static-mapped %.*s to %.*s\n", usm->mountpoint_len, usm->mountpoint, usm->document_root_len, usm->document_root); + + usm->next = NULL; + return 1; case LONG_ARGS_ATTACH_DAEMON: if (uwsgi.startup_daemons_cnt < MAX_DAEMONS) { uwsgi.startup_daemons[uwsgi.startup_daemons_cnt] = optarg; diff --git a/uwsgi.h b/uwsgi.h index b119e5e0..9551d054 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -216,6 +216,17 @@ struct uwsgi_help_item { char *value; }; +struct uwsgi_static_map { + + char *mountpoint; + int mountpoint_len; + + char *document_root; + int document_root_len; + + struct uwsgi_static_map *next; +}; + union uwsgi_sockaddr { struct sockaddr sa; @@ -380,6 +391,8 @@ struct uwsgi_opt { #define LONG_ARGS_PIDFILE2 17088 #define LONG_ARGS_MAP_SOCKET 17089 #define LONG_ARGS_SHARED_SOCKET 17090 +#define LONG_ARGS_STATIC_MAP 17091 +#define LONG_ARGS_FILE_SERVE_MODE 17092 #define UWSGI_OK 0 @@ -807,6 +820,9 @@ struct uwsgi_server { int log_syslog; char *check_static; size_t check_static_len; + int file_serve_mode; + + struct uwsgi_static_map *static_maps; char *logfile; int logfile_chown; @@ -1710,3 +1726,6 @@ int uwsgi_netlink_del(char *); int uwsgi_amqp_consume_queue(int, char *, char *, char *, char *); char *uwsgi_amqp_consume(int, uint64_t *, char **); + +int uwsgi_file_serve(struct wsgi_request *, char *, uint16_t, char *, uint16_t); +inline int uwsgi_starts_with(char *, int, char *, int);