From 37ccff581dcab1f2c602dc404e8e4ba7b19ca63a Mon Sep 17 00:00:00 2001 From: Unbit Date: Sat, 28 Jun 2014 09:40:05 +0200 Subject: [PATCH] completed virtual propfind support in rados --- core/webdav.c | 19 +++++--- plugins/rados/rados.c | 102 ++++++++++++++++++++++++++++++++++++++++++ uwsgi.h | 9 ++++ 3 files changed, 123 insertions(+), 7 deletions(-) diff --git a/core/webdav.c b/core/webdav.c index 3691cd4a..4c3f2f65 100644 --- a/core/webdav.c +++ b/core/webdav.c @@ -17,7 +17,7 @@ struct uwsgi_buffer *uwsgi_webdav_multistatus_new() { struct uwsgi_buffer *ub = uwsgi_buffer_new(uwsgi.page_size); if (uwsgi_buffer_append(ub, "\n", 40)) goto error; if (uwsgi_buffer_append(ub, "\n", 31)) goto error; - + return ub; error: uwsgi_buffer_destroy(ub); return NULL; @@ -63,19 +63,25 @@ int uwsgi_webdav_propfind_item_add(struct uwsgi_buffer *ub, char *href, uint16_t if (uwsgi_webdav_multistatus_response_new(ub)) return -1; if (uwsgi_buffer_append(ub, "", 8)) return -1; if (uwsgi_buffer_append(ub, href, href_len)) return -1; + if (uwsgi_buffer_append(ub, "\n", 10)) return -1; if (uwsgi_webdav_multistatus_propstat_new(ub)) return -1; if (uwsgi_webdav_multistatus_prop_new(ub)) return -1; - // getcontentlength - if (uwsgi_buffer_append(ub, "", 20)) return -1; - if (uwsgi_buffer_num64(ub, cl)) return -1; - if (uwsgi_buffer_append(ub, "", 21)) return -1; + if (href[href_len-1] == '/') { + if (uwsgi_buffer_append(ub, "\n", 49)) return -1; + } + else { + // getcontentlength + if (uwsgi_buffer_append(ub, "", 20)) return -1; + if (uwsgi_buffer_num64(ub, cl)) return -1; + if (uwsgi_buffer_append(ub, "\n", 22)) return -1; + } // getlastmodified if (mtime > 0) { if (uwsgi_buffer_append(ub, "", 19)) return -1; if (uwsgi_buffer_httpdate(ub, mtime)) return -1; - if (uwsgi_buffer_append(ub, "", 20)) return -1; + if (uwsgi_buffer_append(ub, "\n", 21)) return -1; } // displayname @@ -99,7 +105,6 @@ int uwsgi_webdav_propfind_item_add(struct uwsgi_buffer *ub, char *href, uint16_t if (uwsgi_webdav_multistatus_prop_close(ub)) return -1; if (uwsgi_webdav_multistatus_propstat_close(ub)) return -1; - if (uwsgi_buffer_append(ub, "\n", 10)) return -1; if (uwsgi_webdav_multistatus_response_close(ub)) return -1; return 0; } diff --git a/plugins/rados/rados.c b/plugins/rados/rados.c index b9b81a27..27cfddd5 100644 --- a/plugins/rados/rados.c +++ b/plugins/rados/rados.c @@ -46,6 +46,7 @@ struct uwsgi_rados_mountpoint { char *allow_put; char *allow_delete; char *allow_mkcol; + char *allow_propfind; }; static struct uwsgi_option uwsgi_rados_options[] = { @@ -313,6 +314,77 @@ static int uwsgi_rados_read_async(struct wsgi_request *wsgi_req, rados_ioctx_t c return ret; } +static void uwsgi_rados_propfind(struct wsgi_request *wsgi_req, rados_ioctx_t ctx, char *key, uint64_t size, time_t mtime, int timeout) { + 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 > 1) { + 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); +} static void uwsgi_rados_add_mountpoint(char *arg, size_t arg_len) { struct uwsgi_rados_mountpoint *urmp = uwsgi_calloc(sizeof(struct uwsgi_rados_mountpoint)); @@ -324,6 +396,7 @@ static void uwsgi_rados_add_mountpoint(char *arg, size_t arg_len) { "allow_put", &urmp->allow_put, "allow_delete", &urmp->allow_delete, "allow_mkcol", &urmp->allow_mkcol, + "allow_propfind", &urmp->allow_propfind, NULL)) { uwsgi_log("unable to parse rados mountpoint definition\n"); exit(1); @@ -538,12 +611,32 @@ static int uwsgi_rados_request(struct wsgi_request *wsgi_req) { 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 == 0) { + if (!urmp->allow_propfind) { + goto nopropfind; + } + if (!uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "GET", 3) || !uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "PROPFIND", 8)) { + uwsgi_rados_propfind(wsgi_req, ctx, NULL, 0, 0, timeout); + goto end; + } +nopropfind: + 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) { @@ -611,6 +704,15 @@ static int uwsgi_rados_request(struct wsgi_request *wsgi_req) { 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; diff --git a/uwsgi.h b/uwsgi.h index feeb8209..e0b630ca 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -4821,6 +4821,15 @@ void uwsgi_emperor_ini_attrs(char *, char *, struct uwsgi_dyn_dict **); int uwsgi_buffer_httpdate(struct uwsgi_buffer *, time_t); int uwsgi_buffer_append_xml(struct uwsgi_buffer *, char *, size_t); +struct uwsgi_buffer *uwsgi_webdav_multistatus_new(); +int uwsgi_webdav_propfind_item_add(struct uwsgi_buffer *, char *, uint16_t, uint64_t, time_t, char *, uint16_t, char *, uint16_t, char *, uint16_t); +int uwsgi_webdav_multistatus_close(struct uwsgi_buffer *); +int uwsgi_webdav_multistatus_response_new(struct uwsgi_buffer *); +int uwsgi_webdav_multistatus_response_close(struct uwsgi_buffer *); +int uwsgi_webdav_multistatus_propstat_new(struct uwsgi_buffer *); +int uwsgi_webdav_multistatus_propstat_close(struct uwsgi_buffer *); +int uwsgi_webdav_multistatus_prop_new(struct uwsgi_buffer *); +int uwsgi_webdav_multistatus_prop_close(struct uwsgi_buffer *); #ifdef __cplusplus } #endif