mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-01-18 02:39:44 +00:00
Fix basic bugs in new HTTP server code
All 'rhizomeprotocol' HTTP tests still fail
This commit is contained in:
parent
04efb92ff6
commit
6488f7ad65
@ -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,, "")
|
||||
|
281
http_server.c
281
http_server.c
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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++){
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user