|
|
|
@ -22,13 +22,12 @@
|
|
|
|
|
VEC(u8)
|
|
|
|
|
|
|
|
|
|
struct http_session {
|
|
|
|
|
/* HTTP State */
|
|
|
|
|
struct sockaddr client_address; /* client requesting connection! */
|
|
|
|
|
int socket;
|
|
|
|
|
struct http_parser http_parser;
|
|
|
|
|
struct http_request http_request;
|
|
|
|
|
struct vec_u8 request;
|
|
|
|
|
struct vec_u8 response;
|
|
|
|
|
struct vec_u8 request_buffer;
|
|
|
|
|
struct vec_u8 response_buffer;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -39,6 +38,7 @@ static inline int
|
|
|
|
|
http_session_init(struct http_session *session, int socket_descriptor, const struct sockaddr *socket_address)
|
|
|
|
|
{
|
|
|
|
|
assert(session != NULL);
|
|
|
|
|
assert(socket_descriptor >= 0);
|
|
|
|
|
assert(socket_address != NULL);
|
|
|
|
|
|
|
|
|
|
session->socket = socket_descriptor;
|
|
|
|
@ -46,17 +46,16 @@ http_session_init(struct http_session *session, int socket_descriptor, const str
|
|
|
|
|
|
|
|
|
|
http_parser_init(&session->http_parser, HTTP_REQUEST);
|
|
|
|
|
|
|
|
|
|
/* Set the session as the data the http-parser has access to */
|
|
|
|
|
/* Set the http_request member as the data pointer the http_parser callbacks receive */
|
|
|
|
|
session->http_parser.data = &session->http_request;
|
|
|
|
|
|
|
|
|
|
memset(&session->http_request, 0, sizeof(struct http_parser));
|
|
|
|
|
|
|
|
|
|
int rc;
|
|
|
|
|
rc = vec_u8_init(&session->request, HTTP_SESSION_DEFAULT_REQUEST_RESPONSE_SIZE);
|
|
|
|
|
int rc = vec_u8_init(&session->request_buffer, HTTP_SESSION_DEFAULT_REQUEST_RESPONSE_SIZE);
|
|
|
|
|
if (rc < 0) return -1;
|
|
|
|
|
|
|
|
|
|
/* Defer allocating response until we've matched a route */
|
|
|
|
|
session->response.buffer = NULL;
|
|
|
|
|
session->response_buffer.buffer = NULL;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -65,19 +64,23 @@ static inline int
|
|
|
|
|
http_session_init_response_buffer(struct http_session *session, size_t capacity)
|
|
|
|
|
{
|
|
|
|
|
assert(session != NULL);
|
|
|
|
|
assert(session->response.buffer == NULL);
|
|
|
|
|
assert(session->response_buffer.buffer == NULL);
|
|
|
|
|
|
|
|
|
|
int rc = vec_u8_init(&session->response, capacity);
|
|
|
|
|
int rc = vec_u8_init(&session->response_buffer, capacity);
|
|
|
|
|
if (rc < 0) {
|
|
|
|
|
vec_u8_deinit(&session->request);
|
|
|
|
|
vec_u8_deinit(&session->request_buffer);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline struct http_session *
|
|
|
|
|
http_session_alloc(int socket_descriptor, const struct sockaddr *socket_address)
|
|
|
|
|
{
|
|
|
|
|
assert(socket_descriptor >= 0);
|
|
|
|
|
assert(socket_address != NULL);
|
|
|
|
|
|
|
|
|
|
struct http_session *session = calloc(sizeof(struct http_session), 1);
|
|
|
|
|
if (session == NULL) return NULL;
|
|
|
|
|
|
|
|
|
@ -98,14 +101,16 @@ static inline void
|
|
|
|
|
http_session_deinit(struct http_session *session)
|
|
|
|
|
{
|
|
|
|
|
assert(session);
|
|
|
|
|
vec_u8_deinit(&session->request);
|
|
|
|
|
vec_u8_deinit(&session->response);
|
|
|
|
|
|
|
|
|
|
vec_u8_deinit(&session->request_buffer);
|
|
|
|
|
vec_u8_deinit(&session->response_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
http_session_free(struct http_session *session)
|
|
|
|
|
{
|
|
|
|
|
assert(session);
|
|
|
|
|
|
|
|
|
|
http_session_deinit(session);
|
|
|
|
|
free(session);
|
|
|
|
|
}
|
|
|
|
@ -119,6 +124,9 @@ http_session_free(struct http_session *session)
|
|
|
|
|
static inline int
|
|
|
|
|
http_session_send_err(struct http_session *session, int status_code, void_cb on_eagain)
|
|
|
|
|
{
|
|
|
|
|
assert(session != NULL);
|
|
|
|
|
assert(status_code >= 100 && status_code <= 599);
|
|
|
|
|
|
|
|
|
|
return tcp_session_send(session->socket, http_header_build(status_code), http_header_len(status_code),
|
|
|
|
|
on_eagain);
|
|
|
|
|
}
|
|
|
|
@ -126,25 +134,29 @@ http_session_send_err(struct http_session *session, int status_code, void_cb on_
|
|
|
|
|
static inline int
|
|
|
|
|
http_session_send_err_oneshot(struct http_session *session, int status_code)
|
|
|
|
|
{
|
|
|
|
|
assert(session != NULL);
|
|
|
|
|
assert(status_code >= 100 && status_code <= 599);
|
|
|
|
|
|
|
|
|
|
return tcp_session_send_oneshot(session->socket, http_header_build(status_code), http_header_len(status_code));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
|
http_session_send_response(struct http_session *session, const char *response_content_type, void_cb on_eagain)
|
|
|
|
|
{
|
|
|
|
|
struct vec_u8 *response = &session->response;
|
|
|
|
|
assert(response != NULL);
|
|
|
|
|
assert(session != NULL);
|
|
|
|
|
assert(session->response_buffer.buffer != NULL);
|
|
|
|
|
assert(response_content_type != NULL);
|
|
|
|
|
|
|
|
|
|
int rc;
|
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
|
|
/* Determine values to template into our HTTP response */
|
|
|
|
|
const char *content_type = strlen(response_content_type) > 0 ? response_content_type : "text/plain";
|
|
|
|
|
struct vec_u8 *response_buffer = &session->response_buffer;
|
|
|
|
|
|
|
|
|
|
/* Send HTTP Response Header and Body */
|
|
|
|
|
rc = http_header_200_write(session->socket, content_type, response->length);
|
|
|
|
|
rc = http_header_200_write(session->socket, response_content_type, response_buffer->length);
|
|
|
|
|
if (rc < 0) goto err;
|
|
|
|
|
|
|
|
|
|
rc = tcp_session_send(session->socket, (const char *)response->buffer, response->length, on_eagain);
|
|
|
|
|
rc = tcp_session_send(session->socket, (const char *)response_buffer->buffer, response_buffer->length,
|
|
|
|
|
on_eagain);
|
|
|
|
|
if (rc < 0) goto err;
|
|
|
|
|
|
|
|
|
|
http_total_increment_2xx();
|
|
|
|
@ -161,6 +173,8 @@ err:
|
|
|
|
|
static inline void
|
|
|
|
|
http_session_close(struct http_session *session)
|
|
|
|
|
{
|
|
|
|
|
assert(session != NULL);
|
|
|
|
|
|
|
|
|
|
return tcp_session_close(session->socket, &session->client_address);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -170,15 +184,15 @@ http_session_close(struct http_session *session)
|
|
|
|
|
* @return 0 if message parsing complete, -1 on error, -2 if buffers run out of space
|
|
|
|
|
*/
|
|
|
|
|
static inline int
|
|
|
|
|
http_session_receive(struct http_session *session, void_cb on_eagain)
|
|
|
|
|
http_session_receive_request(struct http_session *session, void_cb on_eagain)
|
|
|
|
|
{
|
|
|
|
|
assert(session != NULL);
|
|
|
|
|
|
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
|
|
struct vec_u8 *request = &session->request;
|
|
|
|
|
assert(request->capacity > 0);
|
|
|
|
|
assert(request->length < request->capacity);
|
|
|
|
|
struct vec_u8 *request_buffer = &session->request_buffer;
|
|
|
|
|
assert(request_buffer->capacity > 0);
|
|
|
|
|
assert(request_buffer->length < request_buffer->capacity);
|
|
|
|
|
|
|
|
|
|
while (!session->http_request.message_end) {
|
|
|
|
|
/* Read from the Socket */
|
|
|
|
@ -189,20 +203,20 @@ http_session_receive(struct http_session *session, void_cb on_eagain)
|
|
|
|
|
|
|
|
|
|
/* If header parsing is complete, resize using content-length */
|
|
|
|
|
if (session->http_request.header_end && session->http_request.body != NULL) {
|
|
|
|
|
int header_size = (uint8_t *)session->http_request.body - session->request.buffer;
|
|
|
|
|
int header_size = (uint8_t *)session->http_request.body - session->request_buffer.buffer;
|
|
|
|
|
assert(header_size > 0);
|
|
|
|
|
int required_size = header_size + session->http_request.body_length;
|
|
|
|
|
|
|
|
|
|
assert(required_size > 0);
|
|
|
|
|
|
|
|
|
|
if (required_size > request->capacity) {
|
|
|
|
|
uint8_t *old_buffer = request->buffer;
|
|
|
|
|
if (vec_u8_resize(request, required_size) != 0) {
|
|
|
|
|
if (required_size > request_buffer->capacity) {
|
|
|
|
|
uint8_t *old_buffer = request_buffer->buffer;
|
|
|
|
|
if (vec_u8_resize(request_buffer, required_size) != 0) {
|
|
|
|
|
debuglog("Failed to resize request vector to %d bytes\n", required_size);
|
|
|
|
|
goto err_nobufs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (old_buffer != request->buffer) {
|
|
|
|
|
if (old_buffer != request_buffer->buffer) {
|
|
|
|
|
/* buffer moved, so invalidate to reparse */
|
|
|
|
|
memset(&session->http_request, 0, sizeof(struct http_request));
|
|
|
|
|
http_parser_init(&session->http_parser, HTTP_REQUEST);
|
|
|
|
@ -210,16 +224,16 @@ http_session_receive(struct http_session *session, void_cb on_eagain)
|
|
|
|
|
session->http_parser.data = &session->http_request;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (request->length == request->capacity) {
|
|
|
|
|
} else if (request_buffer->length == request_buffer->capacity) {
|
|
|
|
|
/* Otherwise, we have a huge header and should just grow */
|
|
|
|
|
uint8_t *old_buffer = request->buffer;
|
|
|
|
|
uint8_t *old_buffer = request_buffer->buffer;
|
|
|
|
|
|
|
|
|
|
if (vec_u8_grow(request) != 0) {
|
|
|
|
|
if (vec_u8_grow(request_buffer) != 0) {
|
|
|
|
|
debuglog("Failed to grow request buffer\n");
|
|
|
|
|
goto err_nobufs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (old_buffer != request->buffer) {
|
|
|
|
|
if (old_buffer != request_buffer->buffer) {
|
|
|
|
|
/* buffer moved, so invalidate to reparse */
|
|
|
|
|
memset(&session->http_request, 0, sizeof(struct http_request));
|
|
|
|
|
http_parser_init(&session->http_parser, HTTP_REQUEST);
|
|
|
|
@ -228,8 +242,8 @@ http_session_receive(struct http_session *session, void_cb on_eagain)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ssize_t bytes_received = recv(session->socket, &request->buffer[request->length],
|
|
|
|
|
request->capacity - request->length, 0);
|
|
|
|
|
ssize_t bytes_received = recv(session->socket, &request_buffer->buffer[request_buffer->length],
|
|
|
|
|
request_buffer->capacity - request_buffer->length, 0);
|
|
|
|
|
|
|
|
|
|
if (bytes_received < 0) {
|
|
|
|
|
if (errno == EAGAIN) {
|
|
|
|
@ -260,16 +274,16 @@ http_session_receive(struct http_session *session, void_cb on_eagain)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(bytes_received > 0);
|
|
|
|
|
request->length += bytes_received;
|
|
|
|
|
request_buffer->length += bytes_received;
|
|
|
|
|
|
|
|
|
|
#ifdef LOG_HTTP_PARSER
|
|
|
|
|
debuglog("http_parser_execute(%p, %p, %p, %zu\n)", parser, settings,
|
|
|
|
|
&session->request.buffer[session->request.length], bytes_received);
|
|
|
|
|
&session->request_buffer.buffer[session->request_buffer.length], bytes_received);
|
|
|
|
|
#endif
|
|
|
|
|
size_t bytes_parsed =
|
|
|
|
|
http_parser_execute(parser, settings,
|
|
|
|
|
(const char *)&request->buffer[session->http_request.length_parsed],
|
|
|
|
|
(size_t)request->length - session->http_request.length_parsed);
|
|
|
|
|
(const char *)&request_buffer->buffer[session->http_request.length_parsed],
|
|
|
|
|
(size_t)request_buffer->length - session->http_request.length_parsed);
|
|
|
|
|
|
|
|
|
|
if (bytes_parsed < (size_t)bytes_received) {
|
|
|
|
|
debuglog("Error: %s, Description: %s\n",
|
|
|
|
@ -304,3 +318,31 @@ err:
|
|
|
|
|
rc = -1;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Writes to the HTTP response buffer
|
|
|
|
|
* On success, the number of bytes written is returned. On error, -1 is returned,
|
|
|
|
|
*/
|
|
|
|
|
static inline int
|
|
|
|
|
http_session_write_response(struct http_session *session, const uint8_t *source, size_t n)
|
|
|
|
|
{
|
|
|
|
|
assert(session);
|
|
|
|
|
assert(session->response_buffer.buffer != NULL);
|
|
|
|
|
assert(source);
|
|
|
|
|
|
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
|
|
if (session->response_buffer.capacity - session->response_buffer.length < n) {
|
|
|
|
|
rc = vec_u8_grow(&session->response_buffer);
|
|
|
|
|
if (rc != 0) goto DONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(session->response_buffer.capacity - session->response_buffer.length >= n);
|
|
|
|
|
|
|
|
|
|
memcpy(&session->response_buffer.buffer[session->response_buffer.length], source, n);
|
|
|
|
|
session->response_buffer.length += n;
|
|
|
|
|
rc = n;
|
|
|
|
|
|
|
|
|
|
DONE:
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|