mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-01-29 15:43:56 +00:00
Support more HTTP multipart inner headers
Now Content-Length and Content-Type are parsed as well as Content-Disposition
This commit is contained in:
parent
9d54c629b2
commit
b9faf54c91
303
http_server.c
303
http_server.c
@ -281,11 +281,13 @@ static const char * _reserve(struct http_request *r, struct substring str)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static const char * _reserve_str(struct http_request *r, const char *str)
|
||||
{
|
||||
struct substring sub = { .start = str, .end = str + strlen(str) };
|
||||
return _reserve(r, sub);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int _end_of_content(struct http_request *r)
|
||||
{
|
||||
@ -556,6 +558,60 @@ static unsigned _parse_ranges(struct http_request *r, struct http_range *range,
|
||||
return i;
|
||||
}
|
||||
|
||||
static int _parse_content_type(struct http_request *r, struct mime_content_type *ct)
|
||||
{
|
||||
size_t n = _parse_token(r, ct->type, sizeof ct->type);
|
||||
if (n == 0)
|
||||
return 0;
|
||||
if (n >= sizeof ct->type) {
|
||||
WARNF("HTTP Content-Type type truncated: %s", alloca_str_toprint(ct->type));
|
||||
return 0;
|
||||
}
|
||||
if (!_skip_literal(r, "/"))
|
||||
return 0;
|
||||
n = _parse_token(r, ct->subtype, sizeof ct->subtype);
|
||||
if (n == 0)
|
||||
return 0;
|
||||
if (n >= sizeof ct->subtype) {
|
||||
WARNF("HTTP Content-Type subtype truncated: %s", alloca_str_toprint(ct->subtype));
|
||||
return 0;
|
||||
}
|
||||
while (_skip_optional_space(r) && _skip_literal(r, ";") && _skip_optional_space(r)) {
|
||||
const char *start = r->cursor;
|
||||
if (_skip_literal(r, "charset=")) {
|
||||
size_t n = _parse_token_or_quoted_string(r, ct->charset, sizeof ct->charset);
|
||||
if (n == 0)
|
||||
return 0;
|
||||
if (n >= sizeof ct->charset) {
|
||||
WARNF("HTTP Content-Type charset truncated: %s", alloca_str_toprint(ct->charset));
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
r->cursor = start;
|
||||
if (_skip_literal(r, "boundary=")) {
|
||||
size_t n = _parse_token_or_quoted_string(r, ct->multipart_boundary, sizeof ct->multipart_boundary);
|
||||
if (n == 0)
|
||||
return 0;
|
||||
if (n >= sizeof ct->multipart_boundary) {
|
||||
WARNF("HTTP Content-Type boundary truncated: %s", alloca_str_toprint(ct->multipart_boundary));
|
||||
return 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
r->cursor = start;
|
||||
struct substring param;
|
||||
if (_skip_token(r, ¶m) && _skip_literal(r, "=") && _parse_token_or_quoted_string(r, NULL, 0)) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Skipping HTTP Content-Type parameter: %s", alloca_substring_toprint(param));
|
||||
continue;
|
||||
}
|
||||
WARNF("Malformed HTTP Content-Type: %s", alloca_toprint(50, r->cursor, r->end - r->cursor));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _parse_quoted_rfc822_time(struct http_request *r, time_t *timep)
|
||||
{
|
||||
char datestr[40];
|
||||
@ -743,6 +799,13 @@ static int http_request_parse_header(struct http_request *r)
|
||||
_rewind(r);
|
||||
const char *const sol = r->cursor;
|
||||
if (_skip_literal_nocase(r, "Content-Length:")) {
|
||||
if (r->request_header.content_length != CONTENT_LENGTH_UNKNOWN) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Skipping duplicate HTTP header Content-Length: %s", alloca_toprint(50, sol, r->end - sol));
|
||||
r->cursor = nextline;
|
||||
_commit(r);
|
||||
return 0;
|
||||
}
|
||||
_skip_optional_space(r);
|
||||
http_size_t length;
|
||||
if (_parse_http_size_t(r, &length) && _skip_optional_space(r) && r->cursor == eol) {
|
||||
@ -757,50 +820,35 @@ static int http_request_parse_header(struct http_request *r)
|
||||
}
|
||||
_rewind(r);
|
||||
if (_skip_literal_nocase(r, "Content-Type:")) {
|
||||
if (r->request_header.content_type.type[0]) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Skipping duplicate HTTP header Content-Type: %s", alloca_toprint(50, sol, r->end - sol));
|
||||
r->cursor = nextline;
|
||||
_commit(r);
|
||||
return 0;
|
||||
}
|
||||
_skip_optional_space(r);
|
||||
struct substring type = substring_NULL;
|
||||
struct substring subtype = substring_NULL;
|
||||
char boundary[BOUNDARY_STRING_MAXLEN + 1];
|
||||
boundary[0] = '\0';
|
||||
if (_skip_token(r, &type) && _skip_literal(r, "/") && _skip_token(r, &subtype)) {
|
||||
// Parse zero or more content-type parameters.
|
||||
for (_skip_optional_space(r); r->cursor < eol && _skip_literal(r, ";"); _skip_optional_space(r)) {
|
||||
_skip_optional_space(r);
|
||||
const char *startparam = r->cursor;
|
||||
if (_skip_literal(r, "boundary=")) {
|
||||
size_t n = _parse_token_or_quoted_string(r, boundary, sizeof boundary);
|
||||
if (n == 0 || n >= sizeof boundary || !is_valid_http_boundary_string(boundary))
|
||||
goto malformed;
|
||||
continue;
|
||||
}
|
||||
// Silently ignore unrecognised parameters (eg, charset=) if they are well formed.
|
||||
r->cursor = startparam; // partial rewind
|
||||
if (_skip_token(r, NULL) && _skip_literal(r, "=") && _parse_token_or_quoted_string(r, NULL, 0))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (r->cursor == eol) {
|
||||
r->cursor = nextline;
|
||||
_commit(r);
|
||||
if ( (r->request_header.content_type = _reserve(r, type)) == NULL
|
||||
|| (r->request_header.content_subtype = _reserve(r, subtype)) == NULL
|
||||
|| (boundary[0] && (r->request_header.boundary = _reserve_str(r, boundary)) == NULL)
|
||||
)
|
||||
return 0; // error
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Parsed HTTP request Content-type: %s/%s%s%s",
|
||||
r->request_header.content_type,
|
||||
r->request_header.content_subtype,
|
||||
r->request_header.boundary ? "; boundary=" : "",
|
||||
r->request_header.boundary ? alloca_str_toprint(r->request_header.boundary) : ""
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
if ( _parse_content_type(r, &r->request_header.content_type)
|
||||
&& _skip_optional_space(r)
|
||||
&& r->cursor == eol
|
||||
) {
|
||||
r->cursor = nextline;
|
||||
_commit(r);
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Parsed HTTP request Content-type: %s", alloca_mime_content_type(&r->request_header.content_type));
|
||||
return 0;
|
||||
}
|
||||
goto malformed;
|
||||
}
|
||||
_rewind(r);
|
||||
if (_skip_literal_nocase(r, "Range:")) {
|
||||
if (r->request_header.content_range_count) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Skipping duplicate HTTP header Range: %s", alloca_toprint(50, sol, r->end - sol));
|
||||
r->cursor = nextline;
|
||||
_commit(r);
|
||||
return 0;
|
||||
}
|
||||
_skip_optional_space(r);
|
||||
unsigned int n;
|
||||
if ( _skip_literal(r, "bytes=")
|
||||
@ -858,7 +906,7 @@ static int http_request_start_body(struct http_request *r)
|
||||
DEBUGF("Malformed HTTP %s request: non-zero Content-Length not allowed", r->verb);
|
||||
return 400;
|
||||
}
|
||||
if (r->request_header.content_type) {
|
||||
if (r->request_header.content_type.type) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Malformed HTTP %s request: Content-Type not allowed", r->verb);
|
||||
return 400;
|
||||
@ -871,26 +919,28 @@ static int http_request_start_body(struct http_request *r)
|
||||
DEBUGF("Malformed HTTP %s request: missing Content-Length header", r->verb);
|
||||
return 411;
|
||||
}
|
||||
if (r->request_header.content_type == NULL) {
|
||||
if (r->request_header.content_type.type == NULL) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Malformed HTTP %s request: missing Content-Type header", r->verb);
|
||||
return 400;
|
||||
}
|
||||
if ( strcmp(r->request_header.content_type, "multipart") == 0
|
||||
&& strcmp(r->request_header.content_subtype, "form-data") == 0
|
||||
if ( strcmp(r->request_header.content_type.type, "multipart") == 0
|
||||
&& strcmp(r->request_header.content_type.subtype, "form-data") == 0
|
||||
) {
|
||||
if (r->request_header.boundary == NULL || r->request_header.boundary[0] == '\0') {
|
||||
if ( r->request_header.content_type.multipart_boundary == NULL
|
||||
|| r->request_header.content_type.multipart_boundary[0] == '\0'
|
||||
) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Malformed HTTP %s request: Content-Type %s/%s missing boundary parameter",
|
||||
r->verb, r->request_header.content_type, r->request_header.content_subtype);
|
||||
r->verb, r->request_header.content_type.type, r->request_header.content_type.subtype);
|
||||
return 400;
|
||||
}
|
||||
r->parser = http_request_parse_body_form_data;
|
||||
r->form_data_state = START;
|
||||
} else {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Unsupported HTTP %s request: Content-Type %s/%s not supported",
|
||||
r->verb, r->request_header.content_type, r->request_header.content_subtype);
|
||||
DEBUGF("Unsupported HTTP %s request: Content-Type %s not supported",
|
||||
r->verb, alloca_mime_content_type(&r->request_header.content_type));
|
||||
return 415;
|
||||
}
|
||||
}
|
||||
@ -909,7 +959,7 @@ static int http_request_start_body(struct http_request *r)
|
||||
*/
|
||||
static int _skip_mime_boundary(struct http_request *r)
|
||||
{
|
||||
if (!_skip_literal(r, "--") || !_skip_literal(r, r->request_header.boundary))
|
||||
if (!_skip_literal(r, "--") || !_skip_literal(r, r->request_header.content_type.multipart_boundary))
|
||||
return 0;
|
||||
if (_skip_literal(r, "--") && _skip_crlf(r))
|
||||
return 2;
|
||||
@ -988,6 +1038,43 @@ malformed:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void http_request_form_data_start_part(struct http_request *r, int b)
|
||||
{
|
||||
switch (r->form_data_state) {
|
||||
case BODY:
|
||||
if ( r->part_header.content_length != CONTENT_LENGTH_UNKNOWN
|
||||
&& r->part_body_length != r->part_header.content_length
|
||||
) {
|
||||
WARNF("HTTP multipart part body length (%"PRIhttp_size_t") does not match Content-Length header (%"PRIhttp_size_t")",
|
||||
r->part_body_length,
|
||||
r->part_header.content_length
|
||||
);
|
||||
}
|
||||
// fall through...
|
||||
case HEADER:
|
||||
if (r->form_data.handle_mime_part_end) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("handle_mime_part_end()");
|
||||
r->form_data.handle_mime_part_end(r);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (b == 1) {
|
||||
r->form_data_state = HEADER;
|
||||
bzero(&r->part_header, sizeof r->part_header);
|
||||
r->part_body_length = 0;
|
||||
r->part_header.content_length = CONTENT_LENGTH_UNKNOWN;
|
||||
if (r->form_data.handle_mime_part_start) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("handle_mime_part_start()");
|
||||
r->form_data.handle_mime_part_start(r);
|
||||
}
|
||||
} else
|
||||
r->form_data_state = EPILOGUE;
|
||||
}
|
||||
|
||||
/* If parsing completes (ie, parsed to end of epilogue), then sets r->parser to NULL and returns 0,
|
||||
* so this function will not be called again. If parsing cannot complete due to running out of
|
||||
* data, returns 100, so this function will not be called again until more data has been read.
|
||||
@ -1029,15 +1116,7 @@ static int http_request_parse_body_form_data(struct http_request *r)
|
||||
}
|
||||
_rewind_crlf(r);
|
||||
_commit(r);
|
||||
if (b == 1) {
|
||||
r->form_data_state = HEADER;
|
||||
if (r->form_data.handle_mime_part_start) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("handle_mime_part_start()");
|
||||
r->form_data.handle_mime_part_start(r);
|
||||
}
|
||||
} else
|
||||
r->form_data_state = EPILOGUE;
|
||||
http_request_form_data_start_part(r, b);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1074,6 +1153,16 @@ static int http_request_parse_body_form_data(struct http_request *r)
|
||||
// A blank line finishes the headers. The CRLF does not form part of the body.
|
||||
if (_skip_crlf(r)) {
|
||||
_commit(r);
|
||||
if (r->form_data.handle_mime_part_header) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("handle_mime_part_header(Content-Length: %"PRIhttp_size_t", Content-Type: %s, Content-Disposition: %s)",
|
||||
r->part_header.content_length,
|
||||
alloca_mime_content_type(&r->part_header.content_type),
|
||||
alloca_mime_content_disposition(&r->part_header.content_disposition)
|
||||
);
|
||||
r->form_data.handle_mime_part_header(r, &r->part_header);
|
||||
}
|
||||
|
||||
r->form_data_state = BODY;
|
||||
return 0;
|
||||
}
|
||||
@ -1086,23 +1175,9 @@ static int http_request_parse_body_form_data(struct http_request *r)
|
||||
if ((b = _skip_mime_boundary(r))) {
|
||||
_rewind_crlf(r);
|
||||
_commit(r);
|
||||
if (r->form_data.handle_mime_part_end) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("handle_mime_part_end()");
|
||||
r->form_data.handle_mime_part_end(r);
|
||||
}
|
||||
// A boundary in the middle of headers finishes the current part and starts a new part.
|
||||
// An end boundary terminates the current part and starts the epilogue.
|
||||
if (b == 1) {
|
||||
r->form_data_state = HEADER;
|
||||
if (r->form_data.handle_mime_part_start) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("handle_mime_part_start()");
|
||||
r->form_data.handle_mime_part_start(r);
|
||||
}
|
||||
}
|
||||
else
|
||||
r->form_data_state = EPILOGUE;
|
||||
http_request_form_data_start_part(r, b);
|
||||
return 0;
|
||||
}
|
||||
if (_run_out(r))
|
||||
@ -1115,27 +1190,54 @@ static int http_request_parse_body_form_data(struct http_request *r)
|
||||
strncpy(labelstr, label.start, labellen)[labellen] = '\0';
|
||||
str_tolower_inplace(labelstr);
|
||||
const char *value = r->cursor;
|
||||
if (strcmp(labelstr, "content-disposition") == 0) {
|
||||
struct mime_content_disposition cd;
|
||||
bzero(&cd, sizeof cd);
|
||||
if (_parse_content_disposition(r, &cd) && _skip_optional_space(r) && _skip_crlf(r)) {
|
||||
if (strcmp(labelstr, "content-length") == 0) {
|
||||
if (r->part_header.content_length != CONTENT_LENGTH_UNKNOWN) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Skipping duplicate HTTP multipart header Content-Length: %s", alloca_toprint(50, sol, r->end - sol));
|
||||
return 400;
|
||||
}
|
||||
http_size_t length;
|
||||
if (_parse_http_size_t(r, &length) && _skip_optional_space(r) && _skip_crlf(r)) {
|
||||
_rewind_crlf(r);
|
||||
_commit(r);
|
||||
if (r->form_data.handle_mime_content_disposition) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("handle_mime_content_disposition(%s)", alloca_mime_content_disposition(&cd));
|
||||
r->form_data.handle_mime_content_disposition(r, &cd);
|
||||
}
|
||||
r->part_header.content_length = length;
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Parsed HTTP multipart header Content-Length: %"PRIhttp_size_t, r->part_header.content_length);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (strcmp(labelstr, "content-type") == 0) {
|
||||
if (r->part_header.content_type.type[0]) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Skipping duplicate HTTP multipart header Content-Type: %s", alloca_toprint(50, sol, r->end - sol));
|
||||
return 400;
|
||||
}
|
||||
if (_parse_content_type(r, &r->part_header.content_type) && _skip_optional_space(r) && _skip_crlf(r)) {
|
||||
_rewind_crlf(r);
|
||||
_commit(r);
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Parsed HTTP multipart header Content-Type: %s", alloca_mime_content_type(&r->part_header.content_type));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (strcmp(labelstr, "content-disposition") == 0) {
|
||||
if (r->part_header.content_disposition.type[0]) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Skipping duplicate HTTP multipart header Content-Disposition: %s", alloca_toprint(50, sol, r->end - sol));
|
||||
return 400;
|
||||
}
|
||||
if (_parse_content_disposition(r, &r->part_header.content_disposition) && _skip_optional_space(r) && _skip_crlf(r)) {
|
||||
_rewind_crlf(r);
|
||||
_commit(r);
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Parsed HTTP multipart header Content-Disposition: %s", alloca_mime_content_disposition(&r->part_header.content_disposition));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (_skip_to_crlf(r)) {
|
||||
_commit(r);
|
||||
if (r->form_data.handle_mime_header) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("handle_mime_header(%s, %s)", alloca_str_toprint(labelstr), alloca_toprint(-1, value, value - r->cursor));
|
||||
r->form_data.handle_mime_header(r, labelstr, value, value - r->cursor); // excluding CRLF at end
|
||||
}
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("Skip HTTP multipart header: %s: %s", alloca_str_toprint(labelstr), alloca_toprint(-1, value, value - r->cursor));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1169,25 +1271,14 @@ static int http_request_parse_body_form_data(struct http_request *r)
|
||||
if ((b = _skip_mime_boundary(r))) {
|
||||
_rewind_crlf(r);
|
||||
_commit(r);
|
||||
assert(end_body >= start);
|
||||
r->part_body_length += end_body - start;
|
||||
if (end_body > start && r->form_data.handle_mime_body) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("handle_mime_body(%s length=%zu)", alloca_toprint(80, start, end_body - start), end_body - start);
|
||||
r->form_data.handle_mime_body(r, start, end_body - start); // excluding CRLF at end
|
||||
}
|
||||
if (r->form_data.handle_mime_part_end) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("handle_mime_part_end()");
|
||||
r->form_data.handle_mime_part_end(r);
|
||||
}
|
||||
r->form_data_state = EPILOGUE;
|
||||
if (b == 1) {
|
||||
r->form_data_state = HEADER;
|
||||
if (r->form_data.handle_mime_part_start) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("handle_mime_part_start()");
|
||||
r->form_data.handle_mime_part_start(r);
|
||||
}
|
||||
}
|
||||
http_request_form_data_start_part(r, b);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1198,6 +1289,8 @@ static int http_request_parse_body_form_data(struct http_request *r)
|
||||
}
|
||||
_rewind_optional_cr(r);
|
||||
_commit(r);
|
||||
assert(r->parsed >= start);
|
||||
r->part_body_length += r->parsed - start;
|
||||
if (r->parsed > start && r->form_data.handle_mime_body) {
|
||||
if (r->debug_flag && *r->debug_flag)
|
||||
DEBUGF("handle_mime_body(%s length=%zu)", alloca_toprint(80, start, r->parsed - start), r->parsed - start);
|
||||
@ -1538,18 +1631,6 @@ static const char *httpResultString(int response_code)
|
||||
}
|
||||
}
|
||||
|
||||
static strbuf strbuf_append_quoted_string(strbuf sb, const char *str)
|
||||
{
|
||||
strbuf_putc(sb, '"');
|
||||
for (; *str; ++str) {
|
||||
if (*str == '"' || *str == '\\')
|
||||
strbuf_putc(sb, '\\');
|
||||
strbuf_putc(sb, *str);
|
||||
}
|
||||
strbuf_putc(sb, '"');
|
||||
return sb;
|
||||
}
|
||||
|
||||
/* Render the HTTP response into the current response buffer. Return 1 if it fits, 0 if it does
|
||||
* not. The buffer response_pointer may be NULL, in which case no response is rendered, but the
|
||||
* content_length is still computed
|
||||
|
@ -56,11 +56,16 @@ http_size_t http_range_bytes(const struct http_range *range, unsigned nranges);
|
||||
|
||||
#define CONTENT_LENGTH_UNKNOWN UINT64_MAX
|
||||
|
||||
struct mime_content_type {
|
||||
char type[64];
|
||||
char subtype[64];
|
||||
char multipart_boundary[71];
|
||||
char charset[31];
|
||||
};
|
||||
|
||||
struct http_request_headers {
|
||||
http_size_t content_length;
|
||||
const char *content_type;
|
||||
const char *content_subtype;
|
||||
const char *boundary;
|
||||
struct mime_content_type content_type;
|
||||
unsigned short content_range_count;
|
||||
struct http_range content_ranges[5];
|
||||
};
|
||||
@ -94,11 +99,16 @@ struct mime_content_disposition {
|
||||
time_t read_date;
|
||||
};
|
||||
|
||||
struct mime_part_headers {
|
||||
http_size_t content_length;
|
||||
struct mime_content_type content_type;
|
||||
struct mime_content_disposition content_disposition;
|
||||
};
|
||||
|
||||
struct http_mime_handler {
|
||||
void (*handle_mime_preamble)(struct http_request *, const char *, size_t);
|
||||
void (*handle_mime_part_start)(struct http_request *);
|
||||
void (*handle_mime_content_disposition)(struct http_request *, const struct mime_content_disposition *);
|
||||
void (*handle_mime_header)(struct http_request *, const char *label, const char *, size_t);
|
||||
void (*handle_mime_part_header)(struct http_request *, const struct mime_part_headers *);
|
||||
void (*handle_mime_body)(struct http_request *, const char *, size_t);
|
||||
void (*handle_mime_part_end)(struct http_request *);
|
||||
void (*handle_mime_epilogue)(struct http_request *, const char *, size_t);
|
||||
@ -118,31 +128,49 @@ typedef int (*HTTP_REQUEST_PARSER)(struct http_request *);
|
||||
|
||||
struct http_request {
|
||||
struct sched_ent alarm; // MUST BE FIRST ELEMENT
|
||||
// The following control the lifetime of this struct.
|
||||
enum http_request_phase { RECEIVE, TRANSMIT, DONE } phase;
|
||||
bool_t *debug_flag;
|
||||
bool_t *disable_tx_flag;
|
||||
time_ms_t initiate_time; // time connection was initiated
|
||||
time_ms_t idle_timeout; // disconnect if no bytes received for this long
|
||||
struct sockaddr_in client_sockaddr_in;
|
||||
HTTP_REQUEST_PARSER parser; // current parser function
|
||||
HTTP_REQUEST_PARSER handle_first_line; // called after first line is parsed
|
||||
HTTP_REQUEST_PARSER handle_headers; // called after all headers are parsed
|
||||
HTTP_REQUEST_PARSER handle_content_end; // called after all content is received
|
||||
enum mime_state { START, PREAMBLE, HEADER, BODY, EPILOGUE } form_data_state;
|
||||
struct http_mime_handler form_data; // called to parse multipart/form-data body
|
||||
void (*finalise)(struct http_request *);
|
||||
void (*free)(void*);
|
||||
// These can be set up to point to config flags, to allow debug to be
|
||||
// enabled indpendently for different instances HTTP server instances
|
||||
// that use this code.
|
||||
bool_t *debug_flag;
|
||||
bool_t *disable_tx_flag;
|
||||
// The following are used for parsing the HTTP request.
|
||||
time_ms_t initiate_time; // time connection was initiated
|
||||
time_ms_t idle_timeout; // disconnect if no bytes received for this long
|
||||
struct sockaddr_in client_sockaddr_in; // caller may supply this
|
||||
// The parsed HTTP request is accumulated into the following fields.
|
||||
const char *verb; // points to nul terminated static string, "GET", "PUT", etc.
|
||||
const char *path; // points into buffer; nul terminated
|
||||
uint8_t version_major; // m from from HTTP/m.n
|
||||
uint8_t version_minor; // n from HTTP/m.n
|
||||
struct http_request_headers request_header;
|
||||
// Parsing is done by setting 'parser' to point to a series of parsing
|
||||
// functions as the parsing state progresses.
|
||||
HTTP_REQUEST_PARSER parser; // current parser function
|
||||
// The caller may set these up, and they are invoked by the parser as request
|
||||
// parsing reaches different stages.
|
||||
HTTP_REQUEST_PARSER handle_first_line; // called after first line is parsed
|
||||
HTTP_REQUEST_PARSER handle_headers; // called after all HTTP headers are parsed
|
||||
HTTP_REQUEST_PARSER handle_content_end; // called after all content is received
|
||||
// The following are used for managing the buffer during RECEIVE phase.
|
||||
const char *received; // start of received data in buffer[]
|
||||
const char *end; // end of received data in buffer[]
|
||||
const char *parsed; // start of unparsed data in buffer[]
|
||||
const char *cursor; // for parsing
|
||||
http_size_t request_content_remaining;
|
||||
// The following are used for parsing a multipart body.
|
||||
enum mime_state { START, PREAMBLE, HEADER, BODY, EPILOGUE } form_data_state;
|
||||
struct http_mime_handler form_data;
|
||||
struct mime_part_headers part_header;
|
||||
http_size_t part_body_length;
|
||||
// The following are used for constructing the response that will be sent in
|
||||
// TRANSMIT phase.
|
||||
struct http_response response;
|
||||
// The following are used during TRANSMIT phase to control buffering and
|
||||
// sending.
|
||||
http_size_t response_length; // total response bytes (header + content)
|
||||
http_size_t response_sent; // for counting up to response_length
|
||||
char *response_buffer;
|
||||
@ -150,6 +178,7 @@ struct http_request {
|
||||
size_t response_buffer_length;
|
||||
size_t response_buffer_sent;
|
||||
void (*response_free_buffer)(void*);
|
||||
// This buffer is used during RECEIVE and TRANSMIT phase.
|
||||
char buffer[8 * 1024];
|
||||
};
|
||||
|
||||
|
@ -54,7 +54,7 @@ static void rhizome_direct_clear_temporary_files(rhizome_http_request *r)
|
||||
}
|
||||
}
|
||||
|
||||
int rhizome_direct_import_end(struct http_request *hr)
|
||||
static int rhizome_direct_import_end(struct http_request *hr)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (!r->received_manifest) {
|
||||
@ -168,7 +168,7 @@ int rhizome_direct_enquiry_end(struct http_request *hr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rhizome_direct_addfile_end(struct http_request *hr)
|
||||
static int rhizome_direct_addfile_end(struct http_request *hr)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
// If given a file without a manifest, we should only accept if it we are configured to do so, and
|
||||
@ -271,14 +271,14 @@ int rhizome_direct_addfile_end(struct http_request *hr)
|
||||
}
|
||||
}
|
||||
|
||||
void rhizome_direct_process_mime_start(struct http_request *hr)
|
||||
static void rhizome_direct_process_mime_start(struct http_request *hr)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
assert(r->current_part == NONE);
|
||||
assert(r->part_fd == -1);
|
||||
}
|
||||
|
||||
void rhizome_direct_process_mime_end(struct http_request *hr)
|
||||
static void rhizome_direct_process_mime_end(struct http_request *hr)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (r->part_fd != -1) {
|
||||
@ -302,19 +302,19 @@ void rhizome_direct_process_mime_end(struct http_request *hr)
|
||||
r->current_part = NONE;
|
||||
}
|
||||
|
||||
void rhizome_direct_process_mime_content_disposition(struct http_request *hr, const struct mime_content_disposition *cd)
|
||||
static void rhizome_direct_process_mime_part_header(struct http_request *hr, const struct mime_part_headers *h)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (strcmp(cd->name, "data") == 0) {
|
||||
if (strcmp(h->content_disposition.name, "data") == 0) {
|
||||
r->current_part = DATA;
|
||||
strncpy(r->data_file_name, cd->filename, sizeof r->data_file_name)[sizeof r->data_file_name - 1] = '\0';
|
||||
strncpy(r->data_file_name, h->content_disposition.filename, sizeof r->data_file_name)[sizeof r->data_file_name - 1] = '\0';
|
||||
}
|
||||
else if (strcmp(cd->name, "manifest") == 0) {
|
||||
else if (strcmp(h->content_disposition.name, "manifest") == 0) {
|
||||
r->current_part = MANIFEST;
|
||||
} else
|
||||
return;
|
||||
char path[512];
|
||||
if (form_temporary_file_path(r, path, cd->name) == -1) {
|
||||
if (form_temporary_file_path(r, path, h->content_disposition.name) == -1) {
|
||||
http_request_simple_response(&r->http, 500, "Internal Error: Buffer overrun");
|
||||
return;
|
||||
}
|
||||
@ -325,7 +325,7 @@ void rhizome_direct_process_mime_content_disposition(struct http_request *hr, co
|
||||
}
|
||||
}
|
||||
|
||||
void rhizome_direct_process_mime_body(struct http_request *hr, const char *buf, size_t len)
|
||||
static void rhizome_direct_process_mime_body(struct http_request *hr, const char *buf, size_t len)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (r->part_fd != -1) {
|
||||
@ -344,7 +344,7 @@ int rhizome_direct_import(rhizome_http_request *r, const char *remainder)
|
||||
}
|
||||
r->http.form_data.handle_mime_part_start = rhizome_direct_process_mime_start;
|
||||
r->http.form_data.handle_mime_part_end = rhizome_direct_process_mime_end;
|
||||
r->http.form_data.handle_mime_content_disposition = rhizome_direct_process_mime_content_disposition;
|
||||
r->http.form_data.handle_mime_part_header = rhizome_direct_process_mime_part_header;
|
||||
r->http.form_data.handle_mime_body = rhizome_direct_process_mime_body;
|
||||
r->http.handle_content_end = rhizome_direct_import_end;
|
||||
r->current_part = NONE;
|
||||
@ -361,7 +361,7 @@ int rhizome_direct_enquiry(rhizome_http_request *r, const char *remainder)
|
||||
}
|
||||
r->http.form_data.handle_mime_part_start = rhizome_direct_process_mime_start;
|
||||
r->http.form_data.handle_mime_part_end = rhizome_direct_process_mime_end;
|
||||
r->http.form_data.handle_mime_content_disposition = rhizome_direct_process_mime_content_disposition;
|
||||
r->http.form_data.handle_mime_part_header = rhizome_direct_process_mime_part_header;
|
||||
r->http.form_data.handle_mime_body = rhizome_direct_process_mime_body;
|
||||
r->http.handle_content_end = rhizome_direct_enquiry_end;
|
||||
r->current_part = NONE;
|
||||
@ -394,7 +394,7 @@ int rhizome_direct_addfile(rhizome_http_request *r, const char *remainder)
|
||||
}
|
||||
r->http.form_data.handle_mime_part_start = rhizome_direct_process_mime_start;
|
||||
r->http.form_data.handle_mime_part_end = rhizome_direct_process_mime_end;
|
||||
r->http.form_data.handle_mime_content_disposition = rhizome_direct_process_mime_content_disposition;
|
||||
r->http.form_data.handle_mime_part_header = rhizome_direct_process_mime_part_header;
|
||||
r->http.form_data.handle_mime_body = rhizome_direct_process_mime_body;
|
||||
r->http.handle_content_end = rhizome_direct_addfile_end;
|
||||
r->current_part = NONE;
|
||||
|
@ -402,6 +402,18 @@ strbuf strbuf_append_iovec(strbuf sb, const struct iovec *iov, int iovcnt)
|
||||
return sb;
|
||||
}
|
||||
|
||||
strbuf strbuf_append_quoted_string(strbuf sb, const char *str)
|
||||
{
|
||||
strbuf_putc(sb, '"');
|
||||
for (; *str; ++str) {
|
||||
if (*str == '"' || *str == '\\')
|
||||
strbuf_putc(sb, '\\');
|
||||
strbuf_putc(sb, *str);
|
||||
}
|
||||
strbuf_putc(sb, '"');
|
||||
return sb;
|
||||
}
|
||||
|
||||
strbuf strbuf_append_http_ranges(strbuf sb, const struct http_range *ranges, unsigned nels)
|
||||
{
|
||||
unsigned i;
|
||||
@ -427,31 +439,47 @@ strbuf strbuf_append_http_ranges(strbuf sb, const struct http_range *ranges, uns
|
||||
return sb;
|
||||
}
|
||||
|
||||
strbuf strbuf_append_mime_content_disposition(strbuf sb, const struct mime_content_disposition *cd)
|
||||
strbuf strbuf_append_mime_content_type(strbuf sb, const struct mime_content_type *ct)
|
||||
{
|
||||
strbuf_puts(sb, "type=");
|
||||
strbuf_toprint_quoted(sb, "``", cd->type);
|
||||
strbuf_puts(sb, " name=");
|
||||
strbuf_toprint_quoted(sb, "``", cd->name);
|
||||
strbuf_puts(sb, " filename=");
|
||||
strbuf_toprint_quoted(sb, "``", cd->filename);
|
||||
strbuf_puts(sb, " size=");
|
||||
strbuf_sprintf(sb, "%"PRIhttp_size_t, cd->size);
|
||||
struct tm tm;
|
||||
strbuf_puts(sb, " creation_date=");
|
||||
if (cd->creation_date)
|
||||
strbuf_append_strftime(sb, "%a, %d %b %Y %T %z", gmtime_r(&cd->creation_date, &tm));
|
||||
else
|
||||
strbuf_puts(sb, "0");
|
||||
strbuf_puts(sb, " modification_date=");
|
||||
if (cd->modification_date)
|
||||
strbuf_append_strftime(sb, "%a, %d %b %Y %T %z", gmtime_r(&cd->modification_date, &tm));
|
||||
else
|
||||
strbuf_puts(sb, "0");
|
||||
strbuf_puts(sb, " read_date=");
|
||||
if (cd->read_date)
|
||||
strbuf_append_strftime(sb, "%a, %d %b %Y %T %z", gmtime_r(&cd->read_date, &tm));
|
||||
else
|
||||
strbuf_puts(sb, "0");
|
||||
strbuf_puts(sb, ct->type);
|
||||
strbuf_putc(sb, '/');
|
||||
strbuf_puts(sb, ct->subtype);
|
||||
if (ct->charset) {
|
||||
strbuf_puts(sb, "; charset=");
|
||||
strbuf_append_quoted_string(sb, ct->charset);
|
||||
}
|
||||
if (ct->multipart_boundary) {
|
||||
strbuf_puts(sb, "; boundary=");
|
||||
strbuf_append_quoted_string(sb, ct->multipart_boundary);
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
strbuf strbuf_append_mime_content_disposition(strbuf sb, const struct mime_content_disposition *cd)
|
||||
{
|
||||
strbuf_puts(sb, cd->type);
|
||||
if (cd->name) {
|
||||
strbuf_puts(sb, "; name=");
|
||||
strbuf_append_quoted_string(sb, cd->name);
|
||||
}
|
||||
if (cd->filename) {
|
||||
strbuf_puts(sb, "; filename=");
|
||||
strbuf_append_quoted_string(sb, cd->filename);
|
||||
}
|
||||
if (cd->size)
|
||||
strbuf_sprintf(sb, "; size=%"PRIhttp_size_t, cd->size);
|
||||
struct tm tm;
|
||||
if (cd->creation_date) {
|
||||
strbuf_puts(sb, " creation_date=");
|
||||
strbuf_append_strftime(sb, "\"%a, %d %b %Y %T %z\"", gmtime_r(&cd->creation_date, &tm));
|
||||
}
|
||||
if (cd->modification_date) {
|
||||
strbuf_puts(sb, " modification_date=");
|
||||
strbuf_append_strftime(sb, "\"%a, %d %b %Y %T %z\"", gmtime_r(&cd->modification_date, &tm));
|
||||
}
|
||||
if (cd->read_date) {
|
||||
strbuf_puts(sb, " read_date=");
|
||||
strbuf_append_strftime(sb, "\"%a, %d %b %Y %T %z\"", gmtime_r(&cd->read_date, &tm));
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
@ -145,6 +145,12 @@ struct iovec;
|
||||
strbuf strbuf_append_iovec(strbuf sb, const struct iovec *iov, int iovcnt);
|
||||
#define alloca_iovec(iov,cnt) strbuf_str(strbuf_append_iovec(strbuf_alloca(200), (iov), (cnt)))
|
||||
|
||||
/* Append a string using HTTP quoted-string format: delimited by double quotes (") and
|
||||
* internal double quotes and backslash escaped by leading backslash.
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
strbuf strbuf_append_quoted_string(strbuf sb, const char *str);
|
||||
|
||||
/* Append a representation of a struct http_range[] array.
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
@ -152,7 +158,14 @@ struct http_range;
|
||||
strbuf strbuf_append_http_ranges(strbuf sb, const struct http_range *ranges, unsigned nels);
|
||||
#define alloca_http_ranges(ra) strbuf_str(strbuf_append_http_ranges(strbuf_alloca(25*NELS(ra)), (ra), NELS(ra)))
|
||||
|
||||
/* Append a representation of a struct mime_content_disposition struct.
|
||||
/* Append a representation of a struct mime_content_type in HTTP header format.
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
struct mime_content_type;
|
||||
strbuf strbuf_append_mime_content_type(strbuf, const struct mime_content_type *);
|
||||
#define alloca_mime_content_type(ct) strbuf_str(strbuf_append_mime_content_type(strbuf_alloca(500), (ct)))
|
||||
|
||||
/* Append a representation of a struct mime_content_disposition, in HTTP header format.
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
struct mime_content_disposition;
|
||||
|
Loading…
x
Reference in New Issue
Block a user