Fix basic bugs in new HTTP server code

All 'rhizomeprotocol' HTTP tests still fail
This commit is contained in:
Andrew Bettison 2013-10-25 17:38:51 +10:30
parent 04efb92ff6
commit 6488f7ad65
5 changed files with 234 additions and 125 deletions

View File

@ -233,6 +233,7 @@ ATOM(bool_t, dnaresponses, 0, boolean,, "")
ATOM(bool_t, dnahelper, 0, boolean,, "")
ATOM(bool_t, queues, 0, boolean,, "")
ATOM(bool_t, timing, 0, boolean,, "")
ATOM(bool_t, httpd, 0, boolean,, "")
ATOM(bool_t, io, 0, boolean,, "")
ATOM(bool_t, verbose_io, 0, boolean,, "")
ATOM(bool_t, interactive_io, 0, boolean,, "")

View File

@ -21,6 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <inttypes.h>
#include <time.h>
#include "serval.h"
#include "conf.h"
#include "http_server.h"
#include "log.h"
#include "str.h"
@ -67,6 +68,18 @@ static struct profile_total http_server_stats = {
.name = "http_server_poll",
};
#define DEBUG_DUMP_PARSED(r) do { \
if (config.debug.httpd) \
DEBUGF("%s %s HTTP/%u.%u", r->verb ? r->verb : "NULL", alloca_str_toprint(r->path), r->version_major, r->version_minor);\
} while (0)
#define DEBUG_DUMP_PARSER(r) do { \
if (config.debug.httpd) \
DEBUGF("parsed %d %s cursor %d %s", \
r->parsed - r->received, alloca_toprint(-1, r->received, r->parsed - r->received), \
r->cursor - r->received, alloca_toprint(50, r->cursor, r->end - r->cursor)); \
} while (0)
static void http_server_poll(struct sched_ent *);
static int http_request_parse_verb(struct http_request *r);
static int http_request_parse_path(struct http_request *r);
@ -79,7 +92,6 @@ static void http_request_start_response(struct http_request *r);
void http_request_init(struct http_request *r, int sockfd)
{
bzero(r, sizeof *r);
assert(sockfd != -1);
r->request_header.content_length = CONTENT_LENGTH_UNKNOWN;
r->request_content_remaining = CONTENT_LENGTH_UNKNOWN;
@ -93,7 +105,7 @@ void http_request_init(struct http_request *r, int sockfd)
r->alarm.poll.events = POLLIN;
r->phase = RECEIVE;
r->received = r->end = r->parsed = r->cursor = r->buffer;
r->limit = r->buffer + sizeof r->buffer;
r->end_content = NULL;
r->parser = http_request_parse_verb;
watch(&r->alarm);
schedule(&r->alarm);
@ -273,9 +285,14 @@ static const char * _reserve_str(struct http_request *r, const char *str)
return _reserve(r, sub);
}
static inline int _end_of_content(struct http_request *r)
{
return r->cursor == r->end_content;
}
static inline int _buffer_full(struct http_request *r)
{
return r->parsed == r->received && r->end == r->limit;
return r->parsed == r->received && r->end == (r->end_content ? r->end_content : r->buffer + sizeof r->buffer);
}
static inline int _run_out(struct http_request *r)
@ -296,6 +313,11 @@ static inline void _commit(struct http_request *r)
r->parsed = r->cursor;
}
static inline void _skip_all(struct http_request *r)
{
r->cursor = r->end;
}
static inline int _skip_crlf(struct http_request *r)
{
return !_run_out(r) && *r->cursor == '\r' && ++r->cursor && !_run_out(r) && *r->cursor == '\n' && ++r->cursor;
@ -550,13 +572,16 @@ static int _parse_quoted_rfc822_time(struct http_request *r, time_t *timep)
return 0;
}
/* Return 100 if more received data is needed. Returns 0 if parsing completes or fails. Returns a
* 4nn or 5nn HTTP result code if parsing fails. Returns -1 if an unexpected error occurs.
/* If parsing completes, then sets r->parser to the next parsing function and returns 0. If parsing
* cannot complete due to running out of data, returns 100 without changing r->parser, so this
* function will be called again once more data has been read. Returns a 4nn or 5nn HTTP result
* code if parsing fails. Returns -1 if an unexpected error occurs.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static int http_request_parse_verb(struct http_request *r)
{
DEBUG_DUMP_PARSER(r);
_rewind(r);
assert(r->cursor >= r->received);
assert(!_run_out(r));
@ -574,7 +599,7 @@ static int http_request_parse_verb(struct http_request *r)
}
if (r->verb == NULL) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Malformed HTTP request: invalid verb: %s", alloca_toprint(20, r->cursor, r->end - r->cursor));
DEBUGF("Malformed HTTP request, invalid verb: %s", alloca_toprint(20, r->cursor, r->end - r->cursor));
return 400;
}
_commit(r);
@ -582,13 +607,16 @@ static int http_request_parse_verb(struct http_request *r)
return 0;
}
/* Return 100 if more received data is needed. Returns 0 if parsing completes or fails. Returns a
* 4nn or 5nn HTTP result code if parsing fails. Returns -1 if an unexpected error occurs.
/* If parsing completes, then sets r->parser to the next parsing function and returns 0. If parsing
* cannot complete due to running out of data, returns 100 without changing r->parser, so this
* function will be called again once more data has been read. Returns a 4nn or 5nn HTTP result
* code if parsing fails. Returns -1 if an unexpected error occurs.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static int http_request_parse_path(struct http_request *r)
{
DEBUG_DUMP_PARSER(r);
// Parse path: word immediately following verb, delimited by spaces.
assert(r->path == NULL);
struct substring path;
@ -599,20 +627,23 @@ static int http_request_parse_path(struct http_request *r)
DEBUGF("Malformed HTTP %s request at path: %s", r->verb, alloca_toprint(20, r->parsed, r->end - r->parsed));
return 400;
}
_commit(r);
if ((r->path = _reserve(r, path)) == NULL)
return 0; // error
_commit(r);
r->parser = http_request_parse_http_version;
return 0;
}
/* Return 100 if more received data is needed. Returns 0 if parsing completes or fails. Returns a
* 4nn or 5nn HTTP result code if parsing fails. Returns -1 if an unexpected error occurs.
/* If parsing completes, then sets r->parser to the next parsing function and returns 0. If parsing
* cannot complete due to running out of data, returns 100 without changing r->parser, so this
* function will be called again once more data has been read. Returns a 4nn or 5nn HTTP result
* code if parsing fails. Returns -1 if an unexpected error occurs.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static int http_request_parse_http_version(struct http_request *r)
{
DEBUG_DUMP_PARSER(r);
// Parse HTTP version: HTTP/m.n followed by CRLF.
assert(r->version_major == 0);
assert(r->version_minor == 0);
@ -622,14 +653,14 @@ static int http_request_parse_http_version(struct http_request *r)
&& major > 0 && major < UINT8_MAX
&& _skip_literal(r, ".")
&& _parse_uint(r, &minor)
&& minor > 0 && minor < UINT8_MAX
&& minor < UINT8_MAX
&& _skip_eol(r)
)
) {
if (_run_out(r))
return 100; // read more and try again
if (r->debug_flag && *r->debug_flag)
DEBUGF("HTTP %s malformed request: malformed version: %s", r->verb, alloca_toprint(20, r->parsed, r->end - r->parsed));
DEBUGF("Malformed HTTP %s request at version: %s", r->verb, alloca_toprint(20, r->parsed, r->end - r->parsed));
return 400;
}
_commit(r);
@ -642,16 +673,16 @@ static int http_request_parse_http_version(struct http_request *r)
}
/* Select the header parser. Returns 0 after setting the new parser function. Returns a 4nn or 5nn
* HTTP result code if parsing fails.
* HTTP result code if the request cannot be handled (eg, unsupported HTTP version or invalid path).
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static int http_request_start_parsing_headers(struct http_request *r)
{
DEBUG_DUMP_PARSER(r);
assert(r->verb != NULL);
assert(r->path != NULL);
assert(r->version_major != 0);
assert(r->version_minor != 0);
if (r->version_major != 1) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Unsupported HTTP version: %u.%u", r->version_major, r->version_minor);
@ -661,14 +692,19 @@ static int http_request_start_parsing_headers(struct http_request *r)
return 0;
}
/* Parse one request header line. Returns 100 if more received data is needed. Returns 0 if there
* are no more headers or parsing fails. Returns a 4nn or 5nn HTTP result code if parsing fails.
* Returns -1 if an unexpected error occurs.
/* Parse one request header line.
*
* If the end of headers is parsed (blank line), then sets r->parser to the next parsing function
* and returns 0. If a single header line is successfully parsed, returns 0 after advancing
* r->parsed. If parsing cannot complete due to running out of data, returns 0 without changing
* r->parser, so this function will be called again once more data has been read. Returns a 4nn or
* 5nn HTTP result code if parsing fails. Returns -1 if an unexpected error occurs.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static int http_request_parse_header(struct http_request *r)
{
DEBUG_DUMP_PARSER(r);
_skip_to_eol(r);
const char *const eol = r->cursor;
_skip_eol(r);
@ -777,13 +813,16 @@ malformed:
return 400;
}
/* Select the header parser. Returns 0 after setting the new parser function. Returns a 4nn or 5nn
* HTTP result code if parsing fails.
/* If parsing completes, then sets r->parser to the next parsing function and returns 0. If parsing
* cannot complete due to running out of data, returns 0 without changing r->parser, so this
* function will be called again once more data has been read. Returns a 4nn or 5nn HTTP result
* code if parsing fails. Returns -1 if an unexpected error occurs.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static int http_request_start_body(struct http_request *r)
{
DEBUG_DUMP_PARSER(r);
assert(r->verb != NULL);
assert(r->path != NULL);
assert(r->version_major != 0);
@ -807,7 +846,7 @@ static int http_request_start_body(struct http_request *r)
if (r->request_header.content_length == CONTENT_LENGTH_UNKNOWN) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Malformed HTTP %s request: missing Content-Length header", r->verb);
return 400;
return 411;
}
if (r->request_header.content_type == NULL) {
if (r->debug_flag && *r->debug_flag)
@ -931,9 +970,11 @@ malformed:
return 1;
}
/* Return zero if more received data is needed. The first call that completes parsing of the body
* returns 200. All subsequent calls return 100. Returns a 4nn or 5nn HTTP result code if parsing
* fails.
/* 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.
* Returns a 4nn or 5nn HTTP result code if parsing fails. Returns -1 if an unexpected error
* occurs.
*
* NOTE: No support for nested/mixed parts, as that would considerably complicate the parser. If
* the need arises in future, we will deal with it then. In the meantime, we will have something
@ -960,15 +1001,22 @@ static int http_request_parse_body_form_data(struct http_request *r)
r->form_data.handle_mime_preamble(r, r->parsed, end_preamble - r->parsed);
_rewind_crlf(r);
_commit(r);
r->form_data_state = EPILOGUE;
if (b == 1) {
r->form_data_state = HEADER;
if (r->form_data.handle_mime_part_start)
r->form_data.handle_mime_part_start(r);
}
} else
r->form_data_state = EPILOGUE;
return 0;
}
_skip_to_crlf(r);
if (!_skip_to_crlf(r)) {
if (_end_of_content(r)) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Malformed HTTP %s form data: missing first boundary", r->verb);
return 400;
}
return 100; // need more data
}
at_start = 0;
}
if (r->cursor > r->parsed && r->form_data.handle_mime_preamble)
@ -982,7 +1030,7 @@ static int http_request_parse_body_form_data(struct http_request *r)
return 0;
}
if (_run_out(r))
return 0; // read more and try again
return 100; // read more and try again
_rewind(r);
int b;
if (_skip_crlf(r) && (b = _skip_mime_boundary(r))) {
@ -990,17 +1038,18 @@ static int http_request_parse_body_form_data(struct http_request *r)
_commit(r);
if (r->form_data.handle_mime_part_end)
r->form_data.handle_mime_part_end(r);
r->form_data_state = EPILOGUE;
// Boundary in the middle of headers starts a new part.
if (b == 1) {
r->form_data_state = HEADER;
if (r->form_data.handle_mime_part_start)
r->form_data.handle_mime_part_start(r);
}
else
r->form_data_state = EPILOGUE;
return 0;
}
if (_run_out(r))
return 0; // read more and try again
return 100; // read more and try again
_rewind(r);
struct substring label;
if (_skip_crlf(r) && _skip_token(r, &label) && _skip_literal(r, ":") && _skip_optional_space(r)) {
@ -1027,7 +1076,7 @@ static int http_request_parse_body_form_data(struct http_request *r)
}
}
if (_run_out(r))
return 0; // read more and try again
return 100; // read more and try again
_rewind(r);
}
if (r->debug_flag && *r->debug_flag)
@ -1052,7 +1101,14 @@ static int http_request_parse_body_form_data(struct http_request *r)
}
return 0;
}
_skip_to_crlf(r);
if (!_skip_to_crlf(r)) {
if (_end_of_content(r)) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Malformed HTTP %s form data part: missing end boundary", r->verb);
return 400;
}
return 100; // need more data
}
}
if (r->cursor > r->parsed && r->form_data.handle_mime_body)
r->form_data.handle_mime_body(r, r->parsed, r->parsed - r->cursor);
@ -1063,9 +1119,10 @@ static int http_request_parse_body_form_data(struct http_request *r)
if (r->form_data.handle_mime_epilogue && r->cursor != r->parsed)
r->form_data.handle_mime_epilogue(r, r->parsed, r->cursor - r->parsed);
_commit(r);
r->parser = NULL;
return 0;
}
assert(0); // not reached
abort(); // not reached
}
static ssize_t http_request_read(struct http_request *r, char *buf, size_t len)
@ -1090,21 +1147,43 @@ static ssize_t http_request_read(struct http_request *r, char *buf, size_t len)
static void http_request_receive(struct http_request *r)
{
assert(r->phase == RECEIVE);
r->limit = r->buffer + sizeof r->buffer;
assert(r->end < r->limit);
size_t room = r->limit - r->end;
const char *const bufend = r->buffer + sizeof r->buffer;
assert(r->end <= bufend);
assert(r->parsed >= r->received);
assert(r->parsed <= r->end);
// If buffer is running short on unused space, shift existing content in buffer down to make more
// room if possible.
if ( (room < 128 || (room < 1024 && r->parsed - r->received >= 32))
&& (r->request_content_remaining == CONTENT_LENGTH_UNKNOWN || room < r->request_content_remaining)
// Work out if the end of content falls within the buffer yet. If so, set the end_content
// pointer (and make sure it doesn't move).
if (r->end_content) {
assert(r->request_content_remaining != CONTENT_LENGTH_UNKNOWN);
assert(r->end < r->end_content);
assert(r->end_content - r->end == r->request_content_remaining);
} else if ( r->request_content_remaining != CONTENT_LENGTH_UNKNOWN
&& r->request_content_remaining < bufend - r->end
) {
r->end_content = r->end + r->request_content_remaining;
}
//
// If the end of content mark is within the buffer, then there is no need to make any more room,
// just keep reading up to the end of content. Otherwise, If buffer is running short on unused
// space, shift existing content in buffer down to make more room if possible.
size_t room;
if (r->end_content) {
assert(r->end_content > r->buffer);
assert(r->end_content <= bufend);
room = r->end_content - r->end;
} else {
room = bufend - r->end;
size_t spare = r->parsed - r->received;
if ( spare
&& (room < 128 || (room < 1024 && spare >= 32))
&& (r->request_content_remaining == CONTENT_LENGTH_UNKNOWN || spare >= r->request_content_remaining)
) {
size_t unparsed = r->end - r->parsed;
memmove((char *)r->received, r->parsed, unparsed); // memcpy() does not handle overlapping src and dst
r->parsed = r->received;
r->end = r->received + unparsed;
room = r->limit - r->end;
room = bufend - r->end;
}
}
// If there is no more buffer space, fail the request.
if (room == 0) {
@ -1113,35 +1192,34 @@ static void http_request_receive(struct http_request *r)
http_request_simple_response(r, 431, NULL);
return;
}
// Read up to the end of available buffer space or the end of content, whichever is first.
size_t read_size = room;
if (r->request_content_remaining != CONTENT_LENGTH_UNKNOWN && read_size > r->request_content_remaining) {
r->limit = r->end + r->request_content_remaining;
read_size = r->request_content_remaining;
}
if (read_size != 0) {
// Read as many bytes as possible into the unused buffer space. Any read error
// closes the connection without any response.
ssize_t bytes = http_request_read(r, (char *)r->end, read_size);
// Read up to the end of available buffer space or the end of content, whichever is first. Read
// as many bytes as possible into the unused buffer space. Any read error closes the connection
// without any response.
assert(room > 0);
if (r->request_content_remaining != CONTENT_LENGTH_UNKNOWN)
assert(room <= r->request_content_remaining);
ssize_t bytes = http_request_read(r, (char *)r->end, room);
if (bytes == -1)
return;
assert((size_t) bytes <= room);
// If no data was read, then just return to polling. Don't drop the connection on an empty read,
// because that drops connections when they shouldn't, including during testing. The inactivity
// timeout will drop the connections instead.
// timeout will drop inactive connections.
if (bytes == 0)
return;
r->end += (size_t) bytes;
if (r->request_content_remaining != CONTENT_LENGTH_UNKNOWN)
r->request_content_remaining -= (size_t) bytes;
// We got some data, so reset the inactivity timer and invoke the parsing state machine to process
// it. The state machine invokes the caller-supplied callback functions.
r->alarm.alarm = gettime_ms() + r->idle_timeout;
r->alarm.deadline = r->alarm.alarm + r->idle_timeout;
unschedule(&r->alarm);
schedule(&r->alarm);
}
// Parse the unparsed and received data.
while (r->phase == RECEIVE) {
int result;
if (_run_out(r) && r->request_content_remaining == 0) {
if (r->parsed == r->end_content) {
if (r->handle_content_end)
result = r->handle_content_end(r);
else {
@ -1150,26 +1228,27 @@ static void http_request_receive(struct http_request *r)
result = 500;
}
} else {
HTTP_REQUEST_PARSER oldparser = r->parser;
const char *oldparsed = r->parsed;
_rewind(r);
if (r->parser == NULL) {
if (r->debug_flag && *r->debug_flag)
DEBUG("Internal failure parsing HTTP request: no parser function set");
result = 500;
DEBUGF("No HTTP parser function set -- skipping %zu bytes", (size_t)(r->end - r->cursor));
_skip_all(r);
_commit(r);
result = 0;
} else {
_rewind(r);
result = r->parser(r);
assert(r->parsed >= oldparsed);
}
if (result == 100) {
if (_run_out(r))
if (r->phase != RECEIVE)
break;
if (result == 100)
return; // needs more data; poll again
if (r->debug_flag && *r->debug_flag)
DEBUG("Internal failure parsing HTTP request: parser function returned 100 but not run out");
result = 500;
}
if (result == 0 && r->parsed == oldparsed) {
if (result == 0 && r->parsed == oldparsed && r->parser == oldparser) {
if (r->debug_flag && *r->debug_flag)
DEBUG("Internal failure parsing HTTP request: parser function did not advance");
DEBUG_DUMP_PARSER(r);
result = 500;
}
}
@ -1180,10 +1259,8 @@ static void http_request_receive(struct http_request *r)
DEBUGF("Internal failure parsing HTTP request: invalid result=%d", result);
r->response.result_code = 500;
}
if (r->response.result_code) {
http_request_start_response(r);
return;
}
if (r->response.result_code)
break;
if (result == -1) {
if (r->debug_flag && *r->debug_flag)
DEBUG("Unrecoverable error parsing HTTP request, closing connection");
@ -1214,7 +1291,7 @@ static void http_request_send_response(struct http_request *r)
assert(r->response_buffer_sent <= r->response_buffer_length);
if (r->response_buffer_sent == r->response_buffer_length) {
if (r->response.content_generator) {
// Content generator must fill response_buffer[] and set response_buffer_length.
// Content generator must fill set (or re-set) response_buffer and response_buffer_length.
r->response_buffer_sent = r->response_buffer_length = 0;
if (r->response.content_generator(r) == -1) {
if (r->debug_flag && *r->debug_flag)
@ -1359,6 +1436,7 @@ static const char *httpResultString(int response_code)
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 411: return "Length Required";
case 414: return "Request-URI Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Requested Range Not Satisfiable";
@ -1404,6 +1482,7 @@ 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]);
strbuf_sprintf(sb, "HTTP/1.0 %03u %s\r\n", hr.result_code, result_string);
strbuf_sprintf(sb, "Content-Type: %s", hr.header.content_type);
if (hr.header.boundary) {
@ -1457,8 +1536,25 @@ static void http_request_render_response(struct http_request *r)
}
}
static size_t http_request_drain(struct http_request *r)
{
assert(r->phase == RECEIVE);
char buf[8192];
size_t drained = 0;
ssize_t bytes;
while ((bytes = http_request_read(r, buf, sizeof buf)) != -1 && bytes != 0)
drained += (size_t) bytes;
return drained;
}
static void http_request_start_response(struct http_request *r)
{
assert(r->phase == RECEIVE);
assert(r->response.result_code != 0);
if (r->response.content || r->response.content_generator) {
assert(r->response.header.content_type != NULL);
assert(r->response.header.content_type[0]);
}
// If HTTP responses are disabled (eg, for testing purposes) then skip all response construction
// and close the connection.
if (r->disable_tx_flag && *r->disable_tx_flag) {
@ -1468,11 +1564,9 @@ static void http_request_start_response(struct http_request *r)
}
// Drain the rest of the request that has not been received yet (eg, if sending an error response
// provoked while parsing the early part of a partially-received request). If a read error
// occurs, the connection is closed.
ssize_t bytes;
while ((bytes = http_request_read(r, (char *) r->received, (r->buffer + sizeof r->buffer) - r->received)) > 0)
;
if (bytes == -1)
// occurs, the connection is closed so the phase changes to DONE.
http_request_drain(r);
if (r->phase != RECEIVE)
return;
// If the response cannot be rendered, then render a 500 Server Error instead. If that fails,
// then just close the connection.
@ -1496,9 +1590,20 @@ static void http_request_start_response(struct http_request *r)
watch(&r->alarm);
}
/* Start sending a 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 content is set
* from the 'body' and 'bytes' parameters, which need not point to static data, ie, is no longer
* used once this function returns.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
void http_request_response(struct http_request *r, int result, const char *mime_type, const char *body, uint64_t bytes)
{
assert(result != 0);
assert(r->phase == RECEIVE);
assert(result >= 100);
assert(result < 600);
assert(mime_type != NULL);
assert(mime_type[0]);
r->response.result_code = result;
r->response.header.content_type = mime_type;
r->response.header.content_range_start = 0;
@ -1508,28 +1613,30 @@ void http_request_response(struct http_request *r, int result, const char *mime_
http_request_start_response(r);
}
/* Start sending a short redirection or error response back to the client. The result code must be
* either a redirection (3xx) or client error (4xx) or server error (5xx) code. The 'body' argument
* may be a bare message which is enclosed in an HTML envelope to form the response content, so it
* may contain HTML markup. If the 'body' argument is NULL, then the response content is generated
* automatically from the result code.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
void http_request_simple_response(struct http_request *r, uint16_t result, const char *body)
{
assert(r->phase == RECEIVE);
assert(result >= 300);
assert(result < 600);
strbuf h = NULL;
if (body) {
size_t html_len = strlen(body) + 40;
char html[html_len];
h = strbuf_local(html, html_len);
strbuf_sprintf(h, "<html><h1>%s</h1></html>", body);
h = strbuf_alloca(html_len);
strbuf_sprintf(h, "<html><h1>%03u %s</h1></html>", result, body);
}
r->response.result_code = result;
r->response.header.content_type = body ? "text/html" : NULL;
r->response.header.content_type = "text/html";
r->response.header.content_range_start = 0;
r->response.header.resource_length = r->response.header.content_length = h ? strbuf_len(h) : 0;
r->response.content = h ? strbuf_str(h) : NULL;
r->response.content_generator = NULL;
http_request_start_response(r);
}
void http_request_response_header(struct http_request *r, int result, const char *mime_type, uint64_t bytes)
{
r->response.result_code = result;
r->response.content = NULL;
r->response.content_generator = NULL;
http_request_start_response(r);
}

View File

@ -110,7 +110,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_response(struct http_request *r, int result, const char *mime_type, const char *body, uint64_t bytes);
void http_request_simple_response(struct http_request *r, uint16_t result, const char *body);
void http_request_response_header(struct http_request *r, int result, const char *mime_type, uint64_t bytes);
typedef int (*HTTP_REQUEST_PARSER)(struct http_request *);
struct http_request {
struct sched_ent alarm; // MUST BE FIRST ELEMENT
@ -120,10 +121,10 @@ struct 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_in_addr;
int (*parser)(struct http_request *); // current parser function
int (*handle_first_line)(struct http_request *); // called after first line is parsed
int (*handle_headers)(struct http_request *); // called after all headers are parsed
int (*handle_content_end)(struct http_request *); // called after all content is received
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 *);
@ -135,7 +136,7 @@ struct http_request {
struct http_request_headers request_header;
const char *received; // start of received data in buffer[]
const char *end; // end of received data in buffer[]
const char *limit; // end of content in buffer[]
const char *end_content; // end of content if within buffer[], else NULL
const char *parsed; // start of unparsed data in buffer[]
const char *cursor; // for parsing
http_size_t request_content_remaining;

View File

@ -181,7 +181,7 @@ int rhizome_fetch_queue_bytes(){
return bytes;
}
int rhizome_fetch_status_html(struct strbuf *b)
int rhizome_fetch_status_html(strbuf b)
{
int i,j;
for(i=0;i<NQUEUES;i++){

View File

@ -273,6 +273,7 @@ void rhizome_server_poll(struct sched_ent *alarm)
request->http.client_in_addr = *peerip;
request->http.handle_headers = rhizome_dispatch;
request->http.debug_flag = &config.debug.rhizome_httpd;
request->http.disable_tx_flag = &config.debug.rhizome_nohttptx;
request->http.finalise = rhizome_server_finalise_http_request;
request->http.free = free;
request->http.idle_timeout = RHIZOME_IDLE_TIMEOUT;
@ -330,7 +331,7 @@ static int neighbour_page(rhizome_http_request *r, const char *remainder)
strbuf_puts(b, "</body></html>");
if (strbuf_overrun(b))
return -1;
http_request_simple_response(&r->http, 200, buf);
http_request_response(&r->http, 200, "text/html", buf, strbuf_len(b));
return 0;
}
@ -350,7 +351,7 @@ static int interface_page(rhizome_http_request *r, const char *remainder)
strbuf_puts(b, "</body></html>");
if (strbuf_overrun(b))
return -1;
http_request_simple_response(&r->http, 200, buf);
http_request_response(&r->http, 200, "text/html", buf, strbuf_len(b));
return 0;
}
@ -365,16 +366,15 @@ static int rhizome_status_page(rhizome_http_request *r, const char *remainder)
return 0;
}
char buf[32*1024];
struct strbuf b;
strbuf_init(&b, buf, sizeof buf);
strbuf_puts(&b, "<html><head><meta http-equiv=\"refresh\" content=\"5\" ></head><body>");
strbuf_sprintf(&b, "%d HTTP requests<br>", request_count);
strbuf_sprintf(&b, "%d Bundles transferring via MDP<br>", rhizome_cache_count());
rhizome_fetch_status_html(&b);
strbuf_puts(&b, "</body></html>");
if (strbuf_overrun(&b))
strbuf b = strbuf_local(buf, sizeof buf);
strbuf_puts(b, "<html><head><meta http-equiv=\"refresh\" content=\"5\" ></head><body>");
strbuf_sprintf(b, "%d HTTP requests<br>", request_count);
strbuf_sprintf(b, "%d Bundles transferring via MDP<br>", rhizome_cache_count());
rhizome_fetch_status_html(b);
strbuf_puts(b, "</body></html>");
if (strbuf_overrun(b))
return -1;
http_request_simple_response(&r->http, 200, buf);
http_request_response(&r->http, 200, "text/html", buf, strbuf_len(b));
return 0;
}
@ -521,6 +521,6 @@ static int root_page(rhizome_http_request *r, const char *remainder)
WHY("HTTP Root page buffer overrun");
http_request_simple_response(&r->http, 500, NULL);
} else
http_request_simple_response(&r->http, 200, temp);
http_request_response(&r->http, 200, "text/html", temp, strbuf_len(b));
return 0;
}