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.
This commit is contained in:
Andrew Bettison 2017-10-20 09:55:21 +10:30
parent c64faadb85
commit 29ce8994f2
13 changed files with 152 additions and 86 deletions

View File

@ -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, "<html>\n<h1>%03u %s</h1>", hr->status_code, hr->reason);
strbuf_puts(sb, "\n<dl>");
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 <andrew@servalproject.com>
*/
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);

View File

@ -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);

21
httpd.c
View File

@ -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;

View File

@ -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 {

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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));

View File

@ -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, "</body></html>");
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;
}

View File

@ -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;
}

View File

@ -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, "</body></html>");
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, "</body></html>");
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;
}

View File

@ -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;
}

View File

@ -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 <andrew@servalproject.com>
*/
strbuf strbuf_append_token_or_quoted_string(strbuf sb, const char *str);
/* Escape HTML entities.
* @author Andrew Bettison <andrew@servalproject.com>
*/