diff --git a/conf/kore.conf.example b/conf/kore.conf.example index cd16f6c..3b71465 100644 --- a/conf/kore.conf.example +++ b/conf/kore.conf.example @@ -108,10 +108,17 @@ workers 4 # HTTP specific settings. # http_header_max Maximum size of HTTP headers (in bytes). # +# http_header_timeout Timeout in seconds for receiving the +# HTTP headers before the connection is closed. +# # http_body_max Maximum size of an HTTP body (in bytes). # If set to 0 disallows requests with a body # all together. # +# http_body_timeout Timeout in seconds for receiving the +# HTTP body in full before the connection +# is closed with an 408. +# # http_body_disk_offload Number of bytes after which Kore will use # a temporary file to hold the HTTP body # instead of holding it in memory. If set to @@ -136,7 +143,9 @@ workers 4 # spend inside the HTTP processing loop. # #http_header_max 4096 +#http_header_timeout 10 #http_body_max 1024000 +#http_body_timeout 60 #http_keepalive_time 0 #http_hsts_enable 31536000 #http_request_limit 1000 diff --git a/include/kore/http.h b/include/kore/http.h index 5a67f5e..b19c143 100644 --- a/include/kore/http.h +++ b/include/kore/http.h @@ -51,6 +51,8 @@ extern "C" { #define HTTP_BODY_DISK_OFFLOAD 0 #define HTTP_BODY_PATH_MAX 256 #define HTTP_BOUNDARY_MAX 80 +#define HTTP_HEADER_TIMEOUT 10 +#define HTTP_BODY_TIMEOUT 60 #define HTTP_ARG_TYPE_RAW 0 #define HTTP_ARG_TYPE_BYTE 1 @@ -279,7 +281,9 @@ struct http_media_type { }; extern size_t http_body_max; +extern u_int16_t http_body_timeout; extern u_int16_t http_header_max; +extern u_int16_t http_header_timeout; extern u_int32_t http_request_ms; extern u_int64_t http_hsts_enable; extern u_int16_t http_keepalive_time; @@ -304,6 +308,7 @@ void http_request_wakeup(struct http_request *); void http_process_request(struct http_request *); int http_body_rewind(struct http_request *); int http_media_register(const char *, const char *); +int http_check_timeout(struct connection *, u_int64_t); ssize_t http_body_read(struct http_request *, void *, size_t); int http_body_digest(struct http_request *, char *, size_t); void http_response(struct http_request *, int, const void *, size_t); diff --git a/include/kore/kore.h b/include/kore/kore.h index a7eb460..8e9eab5 100644 --- a/include/kore/kore.h +++ b/include/kore/kore.h @@ -243,6 +243,8 @@ struct connection { struct netbuf *rnb; #if !defined(KORE_NO_HTTP) + u_int64_t http_start; + u_int64_t http_timeout; struct kore_runtime_call *ws_connect; struct kore_runtime_call *ws_message; struct kore_runtime_call *ws_disconnect; diff --git a/src/config.c b/src/config.c index 65e4177..219f808 100644 --- a/src/config.c +++ b/src/config.c @@ -91,7 +91,9 @@ static int configure_static_handler(char *); static int configure_dynamic_handler(char *); static int configure_accesslog(char *); static int configure_http_header_max(char *); +static int configure_http_header_timeout(char *); static int configure_http_body_max(char *); +static int configure_http_body_timeout(char *); static int configure_filemap_ext(char *); static int configure_filemap_index(char *); static int configure_http_media_type(char *); @@ -176,7 +178,9 @@ static struct { { "restrict", configure_restrict }, { "http_media_type", configure_http_media_type }, { "http_header_max", configure_http_header_max }, + { "http_header_timeout", configure_http_header_timeout }, { "http_body_max", configure_http_body_max }, + { "http_body_timeout", configure_http_body_timeout }, { "http_hsts_enable", configure_http_hsts_enable }, { "http_keepalive_time", configure_http_keepalive_time }, { "http_request_ms", configure_http_request_ms }, @@ -872,6 +876,20 @@ configure_http_header_max(char *option) return (KORE_RESULT_OK); } +static int +configure_http_header_timeout(char *option) +{ + int err; + + http_header_timeout = kore_strtonum(option, 10, 1, 65535, &err); + if (err != KORE_RESULT_OK) { + printf("bad http_header_timeout value: %s\n", option); + return (KORE_RESULT_ERROR); + } + + return (KORE_RESULT_OK); +} + static int configure_http_body_max(char *option) { @@ -886,6 +904,20 @@ configure_http_body_max(char *option) return (KORE_RESULT_OK); } +static int +configure_http_body_timeout(char *option) +{ + int err; + + http_body_timeout = kore_strtonum(option, 10, 1, 65535, &err); + if (err != KORE_RESULT_OK) { + printf("bad http_body_timeout value: %s\n", option); + return (KORE_RESULT_ERROR); + } + + return (KORE_RESULT_OK); +} + static int configure_http_body_disk_offload(char *option) { diff --git a/src/connection.c b/src/connection.c index b30f569..cee1c3e 100644 --- a/src/connection.c +++ b/src/connection.c @@ -84,6 +84,8 @@ kore_connection_new(void *owner) c->ws_connect = NULL; c->ws_message = NULL; c->ws_disconnect = NULL; + c->http_start = kore_time_ms(); + c->http_timeout = http_header_timeout * 1000; TAILQ_INIT(&(c->http_requests)); #endif @@ -182,13 +184,14 @@ kore_connection_check_timeout(u_int64_t now) next = TAILQ_NEXT(c, list); if (c->proto == CONN_PROTO_MSG) continue; - if (!(c->flags & CONN_IDLE_TIMER_ACT)) - continue; #if !defined(KORE_NO_HTTP) + if (!http_check_timeout(c, now)) + continue; if (!TAILQ_EMPTY(&c->http_requests)) continue; #endif - kore_connection_check_idletimer(now, c); + if (c->flags & CONN_IDLE_TIMER_ACT) + kore_connection_check_idletimer(now, c); } } @@ -418,7 +421,7 @@ kore_connection_check_idletimer(u_int64_t now, struct connection *c) d = 0; if (d >= c->idle_timer.length) { - kore_debug("%p idle for %" PRIu64 " ms, expiring", c, d); + printf("%p idle for %" PRIu64 " ms, expiring\n", (void *)c, d); kore_connection_disconnect(c); } } diff --git a/src/http.c b/src/http.c index 9da1668..016f313 100644 --- a/src/http.c +++ b/src/http.c @@ -150,13 +150,16 @@ static struct kore_pool http_body_path; u_int32_t http_request_count = 0; u_int32_t http_request_ms = HTTP_REQUEST_MS; +u_int16_t http_body_timeout = HTTP_BODY_TIMEOUT; u_int32_t http_request_limit = HTTP_REQUEST_LIMIT; u_int64_t http_hsts_enable = HTTP_HSTS_ENABLE; u_int16_t http_header_max = HTTP_HEADER_MAX_LEN; u_int16_t http_keepalive_time = HTTP_KEEPALIVE_TIME; +u_int16_t http_header_timeout = HTTP_HEADER_TIMEOUT; + size_t http_body_max = HTTP_BODY_MAX_LEN; -u_int64_t http_body_disk_offload = HTTP_BODY_DISK_OFFLOAD; char *http_body_disk_path = HTTP_BODY_DISK_PATH; +u_int64_t http_body_disk_offload = HTTP_BODY_DISK_OFFLOAD; void http_parent_init(void) @@ -233,6 +236,21 @@ http_server_version(const char *version) http_version_len = l; } +int +http_check_timeout(struct connection *c, u_int64_t now) +{ + if (c->http_timeout == 0) + return (KORE_RESULT_OK); + + if ((now - c->http_start) >= c->http_timeout) { + http_error_response(c, 408); + kore_connection_disconnect(c); + return (KORE_RESULT_ERROR); + } + + return (KORE_RESULT_OK); +} + void http_request_sleep(struct http_request *req) { @@ -834,7 +852,9 @@ http_header_recv(struct netbuf *nb) c->rnb->extra = req; http_request_sleep(req); req->content_length = bytes_left; + c->http_timeout = http_body_timeout * 1000; } else { + c->http_timeout = 0; req->flags |= HTTP_REQUEST_COMPLETE; req->flags &= ~HTTP_REQUEST_EXPECT_BODY; SHA256_Final(req->http_body_digest, &req->hashctx); @@ -844,6 +864,8 @@ http_header_recv(struct netbuf *nb) return (KORE_RESULT_OK); } } + } else { + c->http_timeout = 0; } return (KORE_RESULT_OK); @@ -1919,6 +1941,8 @@ http_response_normal(struct http_request *req, struct connection *c, net_send_queue(c, d, len); if (!(c->flags & CONN_CLOSE_EMPTY)) { + c->http_start = kore_time_ms(); + c->http_timeout = http_header_timeout * 1000; net_recv_reset(c, http_header_max, http_header_recv); (void)net_recv_flush(c); }