I think the new HTTP implementation is basically working, now.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4707 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
2f6a92168e
commit
d02f17a8cf
14
core/core.js
14
core/core.js
@ -1006,6 +1006,20 @@ loadSettings().then(function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
httpd_impl.start(tildefriends.http_port);
|
httpd_impl.start(tildefriends.http_port);
|
||||||
|
|
||||||
|
if (tildefriends.args.httpdc && tildefriends.https_port) {
|
||||||
|
async function start_tls() {
|
||||||
|
const kCertificatePath = "data/httpd/certificate.pem";
|
||||||
|
const kPrivateKeyPath = "data/httpd/privatekey.pem";
|
||||||
|
let privateKey = utf8Decode(await File.readFile(kPrivateKeyPath));
|
||||||
|
let certificate = utf8Decode(await File.readFile(kCertificatePath));
|
||||||
|
let context = new TlsContext();
|
||||||
|
context.setPrivateKey(privateKey);
|
||||||
|
context.setCertificate(certificate);
|
||||||
|
httpd_impl.start(tildefriends.https_port, context);
|
||||||
|
}
|
||||||
|
start_tls();
|
||||||
|
}
|
||||||
}).catch(function(error) {
|
}).catch(function(error) {
|
||||||
print('Failed to load settings.');
|
print('Failed to load settings.');
|
||||||
printError({print: print}, error);
|
printError({print: print}, error);
|
||||||
|
251
src/http.c
251
src/http.c
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
|
#include "tls.h"
|
||||||
#include "util.js.h"
|
#include "util.js.h"
|
||||||
|
|
||||||
#include "picohttpparser.h"
|
#include "picohttpparser.h"
|
||||||
@ -21,27 +22,30 @@ static const int k_timeout_ms = 60000;
|
|||||||
typedef struct _tf_http_connection_t
|
typedef struct _tf_http_connection_t
|
||||||
{
|
{
|
||||||
tf_http_t* http;
|
tf_http_t* http;
|
||||||
|
tf_tls_session_t* tls;
|
||||||
uv_tcp_t tcp;
|
uv_tcp_t tcp;
|
||||||
uv_shutdown_t shutdown;
|
uv_shutdown_t shutdown;
|
||||||
uv_timer_t timeout;
|
uv_timer_t timeout;
|
||||||
|
|
||||||
int ref_count;
|
int ref_count;
|
||||||
|
bool is_handshaking;
|
||||||
|
bool is_receiving_headers;
|
||||||
|
bool is_response_sent;
|
||||||
|
bool is_shutting_down;
|
||||||
|
|
||||||
const char* method;
|
const char* method;
|
||||||
const char* path;
|
const char* path;
|
||||||
const char* query;
|
const char* query;
|
||||||
int minor_version;
|
int minor_version;
|
||||||
|
|
||||||
|
char incoming[8192];
|
||||||
|
|
||||||
char headers_buffer[8192];
|
char headers_buffer[8192];
|
||||||
size_t headers_buffer_length;
|
size_t headers_buffer_length;
|
||||||
int parsed_length;
|
int parsed_length;
|
||||||
|
|
||||||
char buffer[8192];
|
|
||||||
size_t buffer_length;
|
|
||||||
|
|
||||||
struct phr_header headers[32];
|
struct phr_header headers[32];
|
||||||
int headers_length;
|
int headers_length;
|
||||||
bool headers_done;
|
|
||||||
|
|
||||||
tf_http_callback_t* callback;
|
tf_http_callback_t* callback;
|
||||||
tf_http_request_t* request;
|
tf_http_request_t* request;
|
||||||
@ -62,9 +66,16 @@ typedef struct _tf_http_handler_t
|
|||||||
void* user_data;
|
void* user_data;
|
||||||
} tf_http_handler_t;
|
} tf_http_handler_t;
|
||||||
|
|
||||||
|
typedef struct _tf_http_listener_t
|
||||||
|
{
|
||||||
|
tf_http_t* http;
|
||||||
|
tf_tls_context_t* tls;
|
||||||
|
uv_tcp_t tcp;
|
||||||
|
} tf_http_listener_t;
|
||||||
|
|
||||||
typedef struct _tf_http_t
|
typedef struct _tf_http_t
|
||||||
{
|
{
|
||||||
uv_tcp_t** listeners;
|
tf_http_listener_t** listeners;
|
||||||
int listeners_count;
|
int listeners_count;
|
||||||
|
|
||||||
tf_http_connection_t** connections;
|
tf_http_connection_t** connections;
|
||||||
@ -81,6 +92,7 @@ static const char* _http_connection_get_header(const tf_http_connection_t* conne
|
|||||||
static void _http_connection_destroy(tf_http_connection_t* connection, const char* reason);
|
static void _http_connection_destroy(tf_http_connection_t* connection, const char* reason);
|
||||||
static const char* _http_status_text(int status);
|
static const char* _http_status_text(int status);
|
||||||
static void _http_timer_reset(tf_http_connection_t* connection);
|
static void _http_timer_reset(tf_http_connection_t* connection);
|
||||||
|
static void _http_tls_update(tf_http_connection_t* connection);
|
||||||
|
|
||||||
tf_http_t* tf_http_create(uv_loop_t* loop)
|
tf_http_t* tf_http_create(uv_loop_t* loop)
|
||||||
{
|
{
|
||||||
@ -92,17 +104,10 @@ tf_http_t* tf_http_create(uv_loop_t* loop)
|
|||||||
return http;
|
return http;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _http_allocate_buffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf)
|
void _http_allocate_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
|
||||||
{
|
{
|
||||||
tf_http_connection_t* connection = handle->data;
|
tf_http_connection_t* connection = handle->data;
|
||||||
if (!connection->headers_done)
|
*buf = uv_buf_init(connection->incoming, sizeof(connection->incoming));
|
||||||
{
|
|
||||||
*buf = uv_buf_init(connection->headers_buffer + connection->headers_buffer_length, sizeof(connection->headers_buffer) - connection->headers_buffer_length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*buf = uv_buf_init(connection->buffer + connection->buffer_length, sizeof(connection->buffer) - connection->buffer_length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _http_find_handler(tf_http_t* http, const char* path, tf_http_callback_t** out_callback, void** out_user_data)
|
bool _http_find_handler(tf_http_t* http, const char* path, tf_http_callback_t** out_callback, void** out_user_data)
|
||||||
@ -136,6 +141,8 @@ static void _http_connection_on_close(uv_handle_t* handle)
|
|||||||
|
|
||||||
static void _http_connection_destroy(tf_http_connection_t* connection, const char* reason)
|
static void _http_connection_destroy(tf_http_connection_t* connection, const char* reason)
|
||||||
{
|
{
|
||||||
|
connection->is_shutting_down = true;
|
||||||
|
|
||||||
if (connection->tcp.data && !uv_is_closing((uv_handle_t*)&connection->tcp))
|
if (connection->tcp.data && !uv_is_closing((uv_handle_t*)&connection->tcp))
|
||||||
{
|
{
|
||||||
uv_close((uv_handle_t*)&connection->tcp, _http_connection_on_close);
|
uv_close((uv_handle_t*)&connection->tcp, _http_connection_on_close);
|
||||||
@ -144,6 +151,11 @@ static void _http_connection_destroy(tf_http_connection_t* connection, const cha
|
|||||||
{
|
{
|
||||||
uv_close((uv_handle_t*)&connection->timeout, _http_connection_on_close);
|
uv_close((uv_handle_t*)&connection->timeout, _http_connection_on_close);
|
||||||
}
|
}
|
||||||
|
if (connection->tls)
|
||||||
|
{
|
||||||
|
tf_tls_session_destroy(connection->tls);
|
||||||
|
connection->tls = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (connection->ref_count == 0 &&
|
if (connection->ref_count == 0 &&
|
||||||
!connection->tcp.data &&
|
!connection->tcp.data &&
|
||||||
@ -175,11 +187,14 @@ static void _http_builtin_404_handler(tf_http_request_t* request)
|
|||||||
|
|
||||||
static void _http_reset_connection(tf_http_connection_t* connection)
|
static void _http_reset_connection(tf_http_connection_t* connection)
|
||||||
{
|
{
|
||||||
connection->headers_done = false;
|
|
||||||
connection->headers_buffer_length = 0;
|
|
||||||
connection->body_length = 0;
|
connection->body_length = 0;
|
||||||
connection->content_length = 0;
|
connection->content_length = 0;
|
||||||
|
connection->headers_buffer_length = 0;
|
||||||
|
connection->headers_length = 0;
|
||||||
|
connection->is_receiving_headers = true;
|
||||||
|
connection->is_response_sent = false;
|
||||||
connection->parsed_length = 0;
|
connection->parsed_length = 0;
|
||||||
|
connection->path = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _http_websocket_mask_in_place(uint8_t* p, uint32_t mask, size_t size)
|
static void _http_websocket_mask_in_place(uint8_t* p, uint32_t mask, size_t size)
|
||||||
@ -268,6 +283,10 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d
|
|||||||
memmove(connection->body, (char*)connection->body + total_length, connection->body_length - total_length);
|
memmove(connection->body, (char*)connection->body + total_length, connection->body_length - total_length);
|
||||||
connection->body_length -= total_length;
|
connection->body_length -= total_length;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -300,23 +319,17 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d
|
|||||||
tf_http_request_ref(request);
|
tf_http_request_ref(request);
|
||||||
connection->callback(request);
|
connection->callback(request);
|
||||||
tf_http_request_release(request);
|
tf_http_request_release(request);
|
||||||
if (!connection->is_websocket)
|
|
||||||
{
|
|
||||||
_http_reset_connection(connection);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _http_on_read(uv_stream_t* stream, ssize_t read_size, const uv_buf_t* buffer)
|
static size_t _http_on_read_plain_internal(tf_http_connection_t* connection, const void* data, size_t read_size)
|
||||||
{
|
{
|
||||||
tf_http_connection_t* connection = stream->data;
|
if (connection->is_receiving_headers)
|
||||||
_http_timer_reset(connection);
|
|
||||||
if (read_size > 0)
|
|
||||||
{
|
{
|
||||||
if (!connection->headers_done)
|
size_t used_read_size = tf_min(read_size, sizeof(connection->headers_buffer) - connection->headers_buffer_length);
|
||||||
{
|
memcpy(connection->headers_buffer + connection->headers_buffer_length, data, used_read_size);
|
||||||
connection->headers_buffer_length += read_size;
|
connection->headers_buffer_length += used_read_size;
|
||||||
|
|
||||||
const char* method = NULL;
|
const char* method = NULL;
|
||||||
size_t method_length = 0;
|
size_t method_length = 0;
|
||||||
@ -328,7 +341,7 @@ static void _http_on_read(uv_stream_t* stream, ssize_t read_size, const uv_buf_t
|
|||||||
connection->parsed_length = connection->headers_buffer_length;
|
connection->parsed_length = connection->headers_buffer_length;
|
||||||
if (parse_result > 0)
|
if (parse_result > 0)
|
||||||
{
|
{
|
||||||
connection->headers_done = true;
|
connection->is_receiving_headers = false;
|
||||||
connection->headers_length = header_count;
|
connection->headers_length = header_count;
|
||||||
connection->method = method;
|
connection->method = method;
|
||||||
((char*)connection->method)[method_length] = '\0';
|
((char*)connection->method)[method_length] = '\0';
|
||||||
@ -377,24 +390,64 @@ static void _http_on_read(uv_stream_t* stream, ssize_t read_size, const uv_buf_t
|
|||||||
{
|
{
|
||||||
connection->callback = _http_builtin_404_handler;
|
connection->callback = _http_builtin_404_handler;
|
||||||
}
|
}
|
||||||
|
size_t consumed = read_size - (connection->headers_buffer_length - connection->parsed_length) - (read_size - used_read_size);
|
||||||
_http_add_body_bytes(connection, connection->headers_buffer + parse_result, connection->headers_buffer_length - parse_result);
|
_http_add_body_bytes(connection, NULL, 0);
|
||||||
|
return consumed;
|
||||||
}
|
}
|
||||||
else if (parse_result == -2)
|
else if (parse_result == -2)
|
||||||
{
|
{
|
||||||
/* Incomplete. Will try again next time. */
|
/* Incomplete. Will try again next time. */
|
||||||
|
return used_read_size;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tf_printf("phr_parse_request: %d\n", parse_result);
|
tf_printf("phr_parse_request: %d\n", parse_result);
|
||||||
_http_connection_destroy(connection, "failed to parse request headers");
|
_http_connection_destroy(connection, "failed to parse request headers");
|
||||||
|
return used_read_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
connection->buffer_length += read_size;
|
_http_add_body_bytes(connection, data, read_size);
|
||||||
_http_add_body_bytes(connection, connection->buffer, connection->buffer_length);
|
return read_size;
|
||||||
connection->buffer_length = 0;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _http_on_read_plain(tf_http_connection_t* connection, const void* data, size_t read_size)
|
||||||
|
{
|
||||||
|
size_t total_consumed = 0;
|
||||||
|
while (total_consumed < read_size)
|
||||||
|
{
|
||||||
|
size_t consumed = _http_on_read_plain_internal(connection, ((const uint8_t*)data) + total_consumed, read_size - total_consumed);
|
||||||
|
if (!consumed)
|
||||||
|
{
|
||||||
|
_http_connection_destroy(connection, "_http_on_read_plain_internal didn't consume any data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
total_consumed += consumed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _http_on_read(uv_stream_t* stream, ssize_t read_size, const uv_buf_t* buffer)
|
||||||
|
{
|
||||||
|
tf_http_connection_t* connection = stream->data;
|
||||||
|
_http_timer_reset(connection);
|
||||||
|
if (read_size > 0)
|
||||||
|
{
|
||||||
|
if (connection->tls)
|
||||||
|
{
|
||||||
|
if (tf_tls_session_write_encrypted(connection->tls, buffer->base, read_size) < 0)
|
||||||
|
{
|
||||||
|
_http_connection_destroy(connection, "tf_tls_session_write_encrypted");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_http_tls_update(connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_http_on_read_plain(connection, buffer->base, read_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (read_size < 0)
|
else if (read_size < 0)
|
||||||
@ -428,9 +481,21 @@ static void _http_timer_reset(tf_http_connection_t* connection)
|
|||||||
|
|
||||||
static void _http_on_connection(uv_stream_t* stream, int status)
|
static void _http_on_connection(uv_stream_t* stream, int status)
|
||||||
{
|
{
|
||||||
tf_http_t* http = stream->data;
|
tf_http_listener_t* listener = stream->data;
|
||||||
|
tf_http_t* http = listener->http;
|
||||||
tf_http_connection_t* connection = tf_malloc(sizeof(tf_http_connection_t));
|
tf_http_connection_t* connection = tf_malloc(sizeof(tf_http_connection_t));
|
||||||
*connection = (tf_http_connection_t) { .http = http, .tcp = { .data = connection } };
|
*connection = (tf_http_connection_t) { .http = http, .tcp = { .data = connection }, .is_receiving_headers = true };
|
||||||
|
if (listener->tls)
|
||||||
|
{
|
||||||
|
connection->tls = tf_tls_context_create_session(listener->tls);
|
||||||
|
if (!connection->tls)
|
||||||
|
{
|
||||||
|
_http_connection_destroy(connection, "tf_tls_context_create_session");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tf_tls_session_start_accept(connection->tls);
|
||||||
|
connection->is_handshaking = true;
|
||||||
|
}
|
||||||
int r = uv_tcp_init(connection->http->loop, &connection->tcp);
|
int r = uv_tcp_init(connection->http->loop, &connection->tcp);
|
||||||
if (r)
|
if (r)
|
||||||
{
|
{
|
||||||
@ -471,15 +536,20 @@ static void _http_on_connection(uv_stream_t* stream, int status)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (connection->tls)
|
||||||
|
{
|
||||||
|
_http_tls_update(connection);
|
||||||
|
}
|
||||||
|
|
||||||
http->connections = tf_realloc(http->connections, sizeof(tf_http_connection_t*) * (http->connections_count + 1));
|
http->connections = tf_realloc(http->connections, sizeof(tf_http_connection_t*) * (http->connections_count + 1));
|
||||||
http->connections[http->connections_count++] = connection;
|
http->connections[http->connections_count++] = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_http_listen(tf_http_t* http, int port)
|
void tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls)
|
||||||
{
|
{
|
||||||
uv_tcp_t* tcp = tf_malloc(sizeof(uv_tcp_t));
|
tf_http_listener_t* listener = tf_malloc(sizeof(tf_http_listener_t));
|
||||||
*tcp = (uv_tcp_t) { .data = http };
|
*listener = (tf_http_listener_t) { .http = http, .tls = tls, .tcp = { .data = listener } };
|
||||||
int r = uv_tcp_init(http->loop, tcp);
|
int r = uv_tcp_init(http->loop, &listener->tcp);
|
||||||
if (r)
|
if (r)
|
||||||
{
|
{
|
||||||
tf_printf("uv_tcp_init: %s\n", uv_strerror(r));
|
tf_printf("uv_tcp_init: %s\n", uv_strerror(r));
|
||||||
@ -493,7 +563,7 @@ void tf_http_listen(tf_http_t* http, int port)
|
|||||||
.sin_addr = { .s_addr = INADDR_ANY },
|
.sin_addr = { .s_addr = INADDR_ANY },
|
||||||
.sin_port = ntohs(port),
|
.sin_port = ntohs(port),
|
||||||
};
|
};
|
||||||
r = uv_tcp_bind(tcp, (struct sockaddr*)&addr, 0);
|
r = uv_tcp_bind(&listener->tcp, (struct sockaddr*)&addr, 0);
|
||||||
if (r)
|
if (r)
|
||||||
{
|
{
|
||||||
tf_printf("uv_tcp_bind: %s\n", uv_strerror(r));
|
tf_printf("uv_tcp_bind: %s\n", uv_strerror(r));
|
||||||
@ -502,7 +572,7 @@ void tf_http_listen(tf_http_t* http, int port)
|
|||||||
|
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
{
|
{
|
||||||
r = uv_listen((uv_stream_t*)tcp, 16, _http_on_connection);
|
r = uv_listen((uv_stream_t*)&listener->tcp, 16, _http_on_connection);
|
||||||
if (r)
|
if (r)
|
||||||
{
|
{
|
||||||
tf_printf("uv_listen: %s\n", uv_strerror(r));
|
tf_printf("uv_listen: %s\n", uv_strerror(r));
|
||||||
@ -511,8 +581,8 @@ void tf_http_listen(tf_http_t* http, int port)
|
|||||||
|
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
{
|
{
|
||||||
http->listeners = tf_realloc(http->listeners, sizeof(uv_tcp_t*) * (http->listeners_count + 1));
|
http->listeners = tf_realloc(http->listeners, sizeof(tf_http_listener_t*) * (http->listeners_count + 1));
|
||||||
http->listeners[http->listeners_count++] = tcp;
|
http->listeners[http->listeners_count++] = listener;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,17 +597,18 @@ void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _http_free_on_close(uv_handle_t* handle)
|
static void _http_free_listener_on_close(uv_handle_t* handle)
|
||||||
{
|
{
|
||||||
|
tf_http_listener_t* listener = handle->data;
|
||||||
handle->data = NULL;
|
handle->data = NULL;
|
||||||
tf_free(handle);
|
tf_free(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_http_destroy(tf_http_t* http)
|
void tf_http_destroy(tf_http_t* http)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < http->listeners_count; i++)
|
for (int i = 0; i < http->listeners_count; i++)
|
||||||
{
|
{
|
||||||
uv_close((uv_handle_t*)http->listeners[i], _http_free_on_close);
|
uv_close((uv_handle_t*)&http->listeners[i]->tcp, _http_free_listener_on_close);
|
||||||
}
|
}
|
||||||
tf_free(http->listeners);
|
tf_free(http->listeners);
|
||||||
http->listeners = NULL;
|
http->listeners = NULL;
|
||||||
@ -582,9 +653,10 @@ static void _http_on_shutdown(uv_shutdown_t* request, int status)
|
|||||||
request->data = NULL;
|
request->data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _http_write(tf_http_connection_t* connection, const void* data, size_t size)
|
static void _http_write_internal(tf_http_connection_t* connection, const void* data, size_t size)
|
||||||
{
|
{
|
||||||
_http_timer_reset(connection);
|
if (size && !connection->is_shutting_down)
|
||||||
|
{
|
||||||
uv_write_t* write = tf_malloc(sizeof(uv_write_t) + size);
|
uv_write_t* write = tf_malloc(sizeof(uv_write_t) + size);
|
||||||
*write = (uv_write_t) { .data = connection };
|
*write = (uv_write_t) { .data = connection };
|
||||||
memcpy(write + 1, data, size);
|
memcpy(write + 1, data, size);
|
||||||
@ -593,6 +665,66 @@ static void _http_write(tf_http_connection_t* connection, const void* data, size
|
|||||||
{
|
{
|
||||||
tf_printf("uv_write: %s\n", uv_strerror(r));
|
tf_printf("uv_write: %s\n", uv_strerror(r));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _http_tls_update(tf_http_connection_t* connection)
|
||||||
|
{
|
||||||
|
bool again = true;
|
||||||
|
while (again)
|
||||||
|
{
|
||||||
|
again = false;
|
||||||
|
|
||||||
|
if (connection->is_handshaking)
|
||||||
|
{
|
||||||
|
switch (tf_tls_session_handshake(connection->tls))
|
||||||
|
{
|
||||||
|
case k_tls_handshake_done:
|
||||||
|
connection->is_handshaking = false;
|
||||||
|
break;
|
||||||
|
case k_tls_handshake_more:
|
||||||
|
break;
|
||||||
|
case k_tls_handshake_failed:
|
||||||
|
_http_connection_destroy(connection, "tf_tls_session_handshake");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char buffer[8192];
|
||||||
|
int r = tf_tls_session_read_encrypted(connection->tls, buffer, sizeof(buffer));
|
||||||
|
if (r > 0)
|
||||||
|
{
|
||||||
|
_http_write_internal(connection, buffer, r);
|
||||||
|
again = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = tf_tls_session_read_plain(connection->tls, buffer, sizeof(buffer));
|
||||||
|
if (r > 0)
|
||||||
|
{
|
||||||
|
_http_on_read_plain(connection, buffer, r);
|
||||||
|
again = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _http_write(tf_http_connection_t* connection, const void* data, size_t size)
|
||||||
|
{
|
||||||
|
_http_timer_reset(connection);
|
||||||
|
if (connection->tls)
|
||||||
|
{
|
||||||
|
int r = tf_tls_session_write_plain(connection->tls, data, size);
|
||||||
|
if (r < (ssize_t)size)
|
||||||
|
{
|
||||||
|
char buffer[8192];
|
||||||
|
tf_tls_session_get_error(connection->tls, buffer, sizeof(buffer));
|
||||||
|
tf_printf("tf_tls_session_write_plain: %s\n", buffer);
|
||||||
|
}
|
||||||
|
_http_tls_update(connection);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_http_write_internal(connection, data, size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_http_request_send(tf_http_request_t* request, const void* data, size_t size)
|
void tf_http_request_send(tf_http_request_t* request, const void* data, size_t size)
|
||||||
@ -602,6 +734,12 @@ void tf_http_request_send(tf_http_request_t* request, const void* data, size_t s
|
|||||||
|
|
||||||
void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length)
|
void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length)
|
||||||
{
|
{
|
||||||
|
if (request->connection->is_response_sent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
request->connection->is_response_sent = true;
|
||||||
|
|
||||||
const char* status_text = _http_status_text(status);
|
const char* status_text = _http_status_text(status);
|
||||||
/* HTTP/1.x 200 OK\r\n */
|
/* HTTP/1.x 200 OK\r\n */
|
||||||
bool sent_content_length = false;
|
bool sent_content_length = false;
|
||||||
@ -629,9 +767,7 @@ void tf_http_respond(tf_http_request_t* request, int status, const char** header
|
|||||||
headers_length += content_length_buffer_length;
|
headers_length += content_length_buffer_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
uv_write_t* write = tf_malloc(sizeof(uv_write_t) + headers_length + content_length + 1);
|
char* buffer = alloca(headers_length + 1);
|
||||||
*write = (uv_write_t) { .data = request->connection };
|
|
||||||
char* buffer = (char*)(write + 1);
|
|
||||||
int offset = snprintf(buffer, headers_length + 1, "HTTP/1.%d %03d %s\r\n", request->connection->minor_version, status, status_text);
|
int offset = snprintf(buffer, headers_length + 1, "HTTP/1.%d %03d %s\r\n", request->connection->minor_version, status, status_text);
|
||||||
if (headers)
|
if (headers)
|
||||||
{
|
{
|
||||||
@ -647,16 +783,12 @@ void tf_http_respond(tf_http_request_t* request, int status, const char** header
|
|||||||
}
|
}
|
||||||
offset += snprintf(buffer + offset, headers_length + 1 - offset, "\r\n");
|
offset += snprintf(buffer + offset, headers_length + 1 - offset, "\r\n");
|
||||||
assert(offset == headers_length);
|
assert(offset == headers_length);
|
||||||
|
_http_write(request->connection, buffer, headers_length);
|
||||||
if (content_length)
|
if (content_length)
|
||||||
{
|
{
|
||||||
memcpy(buffer + offset, body, content_length);
|
_http_write(request->connection, body, content_length);
|
||||||
}
|
}
|
||||||
_http_timer_reset(request->connection);
|
_http_timer_reset(request->connection);
|
||||||
int r = uv_write(write, (uv_stream_t*)&request->connection->tcp, &(uv_buf_t) { .base = buffer, .len = headers_length + content_length }, 1, _http_on_write);
|
|
||||||
if (r)
|
|
||||||
{
|
|
||||||
tf_printf("uv_write: %s\n", uv_strerror(r));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request->connection->connection_close &&
|
if (request->connection->connection_close &&
|
||||||
!request->connection->shutdown.data)
|
!request->connection->shutdown.data)
|
||||||
@ -682,7 +814,10 @@ void tf_http_request_release(tf_http_request_t* request)
|
|||||||
{
|
{
|
||||||
if (--request->connection->ref_count == 0)
|
if (--request->connection->ref_count == 0)
|
||||||
{
|
{
|
||||||
/* Reset the connection? */
|
if (!request->connection->is_websocket)
|
||||||
|
{
|
||||||
|
_http_reset_connection(request->connection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (--request->ref_count == 0)
|
if (--request->ref_count == 0)
|
||||||
{
|
{
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
typedef struct uv_loop_s uv_loop_t;
|
|
||||||
typedef struct _tf_http_t tf_http_t;
|
|
||||||
typedef struct _tf_http_connection_t tf_http_connection_t;
|
typedef struct _tf_http_connection_t tf_http_connection_t;
|
||||||
typedef struct _tf_http_request_t tf_http_request_t;
|
typedef struct _tf_http_request_t tf_http_request_t;
|
||||||
|
typedef struct _tf_http_t tf_http_t;
|
||||||
|
typedef struct _tf_tls_context_t tf_tls_context_t;
|
||||||
|
typedef struct uv_loop_s uv_loop_t;
|
||||||
|
|
||||||
typedef enum _tf_http_callback_phase_t
|
typedef enum _tf_http_callback_phase_t
|
||||||
{
|
{
|
||||||
@ -35,7 +36,7 @@ typedef struct _tf_http_request_t
|
|||||||
typedef void (tf_http_callback_t)(tf_http_request_t* request);
|
typedef void (tf_http_callback_t)(tf_http_request_t* request);
|
||||||
|
|
||||||
tf_http_t* tf_http_create(uv_loop_t* loop);
|
tf_http_t* tf_http_create(uv_loop_t* loop);
|
||||||
void tf_http_listen(tf_http_t* http, int port);
|
void tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls);
|
||||||
void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_t* callback, void* user_data);
|
void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_t* callback, void* user_data);
|
||||||
void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length);
|
void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length);
|
||||||
size_t tf_http_get_body(const tf_http_request_t* request, const void** out_data);
|
size_t tf_http_get_body(const tf_http_request_t* request, const void** out_data);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
|
#include "tlscontext.js.h"
|
||||||
#include "util.js.h"
|
#include "util.js.h"
|
||||||
|
|
||||||
#include "picohttpparser.h"
|
#include "picohttpparser.h"
|
||||||
@ -291,7 +292,8 @@ static JSValue _httpd_start(JSContext* context, JSValueConst this_val, int argc,
|
|||||||
tf_http_t* http = JS_GetOpaque(this_val, _httpd_class_id);
|
tf_http_t* http = JS_GetOpaque(this_val, _httpd_class_id);
|
||||||
int port = 0;
|
int port = 0;
|
||||||
JS_ToInt32(context, &port, argv[0]);
|
JS_ToInt32(context, &port, argv[0]);
|
||||||
tf_http_listen(http, port);
|
tf_tls_context_t* tls = tf_tls_context_get(JS_DupValue(context, argv[1]));
|
||||||
|
tf_http_listen(http, port, tls);
|
||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,7 +342,7 @@ void tf_httpd_register(JSContext* context)
|
|||||||
JS_SetPropertyStr(context, httpd, "handlers", JS_NewObject(context));
|
JS_SetPropertyStr(context, httpd, "handlers", JS_NewObject(context));
|
||||||
JS_SetPropertyStr(context, httpd, "all", JS_NewCFunction(context, _httpd_all, "all", 2));
|
JS_SetPropertyStr(context, httpd, "all", JS_NewCFunction(context, _httpd_all, "all", 2));
|
||||||
JS_SetPropertyStr(context, httpd, "registerSocketHandler", JS_NewCFunction(context, _httpd_register_socket_handler, "register_socket_handler", 2));
|
JS_SetPropertyStr(context, httpd, "registerSocketHandler", JS_NewCFunction(context, _httpd_register_socket_handler, "register_socket_handler", 2));
|
||||||
JS_SetPropertyStr(context, httpd, "start", JS_NewCFunction(context, _httpd_start, "start", 1));
|
JS_SetPropertyStr(context, httpd, "start", JS_NewCFunction(context, _httpd_start, "start", 2));
|
||||||
JS_SetPropertyStr(context, global, "httpdc", httpd);
|
JS_SetPropertyStr(context, global, "httpdc", httpd);
|
||||||
JS_FreeValue(context, global);
|
JS_FreeValue(context, global);
|
||||||
}
|
}
|
||||||
|
@ -574,7 +574,7 @@ static int _tf_command_usage(const char* file, int argc, char* argv[])
|
|||||||
{
|
{
|
||||||
tf_printf(" %s - %s\n", k_commands[i].name, k_commands[i].description);
|
tf_printf(" %s - %s\n", k_commands[i].name, k_commands[i].description);
|
||||||
}
|
}
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
@ -739,7 +739,7 @@ static void _test_http(const tf_test_options_t* options)
|
|||||||
tf_http_t* http = tf_http_create(&loop);
|
tf_http_t* http = tf_http_create(&loop);
|
||||||
tf_http_add_handler(http, "/hello", _test_http_handler, NULL);
|
tf_http_add_handler(http, "/hello", _test_http_handler, NULL);
|
||||||
tf_http_add_handler(http, "/post", _test_http_handler_post, NULL);
|
tf_http_add_handler(http, "/post", _test_http_handler_post, NULL);
|
||||||
tf_http_listen(http, 23456);
|
tf_http_listen(http, 23456, NULL);
|
||||||
|
|
||||||
test_http_t test = { .loop = &loop };
|
test_http_t test = { .loop = &loop };
|
||||||
uv_async_init(&loop, &test.async, _test_http_async);
|
uv_async_init(&loop, &test.async, _test_http_async);
|
||||||
|
Loading…
Reference in New Issue
Block a user