From 29ce8994f2e1c618e2cab4997e0b5718c9ef4609 Mon Sep 17 00:00:00 2001 From: Andrew Bettison Date: Fri, 20 Oct 2017 09:55:21 +1030 Subject: [PATCH] Improve HTTP Content-Type handling Undo a change from late 2015 that allowed the format=text+binarysig parameter of the rhizome/manifest MIME content-type to be optional -- it is easy to provide that parameter via libcurl, and the lbard code has just been updated to provide it. Make Content-Type handling consistent throughout the Serval DNA code by using 'struct mime_content_type' to represent both parsed (request) and sent (response) Content-Type fields, replacing ad-hoc in-line string literals. Fix some TODOs by adding the "serval/sid", "rhizome/bid" and "rhizome/bundlesecret" MIME types, which remain optional for the time being to preserve REST API compatibility with clients that do not set the Content-Type of their 'bundle-author', 'bundle-id' and 'bundle-secret' parameters. --- http_server.c | 71 +++++++++++++++++++++++++------------------ http_server.h | 19 +++++++----- httpd.c | 21 +++++++++++++ httpd.h | 9 +++++- keyring_restful.c | 4 +-- meshmb_restful.c | 8 ++--- meshms_restful.c | 8 ++--- rhizome_direct_http.c | 11 +++---- rhizome_http.c | 6 ++-- rhizome_restful.c | 48 +++++++++++++++++------------ server_httpd.c | 11 ++++--- strbuf_helpers.c | 15 +++++++-- strbuf_helpers.h | 7 +++++ 13 files changed, 152 insertions(+), 86 deletions(-) diff --git a/http_server.c b/http_server.c index 8ada1954..7ab55577 100644 --- a/http_server.c +++ b/http_server.c @@ -71,10 +71,19 @@ static struct { #undef VERB_ENTRY }; -const char CONTENT_TYPE_TEXT[] = "text/plain"; -const char CONTENT_TYPE_HTML[] = "text/html"; -const char CONTENT_TYPE_JSON[] = "application/json"; -const char CONTENT_TYPE_BLOB[] = "application/octet-stream"; +int mime_content_types_are_equal(const struct mime_content_type *a, const struct mime_content_type *b) { + return strcmp(a->type, b->type) == 0 + && strcmp(a->subtype, b->subtype) == 0 + && strcmp(a->multipart_boundary, b->multipart_boundary) == 0 + && strcmp(a->charset, b->charset) == 0 + && strcmp(a->format, b->format) == 0; +} + +const struct mime_content_type CONTENT_TYPE_FAVICON = { .type = "image", .subtype = "vnd.microsoft.icon" }; +const struct mime_content_type CONTENT_TYPE_TEXT = { .type = "text", .subtype = "plain", .charset = "utf-8" }; +const struct mime_content_type CONTENT_TYPE_HTML = { .type = "text", .subtype = "html", .charset = "utf-8" }; +const struct mime_content_type CONTENT_TYPE_JSON = { .type = "application", .subtype = "json" }; +const struct mime_content_type CONTENT_TYPE_BLOB = { .type = "application", .subtype = "octet-stream" }; static struct profile_total http_server_stats = { .name = "http_server_poll", @@ -2212,10 +2221,12 @@ static const char *http_reason_phrase(int response_code) static strbuf strbuf_status_body(strbuf sb, struct http_response *hr) { - if ( hr->header.content_type == CONTENT_TYPE_TEXT - || (hr->header.content_type && strcmp(hr->header.content_type, CONTENT_TYPE_TEXT) == 0) + if ( hr->header.content_type == &CONTENT_TYPE_TEXT + || ( hr->header.content_type + && strcmp(hr->header.content_type->type, CONTENT_TYPE_TEXT.type) == 0 + && strcmp(hr->header.content_type->subtype, CONTENT_TYPE_TEXT.subtype) == 0) ) { - hr->header.content_type = CONTENT_TYPE_TEXT; + hr->header.content_type = &CONTENT_TYPE_TEXT; strbuf_sprintf(sb, "%03u %s", hr->status_code, hr->reason); unsigned i; for (i = 0; i < NELS(hr->result_extra); ++i) @@ -2227,10 +2238,12 @@ static strbuf strbuf_status_body(strbuf sb, struct http_response *hr) } strbuf_puts(sb, "\r\n"); } - else if ( hr->header.content_type == CONTENT_TYPE_JSON - || (hr->header.content_type && strcmp(hr->header.content_type, CONTENT_TYPE_JSON) == 0) + else if ( hr->header.content_type == &CONTENT_TYPE_JSON + || ( hr->header.content_type + && strcmp(hr->header.content_type->type, CONTENT_TYPE_JSON.type) == 0 + && strcmp(hr->header.content_type->subtype, CONTENT_TYPE_JSON.subtype) == 0) ) { - hr->header.content_type = CONTENT_TYPE_JSON; + hr->header.content_type = &CONTENT_TYPE_JSON; strbuf_sprintf(sb, "{\n \"http_status_code\": %u,\n \"http_status_message\": ", hr->status_code); strbuf_json_string(sb, hr->reason); unsigned i; @@ -2244,7 +2257,7 @@ static strbuf strbuf_status_body(strbuf sb, struct http_response *hr) strbuf_puts(sb, "\n}"); } else { - hr->header.content_type = CONTENT_TYPE_HTML; + hr->header.content_type = &CONTENT_TYPE_HTML; strbuf_sprintf(sb, "\n

%03u %s

", hr->status_code, hr->reason); strbuf_puts(sb, "\n
"); unsigned i; @@ -2323,18 +2336,13 @@ static int _render_response(struct http_request *r) hr.header.content_range_start = 0; } assert(hr.header.content_type != NULL); - assert(hr.header.content_type[0]); + assert(hr.header.content_type->type[0]); + assert(hr.header.content_type->subtype[0]); strbuf_sprintf(sb, "HTTP/1.%d %03u %s\r\n", hr.header.minor_version, hr.status_code, hr.reason); strbuf_puts(sb, "Connection: Close\r\n"); strbuf_sprintf(sb, "Server: servald %s\r\n", version_servald); - strbuf_sprintf(sb, "Content-Type: %s", hr.header.content_type); - if (hr.header.boundary) { - strbuf_puts(sb, "; boundary="); - if (strchr(hr.header.boundary, '"') || strchr(hr.header.boundary, '\\')) - strbuf_append_quoted_string(sb, hr.header.boundary); - else - strbuf_puts(sb, hr.header.boundary); - } + strbuf_puts(sb, "Content-Type: "); + strbuf_append_mime_content_type(sb, hr.header.content_type); strbuf_puts(sb, "\r\n"); if (hr.status_code == 206) { // Must only use result code 206 (Partial Content) if the content is in fact less than the whole @@ -2439,7 +2447,8 @@ static void http_request_start_response(struct http_request *r) _release_reserved(r); if (r->response.content || r->response.content_generator) { assert(r->response.header.content_type != NULL); - assert(r->response.header.content_type[0]); + assert(r->response.header.content_type->type[0]); + assert(r->response.header.content_type->subtype[0]); } // If HTTP responses are disabled (eg, for testing purposes) then skip all response construction // and close the connection. @@ -2480,19 +2489,20 @@ static void http_request_start_response(struct http_request *r) } /* Start sending a static (pre-computed) response back to the client. The response's Content-Type - * is set by the 'mime_type' parameter (in the standard format "type/subtype"). The response's + * is set by the 'content_type' parameter (in the standard format "type/subtype"). The response's * content is set from the 'body' and 'bytes' parameters, which need not point to persistent data, * ie, the memory pointed to by 'body' is no longer referenced once this function returns. * * @author Andrew Bettison */ -void http_request_response_static(struct http_request *r, int result, const char *mime_type, const char *body, uint64_t bytes) +void http_request_response_static(struct http_request *r, int result, const struct mime_content_type *content_type, const char *body, uint64_t bytes) { assert(r->phase == RECEIVE); - assert(mime_type != NULL); - assert(mime_type[0]); + assert(content_type != NULL); + assert(content_type->type[0]); + assert(content_type->subtype[0]); r->response.status_code = result; - r->response.header.content_type = mime_type; + r->response.header.content_type = content_type; r->response.header.content_range_start = 0; r->response.header.content_length = r->response.header.resource_length = bytes; r->response.content = body; @@ -2500,13 +2510,14 @@ void http_request_response_static(struct http_request *r, int result, const char http_request_start_response(r); } -void http_request_response_generated(struct http_request *r, int result, const char *mime_type, HTTP_CONTENT_GENERATOR generator) +void http_request_response_generated(struct http_request *r, int result, const struct mime_content_type *content_type, HTTP_CONTENT_GENERATOR generator) { assert(r->phase == RECEIVE); - assert(mime_type != NULL); - assert(mime_type[0]); + assert(content_type != NULL); + assert(content_type->type[0]); + assert(content_type->subtype[0]); r->response.status_code = result; - r->response.header.content_type = mime_type; + r->response.header.content_type = content_type; r->response.content = NULL; r->response.content_generator = generator; http_request_start_response(r); diff --git a/http_server.h b/http_server.h index 0d7496a6..3f54c950 100644 --- a/http_server.h +++ b/http_server.h @@ -60,11 +60,6 @@ http_size_t http_range_bytes(const struct http_range *range, unsigned nranges); #define CONTENT_LENGTH_UNKNOWN UINT64_MAX -extern const char CONTENT_TYPE_TEXT[]; -extern const char CONTENT_TYPE_HTML[]; -extern const char CONTENT_TYPE_JSON[]; -extern const char CONTENT_TYPE_BLOB[]; - struct mime_content_type { char type[64]; char subtype[64]; @@ -73,6 +68,14 @@ struct mime_content_type { char format[31]; }; +int mime_content_types_are_equal(const struct mime_content_type *a, const struct mime_content_type *b); + +extern const struct mime_content_type CONTENT_TYPE_FAVICON; +extern const struct mime_content_type CONTENT_TYPE_TEXT; +extern const struct mime_content_type CONTENT_TYPE_HTML; +extern const struct mime_content_type CONTENT_TYPE_JSON; +extern const struct mime_content_type CONTENT_TYPE_BLOB; + struct http_client_authorization { enum http_authorization_scheme { NOAUTH = 0, BASIC } scheme; union { @@ -111,7 +114,7 @@ struct http_response_headers { http_size_t content_length; http_size_t content_range_start; // range_end = range_start + content_length - 1 http_size_t resource_length; // size of entire resource - const char *content_type; // "type/subtype" + const struct mime_content_type *content_type; // one of the CONTENT_TYPE_ consts declared above const char *boundary; struct http_origin allow_origin; const char *allow_methods; @@ -177,8 +180,8 @@ int http_request_set_response_bufsize(struct http_request *r, size_t bufsiz); void http_request_finalise(struct http_request *r); void http_request_pause_response(struct http_request *r, time_ms_t until); void http_request_resume_response(struct http_request *r); -void http_request_response_static(struct http_request *r, int result, const char *mime_type, const char *body, uint64_t bytes); -void http_request_response_generated(struct http_request *r, int result, const char *mime_type, HTTP_CONTENT_GENERATOR *); +void http_request_response_static(struct http_request *r, int result, const struct mime_content_type *content_type, const char *body, uint64_t bytes); +void http_request_response_generated(struct http_request *r, int result, const struct mime_content_type *content_type, HTTP_CONTENT_GENERATOR *); void http_request_simple_response(struct http_request *r, uint16_t result, const char *body); typedef int (HTTP_CONTENT_GENERATOR_STRBUF_CHUNKER)(struct http_request *, strbuf); diff --git a/httpd.c b/httpd.c index 1708421c..f4899916 100644 --- a/httpd.c +++ b/httpd.c @@ -27,6 +27,27 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #define RHIZOME_SERVER_MAX_LIVE_REQUESTS 32 +const struct mime_content_type CONTENT_TYPE_SID_HEX = { + .type = "serval", + .subtype = "sid", + .format = "hex" +}; +const struct mime_content_type CONTENT_TYPE_RHIZOME_BUNDLE_ID = { + .type = "rhizome", + .subtype = "bid", + .format = "hex" +}; +const struct mime_content_type CONTENT_TYPE_RHIZOME_BUNDLE_SECRET = { + .type = "rhizome", + .subtype = "bundlesecret", + .format = "hex" +}; +const struct mime_content_type CONTENT_TYPE_RHIZOME_MANIFEST = { + .type = "rhizome", + .subtype = "manifest", + .format = "text+binarysig" +}; + static int httpd_dispatch(struct http_request *); static unsigned int http_request_uuid_counter = 0; static httpd_request * current_httpd_requests = NULL; diff --git a/httpd.h b/httpd.h index 0ffac1e2..b13ee580 100644 --- a/httpd.h +++ b/httpd.h @@ -1,6 +1,7 @@ /* -Serval DNA Rhizome HTTP interface +Serval DNA HTTP interface - common definitions Copyright (C) 2013-2014 Serval Project Inc. +Copyright (C) 2017 Flinders University This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -33,6 +34,12 @@ int is_httpd_server_running(); extern uint16_t httpd_server_port; extern unsigned int current_httpd_request_count; +// Some non-standard MIME types for the Content-Type header +extern const struct mime_content_type CONTENT_TYPE_SID_HEX; +extern const struct mime_content_type CONTENT_TYPE_RHIZOME_BUNDLE_ID; +extern const struct mime_content_type CONTENT_TYPE_RHIZOME_BUNDLE_SECRET; +extern const struct mime_content_type CONTENT_TYPE_RHIZOME_MANIFEST; + enum list_phase { LIST_HEADER = 0, LIST_FIRST, LIST_ROWS, LIST_END, LIST_DONE }; struct form_buf_malloc { diff --git a/keyring_restful.c b/keyring_restful.c index 17f8da21..2639cef9 100644 --- a/keyring_restful.c +++ b/keyring_restful.c @@ -39,7 +39,7 @@ static HTTP_HANDLER restful_keyring_set; static int restful_keyring_(httpd_request *r, const char *remainder) { - r->http.response.header.content_type = CONTENT_TYPE_JSON; + r->http.response.header.content_type = &CONTENT_TYPE_JSON; int ret = authorize_restful(&r->http); if (ret) return ret; @@ -134,7 +134,7 @@ static int restful_keyring_identitylist_json(httpd_request *r, const char *remai keyring_enter_pin(keyring, pin); r->u.sidlist.phase = LIST_HEADER; keyring_iterator_start(keyring, &r->u.sidlist.it); - http_request_response_generated(&r->http, 200, CONTENT_TYPE_JSON, restful_keyring_identitylist_json_content); + http_request_response_generated(&r->http, 200, &CONTENT_TYPE_JSON, restful_keyring_identitylist_json_content); return 1; } diff --git a/meshmb_restful.c b/meshmb_restful.c index 69bc7aed..475a5e1a 100644 --- a/meshmb_restful.c +++ b/meshmb_restful.c @@ -435,7 +435,7 @@ static int restful_meshmb_list(httpd_request *r, const char *remainder) r->u.plylist.rowcount = 0; r->u.plylist.end_offset = r->ui64; - http_request_response_generated(&r->http, 200, CONTENT_TYPE_JSON, restful_meshmb_list_json_content); + http_request_response_generated(&r->http, 200, &CONTENT_TYPE_JSON, restful_meshmb_list_json_content); return 1; } @@ -673,7 +673,7 @@ static int restful_meshmb_feedlist(httpd_request *r, const char *remainder) r->u.meshmb_feeds.generation = meshmb_flush(session->feeds); bzero(&r->u.meshmb_feeds.bundle_id, sizeof r->u.meshmb_feeds.bundle_id); - http_request_response_generated(&r->http, 200, CONTENT_TYPE_JSON, restful_meshmb_feedlist_json_content); + http_request_response_generated(&r->http, 200, &CONTENT_TYPE_JSON, restful_meshmb_feedlist_json_content); return 1; } @@ -878,14 +878,14 @@ static int restful_meshmb_activity(httpd_request *r, const char *remainder) r->u.meshmb_feeds.current_msg_offset = 0; bzero(&r->u.meshmb_feeds.bundle_id, sizeof r->u.meshmb_feeds.bundle_id); - http_request_response_generated(&r->http, 200, CONTENT_TYPE_JSON, restful_meshmb_activity_json_content); + http_request_response_generated(&r->http, 200, &CONTENT_TYPE_JSON, restful_meshmb_activity_json_content); return 1; } DECLARE_HANDLER("/restful/meshmb/", restful_meshmb_); static int restful_meshmb_(httpd_request *r, const char *remainder) { - r->http.response.header.content_type = CONTENT_TYPE_JSON; + r->http.response.header.content_type = &CONTENT_TYPE_JSON; if (!is_rhizome_http_enabled()) return 404; int ret = authorize_restful(&r->http); diff --git a/meshms_restful.c b/meshms_restful.c index 46ebb825..8a63ebfb 100644 --- a/meshms_restful.c +++ b/meshms_restful.c @@ -150,7 +150,7 @@ static HTTP_HANDLER restful_meshms_read_to_offset; static int restful_meshms_(httpd_request *r, const char *remainder) { - r->http.response.header.content_type = CONTENT_TYPE_JSON; + r->http.response.header.content_type = &CONTENT_TYPE_JSON; if (!is_rhizome_http_enabled()) return 404; int ret = authorize_restful(&r->http); @@ -246,7 +246,7 @@ static int restful_meshms_conversationlist_json(httpd_request *r, const char *re return http_request_meshms_response(r, 0, NULL, status); if (r->u.mclist.conv != NULL) meshms_conversation_iterator_start(&r->u.mclist.iter, r->u.mclist.conv); - http_request_response_generated(&r->http, 200, CONTENT_TYPE_JSON, restful_meshms_conversationlist_json_content); + http_request_response_generated(&r->http, 200, &CONTENT_TYPE_JSON, restful_meshms_conversationlist_json_content); return 1; } @@ -363,7 +363,7 @@ static int restful_meshms_messagelist_json(httpd_request *r, const char *remaind enum meshms_status status; if (meshms_failed(status = reopen_meshms_message_iterator(r))) return http_request_meshms_response(r, 0, NULL, status); - http_request_response_generated(&r->http, 200, CONTENT_TYPE_JSON, restful_meshms_messagelist_json_content); + http_request_response_generated(&r->http, 200, &CONTENT_TYPE_JSON, restful_meshms_messagelist_json_content); return 1; } @@ -381,7 +381,7 @@ static int restful_meshms_newsince_messagelist_json(httpd_request *r, const char if (meshms_failed(status = reopen_meshms_message_iterator(r))) return http_request_meshms_response(r, 0, NULL, status); r->u.msglist.end_time = gettime_ms() + config.api.restful.newsince_timeout * 1000; - http_request_response_generated(&r->http, 200, CONTENT_TYPE_JSON, restful_meshms_messagelist_json_content); + http_request_response_generated(&r->http, 200, &CONTENT_TYPE_JSON, restful_meshms_messagelist_json_content); return 1; } diff --git a/rhizome_direct_http.c b/rhizome_direct_http.c index eee27f36..afa769bb 100644 --- a/rhizome_direct_http.c +++ b/rhizome_direct_http.c @@ -94,7 +94,7 @@ static void http_request_rhizome_bundle_status_response(httpd_request *r, struct break; } if (m) - http_request_response_static(&r->http, http_status, CONTENT_TYPE_TEXT, (const char *)m->manifestdata, m->manifest_all_bytes); + http_request_response_static(&r->http, http_status, &CONTENT_TYPE_TEXT, (const char *)m->manifestdata, m->manifest_all_bytes); else http_request_simple_response(&r->http, http_status, rhizome_bundle_result_message(result)); } @@ -188,7 +188,7 @@ int rhizome_direct_enquiry_end(struct http_request *hr) if (http_request_set_response_bufsize(&r->http, bytes) == -1) http_request_simple_response(&r->http, 500, "Internal Error: Out of memory"); else - http_request_response_static(&r->http, 200, "binary/octet-stream", (const char *)c->buffer, bytes); + http_request_response_static(&r->http, 200, &CONTENT_TYPE_BLOB, (const char *)c->buffer, bytes); rhizome_direct_bundle_iterator_free(&c); } else http_request_simple_response(&r->http, 500, "Internal Error: No response to enquiry"); @@ -504,10 +504,9 @@ void rhizome_direct_http_dispatch(rhizome_direct_sync_request *r) strbuf_sprintf(content_preamble, "--%s\r\n" "Content-Disposition: form-data; name=\"data\"; filename=\"IHAVEs\"\r\n" - "Content-Type: %s\r\n" - "\r\n", - boundary, CONTENT_TYPE_BLOB - ); + "Content-Type: ", boundary); + strbuf_append_mime_content_type(content_preamble, &CONTENT_TYPE_BLOB); + strbuf_puts(content_preamble, "\r\n\r\n"); strbuf_sprintf(content_postamble, "\r\n--%s--\r\n", boundary); assert(!strbuf_overrun(content_preamble)); assert(!strbuf_overrun(content_postamble)); diff --git a/rhizome_http.c b/rhizome_http.c index c8bb1c8e..3c43ae42 100644 --- a/rhizome_http.c +++ b/rhizome_http.c @@ -50,7 +50,7 @@ static int rhizome_file_page(httpd_request *r, const char *remainder) return ret; // backwards compatibility, rhizome_fetch used to allow HTTP/1.0 responses only r->http.response.header.minor_version=0; - http_request_response_generated(&r->http, 200, CONTENT_TYPE_BLOB, rhizome_payload_content); + http_request_response_generated(&r->http, 200, &CONTENT_TYPE_BLOB, rhizome_payload_content); return 1; } @@ -71,7 +71,7 @@ static int manifest_by_prefix_page(httpd_request *r, const char *remainder) case RHIZOME_BUNDLE_STATUS_SAME: // backwards compatibility, rhizome_fetch used to allow HTTP/1.0 responses only r->http.response.header.minor_version=0; - http_request_response_static(&r->http, 200, CONTENT_TYPE_BLOB, (const char *)r->manifest->manifestdata, r->manifest->manifest_all_bytes); + http_request_response_static(&r->http, 200, &CONTENT_TYPE_BLOB, (const char *)r->manifest->manifestdata, r->manifest->manifest_all_bytes); return 1; case RHIZOME_BUNDLE_STATUS_NEW: return 404; @@ -97,6 +97,6 @@ static int rhizome_status_page(httpd_request *r, const char *remainder) strbuf_puts(b, ""); if (strbuf_overrun(b)) return -1; - http_request_response_static(&r->http, 200, CONTENT_TYPE_HTML, buf, strbuf_len(b)); + http_request_response_static(&r->http, 200, &CONTENT_TYPE_HTML, buf, strbuf_len(b)); return 1; } diff --git a/rhizome_restful.c b/rhizome_restful.c index 0bb76481..47820931 100644 --- a/rhizome_restful.c +++ b/rhizome_restful.c @@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "serval.h" +#include "rhizome.h" #include "conf.h" #include "httpd.h" #include "str.h" @@ -226,13 +227,13 @@ static int restful_open_cursor(httpd_request *r) if (ret == -1) return http_request_rhizome_response(r, 500, "Failed to open list"); - http_request_response_generated(&r->http, 200, CONTENT_TYPE_JSON, restful_rhizome_bundlelist_json_content); + http_request_response_generated(&r->http, 200, &CONTENT_TYPE_JSON, restful_rhizome_bundlelist_json_content); return 1; } static int restful_rhizome_bundlelist_json(httpd_request *r, const char *remainder) { - r->http.response.header.content_type = CONTENT_TYPE_JSON; + r->http.response.header.content_type = &CONTENT_TYPE_JSON; r->http.render_extra_headers = render_manifest_headers; if (!is_rhizome_http_enabled()) return 404; @@ -259,7 +260,7 @@ static int restful_rhizome_bundlelist_json_content(struct http_request *hr, unsi static int restful_rhizome_newsince(httpd_request *r, const char *remainder) { - r->http.response.header.content_type = CONTENT_TYPE_JSON; + r->http.response.header.content_type = &CONTENT_TYPE_JSON; if (!is_rhizome_http_enabled()) return 404; int ret = authorize_restful(&r->http); @@ -418,7 +419,7 @@ static int insert_mime_part_body(struct http_request *, char *, size_t); static int restful_rhizome_insert(httpd_request *r, const char *remainder) { - r->http.response.header.content_type = CONTENT_TYPE_JSON; + r->http.response.header.content_type = &CONTENT_TYPE_JSON; r->http.render_extra_headers = render_manifest_headers; if (!is_rhizome_http_enabled()) return 404; @@ -583,7 +584,11 @@ static int insert_mime_part_header(struct http_request *hr, const struct mime_pa // Reject a request if this parameter comes after the manifest part. if (r->u.insert.received_manifest || r->u.insert.importing) return http_response_form_part(r, 400, "Spurious", PART_AUTHOR, NULL, 0); - // TODO enforce correct content type + if (!h->content_type.type[0]) + ; // missing Content-Type defaults to CONTENT_TYPE_SID_HEX + // TODO deprecate this default and insist that Content-Type be supplied + else if (!mime_content_types_are_equal(&h->content_type, &CONTENT_TYPE_SID_HEX)) + return http_response_form_part(r, 415, "Unsupported Content-Type in", PART_AUTHOR, NULL, 0); r->u.insert.current_part = PART_AUTHOR; assert(r->u.insert.author_hex_len == 0); } @@ -593,7 +598,11 @@ static int insert_mime_part_header(struct http_request *hr, const struct mime_pa // Reject a request if this parameter comes after the manifest part. if (r->u.insert.received_manifest || r->u.insert.importing) return http_response_form_part(r, 400, "Spurious", PART_SECRET, NULL, 0); - // TODO enforce correct content type + if (!h->content_type.type[0]) + ; // missing Content-Type defaults to CONTENT_TYPE_RHIZOME_BUNDLE_SECRET + // TODO deprecate this default and insist that Content-Type be supplied + else if (!mime_content_types_are_equal(&h->content_type, &CONTENT_TYPE_RHIZOME_BUNDLE_SECRET)) + return http_response_form_part(r, 415, "Unsupported Content-Type in", PART_SECRET, NULL, 0); r->u.insert.current_part = PART_SECRET; assert(r->u.insert.secret_text_len == 0); } @@ -603,7 +612,11 @@ static int insert_mime_part_header(struct http_request *hr, const struct mime_pa // Reject a request if this parameter comes after the manifest part. if (r->u.insert.received_manifest || r->u.insert.importing) return http_response_form_part(r, 400, "Spurious", PART_BUNDLEID, NULL, 0); - // TODO enforce correct content type + if (!h->content_type.type[0]) + ; // missing Content-Type defaults to CONTENT_TYPE_RHIZOME_BUNDLE_ID + // TODO deprecate this default and insist that Content-Type be supplied + else if (!mime_content_types_are_equal(&h->content_type, &CONTENT_TYPE_RHIZOME_BUNDLE_ID)) + return http_response_form_part(r, 415, "Unsupported Content-Type in", PART_BUNDLEID, NULL, 0); r->u.insert.current_part = PART_BUNDLEID; assert(r->u.insert.bid_text_len == 0); } @@ -612,13 +625,8 @@ static int insert_mime_part_header(struct http_request *hr, const struct mime_pa if (r->u.insert.received_manifest) return http_response_form_part(r, 400, "Duplicate", PART_MANIFEST, NULL, 0); form_buf_malloc_init(&r->u.insert.manifest, MAX_MANIFEST_BYTES); - if ( strcmp(h->content_type.type, "rhizome") != 0 - || strcmp(h->content_type.subtype, "manifest") != 0 - ) + if (!mime_content_types_are_equal(&h->content_type, &CONTENT_TYPE_RHIZOME_MANIFEST)) return http_response_form_part(r, 415, "Unsupported Content-Type in", PART_MANIFEST, NULL, 0); - if ((strcmp(h->content_type.format, "text+binarysig") != 0) - &&strlen(h->content_type.format)) - return http_response_form_part(r, 415, "Unsupported rhizome/manifest format in", PART_MANIFEST, NULL, 0); r->u.insert.current_part = PART_MANIFEST; } else if (strcmp(h->content_disposition.name, PART_PAYLOAD) == 0) { @@ -880,7 +888,7 @@ static int restful_rhizome_insert_end(struct http_request *hr) return http_request_rhizome_response(r, http_status, NULL); }else{ rhizome_authenticate_author(r->manifest); - http_request_response_static(&r->http, http_status, "rhizome-manifest/text", + http_request_response_static(&r->http, http_status, &CONTENT_TYPE_RHIZOME_MANIFEST, (const char *)r->manifest->manifestdata, r->manifest->manifest_all_bytes ); } @@ -893,7 +901,7 @@ static HTTP_HANDLER restful_rhizome_bid_decrypted_bin; static int restful_rhizome_(httpd_request *r, const char *remainder) { - r->http.response.header.content_type = CONTENT_TYPE_JSON; + r->http.response.header.content_type = &CONTENT_TYPE_JSON; r->http.render_extra_headers = render_manifest_headers; if (!is_rhizome_http_enabled()) return 404; @@ -949,7 +957,7 @@ static int restful_rhizome_bid_rhm(httpd_request *r, const char *remainder) return 404; if (r->manifest == NULL) return http_request_rhizome_response(r, 404, "Bundle not found"); // Not Found - http_request_response_static(&r->http, 200, "rhizome-manifest/text", + http_request_response_static(&r->http, 200, &CONTENT_TYPE_RHIZOME_MANIFEST, (const char *)r->manifest->manifestdata, r->manifest->manifest_all_bytes ); return 1; @@ -962,13 +970,13 @@ static int restful_rhizome_bid_raw_bin(httpd_request *r, const char *remainder) if (r->manifest == NULL) return http_request_rhizome_response(r, 404, "Bundle not found"); // Not Found if (r->manifest->filesize == 0) { - http_request_response_static(&r->http, 200, CONTENT_TYPE_BLOB, "", 0); + http_request_response_static(&r->http, 200, &CONTENT_TYPE_BLOB, "", 0); return 1; } int ret = rhizome_response_content_init_filehash(r, &r->manifest->filehash); if (ret) return ret; - http_request_response_generated(&r->http, 200, CONTENT_TYPE_BLOB, rhizome_payload_content); + http_request_response_generated(&r->http, 200, &CONTENT_TYPE_BLOB, rhizome_payload_content); return 1; } @@ -980,14 +988,14 @@ static int restful_rhizome_bid_decrypted_bin(httpd_request *r, const char *remai return http_request_rhizome_response(r, 404, "Bundle not found"); // Not Found if (r->manifest->filesize == 0) { // TODO use Content Type from manifest (once it is implemented) - http_request_response_static(&r->http, 200, CONTENT_TYPE_BLOB, "", 0); + http_request_response_static(&r->http, 200, &CONTENT_TYPE_BLOB, "", 0); return 1; } int ret = rhizome_response_content_init_payload(r, r->manifest); if (ret) return ret; // TODO use Content Type from manifest (once it is implemented) - http_request_response_generated(&r->http, 200, CONTENT_TYPE_BLOB, rhizome_payload_content); + http_request_response_generated(&r->http, 200, &CONTENT_TYPE_BLOB, rhizome_payload_content); return 1; } diff --git a/server_httpd.c b/server_httpd.c index bc37d09e..a23888fe 100644 --- a/server_httpd.c +++ b/server_httpd.c @@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "conf.h" #include "overlay_address.h" #include "overlay_interface.h" +#include "str.h" #include "os.h" #include "route_link.h" @@ -67,7 +68,7 @@ static int root_page(httpd_request *r, const char *remainder) WHY("HTTP Root page buffer overrun"); return 500; } - http_request_response_static(&r->http, 200, CONTENT_TYPE_HTML, temp, strbuf_len(b)); + http_request_response_static(&r->http, 200, &CONTENT_TYPE_HTML, temp, strbuf_len(b)); return 1; } @@ -75,7 +76,7 @@ static int fav_icon_header(httpd_request *r, const char *remainder) { if (*remainder) return 404; - http_request_response_static(&r->http, 200, "image/vnd.microsoft.icon", (const char *)favicon_bytes, favicon_len); + http_request_response_static(&r->http, 200, &CONTENT_TYPE_FAVICON, (const char *)favicon_bytes, favicon_len); return 1; } @@ -96,7 +97,7 @@ static int neighbour_page(httpd_request *r, const char *remainder) strbuf_puts(b, ""); if (strbuf_overrun(b)) return -1; - http_request_response_static(&r->http, 200, CONTENT_TYPE_HTML, buf, strbuf_len(b)); + http_request_response_static(&r->http, 200, &CONTENT_TYPE_HTML, buf, strbuf_len(b)); return 1; } @@ -114,7 +115,7 @@ static int interface_page(httpd_request *r, const char *remainder) strbuf_puts(b, ""); if (strbuf_overrun(b)) return -1; - http_request_response_static(&r->http, 200, CONTENT_TYPE_HTML, buf, strbuf_len(b)); + http_request_response_static(&r->http, 200, &CONTENT_TYPE_HTML, buf, strbuf_len(b)); return 1; } @@ -170,6 +171,6 @@ static int static_page(httpd_request *r, const char *remainder) } } r->u.file.offset=r->http.response.header.content_range_start; - http_request_response_generated(&r->http, 200, CONTENT_TYPE_HTML, static_file_generator); + http_request_response_generated(&r->http, 200, &CONTENT_TYPE_HTML, static_file_generator); return 1; } diff --git a/strbuf_helpers.c b/strbuf_helpers.c index a49e1e38..513ea088 100644 --- a/strbuf_helpers.c +++ b/strbuf_helpers.c @@ -635,6 +635,15 @@ strbuf strbuf_append_quoted_string(strbuf sb, const char *str) return sb; } +strbuf strbuf_append_token_or_quoted_string(strbuf sb, const char *str) +{ + for (const char *s = str; *s; ++s) + if (!is_http_token(*s)) + return strbuf_append_quoted_string(sb, str); + strbuf_puts(sb, str); + return sb; +} + static void _html_char(strbuf sb, char c) { if (c == '&') @@ -943,15 +952,15 @@ strbuf strbuf_append_mime_content_type(strbuf sb, const struct mime_content_type strbuf_puts(sb, ct->subtype); if (ct->charset[0]) { strbuf_puts(sb, "; charset="); - strbuf_append_quoted_string(sb, ct->charset); + strbuf_append_token_or_quoted_string(sb, ct->charset); } if (ct->multipart_boundary[0]) { strbuf_puts(sb, "; boundary="); - strbuf_append_quoted_string(sb, ct->multipart_boundary); + strbuf_append_token_or_quoted_string(sb, ct->multipart_boundary); } if (ct->format[0]) { strbuf_puts(sb, "; format="); - strbuf_append_quoted_string(sb, ct->format); + strbuf_append_token_or_quoted_string(sb, ct->format); } return sb; } diff --git a/strbuf_helpers.h b/strbuf_helpers.h index 1fd780fd..5352fa09 100644 --- a/strbuf_helpers.h +++ b/strbuf_helpers.h @@ -211,6 +211,13 @@ strbuf strbuf_append_file_meta(strbuf sb, const struct file_meta *metap); */ strbuf strbuf_append_quoted_string(strbuf sb, const char *str); +/* Append a string using HTTP token|quoted-string format: if it contains only + * token characters, then unmodified, otherwise as a quoted-string + * (strbuf_append_quoted_string). + * @author Andrew Bettison + */ +strbuf strbuf_append_token_or_quoted_string(strbuf sb, const char *str); + /* Escape HTML entities. * @author Andrew Bettison */