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, "