static int uwsgi_glusterfs_request(struct wsgi_request *wsgi_req) { char filename[PATH_MAX+1]; /* Standard GlusterFS request */ if (!wsgi_req->uh->pktsize) { uwsgi_log( "Empty GlusterFS request. skip.\n"); return -1; } if (uwsgi_parse_vars(wsgi_req)) { return -1; } // blocks empty paths if (wsgi_req->path_info_len == 0 || wsgi_req->path_info_len > PATH_MAX) { uwsgi_403(wsgi_req); return UWSGI_OK; } wsgi_req->app_id = uwsgi_get_app_id(wsgi_req, wsgi_req->appid, wsgi_req->appid_len, glusterfs_plugin.modifier1); if (wsgi_req->app_id == -1 && !uwsgi.no_default_app && uwsgi.default_app > -1) { if (uwsgi_apps[uwsgi.default_app].modifier1 == glusterfs_plugin.modifier1) { wsgi_req->app_id = uwsgi.default_app; } } if (wsgi_req->app_id == -1) { uwsgi_404(wsgi_req); return UWSGI_OK; } struct uwsgi_app *ua = &uwsgi_apps[wsgi_req->app_id]; memcpy(filename, wsgi_req->path_info, wsgi_req->path_info_len); filename[wsgi_req->path_info_len] = 0; glfs_fd_t *fd = glfs_open((glfs_t *) ua->interpreter, filename, O_RDONLY); if (!fd) { uwsgi_404(wsgi_req); return UWSGI_OK; } struct stat st; if (glfs_fstat(fd, &st)) { uwsgi_403(wsgi_req); return UWSGI_OK; } if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) goto end; size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(wsgi_req->path_info, wsgi_req->path_info_len, &mime_type_len); if (mime_type) { if (uwsgi_response_add_content_type(wsgi_req, mime_type, mime_type_len)) goto end; } if (uwsgi_response_add_last_modified(wsgi_req, (uint64_t) st.st_mtime)) goto end; if (uwsgi_response_add_content_length(wsgi_req, st.st_size)) goto end; // skip body on HEAD if (uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "HEAD", 4)) { size_t remains = st.st_size; while(remains > 0) { char buf[8192]; ssize_t rlen = glfs_read (fd, buf, UMIN(remains, 8192), 0); if (rlen <= 0) goto end; if (uwsgi_response_write_body_do(wsgi_req, buf, rlen)) goto end; remains -= rlen; } } end: glfs_close(fd); return UWSGI_OK; }
int uwsgi_routing_func_fastfile(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) { struct stat st; int ret = UWSGI_ROUTE_BREAK; struct uwsgi_router_file_conf *urfc = (struct uwsgi_router_file_conf *) ur->data2; char **subject = (char **) (((char *)(wsgi_req))+ur->subject); uint16_t *subject_len = (uint16_t *) (((char *)(wsgi_req))+ur->subject_len); struct uwsgi_buffer *ub = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, urfc->filename, urfc->filename_len); if (!ub) return UWSGI_ROUTE_BREAK; int fd = open(ub->buf, O_RDONLY); if (fd < 0) { if (ur->custom) ret = UWSGI_ROUTE_NEXT; goto end; } if (fstat(fd, &st)) { goto end2; } struct uwsgi_buffer *ub_s = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, urfc->status, urfc->status_len); if (!ub_s) goto end2; // static file - don't update avg_rt after request wsgi_req->do_not_account_avg_rt = 1; if (urfc->no_headers) { uwsgi_buffer_destroy(ub_s); goto send; } if (uwsgi_response_prepare_headers(wsgi_req, ub_s->buf, ub_s->pos)) { uwsgi_buffer_destroy(ub_s); goto end2; } uwsgi_buffer_destroy(ub_s); if (uwsgi_response_add_content_length(wsgi_req, st.st_size)) goto end2; if (urfc->mime) { size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(ub->buf, ub->pos, &mime_type_len); if (mime_type) { if (uwsgi_response_add_content_type(wsgi_req, mime_type, mime_type_len)) goto end2; } else { if (uwsgi_response_add_content_type(wsgi_req, urfc->content_type, urfc->content_type_len)) goto end2; } } else { if (uwsgi_response_add_content_type(wsgi_req, urfc->content_type, urfc->content_type_len)) goto end2; } send: if (!wsgi_req->headers_sent) { if (uwsgi_response_write_headers_do(wsgi_req)) goto end2; } if (wsgi_req->socket->can_offload) { if (!uwsgi_offload_request_sendfile_do(wsgi_req, fd, st.st_size)) { wsgi_req->via = UWSGI_VIA_OFFLOAD; wsgi_req->response_size += st.st_size; // the fd will be closed by the offload engine goto end; } } if (!uwsgi_simple_sendfile(wsgi_req, fd, 0, st.st_size)) { wsgi_req->via = UWSGI_VIA_SENDFILE; wsgi_req->response_size += st.st_size; } end2: close(fd); end: uwsgi_buffer_destroy(ub); return ret; }
int uwsgi_routing_func_file(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) { char buf[32768]; struct stat st; int ret = UWSGI_ROUTE_BREAK; size_t remains = 0; struct uwsgi_router_file_conf *urfc = (struct uwsgi_router_file_conf *) ur->data2; char **subject = (char **) (((char *)(wsgi_req))+ur->subject); uint16_t *subject_len = (uint16_t *) (((char *)(wsgi_req))+ur->subject_len); struct uwsgi_buffer *ub = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, urfc->filename, urfc->filename_len); if (!ub) return UWSGI_ROUTE_BREAK; int fd = open(ub->buf, O_RDONLY); if (fd < 0) { if (ur->custom) ret = UWSGI_ROUTE_NEXT; goto end; } if (fstat(fd, &st)) { goto end2; } struct uwsgi_buffer *ub_s = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, urfc->status, urfc->status_len); if (!ub_s) goto end2; // static file - don't update avg_rt after request wsgi_req->do_not_account_avg_rt = 1; if (urfc->no_headers) { uwsgi_buffer_destroy(ub_s); goto send; } if (uwsgi_response_prepare_headers(wsgi_req, ub_s->buf, ub_s->pos)) { uwsgi_buffer_destroy(ub_s); goto end2; } uwsgi_buffer_destroy(ub_s); if (!urfc->no_cl) { if (uwsgi_response_add_content_length(wsgi_req, st.st_size)) goto end2; } if (urfc->mime) { size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(ub->buf, ub->pos, &mime_type_len); if (mime_type) { if (uwsgi_response_add_content_type(wsgi_req, mime_type, mime_type_len)) goto end2; } else { if (uwsgi_response_add_content_type(wsgi_req, urfc->content_type, urfc->content_type_len)) goto end2; } } else { if (uwsgi_response_add_content_type(wsgi_req, urfc->content_type, urfc->content_type_len)) goto end2; } send: remains = st.st_size; while(remains) { ssize_t rlen = read(fd, buf, UMIN(32768, remains)); if (rlen <= 0) goto end2; if (uwsgi_response_write_body_do(wsgi_req, buf, rlen)) goto end2; remains -= rlen; } end2: close(fd); end: uwsgi_buffer_destroy(ub); return ret; }
static int uwsgi_wevdav_manage_get(struct wsgi_request *wsgi_req, int send_body) { char filename[PATH_MAX]; size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, wsgi_req->path_info, wsgi_req->path_info_len, filename); if (!filename_len) { uwsgi_404(wsgi_req); return UWSGI_OK; } if (uwsgi_is_dir(filename)) { uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6); if (send_body) { uwsgi_webdav_dirlist(wsgi_req, filename); } return UWSGI_OK; } int fd = open(filename, O_RDONLY); if (fd < 0) { uwsgi_403(wsgi_req); return UWSGI_OK; } struct stat st; if (fstat(fd, &st)) { close(fd); uwsgi_403(wsgi_req); return UWSGI_OK; } if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) goto end; // add content_length if (uwsgi_response_add_content_length(wsgi_req, st.st_size)) goto end; // add last-modified if (uwsgi_response_add_last_modified(wsgi_req, st.st_mtime)) goto end; // add mime_type size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(filename, filename_len, &mime_type_len); if (mime_type) { if (uwsgi_response_add_content_type(wsgi_req, mime_type, mime_type_len)) goto end; } // add ETag (based on file mtime, not rock-solid, but good enough) char *etag = uwsgi_num2str(st.st_mtime); if (uwsgi_response_add_header(wsgi_req, "ETag", 4, etag, strlen(etag))) { free(etag); goto end; } free(etag); // start sending the file (note: we do not use sendfile() api, for being able to use caching and transformations) if (!send_body) goto end; // use a pretty big buffer (for performance reasons) char buf[32768]; size_t remains = st.st_size; while (remains > 0) { ssize_t rlen = read(fd, buf, UMIN(32768, remains)); if (rlen <= 0) { uwsgi_error("uwsgi_wevdav_manage_get/read()"); break; } remains -= rlen; if (uwsgi_response_write_body_do(wsgi_req, buf, rlen)) { break; } } end: close(fd); return UWSGI_OK; }
static int uwsgi_webdav_add_props(struct wsgi_request *wsgi_req, xmlNode *req_prop, xmlNode * multistatus, xmlNsPtr dav_ns, char *uri, char *filename, int with_values) { struct stat st; if (stat(filename, &st)) { uwsgi_error("uwsgi_webdav_add_props()/stat()"); return -1; } int is_collection = 0; xmlNode *response = xmlNewChild(multistatus, dav_ns, BAD_CAST "response", NULL); uint16_t uri_len = strlen(uri) ; char *encoded_uri = uwsgi_malloc( (uri_len * 3) + 1); http_url_encode(uri, &uri_len, encoded_uri); encoded_uri[uri_len] = 0; xmlNewChild(response, dav_ns, BAD_CAST "href", BAD_CAST encoded_uri); free(encoded_uri); xmlNode *r_propstat = xmlNewChild(response, dav_ns, BAD_CAST "propstat", NULL); char *r_status = uwsgi_concat2n(wsgi_req->protocol, wsgi_req->protocol_len, " 200 OK", 7); xmlNewChild(r_propstat, dav_ns, BAD_CAST "status", BAD_CAST r_status); free(r_status); xmlNode *r_prop = xmlNewChild(r_propstat, dav_ns, BAD_CAST "prop", NULL); if (with_values) { if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "displayname")) { char *base_uri = uwsgi_get_last_char(uri, '/'); if (base_uri) { xmlNewChild(r_prop, dav_ns, BAD_CAST "displayname", BAD_CAST base_uri+1); } else { xmlNewChild(r_prop, dav_ns, BAD_CAST "displayname", BAD_CAST uri); } } if (S_ISDIR(st.st_mode)) is_collection = 1; xmlNode *r_type = NULL; if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "resourcetype")) { r_type = xmlNewChild(r_prop, dav_ns, BAD_CAST "resourcetype", NULL); if (is_collection) { xmlNewChild(r_type, dav_ns, BAD_CAST "collection", NULL); is_collection = 1; } } if (!is_collection) { if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getcontentlength")) { char *r_contentlength = uwsgi_num2str(st.st_size); xmlNewChild(r_prop, dav_ns, BAD_CAST "getcontentlength", BAD_CAST r_contentlength); free(r_contentlength); } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getcontenttype")) { size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(filename, strlen(filename), &mime_type_len); if (mime_type) { char *r_ctype = uwsgi_concat2n(mime_type, mime_type_len, "", 0); xmlNewTextChild(r_prop, dav_ns, BAD_CAST "getcontenttype", BAD_CAST r_ctype); free(r_ctype); } } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "creationdate")) { // there is no creation date on UNIX/POSIX, ctime is the nearest thing... char *cdate = uwsgi_webdav_new_date(st.st_ctime); if (cdate) { xmlNewTextChild(r_prop, dav_ns, BAD_CAST "creationdate", BAD_CAST cdate); free(cdate); } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getlastmodified")) { char *mdate = uwsgi_webdav_new_date(st.st_mtime); if (mdate) { xmlNewTextChild(r_prop, dav_ns, BAD_CAST "getlastmodified", BAD_CAST mdate); free(mdate); } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getetag")) { char *etag = uwsgi_num2str(st.st_mtime); xmlNewTextChild(r_prop, dav_ns, BAD_CAST "getetag", BAD_CAST etag); free(etag); } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "executable")) { xmlNewChild(r_prop, dav_ns, BAD_CAST "executable", NULL); } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "owner")) { xmlNewTextChild(r_prop, dav_ns, BAD_CAST "owner", NULL); } if (wsgi_req->remote_user_len > 0) { if (udav.principal_base) { if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "current-user-principal")) { char *current_user_principal = uwsgi_concat2n(udav.principal_base, strlen(udav.principal_base), wsgi_req->remote_user, wsgi_req->remote_user_len); xmlNode *cup = xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-principal", NULL); xmlNewTextChild(cup, dav_ns, BAD_CAST "href", BAD_CAST current_user_principal); if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "resourcetype")) { if (!strcmp(current_user_principal, uri)) { xmlNewChild(r_type, dav_ns, BAD_CAST "principal", NULL); } } free(current_user_principal); } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "current-user-privilege-set")) { xmlNode *cups = xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-privilege-set", NULL); xmlNode *privilege = xmlNewChild(cups, dav_ns, BAD_CAST "privilege", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "all", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "read", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "write", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "write-content", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "write-properties", NULL); } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "supported-report-set")) { xmlNode *report_set = xmlNewChild(r_prop, dav_ns, BAD_CAST "supported-report-set", NULL); xmlNode *supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL); xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "principal-property-search"); supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL); xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "sync-collection"); supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL); xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "expand-property"); supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL); xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "principal-search-property-set"); } uwsgi_webdav_foreach_prop(udav.add_prop, req_prop, r_prop, 0, NULL ); uwsgi_webdav_foreach_prop(udav.add_prop_href, req_prop, r_prop, 1, NULL); uwsgi_webdav_foreach_prop(udav.add_prop_comp,req_prop, r_prop, 2 , NULL); uwsgi_webdav_foreach_prop(udav.add_rtype_prop,req_prop, r_type, 0, "resourcetype"); if (is_collection) { uwsgi_webdav_foreach_prop(udav.add_rtype_collection_prop,req_prop, r_type, 0, "resourcetype"); uwsgi_webdav_foreach_prop(udav.add_collection_prop,req_prop, r_prop, 0, NULL); uwsgi_webdav_foreach_prop(udav.add_collection_prop_href,req_prop, r_prop, 1, NULL); uwsgi_webdav_foreach_prop(udav.add_collection_prop_comp,req_prop, r_prop, 2, NULL); } else { uwsgi_webdav_foreach_prop(udav.add_rtype_object_prop,req_prop, r_type, 0, "resourcetype"); uwsgi_webdav_foreach_prop(udav.add_object_prop,req_prop, r_prop, 0, NULL); uwsgi_webdav_foreach_prop(udav.add_object_prop_href,req_prop, r_prop, 1, NULL); uwsgi_webdav_foreach_prop(udav.add_object_prop_comp,req_prop, r_prop, 2, NULL); } } else { xmlNewChild(r_prop, dav_ns, BAD_CAST "displayname", NULL); xmlNewChild(r_prop, dav_ns, BAD_CAST "resourcetype", NULL); if (!S_ISDIR(st.st_mode)) { xmlNewChild(r_prop, dav_ns, BAD_CAST "getcontentlength", NULL); xmlNewChild(r_prop, dav_ns, BAD_CAST "getcontenttype", NULL); } xmlNewChild(r_prop, dav_ns, BAD_CAST "creationdate", NULL); xmlNewChild(r_prop, dav_ns, BAD_CAST "getlastmodified", NULL); xmlNewChild(r_prop, dav_ns, BAD_CAST "supported-report-set", NULL); if (wsgi_req->remote_user_len > 0) { xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-privilege-set", NULL); if (udav.principal_base) { xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-principal", NULL); } } } #if defined(__linux__) || defined(__APPLE__) // get xattr for user.uwsgi.webdav. #if defined(__linux__) ssize_t rlen = listxattr(filename, NULL, 0); #elif defined(__APPLE__) ssize_t rlen = listxattr(filename, NULL, 0, 0); #endif // do not return -1 as the previous xml is valid !!! if (rlen <= 0) return 0; // use calloc to avoid races char *xattrs = uwsgi_calloc(rlen); #if defined(__linux__) if (listxattr(filename, xattrs, rlen) <= 0) { #elif defined(__APPLE__) if (listxattr(filename, xattrs, rlen, 0) <= 0) { #endif free(xattrs); return 0; } // parse the name list ssize_t i; char *key = NULL; for(i=0;i<rlen;i++) { // check for wrong condition if (xattrs[i] == 0 && key == NULL) break; if (key && xattrs[i] == 0) { if (!uwsgi_starts_with(key, strlen(key), "user.uwsgi.webdav.", 18)) { if (uwsgi_string_list_has_item(udav.skip_prop, key + 18, strlen(key + 18))) continue; xmlNsPtr xattr_ns = NULL; // does it has a namespace ? char *separator = strchr(key + 18, '|'); char *xattr_key = key + 18; if (separator) { xattr_key = separator + 1; *separator = 0; if (!uwsgi_webdav_prop_requested(req_prop, key + 18, xattr_key)) continue; } else { if (!uwsgi_webdav_prop_requested(req_prop, NULL, xattr_key)) continue; } xmlNode *xattr_item = NULL; if (with_values) { #if defined(__linux__) ssize_t rlen2 = getxattr(filename, key, NULL, 0); #elif defined(__APPLE__) ssize_t rlen2 = getxattr(filename, key, NULL, 0, 0, 0); #endif if (rlen > 0) { // leave space for final 0 char *xvalue = uwsgi_calloc(rlen2 + 1); #if defined(__linux__) if (getxattr(filename, key, xvalue, rlen2) > 0) { #elif defined(__APPLE__) if (getxattr(filename, key, xvalue, rlen2, 0 ,0) > 0) { #endif xattr_item = xmlNewTextChild(r_prop, NULL, BAD_CAST xattr_key, BAD_CAST xvalue); } free(xvalue); } else if (rlen == 0) { xattr_item = xmlNewTextChild(r_prop, NULL, BAD_CAST xattr_key, NULL); } } else { xattr_item = xmlNewTextChild(r_prop, NULL, BAD_CAST xattr_key, NULL); } if (separator && xattr_item) { xattr_ns = xmlNewNs(xattr_item, BAD_CAST (key + 18), NULL); *separator = '|'; xmlSetNs(xattr_item, xattr_ns); } } key = NULL; } else if (key == NULL) { key = &xattrs[i]; } } free(xattrs); #endif return 0; } static size_t uwsgi_webdav_expand_path(struct wsgi_request *wsgi_req, char *item, uint16_t item_len, char *filename) { struct uwsgi_app *ua = &uwsgi_apps[wsgi_req->app_id]; char *docroot = ua->interpreter; size_t docroot_len = strlen(docroot); // merge docroot with path_info char *tmp_filename = uwsgi_concat3n(docroot, docroot_len, "/", 1, item, item_len); // try expanding the path if (!realpath(tmp_filename, filename)) { free(tmp_filename); return 0; } free(tmp_filename); return strlen(filename); } static size_t uwsgi_webdav_expand_fake_path(struct wsgi_request *wsgi_req, char *item, uint16_t item_len, char *filename) { char *last_slash = uwsgi_get_last_charn(item, item_len, '/'); if (!last_slash) return 0; size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, item, last_slash - item, filename); if (!filename_len) return 0; // check for overflow if (filename_len + (item_len - (last_slash - item)) >= PATH_MAX) return 0; memcpy(filename + filename_len, last_slash, (item_len - (last_slash - item))); filename_len += (item_len - (last_slash - item)); filename[(int)filename_len] = 0; return filename_len; } static xmlDoc *uwsgi_webdav_manage_prop(struct wsgi_request *wsgi_req, xmlNode *req_prop, char *filename, size_t filename_len, int with_values) { // default 1 depth int depth = 1; uint16_t http_depth_len = 0; char *http_depth = uwsgi_get_var(wsgi_req, "HTTP_DEPTH", 10, &http_depth_len); if (http_depth) { depth = uwsgi_str_num(http_depth, http_depth_len); } xmlDoc *rdoc = xmlNewDoc(BAD_CAST "1.0"); xmlNode *multistatus = xmlNewNode(NULL, BAD_CAST "multistatus"); xmlDocSetRootElement(rdoc, multistatus); xmlNsPtr dav_ns = xmlNewNs(multistatus, BAD_CAST "DAV:", BAD_CAST "D"); xmlSetNs(multistatus, dav_ns); if (depth == 0) { char *uri = uwsgi_concat2n(wsgi_req->path_info, wsgi_req->path_info_len, "", 0); uwsgi_webdav_add_props(wsgi_req, req_prop, multistatus, dav_ns, uri, filename, with_values); free(uri); } else { DIR *collection = opendir(filename); struct dirent de; for (;;) { struct dirent *de_r = NULL; if (readdir_r(collection, &de, &de_r)) { uwsgi_error("uwsgi_wevdav_manage_propfind()/readdir_r()"); break; } if (de_r == NULL) { break; } char *uri = NULL; char *direntry = NULL; if (!strcmp(de.d_name, "..")) { // skip .. continue; } else if (!strcmp(de.d_name, ".")) { uri = uwsgi_concat2n(wsgi_req->path_info, wsgi_req->path_info_len, "", 0); direntry = uwsgi_concat2n(filename, filename_len, "", 0); } else if (wsgi_req->path_info[wsgi_req->path_info_len - 1] == '/') { uri = uwsgi_concat2n(wsgi_req->path_info, wsgi_req->path_info_len, de.d_name, strlen(de.d_name)); direntry = uwsgi_concat3n(filename, filename_len, "/", 1, de.d_name, strlen(de.d_name)); } else { uri = uwsgi_concat3n(wsgi_req->path_info, wsgi_req->path_info_len, "/", 1, de.d_name, strlen(de.d_name)); direntry = uwsgi_concat3n(filename, filename_len, "/", 1, de.d_name, strlen(de.d_name)); } uwsgi_webdav_add_props(wsgi_req, req_prop, multistatus, dav_ns, uri, direntry, with_values); free(uri); free(direntry); } closedir(collection); } return rdoc; }
static int uwsgi_ssh_request_file( struct wsgi_request *wsgi_req, char* filepath, struct uwsgi_ssh_mountpoint *usm ) { int sock = -1; int return_status = 0; LIBSSH2_SESSION *session = NULL; if (uwsgi_init_ssh_session(usm, &sock, &session)) { uwsgi_log("[SSH] session initialization failed. Is the SSH server up?\n"); return_status = 500; goto shutdown; } LIBSSH2_SFTP *sftp_session = NULL; do { sftp_session = libssh2_sftp_init(session); if (!sftp_session) { if ((libssh2_session_last_errno(session)) == LIBSSH2_ERROR_EAGAIN) { if (uwsgi_ssh_waitsocket(sock, session)) { return_status = 500; goto shutdown; } } else { uwsgi_error("uwsgi_ssh_request_file()/libssh2_sftp_init()"); return_status = 500; goto shutdown; } } } while (!sftp_session); // Request file stats via SFTP LIBSSH2_SFTP_ATTRIBUTES file_attrs; int rc; while ((rc = libssh2_sftp_stat(sftp_session, filepath, &file_attrs)) == LIBSSH2_ERROR_EAGAIN) { if (uwsgi_ssh_waitsocket(sock, session)) { return_status = 500; goto shutdown; } } if (rc < 0) { // If it fails, requested file could not exist. if (rc == LIBSSH2_ERROR_SFTP_PROTOCOL && libssh2_sftp_last_error(sftp_session) == LIBSSH2_FX_NO_SUCH_FILE) { return_status = 404; } else { uwsgi_error("uwsgi_ssh_request_file()/libssh2_sftp_stat()"); return_status = 500; } goto sftp_shutdown; } if (wsgi_req->if_modified_since_len) { time_t ims = uwsgi_parse_http_date(wsgi_req->if_modified_since, wsgi_req->if_modified_since_len); if (file_attrs.mtime <= (unsigned long)ims) { if (uwsgi_response_prepare_headers(wsgi_req, "304 Not Modified", 16) || uwsgi_response_write_headers_do(wsgi_req)) { uwsgi_error("uwsgi_parse_http_date()/uwsgi_response_prepare_headers(do)()"); } return_status = 500; goto sftp_shutdown; } } if (uwsgi_response_prepare_headers(wsgi_req, "200", 3)) { uwsgi_error("uwsgi_ssh_request_file()/uwsgi_response_prepare_headers()"); return_status = 500; goto sftp_shutdown; } if (uwsgi_response_add_content_length(wsgi_req, file_attrs.filesize)) { uwsgi_error("uwsgi_ssh_request_file()/uwsgi_response_add_content_length()"); return_status = 500; goto sftp_shutdown; } if (uwsgi_response_add_last_modified(wsgi_req, file_attrs.mtime)) { uwsgi_error("uwsgi_ssh_request_file()/uwsgi_response_add_last_modified()"); return_status = 500; goto sftp_shutdown; } size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(filepath, strlen(filepath), &mime_type_len); if (mime_type) { if (uwsgi_response_add_content_type(wsgi_req, mime_type, mime_type_len)) { uwsgi_error("uwsgi_ssh_request_file()/uwsgi_response_add_content_type()"); // goto sftp_shutdown; } } // Request a file via SFTP LIBSSH2_SFTP_HANDLE *sftp_handle = NULL; do { sftp_handle = libssh2_sftp_open(sftp_session, filepath, LIBSSH2_FXF_READ, 0); if (!sftp_handle) { if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { uwsgi_error("uwsgi_ssh_request_file()/libssh2_sftp_open()"); return_status = 500; goto sftp_shutdown; } else { if (uwsgi_ssh_waitsocket(sock, session)) { return_status = 500; goto sftp_shutdown; } } } } while (!sftp_handle); size_t buffer_size = uwsgi.page_size; void *buffer = alloca(buffer_size); libssh2_uint64_t read_size = 0; while (read_size < file_attrs.filesize) { rc = libssh2_sftp_read(sftp_handle, buffer, buffer_size); if (rc == LIBSSH2_ERROR_EAGAIN) { if (uwsgi_ssh_waitsocket(sock, session)) { return_status = 500; goto sftp_shutdown; } } else if (rc < 0) { uwsgi_error("uwsgi_ssh_request_file()/libssh2_sftp_read()"); break; } else { read_size += rc; if (uwsgi_response_write_body_do(wsgi_req, buffer, rc)) { uwsgi_error("uwsgi_ssh_request_file()/uwsgi_response_write_body_do()"); break; } } } while ((rc = libssh2_sftp_close(sftp_handle)) == LIBSSH2_ERROR_EAGAIN) { if (uwsgi_ssh_waitsocket(sock, session)) { return_status = 500; goto sftp_shutdown; } }; if (rc < 0) { uwsgi_error("uwsgi_ssh_request_file()/libssh2_sftp_close()"); } sftp_shutdown: while ((rc = libssh2_sftp_shutdown(sftp_session)) == LIBSSH2_ERROR_EAGAIN) { uwsgi_ssh_waitsocket(sock, session); } if (rc < 0) { uwsgi_error("uwsgi_ssh_request_file()/libssh2_sftp_shutdown()"); } shutdown: while (libssh2_session_disconnect(session, "Normal Shutdown, thank you!") == LIBSSH2_ERROR_EAGAIN) { uwsgi_ssh_waitsocket(sock, session); } libssh2_session_free(session); close(sock); libssh2_exit(); return return_status; }
static int uwsgi_rados_request(struct wsgi_request *wsgi_req) { char filename[PATH_MAX+1]; if (!wsgi_req->len) { uwsgi_log( "Empty request. skip.\n"); return -1; } if (uwsgi_parse_vars(wsgi_req)) { return -1; } // blocks empty paths if (wsgi_req->path_info_len == 0 || wsgi_req->path_info_len > PATH_MAX) { uwsgi_403(wsgi_req); return UWSGI_OK; } wsgi_req->app_id = uwsgi_get_app_id(wsgi_req, wsgi_req->appid, wsgi_req->appid_len, rados_plugin.modifier1); if (wsgi_req->app_id == -1 && !uwsgi.no_default_app && uwsgi.default_app > -1) { if (uwsgi_apps[uwsgi.default_app].modifier1 == rados_plugin.modifier1) { wsgi_req->app_id = uwsgi.default_app; } } if (wsgi_req->app_id == -1) { uwsgi_404(wsgi_req); return UWSGI_OK; } struct uwsgi_app *ua = &uwsgi_apps[wsgi_req->app_id]; if (wsgi_req->path_info_len > ua->mountpoint_len && memcmp(wsgi_req->path_info, ua->mountpoint, ua->mountpoint_len) == 0) { memcpy(filename, wsgi_req->path_info+ua->mountpoint_len, wsgi_req->path_info_len-ua->mountpoint_len); filename[wsgi_req->path_info_len-ua->mountpoint_len] = 0; } else { memcpy(filename, wsgi_req->path_info, wsgi_req->path_info_len); filename[wsgi_req->path_info_len] = 0; } // in multithread mode the memory is different (as we need a ctx for each thread) !!! rados_ioctx_t ctx; if (uwsgi.threads > 1) { rados_ioctx_t *ctxes = (rados_ioctx_t *) ua->responder0; ctx = ctxes[wsgi_req->async_id]; } else { ctx = (rados_ioctx_t) ua->responder0; } struct uwsgi_rados_mountpoint *urmp = (struct uwsgi_rados_mountpoint *) ua->responder1; uint64_t stat_size = 0; time_t stat_mtime = 0; struct uwsgi_rados_io *urio = &urados.urio[wsgi_req->async_id]; if (uwsgi.async > 0) { // no need to lock here (the rid protect us) if (pipe(urio->fds)) { uwsgi_error("uwsgi_rados_read_async()/pipe()"); uwsgi_500(wsgi_req); return UWSGI_OK; } } int ret = -1; int timeout = urmp->timeout ? urmp->timeout : urados.timeout; if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "OPTIONS", 7)) { if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) goto end; if (uwsgi_response_add_header(wsgi_req, "Dav", 3, "1", 1)) goto end; struct uwsgi_buffer *ub_allow = uwsgi_buffer_new(64); if (uwsgi_buffer_append(ub_allow, "OPTIONS, GET, HEAD", 18)) { uwsgi_buffer_destroy(ub_allow); goto end; } if (urmp->allow_put) { if (uwsgi_buffer_append(ub_allow, ", PUT", 5)) { uwsgi_buffer_destroy(ub_allow); goto end; } } if (urmp->allow_delete) { if (uwsgi_buffer_append(ub_allow, ", DELETE", 8)) { uwsgi_buffer_destroy(ub_allow); goto end; } } if (urmp->allow_mkcol) { if (uwsgi_buffer_append(ub_allow, ", MKCOL", 7)) { uwsgi_buffer_destroy(ub_allow); goto end; } } if (urmp->allow_propfind) { if (uwsgi_buffer_append(ub_allow, ", PROPFIND", 10)) { uwsgi_buffer_destroy(ub_allow); goto end; } } uwsgi_response_add_header(wsgi_req, "Allow", 5, ub_allow->buf, ub_allow->pos); uwsgi_buffer_destroy(ub_allow); goto end; } // empty paths are mapped to propfind if (wsgi_req->path_info_len == 1 && wsgi_req->path_info[0] == '/') { if (urmp->allow_propfind && !uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "PROPFIND", 8)) { uwsgi_rados_propfind(wsgi_req, ctx, NULL, 0, 0, timeout); goto end; } uwsgi_405(wsgi_req); goto end; } // MKCOL does not require stat if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "MKCOL", 5)) { if (!urmp->allow_mkcol) { uwsgi_405(wsgi_req); goto end; } ret = rados_pool_create(urmp->cluster, filename); if (ret < 0) { if (ret == -EEXIST) { uwsgi_405(wsgi_req); } else { uwsgi_500(wsgi_req); } goto end; } uwsgi_response_prepare_headers(wsgi_req, "201 Created", 11); goto end; } if (uwsgi.async > 0) { ret = uwsgi_rados_async_stat(urio, ctx, filename, &stat_size, &stat_mtime, timeout); } else { ret = rados_stat(ctx, filename, &stat_size, &stat_mtime); } // PUT AND MKCOL can be used for non-existent objects if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "PUT", 3)) { if (!urmp->allow_put) { uwsgi_405(wsgi_req); goto end; } if (ret == 0) { if (uwsgi_rados_delete(wsgi_req, ctx, filename, timeout)) { uwsgi_500(wsgi_req); goto end; } } if (uwsgi_rados_put(wsgi_req, ctx, filename, urmp->put_buffer_size, timeout)) { uwsgi_500(wsgi_req); goto end; } uwsgi_response_prepare_headers(wsgi_req, "201 Created", 11); goto end; } else if (ret < 0) { if (ret == -ENOENT) uwsgi_404(wsgi_req); else uwsgi_403(wsgi_req); goto end; } if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "DELETE", 6)) { if (!urmp->allow_delete) { uwsgi_405(wsgi_req); goto end; } if (uwsgi_rados_delete(wsgi_req, ctx, filename, timeout)) { uwsgi_403(wsgi_req); goto end; } uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6); goto end; } if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "PROPFIND", 8)) { if (!urmp->allow_propfind) { uwsgi_405(wsgi_req); goto end; } uwsgi_rados_propfind(wsgi_req, ctx, filename, stat_size, stat_mtime, timeout); goto end; } if (uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "HEAD", 4) && uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "GET", 3)) { uwsgi_405(wsgi_req); goto end; } uint64_t offset = 0; uint64_t remains = stat_size; uwsgi_request_fix_range_for_size(wsgi_req, remains); switch (wsgi_req->range_parsed) { case UWSGI_RANGE_INVALID: if (uwsgi_response_prepare_headers(wsgi_req, "416 Requested Range Not Satisfiable", 35)) goto end; if (uwsgi_response_add_content_range(wsgi_req, -1, -1, stat_size)) goto end; return 0; case UWSGI_RANGE_VALID: offset = wsgi_req->range_from; remains = wsgi_req->range_to - wsgi_req->range_from + 1; if (uwsgi_response_prepare_headers(wsgi_req, "206 Partial Content", 19)) goto end; break; default: /* UWSGI_RANGE_NOT_PARSED */ if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) return -1; } size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(wsgi_req->path_info, wsgi_req->path_info_len, &mime_type_len); if (mime_type) { if (uwsgi_response_add_content_type(wsgi_req, mime_type, mime_type_len)) goto end; } if (uwsgi_response_add_last_modified(wsgi_req, (uint64_t) stat_mtime)) goto end; // set Content-Length to actual result size if (uwsgi_response_add_content_length(wsgi_req, remains)) goto end; if (wsgi_req->range_parsed == UWSGI_RANGE_VALID) { // here use the original size !!! if (uwsgi_response_add_content_range(wsgi_req, wsgi_req->range_from, wsgi_req->range_to, stat_size)) goto end; } // skip body on HEAD if (uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "HEAD", 4)) { if (uwsgi.async > 0) { if (uwsgi_rados_read_async(wsgi_req, ctx, filename, offset, remains, urmp->buffer_size, timeout)) goto end; } else { if (uwsgi_rados_read_sync(wsgi_req, ctx, filename, offset, remains, urmp->buffer_size)) goto end; } } end: if (uwsgi.async > 0) { close(urio->fds[0]); close(urio->fds[1]); } return UWSGI_OK; }
static void uwsgi_rados_propfind(struct wsgi_request *wsgi_req, rados_ioctx_t ctx, char *key, uint64_t size, time_t mtime, int timeout) { // consume the body size_t remains = wsgi_req->post_cl; while(remains > 0) { ssize_t body_len = 0; char *body = uwsgi_request_body_read(wsgi_req, UMIN(remains, 32768), &body_len); if (!body || body == uwsgi.empty) break; remains -= body_len; } if (uwsgi_response_prepare_headers(wsgi_req, "207 Multi-Status", 16)) return; if (uwsgi_response_add_content_type(wsgi_req, "text/xml; charset=\"utf-8\"", 25)) return; struct uwsgi_buffer *ub = uwsgi_webdav_multistatus_new(); if (!ub) return; if (key) { size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(key, strlen(key), &mime_type_len); char *slashed = uwsgi_concat2("/", key); if (uwsgi_webdav_propfind_item_add(ub, slashed, strlen(key)+1, size, mtime, mime_type, mime_type_len, NULL, 0, NULL, 0)) { free(slashed); goto end; } free(slashed); if (uwsgi_webdav_multistatus_close(ub)) goto end; uwsgi_response_write_body_do(wsgi_req, ub->buf, ub->pos); goto end; } // request for / size_t depth = 0; uint16_t http_depth_len = 0; char *http_depth = uwsgi_get_var(wsgi_req, "HTTP_DEPTH", 10, &http_depth_len); if (http_depth) { depth = uwsgi_str_num(http_depth, http_depth_len); } if (depth == 0) { if (uwsgi_webdav_propfind_item_add(ub, "/", 1, 0, 0, NULL, 0, NULL, 0, NULL, 0)) { goto end; } if (uwsgi_webdav_multistatus_close(ub)) goto end; uwsgi_response_write_body_do(wsgi_req, ub->buf, ub->pos); goto end; } struct uwsgi_rados_io *urio = &urados.urio[wsgi_req->async_id]; rados_list_ctx_t ctx_list; if (rados_objects_list_open(ctx, &ctx_list) < 0) { goto end; } char *entry = NULL; while(rados_objects_list_next(ctx_list, (const char **)&entry, NULL) == 0) { uint64_t stat_size = 0; time_t stat_mtime = 0; if (uwsgi.async > 0) { if (uwsgi_rados_async_stat(urio, ctx, entry, &stat_size, &stat_mtime, timeout) < 0) goto end; } else { if (rados_stat(ctx, entry, &stat_size, &stat_mtime) < 0) goto end; } size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(entry, strlen(entry), &mime_type_len); char *slashed = uwsgi_concat2("/", entry); if (uwsgi_webdav_propfind_item_add(ub, slashed, strlen(entry)+1, stat_size, stat_mtime, mime_type, mime_type_len, NULL, 0, NULL, 0)) { free(slashed); goto end; } free(slashed); if (uwsgi_response_write_body_do(wsgi_req, ub->buf, ub->pos)) goto end; // reset buffer; ub->pos = 0; } rados_objects_list_close(ctx_list); if (uwsgi_webdav_multistatus_close(ub)) goto end; uwsgi_response_write_body_do(wsgi_req, ub->buf, ub->pos); end: uwsgi_buffer_destroy(ub); }
int uwsgi_real_file_serve(struct wsgi_request *wsgi_req, char *real_filename, size_t real_filename_len, struct stat *st) { size_t mime_type_size = 0; char http_last_modified[49]; if (uwsgi.threads > 1) pthread_mutex_lock(&uwsgi.lock_static); char *mime_type = uwsgi_get_mime_type(real_filename, real_filename_len, &mime_type_size); if (uwsgi.threads > 1) pthread_mutex_unlock(&uwsgi.lock_static); 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) { uwsgi_response_prepare_headers(wsgi_req, "304 Not Modified", 16); return uwsgi_response_write_headers_do(wsgi_req); } } #ifdef UWSGI_DEBUG 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 (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); uwsgi_add_expires_path_info(wsgi_req, st); uwsgi_add_expires_uri(wsgi_req, st); #endif // Content-Type (if available) if (mime_type_size > 0 && mime_type) { if (uwsgi_response_add_content_type(wsgi_req, mime_type, mime_type_size)) return -1; // check for content-type related headers uwsgi_add_expires_type(wsgi_req, mime_type, mime_type_size, st); } // increase static requests counter uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].static_requests++; // nginx if (uwsgi.file_serve_mode == 1) { if (uwsgi_response_add_header(wsgi_req, "X-Accel-Redirect", 16, real_filename, real_filename_len)) return -1; // this is the final header (\r\n added) 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; } // apache else if (uwsgi.file_serve_mode == 2) { if (uwsgi_response_add_header(wsgi_req, "X-Sendfile", 10, real_filename, real_filename_len)) return -1; // this is the final header (\r\n added) 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; } // raw else { // here we need to choose if we want the gzip variant; 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 (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; // if it is a HEAD request just skip transfer if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "HEAD", 4)) { wsgi_req->status = 200; return 0; } // Ok, the file must be transferred from uWSGI // offloading will be automatically managed 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, wsgi_req->range_from, fsize); } wsgi_req->status = 200; return 0; }
static int uwsgi_rados_request(struct wsgi_request *wsgi_req) { char filename[PATH_MAX+1]; if (!wsgi_req->uh->pktsize) { uwsgi_log( "Empty request. skip.\n"); return -1; } if (uwsgi_parse_vars(wsgi_req)) { return -1; } // blocks empty paths if (wsgi_req->path_info_len == 0 || wsgi_req->path_info_len > PATH_MAX) { uwsgi_403(wsgi_req); return UWSGI_OK; } wsgi_req->app_id = uwsgi_get_app_id(wsgi_req, wsgi_req->appid, wsgi_req->appid_len, rados_plugin.modifier1); if (wsgi_req->app_id == -1 && !uwsgi.no_default_app && uwsgi.default_app > -1) { if (uwsgi_apps[uwsgi.default_app].modifier1 == rados_plugin.modifier1) { wsgi_req->app_id = uwsgi.default_app; } } if (wsgi_req->app_id == -1) { uwsgi_404(wsgi_req); return UWSGI_OK; } struct uwsgi_app *ua = &uwsgi_apps[wsgi_req->app_id]; if (wsgi_req->path_info_len > ua->mountpoint_len && memcmp(wsgi_req->path_info, ua->mountpoint, ua->mountpoint_len) == 0) { memcpy(filename, wsgi_req->path_info+ua->mountpoint_len, wsgi_req->path_info_len-ua->mountpoint_len); } else { memcpy(filename, wsgi_req->path_info, wsgi_req->path_info_len); } filename[wsgi_req->path_info_len] = 0; struct { uint64_t size; time_t mtime; } st; rados_ioctx_t ctx = ua->responder1; int r = rados_stat(ctx, filename, &st.size, &st.mtime); if (r < 0) { if (r == -ENOENT) uwsgi_404(wsgi_req); else uwsgi_403(wsgi_req); return UWSGI_OK; } if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) goto end; size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(wsgi_req->path_info, wsgi_req->path_info_len, &mime_type_len); if (mime_type) { if (uwsgi_response_add_content_type(wsgi_req, mime_type, mime_type_len)) goto end; } if (uwsgi_response_add_last_modified(wsgi_req, (uint64_t) st.mtime)) goto end; if (uwsgi_response_add_content_length(wsgi_req, st.size)) goto end; // skip body on HEAD if (uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "HEAD", 4)) { size_t remains = st.size; if (uwsgi.async > 1) { if (uwsgi_rados_read_async(wsgi_req, ctx, filename, remains)) goto end; } else { if (uwsgi_rados_read_sync(wsgi_req, ctx, filename, remains)) goto end; } } end: return UWSGI_OK; }