In order to be compatible with different editor, convert all Tab to spaces
Signed-off-by: Jianhui Zhao <jianhuizhao329@gmail.com>main
parent
8eb57bef1e
commit
ee31e3f1fa
|
@ -4,67 +4,67 @@
|
||||||
|
|
||||||
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
|
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
|
||||||
{
|
{
|
||||||
printf("Got signal: %d\n", w->signum);
|
printf("Got signal: %d\n", w->signum);
|
||||||
ev_break(loop, EVBREAK_ALL);
|
ev_break(loop, EVBREAK_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void route_test(struct uh_connection *con)
|
void route_test(struct uh_connection *con)
|
||||||
{
|
{
|
||||||
struct uh_value *url = uh_get_url(con);
|
struct uh_value *url = uh_get_url(con);
|
||||||
struct uh_value *header_host = uh_get_header(con, "Host");
|
struct uh_value *header_host = uh_get_header(con, "Host");
|
||||||
struct uh_value *header_ua = uh_get_header(con, "User-Agent");
|
struct uh_value *header_ua = uh_get_header(con, "User-Agent");
|
||||||
|
|
||||||
uh_send_head(con, UH_STATUS_OK, -1, NULL);
|
uh_send_head(con, UH_STATUS_OK, -1, NULL);
|
||||||
uh_printf_chunk(con, "<h1>Hello World</h1>");
|
uh_printf_chunk(con, "<h1>Hello World</h1>");
|
||||||
uh_printf_chunk(con, "<h1>Libuhttp v%s</h1>", uh_version());
|
uh_printf_chunk(con, "<h1>Libuhttp v%s</h1>", uh_version());
|
||||||
uh_printf_chunk(con, "<h1>Url: %.*s</h1>", (int)url->len, url->at);
|
uh_printf_chunk(con, "<h1>Url: %.*s</h1>", (int)url->len, url->at);
|
||||||
|
|
||||||
if (header_host)
|
if (header_host)
|
||||||
uh_printf_chunk(con, "<h1>Host: %.*s</h1>", (int)header_host->len, header_host->at);
|
uh_printf_chunk(con, "<h1>Host: %.*s</h1>", (int)header_host->len, header_host->at);
|
||||||
|
|
||||||
if (header_ua)
|
if (header_ua)
|
||||||
uh_printf_chunk(con, "<h1>User-Agent: %.*s</h1>", (int)header_ua->len, header_ua->at);
|
uh_printf_chunk(con, "<h1>User-Agent: %.*s</h1>", (int)header_ua->len, header_ua->at);
|
||||||
|
|
||||||
uh_send_chunk(con, NULL, 0);
|
uh_send_chunk(con, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct ev_loop *loop = EV_DEFAULT;
|
struct ev_loop *loop = EV_DEFAULT;
|
||||||
ev_signal *sig_watcher = NULL;
|
ev_signal *sig_watcher = NULL;
|
||||||
struct uh_server *srv = NULL;
|
struct uh_server *srv = NULL;
|
||||||
|
|
||||||
uh_log_info("libuhttp version: %s\n", uh_version());
|
uh_log_info("libuhttp version: %s\n", uh_version());
|
||||||
|
|
||||||
sig_watcher = calloc(1, sizeof(ev_signal));
|
sig_watcher = calloc(1, sizeof(ev_signal));
|
||||||
if (!sig_watcher)
|
if (!sig_watcher)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ev_signal_init(sig_watcher, signal_cb, SIGINT);
|
ev_signal_init(sig_watcher, signal_cb, SIGINT);
|
||||||
ev_signal_start(loop, sig_watcher);
|
ev_signal_start(loop, sig_watcher);
|
||||||
|
|
||||||
srv = uh_server_new(loop, "0.0.0.0", 8000);
|
srv = uh_server_new(loop, "0.0.0.0", 8000);
|
||||||
if (!srv) {
|
if (!srv) {
|
||||||
uh_log_err("uh_server_new failed\n");
|
uh_log_err("uh_server_new failed\n");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
if (uh_ssl_init(srv, "server-cert.pem", "server-key.pem") < 0)
|
if (uh_ssl_init(srv, "server-cert.pem", "server-key.pem") < 0)
|
||||||
goto err;
|
goto err;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uh_register_route(srv, "/test", route_test);
|
uh_register_route(srv, "/test", route_test);
|
||||||
|
|
||||||
uh_log_info("Listen on 8000...\n");
|
uh_log_info("Listen on 8000...\n");
|
||||||
|
|
||||||
ev_run(loop, 0);
|
ev_run(loop, 0);
|
||||||
|
|
||||||
err:
|
err:
|
||||||
free(sig_watcher);
|
free(sig_watcher);
|
||||||
uh_server_free(srv);
|
uh_server_free(srv);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
738
src/uhttp.c
738
src/uhttp.c
|
@ -10,178 +10,178 @@
|
||||||
#include "uhttp_ssl.h"
|
#include "uhttp_ssl.h"
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
int code;
|
int code;
|
||||||
const char *reason;
|
const char *reason;
|
||||||
} http_status_message[] = {
|
} http_status_message[] = {
|
||||||
{UH_STATUS_CONTINUE, "Continue"},
|
{UH_STATUS_CONTINUE, "Continue"},
|
||||||
{UH_STATUS_SWITCHING_PROTOCOLS, "Switching Protocols"},
|
{UH_STATUS_SWITCHING_PROTOCOLS, "Switching Protocols"},
|
||||||
{UH_STATUS_PROCESSING, "Processing"},
|
{UH_STATUS_PROCESSING, "Processing"},
|
||||||
{UH_STATUS_OK, "OK"},
|
{UH_STATUS_OK, "OK"},
|
||||||
{UH_STATUS_CREATED, "Created"},
|
{UH_STATUS_CREATED, "Created"},
|
||||||
{UH_STATUS_ACCEPTED, "Accepted"},
|
{UH_STATUS_ACCEPTED, "Accepted"},
|
||||||
{UH_STATUS_NON_AUTHORITATIVE_INFORMATION, "Non-Authoritative Information"},
|
{UH_STATUS_NON_AUTHORITATIVE_INFORMATION, "Non-Authoritative Information"},
|
||||||
{UH_STATUS_NO_CONTENT, "No Content"},
|
{UH_STATUS_NO_CONTENT, "No Content"},
|
||||||
{UH_STATUS_RESET_CONTENT, "Reset Content"},
|
{UH_STATUS_RESET_CONTENT, "Reset Content"},
|
||||||
{UH_STATUS_PARTIAL_CONTENT, "Partial Content"},
|
{UH_STATUS_PARTIAL_CONTENT, "Partial Content"},
|
||||||
{UH_STATUS_MULTI_STATUS, "Multi-Status"},
|
{UH_STATUS_MULTI_STATUS, "Multi-Status"},
|
||||||
{UH_STATUS_ALREADY_REPORTED, "Already Reported"},
|
{UH_STATUS_ALREADY_REPORTED, "Already Reported"},
|
||||||
{UH_STATUS_IM_USED, "IM Used"},
|
{UH_STATUS_IM_USED, "IM Used"},
|
||||||
{UH_STATUS_MULTIPLE_CHOICES, "Multiple Choices"},
|
{UH_STATUS_MULTIPLE_CHOICES, "Multiple Choices"},
|
||||||
{UH_STATUS_MOVED_PERMANENTLY, "Moved Permanently"},
|
{UH_STATUS_MOVED_PERMANENTLY, "Moved Permanently"},
|
||||||
{UH_STATUS_FOUND, "Found"},
|
{UH_STATUS_FOUND, "Found"},
|
||||||
{UH_STATUS_SEE_OTHER, "See Other"},
|
{UH_STATUS_SEE_OTHER, "See Other"},
|
||||||
{UH_STATUS_NOT_MODIFIED, "Not Modified"},
|
{UH_STATUS_NOT_MODIFIED, "Not Modified"},
|
||||||
{UH_STATUS_USE_PROXY, "Use Proxy"},
|
{UH_STATUS_USE_PROXY, "Use Proxy"},
|
||||||
{UH_STATUS_TEMPORARY_REDIRECT, "Temporary Redirect"},
|
{UH_STATUS_TEMPORARY_REDIRECT, "Temporary Redirect"},
|
||||||
{UH_STATUS_PERMANENT_REDIRECT, "Permanent Redirect"},
|
{UH_STATUS_PERMANENT_REDIRECT, "Permanent Redirect"},
|
||||||
{UH_STATUS_BAD_REQUEST, "Bad Request"},
|
{UH_STATUS_BAD_REQUEST, "Bad Request"},
|
||||||
{UH_STATUS_UNAUTHORIZED, "Unauthorized"},
|
{UH_STATUS_UNAUTHORIZED, "Unauthorized"},
|
||||||
{UH_STATUS_PAYMENT_REQUIRED, "Payment Required"},
|
{UH_STATUS_PAYMENT_REQUIRED, "Payment Required"},
|
||||||
{UH_STATUS_FORBIDDEN, "Forbidden"},
|
{UH_STATUS_FORBIDDEN, "Forbidden"},
|
||||||
{UH_STATUS_NOT_FOUND, "Not Found"},
|
{UH_STATUS_NOT_FOUND, "Not Found"},
|
||||||
{UH_STATUS_METHOD_NOT_ALLOWED, "Method Not Allowed"},
|
{UH_STATUS_METHOD_NOT_ALLOWED, "Method Not Allowed"},
|
||||||
{UH_STATUS_NOT_ACCEPTABLE, "Not Acceptable"},
|
{UH_STATUS_NOT_ACCEPTABLE, "Not Acceptable"},
|
||||||
{UH_STATUS_PROXY_AUTHENTICATION_REQUIRED, "Proxy Authentication Required"},
|
{UH_STATUS_PROXY_AUTHENTICATION_REQUIRED, "Proxy Authentication Required"},
|
||||||
{UH_STATUS_REQUEST_TIMEOUT, "Request Timeout"},
|
{UH_STATUS_REQUEST_TIMEOUT, "Request Timeout"},
|
||||||
{UH_STATUS_CONFLICT, "Conflict"},
|
{UH_STATUS_CONFLICT, "Conflict"},
|
||||||
{UH_STATUS_GONE, "Gone"},
|
{UH_STATUS_GONE, "Gone"},
|
||||||
{UH_STATUS_LENGTH_REQUIRED, "Length Required"},
|
{UH_STATUS_LENGTH_REQUIRED, "Length Required"},
|
||||||
{UH_STATUS_PRECONDITION_FAILED, "Precondition Failed"},
|
{UH_STATUS_PRECONDITION_FAILED, "Precondition Failed"},
|
||||||
{UH_STATUS_PAYLOAD_TOO_LARGE, "Payload Too Large"},
|
{UH_STATUS_PAYLOAD_TOO_LARGE, "Payload Too Large"},
|
||||||
{UH_STATUS_URI_TOO_LONG, "URI Too Long"},
|
{UH_STATUS_URI_TOO_LONG, "URI Too Long"},
|
||||||
{UH_STATUS_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type"},
|
{UH_STATUS_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type"},
|
||||||
{UH_STATUS_RANGE_NOT_SATISFIABLE, "Range Not Satisfiable"},
|
{UH_STATUS_RANGE_NOT_SATISFIABLE, "Range Not Satisfiable"},
|
||||||
{UH_STATUS_EXPECTATION_FAILED, "Expectation Failed"},
|
{UH_STATUS_EXPECTATION_FAILED, "Expectation Failed"},
|
||||||
{UH_STATUS_MISDIRECTED_REQUEST, "Misdirected Request"},
|
{UH_STATUS_MISDIRECTED_REQUEST, "Misdirected Request"},
|
||||||
{UH_STATUS_UNPROCESSABLE_ENTITY, "Unprocessable Entity"},
|
{UH_STATUS_UNPROCESSABLE_ENTITY, "Unprocessable Entity"},
|
||||||
{UH_STATUS_LOCKED, "Locked"},
|
{UH_STATUS_LOCKED, "Locked"},
|
||||||
{UH_STATUS_FAILED_DEPENDENCY, "Failed Dependency"},
|
{UH_STATUS_FAILED_DEPENDENCY, "Failed Dependency"},
|
||||||
{UH_STATUS_UPGRADE_REQUIRED, "Upgrade Required"},
|
{UH_STATUS_UPGRADE_REQUIRED, "Upgrade Required"},
|
||||||
{UH_STATUS_PRECONDITION_REQUIRED, "Precondition Required"},
|
{UH_STATUS_PRECONDITION_REQUIRED, "Precondition Required"},
|
||||||
{UH_STATUS_TOO_MANY_REQUESTS, "Too Many Requests"},
|
{UH_STATUS_TOO_MANY_REQUESTS, "Too Many Requests"},
|
||||||
{UH_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE, "Request Header Fields Too Large"},
|
{UH_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE, "Request Header Fields Too Large"},
|
||||||
{UH_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS, "Unavailable For Legal Reasons"},
|
{UH_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS, "Unavailable For Legal Reasons"},
|
||||||
{UH_STATUS_INTERNAL_SERVER_ERROR, "Internal Server Error"},
|
{UH_STATUS_INTERNAL_SERVER_ERROR, "Internal Server Error"},
|
||||||
{UH_STATUS_NOT_IMPLEMENTED, "Not Implemented"},
|
{UH_STATUS_NOT_IMPLEMENTED, "Not Implemented"},
|
||||||
{UH_STATUS_BAD_GATEWAY, "Bad Gateway"},
|
{UH_STATUS_BAD_GATEWAY, "Bad Gateway"},
|
||||||
{UH_STATUS_SERVICE_UNAVAILABLE, "Service Unavailable"},
|
{UH_STATUS_SERVICE_UNAVAILABLE, "Service Unavailable"},
|
||||||
{UH_STATUS_GATEWAY_TIMEOUT, "Gateway Timeout"},
|
{UH_STATUS_GATEWAY_TIMEOUT, "Gateway Timeout"},
|
||||||
{UH_STATUS_HTTP_VERSION_NOT_SUPPORTED, "HTTP Version Not Supported"},
|
{UH_STATUS_HTTP_VERSION_NOT_SUPPORTED, "HTTP Version Not Supported"},
|
||||||
{UH_STATUS_VARIANT_ALSO_NEGOTIATES, "Variant Also Negotiates"},
|
{UH_STATUS_VARIANT_ALSO_NEGOTIATES, "Variant Also Negotiates"},
|
||||||
{UH_STATUS_INSUFFICIENT_STORAGE, "Insufficient Storage"},
|
{UH_STATUS_INSUFFICIENT_STORAGE, "Insufficient Storage"},
|
||||||
{UH_STATUS_LOOP_DETECTED, "Loop Detected"},
|
{UH_STATUS_LOOP_DETECTED, "Loop Detected"},
|
||||||
{UH_STATUS_NOT_EXTENDED, "Not Extended"},
|
{UH_STATUS_NOT_EXTENDED, "Not Extended"},
|
||||||
{UH_STATUS_NETWORK_AUTHENTICATION_REQUIRED, "Network Authentication Required"}
|
{UH_STATUS_NETWORK_AUTHENTICATION_REQUIRED, "Network Authentication Required"}
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *uh_version()
|
const char *uh_version()
|
||||||
{
|
{
|
||||||
return UHTTP_VERSION_STRING;
|
return UHTTP_VERSION_STRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *get_http_status_message(int code)
|
static const char *get_http_status_message(int code)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
const char *reason = "OK";
|
const char *reason = "OK";
|
||||||
|
|
||||||
for (i = 0; http_status_message[i].reason; i++) {
|
for (i = 0; http_status_message[i].reason; i++) {
|
||||||
if (code == http_status_message[i].code)
|
if (code == http_status_message[i].code)
|
||||||
reason = http_status_message[i].reason;
|
reason = http_status_message[i].reason;
|
||||||
}
|
}
|
||||||
return reason;
|
return reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uh_connection_destroy(struct uh_connection *con)
|
static void uh_connection_destroy(struct uh_connection *con)
|
||||||
{
|
{
|
||||||
if (con) {
|
if (con) {
|
||||||
struct ev_loop *loop = con->srv->loop;
|
struct ev_loop *loop = con->srv->loop;
|
||||||
|
|
||||||
if (con->sock > 0)
|
if (con->sock > 0)
|
||||||
close(con->sock);
|
close(con->sock);
|
||||||
|
|
||||||
uh_buf_free(&con->read_buf);
|
uh_buf_free(&con->read_buf);
|
||||||
uh_buf_free(&con->write_buf);
|
uh_buf_free(&con->write_buf);
|
||||||
|
|
||||||
ev_io_stop(loop, &con->read_watcher);
|
ev_io_stop(loop, &con->read_watcher);
|
||||||
ev_io_stop(loop, &con->write_watcher);
|
ev_io_stop(loop, &con->write_watcher);
|
||||||
ev_timer_stop(loop, &con->timer_watcher);
|
ev_timer_stop(loop, &con->timer_watcher);
|
||||||
|
|
||||||
list_del(&con->list);
|
list_del(&con->list);
|
||||||
|
|
||||||
uh_ssl_free(con);
|
uh_ssl_free(con);
|
||||||
free(con);
|
free(con);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void connection_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents)
|
static void connection_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents)
|
||||||
{
|
{
|
||||||
struct uh_connection *con = container_of(w, struct uh_connection, timer_watcher);
|
struct uh_connection *con = container_of(w, struct uh_connection, timer_watcher);
|
||||||
uh_log_info("connection(%p) timeout", con);
|
uh_log_info("connection(%p) timeout", con);
|
||||||
uh_send_error(con, UH_STATUS_REQUEST_TIMEOUT, NULL);
|
uh_send_error(con, UH_STATUS_REQUEST_TIMEOUT, NULL);
|
||||||
uh_connection_destroy(con);
|
uh_connection_destroy(con);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_message_begin(http_parser *parser)
|
static int on_message_begin(http_parser *parser)
|
||||||
{
|
{
|
||||||
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
||||||
|
|
||||||
memset(&con->req, 0, sizeof(struct uh_request));
|
memset(&con->req, 0, sizeof(struct uh_request));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_url(http_parser *parser, const char *at, size_t len)
|
static int on_url(http_parser *parser, const char *at, size_t len)
|
||||||
{
|
{
|
||||||
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
||||||
|
|
||||||
con->req.url.at = at;
|
con->req.url.at = at;
|
||||||
con->req.url.len = len;
|
con->req.url.len = len;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_header_field(http_parser *parser, const char *at, size_t len)
|
static int on_header_field(http_parser *parser, const char *at, size_t len)
|
||||||
{
|
{
|
||||||
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
||||||
struct uh_header *header = con->req.header;
|
struct uh_header *header = con->req.header;
|
||||||
|
|
||||||
header[con->req.header_num].field.at = at;
|
header[con->req.header_num].field.at = at;
|
||||||
header[con->req.header_num].field.len = len;
|
header[con->req.header_num].field.len = len;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_header_value(http_parser *parser, const char *at, size_t len)
|
static int on_header_value(http_parser *parser, const char *at, size_t len)
|
||||||
{
|
{
|
||||||
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
||||||
struct uh_header *header = con->req.header;
|
struct uh_header *header = con->req.header;
|
||||||
|
|
||||||
header[con->req.header_num].value.at = at;
|
header[con->req.header_num].value.at = at;
|
||||||
header[con->req.header_num].value.len = len;
|
header[con->req.header_num].value.len = len;
|
||||||
con->req.header_num += 1;
|
con->req.header_num += 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_headers_complete(http_parser *parser)
|
static int on_headers_complete(http_parser *parser)
|
||||||
{
|
{
|
||||||
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
||||||
|
|
||||||
if (parser->method != HTTP_GET && parser->method != HTTP_POST) {
|
if (parser->method != HTTP_GET && parser->method != HTTP_POST) {
|
||||||
uh_send_error(con, UH_STATUS_METHOD_NOT_ALLOWED, NULL);
|
uh_send_error(con, UH_STATUS_METHOD_NOT_ALLOWED, NULL);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_body(http_parser *parser, const char *at, size_t len)
|
static int on_body(http_parser *parser, const char *at, size_t len)
|
||||||
{
|
{
|
||||||
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
||||||
|
|
||||||
if (!con->req.body.at)
|
if (!con->req.body.at)
|
||||||
con->req.body.at = at;
|
con->req.body.at = at;
|
||||||
|
|
||||||
con->req.body.len += len;
|
con->req.body.len += len;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,415 +189,415 @@ static int on_body(http_parser *parser, const char *at, size_t len)
|
||||||
/* Return 1 for equal */
|
/* Return 1 for equal */
|
||||||
static int uh_value_cmp(struct uh_value *uv, const char *str)
|
static int uh_value_cmp(struct uh_value *uv, const char *str)
|
||||||
{
|
{
|
||||||
if (uv->len != strlen(str))
|
if (uv->len != strlen(str))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return (!strncasecmp(uv->at, str, uv->len));
|
return (!strncasecmp(uv->at, str, uv->len));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_message_complete(http_parser *parser)
|
static int on_message_complete(http_parser *parser)
|
||||||
{
|
{
|
||||||
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
|
||||||
struct uh_route *r;
|
struct uh_route *r;
|
||||||
#if (UHTTP_DEBUG)
|
#if (UHTTP_DEBUG)
|
||||||
int i;
|
int i;
|
||||||
struct uh_header *header = con->req.header;
|
struct uh_header *header = con->req.header;
|
||||||
|
|
||||||
uh_log_debug("Url:[%.*s]\n", (int)con->req.url.len, con->req.url.at);
|
uh_log_debug("Url:[%.*s]\n", (int)con->req.url.len, con->req.url.at);
|
||||||
|
|
||||||
for (i = 0; i < con->req.header_num; i++) {
|
for (i = 0; i < con->req.header_num; i++) {
|
||||||
uh_log_debug("[%.*s:%.*s]\n", (int)header[i].field.len, header[i].field.at,
|
uh_log_debug("[%.*s:%.*s]\n", (int)header[i].field.len, header[i].field.at,
|
||||||
(int)header[i].value.len, header[i].value.at);
|
(int)header[i].value.len, header[i].value.at);
|
||||||
}
|
}
|
||||||
|
|
||||||
uh_log_debug("Body:[%.*s]\n", (int)con->req.body.len, con->req.body.at);
|
uh_log_debug("Body:[%.*s]\n", (int)con->req.body.len, con->req.body.at);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
list_for_each_entry(r, &con->srv->routes, list) {
|
list_for_each_entry(r, &con->srv->routes, list) {
|
||||||
if (uh_value_cmp(&con->req.url, r->path)) {
|
if (uh_value_cmp(&con->req.url, r->path)) {
|
||||||
r->cb(con);
|
r->cb(con);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uh_send_error(con, UH_STATUS_NOT_FOUND, NULL);
|
uh_send_error(con, UH_STATUS_NOT_FOUND, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static http_parser_settings parser_settings = {
|
static http_parser_settings parser_settings = {
|
||||||
.on_message_begin = on_message_begin,
|
.on_message_begin = on_message_begin,
|
||||||
.on_url = on_url,
|
.on_url = on_url,
|
||||||
.on_header_field = on_header_field,
|
.on_header_field = on_header_field,
|
||||||
.on_header_value = on_header_value,
|
.on_header_value = on_header_value,
|
||||||
.on_headers_complete = on_headers_complete,
|
.on_headers_complete = on_headers_complete,
|
||||||
.on_body = on_body,
|
.on_body = on_body,
|
||||||
.on_message_complete = on_message_complete
|
.on_message_complete = on_message_complete
|
||||||
};
|
};
|
||||||
|
|
||||||
static void connection_read_cb(struct ev_loop *loop, ev_io *w, int revents)
|
static void connection_read_cb(struct ev_loop *loop, ev_io *w, int revents)
|
||||||
{
|
{
|
||||||
struct uh_connection *con = container_of(w, struct uh_connection, read_watcher);
|
struct uh_connection *con = container_of(w, struct uh_connection, read_watcher);
|
||||||
struct uh_buf *buf = &con->read_buf;
|
struct uh_buf *buf = &con->read_buf;
|
||||||
char *base;
|
char *base;
|
||||||
int len, parsered;
|
int len, parsered;
|
||||||
|
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
if (con->flags & UH_CON_SSL_HANDSHAKE_DONE)
|
if (con->flags & UH_CON_SSL_HANDSHAKE_DONE)
|
||||||
goto handshake_done;
|
goto handshake_done;
|
||||||
|
|
||||||
uh_ssl_handshake(con);
|
uh_ssl_handshake(con);
|
||||||
if (con->flags & UH_CON_CLOSE)
|
if (con->flags & UH_CON_CLOSE)
|
||||||
uh_connection_destroy(con);
|
uh_connection_destroy(con);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
handshake_done:
|
handshake_done:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (uh_buf_available(buf) < UH_BUFFER_SIZE)
|
if (uh_buf_available(buf) < UH_BUFFER_SIZE)
|
||||||
uh_buf_grow(buf, UH_BUFFER_SIZE);
|
uh_buf_grow(buf, UH_BUFFER_SIZE);
|
||||||
|
|
||||||
base = buf->base + buf->len;
|
base = buf->base + buf->len;
|
||||||
|
|
||||||
len = uh_ssl_read(con, base, UH_BUFFER_SIZE);
|
len = uh_ssl_read(con, base, UH_BUFFER_SIZE);
|
||||||
if (unlikely(len <= 0)) {
|
if (unlikely(len <= 0)) {
|
||||||
if (con->flags & UH_CON_CLOSE)
|
if (con->flags & UH_CON_CLOSE)
|
||||||
uh_connection_destroy(con);
|
uh_connection_destroy(con);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf->len += len;
|
buf->len += len;
|
||||||
|
|
||||||
#if (UHTTP_DEBUG)
|
#if (UHTTP_DEBUG)
|
||||||
uh_log_debug("read:[%.*s]\n", len, base);
|
uh_log_debug("read:[%.*s]\n", len, base);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!(con->flags & UH_CON_PARSERING)) {
|
if (!(con->flags & UH_CON_PARSERING)) {
|
||||||
if (!memmem(buf->base, buf->len, "\r\n\r\n", 4)) {
|
if (!memmem(buf->base, buf->len, "\r\n\r\n", 4)) {
|
||||||
if (buf->len > UH_MAX_HTTP_HEAD_SIZE) {
|
if (buf->len > UH_MAX_HTTP_HEAD_SIZE) {
|
||||||
uh_log_err("HTTP head size too big");
|
uh_log_err("HTTP head size too big");
|
||||||
uh_send_error(con, UH_STATUS_BAD_REQUEST, NULL);
|
uh_send_error(con, UH_STATUS_BAD_REQUEST, NULL);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
base = buf->base;
|
base = buf->base;
|
||||||
len = buf->len;
|
len = buf->len;
|
||||||
con->flags |= UH_CON_PARSERING;
|
con->flags |= UH_CON_PARSERING;
|
||||||
}
|
}
|
||||||
|
|
||||||
parsered = http_parser_execute(&con->parser, &parser_settings, base, len);
|
parsered = http_parser_execute(&con->parser, &parser_settings, base, len);
|
||||||
if (unlikely(parsered != len && !(con->flags & UH_CON_CLOSE))) {
|
if (unlikely(parsered != len && !(con->flags & UH_CON_CLOSE))) {
|
||||||
uh_log_err("http parser failed:%s", http_errno_description(HTTP_PARSER_ERRNO(&con->parser)));
|
uh_log_err("http parser failed:%s", http_errno_description(HTTP_PARSER_ERRNO(&con->parser)));
|
||||||
uh_send_error(con, UH_STATUS_BAD_REQUEST, NULL);
|
uh_send_error(con, UH_STATUS_BAD_REQUEST, NULL);
|
||||||
} else {
|
} else {
|
||||||
ev_timer_mode(loop, &con->timer_watcher, UH_CONNECTION_TIMEOUT, 0);
|
ev_timer_mode(loop, &con->timer_watcher, UH_CONNECTION_TIMEOUT, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void connection_write_cb(struct ev_loop *loop, ev_io *w, int revents)
|
static void connection_write_cb(struct ev_loop *loop, ev_io *w, int revents)
|
||||||
{
|
{
|
||||||
struct uh_connection *con = container_of(w, struct uh_connection, write_watcher);
|
struct uh_connection *con = container_of(w, struct uh_connection, write_watcher);
|
||||||
struct uh_buf *buf = &con->write_buf;
|
struct uh_buf *buf = &con->write_buf;
|
||||||
|
|
||||||
if (buf->len > 0) {
|
if (buf->len > 0) {
|
||||||
int len = uh_ssl_write(con, buf->base, buf->len);
|
int len = uh_ssl_write(con, buf->base, buf->len);
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
uh_buf_remove(buf, len);
|
uh_buf_remove(buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf->len == 0) {
|
if (buf->len == 0) {
|
||||||
ev_io_stop(loop, w);
|
ev_io_stop(loop, w);
|
||||||
|
|
||||||
if (!http_should_keep_alive(&con->parser))
|
if (!http_should_keep_alive(&con->parser))
|
||||||
con->flags |= UH_CON_CLOSE;
|
con->flags |= UH_CON_CLOSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (con->flags & UH_CON_CLOSE)
|
if (con->flags & UH_CON_CLOSE)
|
||||||
uh_connection_destroy(con);
|
uh_connection_destroy(con);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uh_accept_cb(struct ev_loop *loop, ev_io *w, int revents)
|
static void uh_accept_cb(struct ev_loop *loop, ev_io *w, int revents)
|
||||||
{
|
{
|
||||||
int sock = -1;
|
int sock = -1;
|
||||||
struct uh_server *srv = container_of(w, struct uh_server, read_watcher);
|
struct uh_server *srv = container_of(w, struct uh_server, read_watcher);
|
||||||
struct uh_connection *con = NULL;
|
struct uh_connection *con = NULL;
|
||||||
ev_io *read_watcher, *write_watcher;
|
ev_io *read_watcher, *write_watcher;
|
||||||
ev_timer *timer_watcher;
|
ev_timer *timer_watcher;
|
||||||
|
|
||||||
con = calloc(1, sizeof(struct uh_connection));
|
con = calloc(1, sizeof(struct uh_connection));
|
||||||
if (unlikely(!con)) {
|
if (unlikely(!con)) {
|
||||||
uh_log_err("calloc");
|
uh_log_err("calloc");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
con->srv = srv;
|
con->srv = srv;
|
||||||
list_add(&con->list, &srv->connections);
|
list_add(&con->list, &srv->connections);
|
||||||
|
|
||||||
sock = uh_ssl_accept(con);
|
sock = uh_ssl_accept(con);
|
||||||
if (unlikely(sock < 0))
|
if (unlikely(sock < 0))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
read_watcher = &con->read_watcher;
|
read_watcher = &con->read_watcher;
|
||||||
ev_io_init(read_watcher, connection_read_cb, sock, EV_READ);
|
ev_io_init(read_watcher, connection_read_cb, sock, EV_READ);
|
||||||
ev_io_start(loop,read_watcher);
|
ev_io_start(loop,read_watcher);
|
||||||
|
|
||||||
write_watcher = &con->write_watcher;
|
write_watcher = &con->write_watcher;
|
||||||
ev_io_init(write_watcher, connection_write_cb, sock, EV_WRITE);
|
ev_io_init(write_watcher, connection_write_cb, sock, EV_WRITE);
|
||||||
|
|
||||||
timer_watcher = &con->timer_watcher;
|
timer_watcher = &con->timer_watcher;
|
||||||
ev_timer_init(timer_watcher, connection_timeout_cb, UH_CONNECTION_TIMEOUT, 0);
|
ev_timer_init(timer_watcher, connection_timeout_cb, UH_CONNECTION_TIMEOUT, 0);
|
||||||
ev_timer_start(loop, timer_watcher);
|
ev_timer_start(loop, timer_watcher);
|
||||||
|
|
||||||
http_parser_init(&con->parser, HTTP_REQUEST);
|
http_parser_init(&con->parser, HTTP_REQUEST);
|
||||||
|
|
||||||
uh_log_info("new connection:%p", con);
|
uh_log_info("new connection:%p", con);
|
||||||
return;
|
return;
|
||||||
err:
|
err:
|
||||||
uh_connection_destroy(con);
|
uh_connection_destroy(con);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct uh_server *uh_server_new(struct ev_loop *loop, const char *ipaddr, int port)
|
struct uh_server *uh_server_new(struct ev_loop *loop, const char *ipaddr, int port)
|
||||||
{
|
{
|
||||||
struct uh_server *srv = NULL;
|
struct uh_server *srv = NULL;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
int sock = -1, on = 1;
|
int sock = -1, on = 1;
|
||||||
ev_io *read_watcher;
|
ev_io *read_watcher;
|
||||||
|
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_port = htons(port);
|
addr.sin_port = htons(port);
|
||||||
|
|
||||||
if (inet_pton(AF_INET, ipaddr, &addr.sin_addr) <= 0) {
|
if (inet_pton(AF_INET, ipaddr, &addr.sin_addr) <= 0) {
|
||||||
uh_log_err("invalid ipaddr");
|
uh_log_err("invalid ipaddr");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
srv = calloc(1, sizeof(struct uh_server));
|
srv = calloc(1, sizeof(struct uh_server));
|
||||||
if (!srv) {
|
if (!srv) {
|
||||||
uh_log_err("calloc");
|
uh_log_err("calloc");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
INIT_LIST_HEAD(&srv->routes);
|
INIT_LIST_HEAD(&srv->routes);
|
||||||
INIT_LIST_HEAD(&srv->connections);
|
INIT_LIST_HEAD(&srv->connections);
|
||||||
|
|
||||||
sock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
sock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
||||||
if (sock < 0) {
|
if (sock < 0) {
|
||||||
uh_log_err("socket");
|
uh_log_err("socket");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srv->sock = sock;
|
srv->sock = sock;
|
||||||
srv->loop = loop;
|
srv->loop = loop;
|
||||||
|
|
||||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||||
|
|
||||||
if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
|
if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
|
||||||
uh_log_err("bind");
|
uh_log_err("bind");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listen(sock, SOMAXCONN) < 0) {
|
if (listen(sock, SOMAXCONN) < 0) {
|
||||||
uh_log_err("listen");
|
uh_log_err("listen");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
read_watcher = &srv->read_watcher;
|
read_watcher = &srv->read_watcher;
|
||||||
ev_io_init(read_watcher, uh_accept_cb, sock, EV_READ);
|
ev_io_init(read_watcher, uh_accept_cb, sock, EV_READ);
|
||||||
ev_io_start(loop, read_watcher);
|
ev_io_start(loop, read_watcher);
|
||||||
|
|
||||||
return srv;
|
return srv;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
uh_server_free(srv);
|
uh_server_free(srv);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_server_free(struct uh_server *srv)
|
void uh_server_free(struct uh_server *srv)
|
||||||
{
|
{
|
||||||
if (srv) {
|
if (srv) {
|
||||||
struct uh_connection *con, *tmp_c;
|
struct uh_connection *con, *tmp_c;
|
||||||
struct uh_route *r, *tmp_r;
|
struct uh_route *r, *tmp_r;
|
||||||
|
|
||||||
if (srv->sock > 0)
|
if (srv->sock > 0)
|
||||||
close(srv->sock);
|
close(srv->sock);
|
||||||
|
|
||||||
ev_io_stop(srv->loop, &srv->read_watcher);
|
ev_io_stop(srv->loop, &srv->read_watcher);
|
||||||
|
|
||||||
list_for_each_entry_safe(con, tmp_c, &srv->connections, list) {
|
list_for_each_entry_safe(con, tmp_c, &srv->connections, list) {
|
||||||
uh_connection_destroy(con);
|
uh_connection_destroy(con);
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry_safe(r, tmp_r, &srv->routes, list) {
|
list_for_each_entry_safe(r, tmp_r, &srv->routes, list) {
|
||||||
list_del(&r->list);
|
list_del(&r->list);
|
||||||
free(r->path);
|
free(r->path);
|
||||||
free(r);
|
free(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
uh_ssl_ctx_free(srv);
|
uh_ssl_ctx_free(srv);
|
||||||
|
|
||||||
free(srv);
|
free(srv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int uh_send(struct uh_connection *con, const void *buf, int len)
|
int uh_send(struct uh_connection *con, const void *buf, int len)
|
||||||
{
|
{
|
||||||
len = uh_buf_append(&con->write_buf, buf, len);
|
len = uh_buf_append(&con->write_buf, buf, len);
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
ev_io_start(con->srv->loop, &con->write_watcher);
|
ev_io_start(con->srv->loop, &con->write_watcher);
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
int uh_printf(struct uh_connection *con, const char *fmt, ...)
|
int uh_printf(struct uh_connection *con, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
int len = 0;
|
int len = 0;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char *str = NULL;
|
char *str = NULL;
|
||||||
|
|
||||||
assert(fmt);
|
assert(fmt);
|
||||||
|
|
||||||
if (*fmt) {
|
if (*fmt) {
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
len = vasprintf(&str, fmt, ap);
|
len = vasprintf(&str, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len >= 0) {
|
if (len >= 0) {
|
||||||
len = uh_send(con, str, len);
|
len = uh_send(con, str, len);
|
||||||
free(str);
|
free(str);
|
||||||
}
|
}
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_status_line(struct uh_connection *con, int code)
|
static void send_status_line(struct uh_connection *con, int code)
|
||||||
{
|
{
|
||||||
const char *reason = get_http_status_message(code);
|
const char *reason = get_http_status_message(code);
|
||||||
uh_printf(con, "HTTP/1.1 %d %s\r\nServer: Libuhttp %s\r\n",
|
uh_printf(con, "HTTP/1.1 %d %s\r\nServer: Libuhttp %s\r\n",
|
||||||
code, reason, UHTTP_VERSION_STRING);
|
code, reason, UHTTP_VERSION_STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_send_head(struct uh_connection *con, int status, int length, const char *extra_headers)
|
void uh_send_head(struct uh_connection *con, int status, int length, const char *extra_headers)
|
||||||
{
|
{
|
||||||
send_status_line(con, status);
|
send_status_line(con, status);
|
||||||
|
|
||||||
if (length < 0)
|
if (length < 0)
|
||||||
uh_printf(con, "%s", "Transfer-Encoding: chunked\r\n");
|
uh_printf(con, "%s", "Transfer-Encoding: chunked\r\n");
|
||||||
else
|
else
|
||||||
uh_printf(con, "Content-Length: %d\r\n", length);
|
uh_printf(con, "Content-Length: %d\r\n", length);
|
||||||
|
|
||||||
if (extra_headers)
|
if (extra_headers)
|
||||||
uh_send(con, extra_headers, strlen(extra_headers));
|
uh_send(con, extra_headers, strlen(extra_headers));
|
||||||
|
|
||||||
uh_send(con, "\r\n", 2);
|
uh_send(con, "\r\n", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_send_error(struct uh_connection *con, int code, const char *reason)
|
void uh_send_error(struct uh_connection *con, int code, const char *reason)
|
||||||
{
|
{
|
||||||
http_parser *parser = &con->parser;
|
http_parser *parser = &con->parser;
|
||||||
|
|
||||||
if (!reason)
|
if (!reason)
|
||||||
reason = get_http_status_message(code);
|
reason = get_http_status_message(code);
|
||||||
|
|
||||||
if (http_should_keep_alive(parser) && code < UH_STATUS_BAD_REQUEST) {
|
if (http_should_keep_alive(parser) && code < UH_STATUS_BAD_REQUEST) {
|
||||||
uh_send_head(con, code, strlen(reason), "Content-Type: text/plain\r\nConnection: keep-alive\r\n");
|
uh_send_head(con, code, strlen(reason), "Content-Type: text/plain\r\nConnection: keep-alive\r\n");
|
||||||
} else {
|
} else {
|
||||||
uh_send_head(con, code, strlen(reason), "Content-Type: text/plain\r\nConnection: close\r\n");
|
uh_send_head(con, code, strlen(reason), "Content-Type: text/plain\r\nConnection: close\r\n");
|
||||||
con->flags |= UH_CON_CLOSE;
|
con->flags |= UH_CON_CLOSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_redirect(struct uh_connection *con, int code, const char *location)
|
void uh_redirect(struct uh_connection *con, int code, const char *location)
|
||||||
{
|
{
|
||||||
char body[128] = "";
|
char body[128] = "";
|
||||||
http_parser *parser = &con->parser;
|
http_parser *parser = &con->parser;
|
||||||
|
|
||||||
snprintf(body, sizeof(body), "<p>Moved <a href=\"%s\">here</a></p>", location);
|
snprintf(body, sizeof(body), "<p>Moved <a href=\"%s\">here</a></p>", location);
|
||||||
|
|
||||||
send_status_line(con, code);
|
send_status_line(con, code);
|
||||||
|
|
||||||
uh_printf(con,
|
uh_printf(con,
|
||||||
"Location: %s\r\n"
|
"Location: %s\r\n"
|
||||||
"Content-Type: text/html\r\n"
|
"Content-Type: text/html\r\n"
|
||||||
"Content-Length: %zu\r\n"
|
"Content-Length: %zu\r\n"
|
||||||
"Cache-Control: no-cache\r\n", location, strlen(body));
|
"Cache-Control: no-cache\r\n", location, strlen(body));
|
||||||
|
|
||||||
uh_send(con, "\r\n", 2);
|
uh_send(con, "\r\n", 2);
|
||||||
|
|
||||||
if (parser->method != HTTP_HEAD)
|
if (parser->method != HTTP_HEAD)
|
||||||
uh_send(con, body, strlen(body));
|
uh_send(con, body, strlen(body));
|
||||||
}
|
}
|
||||||
|
|
||||||
int uh_send_chunk(struct uh_connection *con, const char *buf, int len)
|
int uh_send_chunk(struct uh_connection *con, const char *buf, int len)
|
||||||
{
|
{
|
||||||
int slen = 0;
|
int slen = 0;
|
||||||
slen += uh_printf(con, "%X\r\n", len);
|
slen += uh_printf(con, "%X\r\n", len);
|
||||||
slen += uh_send(con, buf, len);
|
slen += uh_send(con, buf, len);
|
||||||
slen += uh_send(con, "\r\n", 2);
|
slen += uh_send(con, "\r\n", 2);
|
||||||
return slen;
|
return slen;
|
||||||
}
|
}
|
||||||
|
|
||||||
int uh_printf_chunk(struct uh_connection *con, const char *fmt, ...)
|
int uh_printf_chunk(struct uh_connection *con, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
int len = 0;
|
int len = 0;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char *str = NULL;
|
char *str = NULL;
|
||||||
|
|
||||||
assert(fmt);
|
assert(fmt);
|
||||||
|
|
||||||
if (*fmt) {
|
if (*fmt) {
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
len = vasprintf(&str, fmt, ap);
|
len = vasprintf(&str, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len >= 0) {
|
if (len >= 0) {
|
||||||
len = uh_send_chunk(con, str, len);
|
len = uh_send_chunk(con, str, len);
|
||||||
free(str);
|
free(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
int uh_register_route(struct uh_server *srv, const char *path, uh_route_handler_t cb)
|
int uh_register_route(struct uh_server *srv, const char *path, uh_route_handler_t cb)
|
||||||
{
|
{
|
||||||
struct uh_route *r;
|
struct uh_route *r;
|
||||||
|
|
||||||
assert(path);
|
assert(path);
|
||||||
|
|
||||||
r = calloc(1, sizeof(struct uh_route));
|
r = calloc(1, sizeof(struct uh_route));
|
||||||
if (!r) {
|
if (!r) {
|
||||||
uh_log_err("calloc");
|
uh_log_err("calloc");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
r->path = strdup(path);
|
r->path = strdup(path);
|
||||||
if (!r->path) {
|
if (!r->path) {
|
||||||
uh_log_err("strdup");
|
uh_log_err("strdup");
|
||||||
free(r);
|
free(r);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
r->cb = cb;
|
r->cb = cb;
|
||||||
list_add(&r->list, &srv->routes);
|
list_add(&r->list, &srv->routes);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline struct uh_value *uh_get_url(struct uh_connection *con)
|
inline struct uh_value *uh_get_url(struct uh_connection *con)
|
||||||
{
|
{
|
||||||
return &con->req.url;
|
return &con->req.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct uh_value *uh_get_header(struct uh_connection *con, const char *name)
|
struct uh_value *uh_get_header(struct uh_connection *con, const char *name)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct uh_header *header = con->req.header;
|
struct uh_header *header = con->req.header;
|
||||||
|
|
||||||
for (i = 0; i < con->req.header_num; i++) {
|
for (i = 0; i < con->req.header_num; i++) {
|
||||||
if (uh_value_cmp(&header[i].field, name))
|
if (uh_value_cmp(&header[i].field, name))
|
||||||
return &header[i].value;
|
return &header[i].value;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
128
src/uhttp.h
128
src/uhttp.h
|
@ -8,73 +8,73 @@
|
||||||
|
|
||||||
/* HTTP Status Codes */
|
/* HTTP Status Codes */
|
||||||
enum uh_status {
|
enum uh_status {
|
||||||
UH_STATUS_CONTINUE = 100,
|
UH_STATUS_CONTINUE = 100,
|
||||||
UH_STATUS_SWITCHING_PROTOCOLS = 101,
|
UH_STATUS_SWITCHING_PROTOCOLS = 101,
|
||||||
UH_STATUS_PROCESSING = 102,
|
UH_STATUS_PROCESSING = 102,
|
||||||
UH_STATUS_OK = 200,
|
UH_STATUS_OK = 200,
|
||||||
UH_STATUS_CREATED = 201,
|
UH_STATUS_CREATED = 201,
|
||||||
UH_STATUS_ACCEPTED = 202,
|
UH_STATUS_ACCEPTED = 202,
|
||||||
UH_STATUS_NON_AUTHORITATIVE_INFORMATION = 203,
|
UH_STATUS_NON_AUTHORITATIVE_INFORMATION = 203,
|
||||||
UH_STATUS_NO_CONTENT = 204,
|
UH_STATUS_NO_CONTENT = 204,
|
||||||
UH_STATUS_RESET_CONTENT = 205,
|
UH_STATUS_RESET_CONTENT = 205,
|
||||||
UH_STATUS_PARTIAL_CONTENT = 206,
|
UH_STATUS_PARTIAL_CONTENT = 206,
|
||||||
UH_STATUS_MULTI_STATUS = 207,
|
UH_STATUS_MULTI_STATUS = 207,
|
||||||
UH_STATUS_ALREADY_REPORTED = 208,
|
UH_STATUS_ALREADY_REPORTED = 208,
|
||||||
UH_STATUS_IM_USED = 226,
|
UH_STATUS_IM_USED = 226,
|
||||||
UH_STATUS_MULTIPLE_CHOICES = 300,
|
UH_STATUS_MULTIPLE_CHOICES = 300,
|
||||||
UH_STATUS_MOVED_PERMANENTLY = 301,
|
UH_STATUS_MOVED_PERMANENTLY = 301,
|
||||||
UH_STATUS_FOUND = 302,
|
UH_STATUS_FOUND = 302,
|
||||||
UH_STATUS_SEE_OTHER = 303,
|
UH_STATUS_SEE_OTHER = 303,
|
||||||
UH_STATUS_NOT_MODIFIED = 304,
|
UH_STATUS_NOT_MODIFIED = 304,
|
||||||
UH_STATUS_USE_PROXY = 305,
|
UH_STATUS_USE_PROXY = 305,
|
||||||
UH_STATUS_TEMPORARY_REDIRECT = 307,
|
UH_STATUS_TEMPORARY_REDIRECT = 307,
|
||||||
UH_STATUS_PERMANENT_REDIRECT = 308,
|
UH_STATUS_PERMANENT_REDIRECT = 308,
|
||||||
UH_STATUS_BAD_REQUEST = 400,
|
UH_STATUS_BAD_REQUEST = 400,
|
||||||
UH_STATUS_UNAUTHORIZED = 401,
|
UH_STATUS_UNAUTHORIZED = 401,
|
||||||
UH_STATUS_PAYMENT_REQUIRED = 402,
|
UH_STATUS_PAYMENT_REQUIRED = 402,
|
||||||
UH_STATUS_FORBIDDEN = 403,
|
UH_STATUS_FORBIDDEN = 403,
|
||||||
UH_STATUS_NOT_FOUND = 404,
|
UH_STATUS_NOT_FOUND = 404,
|
||||||
UH_STATUS_METHOD_NOT_ALLOWED = 405,
|
UH_STATUS_METHOD_NOT_ALLOWED = 405,
|
||||||
UH_STATUS_NOT_ACCEPTABLE = 406,
|
UH_STATUS_NOT_ACCEPTABLE = 406,
|
||||||
UH_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
|
UH_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||||
UH_STATUS_REQUEST_TIMEOUT = 408,
|
UH_STATUS_REQUEST_TIMEOUT = 408,
|
||||||
UH_STATUS_CONFLICT = 409,
|
UH_STATUS_CONFLICT = 409,
|
||||||
UH_STATUS_GONE = 410,
|
UH_STATUS_GONE = 410,
|
||||||
UH_STATUS_LENGTH_REQUIRED = 411,
|
UH_STATUS_LENGTH_REQUIRED = 411,
|
||||||
UH_STATUS_PRECONDITION_FAILED = 412,
|
UH_STATUS_PRECONDITION_FAILED = 412,
|
||||||
UH_STATUS_PAYLOAD_TOO_LARGE = 413,
|
UH_STATUS_PAYLOAD_TOO_LARGE = 413,
|
||||||
UH_STATUS_URI_TOO_LONG = 414,
|
UH_STATUS_URI_TOO_LONG = 414,
|
||||||
UH_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,
|
UH_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,
|
||||||
UH_STATUS_RANGE_NOT_SATISFIABLE = 416,
|
UH_STATUS_RANGE_NOT_SATISFIABLE = 416,
|
||||||
UH_STATUS_EXPECTATION_FAILED = 417,
|
UH_STATUS_EXPECTATION_FAILED = 417,
|
||||||
UH_STATUS_MISDIRECTED_REQUEST = 421,
|
UH_STATUS_MISDIRECTED_REQUEST = 421,
|
||||||
UH_STATUS_UNPROCESSABLE_ENTITY = 422,
|
UH_STATUS_UNPROCESSABLE_ENTITY = 422,
|
||||||
UH_STATUS_LOCKED = 423,
|
UH_STATUS_LOCKED = 423,
|
||||||
UH_STATUS_FAILED_DEPENDENCY = 424,
|
UH_STATUS_FAILED_DEPENDENCY = 424,
|
||||||
UH_STATUS_UPGRADE_REQUIRED = 426,
|
UH_STATUS_UPGRADE_REQUIRED = 426,
|
||||||
UH_STATUS_PRECONDITION_REQUIRED = 428,
|
UH_STATUS_PRECONDITION_REQUIRED = 428,
|
||||||
UH_STATUS_TOO_MANY_REQUESTS = 429,
|
UH_STATUS_TOO_MANY_REQUESTS = 429,
|
||||||
UH_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
UH_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||||
UH_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451,
|
UH_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451,
|
||||||
UH_STATUS_INTERNAL_SERVER_ERROR = 500,
|
UH_STATUS_INTERNAL_SERVER_ERROR = 500,
|
||||||
UH_STATUS_NOT_IMPLEMENTED = 501,
|
UH_STATUS_NOT_IMPLEMENTED = 501,
|
||||||
UH_STATUS_BAD_GATEWAY = 502,
|
UH_STATUS_BAD_GATEWAY = 502,
|
||||||
UH_STATUS_SERVICE_UNAVAILABLE = 503,
|
UH_STATUS_SERVICE_UNAVAILABLE = 503,
|
||||||
UH_STATUS_GATEWAY_TIMEOUT = 504,
|
UH_STATUS_GATEWAY_TIMEOUT = 504,
|
||||||
UH_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505,
|
UH_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||||
UH_STATUS_VARIANT_ALSO_NEGOTIATES = 506,
|
UH_STATUS_VARIANT_ALSO_NEGOTIATES = 506,
|
||||||
UH_STATUS_INSUFFICIENT_STORAGE = 507,
|
UH_STATUS_INSUFFICIENT_STORAGE = 507,
|
||||||
UH_STATUS_LOOP_DETECTED = 508,
|
UH_STATUS_LOOP_DETECTED = 508,
|
||||||
UH_STATUS_NOT_EXTENDED = 510,
|
UH_STATUS_NOT_EXTENDED = 510,
|
||||||
UH_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511
|
UH_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uh_server;
|
struct uh_server;
|
||||||
struct uh_connection;
|
struct uh_connection;
|
||||||
|
|
||||||
struct uh_value {
|
struct uh_value {
|
||||||
const char *at;
|
const char *at;
|
||||||
size_t len;
|
size_t len;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (*uh_route_handler_t)(struct uh_connection *con);
|
typedef void (*uh_route_handler_t)(struct uh_connection *con);
|
||||||
|
@ -131,9 +131,9 @@ void uh_redirect(struct uh_connection *con, int code, const char *location);
|
||||||
* to tell the client that everything was sent.
|
* to tell the client that everything was sent.
|
||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* char data[] = "Hello World";
|
* char data[] = "Hello World";
|
||||||
* uh_send_chunk(con, data, strlen(data));
|
* uh_send_chunk(con, data, strlen(data));
|
||||||
* uh_send_chunk(con, NULL, 0); // Tell the client we're finished
|
* uh_send_chunk(con, NULL, 0); // Tell the client we're finished
|
||||||
*/
|
*/
|
||||||
int uh_send_chunk(struct uh_connection *con, const char *buf, int len);
|
int uh_send_chunk(struct uh_connection *con, const char *buf, int len);
|
||||||
|
|
||||||
|
|
|
@ -5,64 +5,64 @@
|
||||||
|
|
||||||
int uh_buf_init(struct uh_buf *buf, size_t initial_size)
|
int uh_buf_init(struct uh_buf *buf, size_t initial_size)
|
||||||
{
|
{
|
||||||
buf->len = buf->size = 0;
|
buf->len = buf->size = 0;
|
||||||
|
|
||||||
if (buf->base) {
|
if (buf->base) {
|
||||||
free(buf->base);
|
free(buf->base);
|
||||||
buf->base = NULL;
|
buf->base = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initial_size > 0) {
|
if (initial_size > 0) {
|
||||||
buf->base = malloc(initial_size);
|
buf->base = malloc(initial_size);
|
||||||
if (!buf->base)
|
if (!buf->base)
|
||||||
return -1;
|
return -1;
|
||||||
buf->size = initial_size;
|
buf->size = initial_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int uh_buf_grow(struct uh_buf *buf, size_t size)
|
int uh_buf_grow(struct uh_buf *buf, size_t size)
|
||||||
{
|
{
|
||||||
void *base = realloc(buf->base, buf->size + size);
|
void *base = realloc(buf->base, buf->size + size);
|
||||||
if (!base)
|
if (!base)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
buf->base = base;
|
buf->base = base;
|
||||||
buf->size += size;
|
buf->size += size;
|
||||||
|
|
||||||
uh_log_debug("uh_buf_grow:%p +%d", buf, size);
|
uh_log_debug("uh_buf_grow:%p +%d", buf, size);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_buf_free(struct uh_buf *buf)
|
void uh_buf_free(struct uh_buf *buf)
|
||||||
{
|
{
|
||||||
uh_buf_init(buf, 0);
|
uh_buf_init(buf, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t uh_buf_append(struct uh_buf *buf, const void *data, size_t len)
|
size_t uh_buf_append(struct uh_buf *buf, const void *data, size_t len)
|
||||||
{
|
{
|
||||||
assert(buf);
|
assert(buf);
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (buf->len + len > buf->size) {
|
if (buf->len + len > buf->size) {
|
||||||
if (uh_buf_grow(buf, len * UH_BUF_SIZE_MULTIPLIER) == -1)
|
if (uh_buf_grow(buf, len * UH_BUF_SIZE_MULTIPLIER) == -1)
|
||||||
len = buf->size - buf->len;
|
len = buf->size - buf->len;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(buf->base + buf->len, data, len);
|
memcpy(buf->base + buf->len, data, len);
|
||||||
buf->len += len;
|
buf->len += len;
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_buf_remove(struct uh_buf *buf, size_t n)
|
void uh_buf_remove(struct uh_buf *buf, size_t n)
|
||||||
{
|
{
|
||||||
if (n > 0 && n <= buf->len) {
|
if (n > 0 && n <= buf->len) {
|
||||||
memmove(buf->base, buf->base + n, buf->len - n);
|
memmove(buf->base, buf->base + n, buf->len - n);
|
||||||
buf->len -= n;
|
buf->len -= n;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,9 +8,9 @@
|
||||||
#define UH_BUF_SIZE_MULTIPLIER 1.5
|
#define UH_BUF_SIZE_MULTIPLIER 1.5
|
||||||
|
|
||||||
struct uh_buf {
|
struct uh_buf {
|
||||||
char *base; /* Buffer pointer */
|
char *base; /* Buffer pointer */
|
||||||
size_t len; /* Data length */
|
size_t len; /* Data length */
|
||||||
size_t size; /* Buffer size */
|
size_t size; /* Buffer size */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define uh_buf_available(b) ((b)->size - (b)->len)
|
#define uh_buf_available(b) ((b)->size - (b)->len)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#ifndef _UHTTP_CONFIG_H
|
#ifndef _UHTTP_CONFIG_H
|
||||||
#define _UHTTP_CONFIG_H
|
#define _UHTTP_CONFIG_H
|
||||||
|
|
||||||
#define UHTTP_VERSION_MAJOR @UHTTP_VERSION_MAJOR@
|
#define UHTTP_VERSION_MAJOR @UHTTP_VERSION_MAJOR@
|
||||||
#define UHTTP_VERSION_MINOR @UHTTP_VERSION_MINOR@
|
#define UHTTP_VERSION_MINOR @UHTTP_VERSION_MINOR@
|
||||||
#define UHTTP_VERSION_STRING "@UHTTP_VERSION_MAJOR@.@UHTTP_VERSION_MINOR@"
|
#define UHTTP_VERSION_STRING "@UHTTP_VERSION_MAJOR@.@UHTTP_VERSION_MINOR@"
|
||||||
|
|
||||||
#define UHTTP_DEBUG @UHTTP_DEBUG_CONFIG@
|
#define UHTTP_DEBUG @UHTTP_DEBUG_CONFIG@
|
||||||
|
|
||||||
|
|
|
@ -10,65 +10,65 @@
|
||||||
#define UH_CONNECTION_TIMEOUT 30
|
#define UH_CONNECTION_TIMEOUT 30
|
||||||
#define UH_MAX_HTTP_HEAD_SIZE 1024
|
#define UH_MAX_HTTP_HEAD_SIZE 1024
|
||||||
#define UH_MAX_HTTP_BODY_SIZE (2 * 1024 * 1024)
|
#define UH_MAX_HTTP_BODY_SIZE (2 * 1024 * 1024)
|
||||||
#define UH_MAX_HTTP_HEADERS 20
|
#define UH_MAX_HTTP_HEADERS 20
|
||||||
|
|
||||||
#define UH_CON_CLOSE (1 << 0)
|
#define UH_CON_CLOSE (1 << 0)
|
||||||
#define UH_CON_SSL_HANDSHAKE_DONE (1 << 1) /* SSL hanshake has completed */
|
#define UH_CON_SSL_HANDSHAKE_DONE (1 << 1) /* SSL hanshake has completed */
|
||||||
#define UH_CON_PARSERING (1 << 2) /* Whether executed http_parser_execute() */
|
#define UH_CON_PARSERING (1 << 2) /* Whether executed http_parser_execute() */
|
||||||
|
|
||||||
#define likely(x) (__builtin_expect(!!(x), 1))
|
#define likely(x) (__builtin_expect(!!(x), 1))
|
||||||
#define unlikely(x) (__builtin_expect(!!(x), 0))
|
#define unlikely(x) (__builtin_expect(!!(x), 0))
|
||||||
|
|
||||||
#define ev_timer_mode(l,w,after,repeat) do { \
|
#define ev_timer_mode(l,w,after,repeat) do { \
|
||||||
ev_timer_stop(l, w); \
|
ev_timer_stop(l, w); \
|
||||||
ev_timer_init(w, ev_cb(w), after, repeat); \
|
ev_timer_init(w, ev_cb(w), after, repeat); \
|
||||||
ev_timer_start(l, w); \
|
ev_timer_start(l, w); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
struct uh_route {
|
struct uh_route {
|
||||||
char *path;
|
char *path;
|
||||||
uh_route_handler_t cb;
|
uh_route_handler_t cb;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uh_server {
|
struct uh_server {
|
||||||
int sock;
|
int sock;
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
void *ssl_ctx;
|
void *ssl_ctx;
|
||||||
#endif
|
#endif
|
||||||
ev_io read_watcher;
|
ev_io read_watcher;
|
||||||
struct ev_loop *loop;
|
struct ev_loop *loop;
|
||||||
struct list_head routes;
|
struct list_head routes;
|
||||||
struct list_head connections;
|
struct list_head connections;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uh_header {
|
struct uh_header {
|
||||||
struct uh_value field;
|
struct uh_value field;
|
||||||
struct uh_value value;
|
struct uh_value value;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uh_request {
|
struct uh_request {
|
||||||
struct uh_value url;
|
struct uh_value url;
|
||||||
struct uh_value body;
|
struct uh_value body;
|
||||||
int header_num;
|
int header_num;
|
||||||
struct uh_header header[UH_MAX_HTTP_HEADERS];
|
struct uh_header header[UH_MAX_HTTP_HEADERS];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uh_connection {
|
struct uh_connection {
|
||||||
int sock;
|
int sock;
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
void *ssl;
|
void *ssl;
|
||||||
#endif
|
#endif
|
||||||
unsigned char flags;
|
unsigned char flags;
|
||||||
struct uh_buf read_buf;
|
struct uh_buf read_buf;
|
||||||
struct uh_buf write_buf;
|
struct uh_buf write_buf;
|
||||||
ev_io read_watcher;
|
ev_io read_watcher;
|
||||||
ev_io write_watcher;
|
ev_io write_watcher;
|
||||||
ev_timer timer_watcher;
|
ev_timer timer_watcher;
|
||||||
struct uh_request req;
|
struct uh_request req;
|
||||||
http_parser parser;
|
http_parser parser;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct uh_server *srv;
|
struct uh_server *srv;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,29 +2,29 @@
|
||||||
|
|
||||||
void __uh_log(const char *filename, int line, int priority, const char *format, ...)
|
void __uh_log(const char *filename, int line, int priority, const char *format, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
static char buf[128];
|
static char buf[128];
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "(%s:%d) ", filename, line);
|
snprintf(buf, sizeof(buf), "(%s:%d) ", filename, line);
|
||||||
|
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), format, ap);
|
vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), format, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
if (priority == LOG_ERR && errno > 0) {
|
if (priority == LOG_ERR && errno > 0) {
|
||||||
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ":%s", strerror(errno));
|
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ":%s", strerror(errno));
|
||||||
errno = 0;
|
errno = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
syslog(priority, "%s", buf);
|
syslog(priority, "%s", buf);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if (UHTTP_DEBUG)
|
#if (UHTTP_DEBUG)
|
||||||
fprintf(stderr, "%s\n", buf);
|
fprintf(stderr, "%s\n", buf);
|
||||||
#else
|
#else
|
||||||
if (priority == LOG_ERR)
|
if (priority == LOG_ERR)
|
||||||
fprintf(stderr, "%s\n", buf);
|
fprintf(stderr, "%s\n", buf);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,13 @@
|
||||||
#define uh_log(priority, format...) __uh_log(__FILENAME__, __LINE__, priority, format)
|
#define uh_log(priority, format...) __uh_log(__FILENAME__, __LINE__, priority, format)
|
||||||
|
|
||||||
#if (UHTTP_DEBUG)
|
#if (UHTTP_DEBUG)
|
||||||
#define uh_log_debug(format...) uh_log(LOG_DEBUG, format)
|
#define uh_log_debug(format...) uh_log(LOG_DEBUG, format)
|
||||||
#else
|
#else
|
||||||
#define uh_log_debug(format...)
|
#define uh_log_debug(format...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define uh_log_info(format...) uh_log(LOG_INFO, format)
|
#define uh_log_info(format...) uh_log(LOG_INFO, format)
|
||||||
#define uh_log_err(format...) uh_log(LOG_ERR, format)
|
#define uh_log_err(format...) uh_log(LOG_ERR, format)
|
||||||
|
|
||||||
void __uh_log(const char *filename, int line, int priority, const char *format, ...);
|
void __uh_log(const char *filename, int line, int priority, const char *format, ...);
|
||||||
|
|
||||||
|
|
236
src/uhttp_ssl.c
236
src/uhttp_ssl.c
|
@ -5,196 +5,196 @@
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
int uh_ssl_init(struct uh_server *srv, const char *cert, const char *key)
|
int uh_ssl_init(struct uh_server *srv, const char *cert, const char *key)
|
||||||
{
|
{
|
||||||
SSL_CTX *ctx = NULL;
|
SSL_CTX *ctx = NULL;
|
||||||
|
|
||||||
SSL_library_init();
|
SSL_library_init();
|
||||||
|
|
||||||
/* registers the error strings for all libssl functions */
|
/* registers the error strings for all libssl functions */
|
||||||
SSL_load_error_strings();
|
SSL_load_error_strings();
|
||||||
|
|
||||||
/* creates a new SSL_CTX object */
|
/* creates a new SSL_CTX object */
|
||||||
ctx = SSL_CTX_new(SSLv23_server_method());
|
ctx = SSL_CTX_new(SSLv23_server_method());
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
uh_log_err("Failed to create SSL context");
|
uh_log_err("Failed to create SSL context");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* loads the first certificate stored in file into ctx */
|
/* loads the first certificate stored in file into ctx */
|
||||||
if (SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM) != SSL_SUCCESS) {
|
if (SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM) != SSL_SUCCESS) {
|
||||||
uh_log_err("OpenSSL Error: loading certificate file failed");
|
uh_log_err("OpenSSL Error: loading certificate file failed");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* adds the first private RSA key found in file to ctx.
|
* adds the first private RSA key found in file to ctx.
|
||||||
*
|
*
|
||||||
* checks the consistency of a private key with the corresponding
|
* checks the consistency of a private key with the corresponding
|
||||||
* certificate loaded into ctx. If more than one key/certificate
|
* certificate loaded into ctx. If more than one key/certificate
|
||||||
* pair (RSA/DSA) is installed, the last item installed will be checked.
|
* pair (RSA/DSA) is installed, the last item installed will be checked.
|
||||||
*/
|
*/
|
||||||
if (SSL_CTX_use_RSAPrivateKey_file(ctx, key, SSL_FILETYPE_PEM) != SSL_SUCCESS) {
|
if (SSL_CTX_use_RSAPrivateKey_file(ctx, key, SSL_FILETYPE_PEM) != SSL_SUCCESS) {
|
||||||
uh_log_err("OpenSSL Error: loading key failed");
|
uh_log_err("OpenSSL Error: loading key failed");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
srv->ssl_ctx = ctx;
|
srv->ssl_ctx = ctx;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
SSL_CTX_free(ctx);
|
SSL_CTX_free(ctx);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void uh_ssl_ctx_free(struct uh_server *srv)
|
void uh_ssl_ctx_free(struct uh_server *srv)
|
||||||
{
|
{
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
if (!srv->ssl_ctx)
|
if (!srv->ssl_ctx)
|
||||||
return;
|
return;
|
||||||
SSL_CTX_free(srv->ssl_ctx);
|
SSL_CTX_free(srv->ssl_ctx);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_ssl_free(struct uh_connection *con)
|
void uh_ssl_free(struct uh_connection *con)
|
||||||
{
|
{
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
if (!con->ssl)
|
if (!con->ssl)
|
||||||
return;
|
return;
|
||||||
SSL_shutdown(con->ssl);
|
SSL_shutdown(con->ssl);
|
||||||
SSL_free(con->ssl);
|
SSL_free(con->ssl);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
static int uh_ssl_err(struct uh_connection *con, int ret, const char *fun)
|
static int uh_ssl_err(struct uh_connection *con, int ret, const char *fun)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
err = SSL_get_error(con->ssl, ret);
|
err = SSL_get_error(con->ssl, ret);
|
||||||
if (err == SSL_ERROR_ZERO_RETURN || ERR_peek_error()) {
|
if (err == SSL_ERROR_ZERO_RETURN || ERR_peek_error()) {
|
||||||
con->flags |= UH_CON_CLOSE;
|
con->flags |= UH_CON_CLOSE;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (UHTTP_USE_OPENSSL)
|
#if (UHTTP_USE_OPENSSL)
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
con->flags |= UH_CON_CLOSE;
|
con->flags |= UH_CON_CLOSE;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
|
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (err == SSL_ERROR_SYSCALL) {
|
if (err == SSL_ERROR_SYSCALL) {
|
||||||
if (errno > 0)
|
if (errno > 0)
|
||||||
uh_log_err("%s", fun);
|
uh_log_err("%s", fun);
|
||||||
con->flags |= UH_CON_CLOSE;
|
con->flags |= UH_CON_CLOSE;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
con->flags |= UH_CON_CLOSE;
|
con->flags |= UH_CON_CLOSE;
|
||||||
uh_log_err("%s() Error: %s", fun, ERR_reason_error_string(err));
|
uh_log_err("%s() Error: %s", fun, ERR_reason_error_string(err));
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int uh_ssl_read(struct uh_connection *con, void *buf, int count)
|
int uh_ssl_read(struct uh_connection *con, void *buf, int count)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
if (!con->ssl)
|
if (!con->ssl)
|
||||||
goto no_ssl;
|
goto no_ssl;
|
||||||
|
|
||||||
ret = SSL_read(con->ssl, buf, count);
|
ret = SSL_read(con->ssl, buf, count);
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return uh_ssl_err(con, ret, "SSL_read");
|
return uh_ssl_err(con, ret, "SSL_read");
|
||||||
no_ssl:
|
no_ssl:
|
||||||
#endif
|
#endif
|
||||||
ret = read(con->sock, buf, count);
|
ret = read(con->sock, buf, count);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
|
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
con->flags |= UH_CON_CLOSE;
|
con->flags |= UH_CON_CLOSE;
|
||||||
uh_log_err("read");
|
uh_log_err("read");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int uh_ssl_write(struct uh_connection *con, void *buf, int count)
|
int uh_ssl_write(struct uh_connection *con, void *buf, int count)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
if (!con->ssl)
|
if (!con->ssl)
|
||||||
goto no_ssl;
|
goto no_ssl;
|
||||||
|
|
||||||
ret = SSL_write(con->ssl, buf, count);
|
ret = SSL_write(con->ssl, buf, count);
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return uh_ssl_err(con, ret, "SSL_write");
|
return uh_ssl_err(con, ret, "SSL_write");
|
||||||
no_ssl:
|
no_ssl:
|
||||||
#endif
|
#endif
|
||||||
ret = write(con->sock, buf, count);
|
ret = write(con->sock, buf, count);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
|
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
|
||||||
return ret;
|
return ret;
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
con->flags |= UH_CON_CLOSE;
|
con->flags |= UH_CON_CLOSE;
|
||||||
uh_log_err("write");
|
uh_log_err("write");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int uh_ssl_accept(struct uh_connection *con)
|
int uh_ssl_accept(struct uh_connection *con)
|
||||||
{
|
{
|
||||||
int sock = -1;
|
int sock = -1;
|
||||||
struct uh_server *srv = con->srv;
|
struct uh_server *srv = con->srv;
|
||||||
|
|
||||||
sock = accept4(srv->sock, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
sock = accept4(srv->sock, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||||
if (unlikely(sock < 0)) {
|
if (unlikely(sock < 0)) {
|
||||||
if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)
|
if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)
|
||||||
uh_log_err("accept4");
|
uh_log_err("accept4");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
con->sock = sock;
|
con->sock = sock;
|
||||||
|
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
if (!srv->ssl_ctx)
|
if (!srv->ssl_ctx)
|
||||||
return sock;
|
return sock;
|
||||||
|
|
||||||
con->ssl = SSL_new(srv->ssl_ctx);
|
con->ssl = SSL_new(srv->ssl_ctx);
|
||||||
if (!con->ssl)
|
if (!con->ssl)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!SSL_set_fd(con->ssl, sock)) {
|
if (!SSL_set_fd(con->ssl, sock)) {
|
||||||
uh_log_err("SSL_set_fd() failed");
|
uh_log_err("SSL_set_fd() failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
SSL_set_accept_state(con->ssl);
|
SSL_set_accept_state(con->ssl);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return sock;
|
return sock;
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_ssl_handshake(struct uh_connection *con)
|
void uh_ssl_handshake(struct uh_connection *con)
|
||||||
{
|
{
|
||||||
#if (UHTTP_SSL_ENABLED)
|
#if (UHTTP_SSL_ENABLED)
|
||||||
int ret = SSL_accept(con->ssl);
|
int ret = SSL_accept(con->ssl);
|
||||||
if (ret == 1) {
|
if (ret == 1) {
|
||||||
con->flags |= UH_CON_SSL_HANDSHAKE_DONE;
|
con->flags |= UH_CON_SSL_HANDSHAKE_DONE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uh_ssl_err(con, ret, "SSL_accept");
|
uh_ssl_err(con, ret, "SSL_accept");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
|
||||||
#ifndef SSL_SUCCESS
|
#ifndef SSL_SUCCESS
|
||||||
#define SSL_SUCCESS 1
|
#define SSL_SUCCESS 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#elif (UHTTP_USE_CYASSL)
|
#elif (UHTTP_USE_CYASSL)
|
||||||
|
|
Loading…
Reference in New Issue