%1$i %2$s
\n" + "%3$s
\n" + "%4$s
\n" + " \n" + "diff --git a/src/client.c b/src/client.c index e5cee98..3fb18c7 100644 --- a/src/client.c +++ b/src/client.c @@ -17,8 +17,13 @@ char *client_addr_str, *client_addr_str_ptr, *server_addr_str, *server_addr_str_ struct timeval client_timeout = {.tv_sec = CLIENT_TIMEOUT, .tv_usec = 0}; -char *get_webroot(char *http_host) { - +char *get_webroot(const char *http_host) { + char *webroot = malloc(strlen(webroot_base) + strlen(http_host) + 1); + unsigned long len = strlen(webroot_base); + while (webroot_base[len - 1] == '/') len--; + long pos = strchr(http_host, ':') - http_host; + sprintf(webroot, "%.*s/%.*s", (int) len, webroot_base, (int) (pos == -1 ? strlen(http_host) : pos), http_host); + return webroot; } void client_terminate() { @@ -34,7 +39,8 @@ int client_request_handler(sock *client, int req_num) { struct timespec begin, end; int ret, client_keep_alive; char buf[64]; - char *host, *hdr_connection; + char msg_buf[4096]; + char *host, *hdr_connection, *webroot; fd_set socket_fds; FD_ZERO(&socket_fds); @@ -47,21 +53,23 @@ int client_request_handler(sock *client, int req_num) { } clock_gettime(CLOCK_MONOTONIC, &begin); - http_req req; - ret = http_receive_request(client, &req); - if (ret != 0) { - return ret; - } - http_res res; sprintf(res.version, "1.1"); res.status = http_get_status(501); res.hdr.field_num = 0; + http_req req; + ret = http_receive_request(client, &req); + if (ret != 0) { + client_keep_alive = 0; + res.status = http_get_status(400); + goto respond; + } + hdr_connection = http_get_header_field(&req.hdr, "Connection"); client_keep_alive = hdr_connection != NULL && strncmp(hdr_connection, "keep-alive", 10) == 0; host = http_get_header_field(&req.hdr, "Host"); - if (host == NULL) { + if (host == NULL || strchr(host, '/') != NULL) { res.status = http_get_status(400); goto respond; } @@ -70,18 +78,44 @@ int client_request_handler(sock *client, int req_num) { log_prefix = log_req_prefix; print(BLD_STR "%s %s" CLR_STR, req.method, req.uri); + webroot = get_webroot(host); + http_uri uri; + uri_init(&uri, webroot, req.uri); + respond: http_add_header_field(&res.hdr, "Date", http_get_date(buf, sizeof(buf))); http_add_header_field(&res.hdr, "Server", SERVER_STR); if (server_keep_alive && client_keep_alive) { http_add_header_field(&res.hdr, "Connection", "keep-alive"); - http_add_header_field(&res.hdr, "Keep-Alive", "timeout=3600, max=100"); + sprintf(buf, "timeout=%i, max=%i", CLIENT_TIMEOUT, REQ_PER_CONNECTION); + http_add_header_field(&res.hdr, "Keep-Alive", buf); } else { http_add_header_field(&res.hdr, "Connection", "close"); } - http_add_header_field(&res.hdr, "Content-Length", "0"); + int len = 0; + if (res.status->code >= 300 && res.status->code < 600) { + http_error_msg *http_msg = http_get_error_msg(res.status->code); + len = sprintf(msg_buf, http_error_document, res.status->code, res.status->msg, + http_msg != NULL ? http_msg->err_msg : "", NULL, + res.status->code >= 300 && res.status->code < 400 ? "info" : "error"); + sprintf(buf, "%i", len); + http_add_header_field(&res.hdr, "Content-Length", buf); + } else { + http_add_header_field(&res.hdr, "Content-Length", "0"); + } http_send_response(client, &res); + if (res.status->code >= 400 && res.status->code < 600) { + int snd_len = 0; + while (snd_len < len) { + if (client->enc) { + ret = SSL_write(client->ssl, msg_buf, len - snd_len); + } else { + ret = send(client->socket, msg_buf, len - snd_len, 0); + } + snd_len += ret; + } + } clock_gettime(CLOCK_MONOTONIC, &end); unsigned long micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000; diff --git a/src/necronda-server.h b/src/necronda-server.h index a7cf175..27e49dc 100644 --- a/src/necronda-server.h +++ b/src/necronda-server.h @@ -47,7 +47,8 @@ #define HTTP_4XX_STR "\x1B[1;31m" #define HTTP_5XX_STR "\x1B[1;31m" -#define SERVER_STR "Necronda/4.0" +#define NECRONDA_VERSION "4.0" +#define SERVER_STR "Necronda/" NECRONDA_VERSION int SOCKETS[NUM_SOCKETS]; pid_t CHILDREN[MAX_CHILDREN]; diff --git a/src/net/http.c b/src/net/http.c index fa32071..37ec4e5 100644 --- a/src/net/http.c +++ b/src/net/http.c @@ -210,6 +210,15 @@ http_status *http_get_status(unsigned short status_code) { return NULL; } +http_error_msg *http_get_error_msg(unsigned short status_code) { + for (int i = 0; i < sizeof(http_error_messages) / sizeof(http_get_error_msg); i++) { + if (http_error_messages[i].code == status_code) { + return &http_error_messages[i]; + } + } + return NULL; +} + const char *http_get_status_color(http_status *status) { unsigned short code = status->code; if (code >= 100 && code < 200) { diff --git a/src/net/http.h b/src/net/http.h index d2c6703..e720a60 100644 --- a/src/net/http.h +++ b/src/net/http.h @@ -14,6 +14,11 @@ typedef struct { char msg[32]; } http_status; +typedef struct { + unsigned short code; + char *err_msg; +} http_error_msg; + typedef struct { char field_num; char *fields[64][2]; @@ -80,6 +85,82 @@ http_status http_statuses[] = { {505, "Server Error", "HTTP Version Not Supported"}, }; +http_error_msg http_error_messages[] = { + {400, "The request could not be understood by the server due to malformed syntax."}, + {401, "The request requires user authentication."}, + {403, "The server understood the request, but is refusing to fulfill it."}, + {404, "The server has not found anything matching the Request-URI."}, + {405, "The method specified in the Request-Line is not allowed for the resource identified by the Request-URI."}, + {406, "The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request."}, + {407, "The request requires user authentication on the proxy."}, + {408, "The client did not produce a request within the time that the server was prepared to wait."}, + {409, "The request could not be completed due to a conflict with the current state of the resource."}, + {410, "The requested resource is no longer available at the server and no forwarding address is known."}, + {411, "The server refuses to accept the request without a defined Content-Length."}, + {412, "The precondition given in one or more of the request-header fields evaluated to false when it was tested on the server."}, + {413, "The server is refusing to process a request because the request entity is larger than the server is willing or able to process."}, + {414, "The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret."}, + {415, "The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method."}, + {417, "The expectation given in an Expect request-header field could not be met by this server, or, if the server is a proxy, the server has unambiguous evidence that the request could not be met by the next-hop server."}, + + {500, "The server encountered an unexpected condition which prevented it from fulfilling the request."}, + {501, "The server does not support the functionality required to fulfill the request."}, + {502, "The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request."}, + {503, "The server is currently unable to handle the request due to a temporary overloading or maintenance of the server."}, + {504, "he server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI or some other auxiliary server it needed to access in attempting to complete the request."}, + {505, "The server does not support, or refuses to support, the HTTP protocol version that was used in the request message."} +}; + +const char *http_error_document = + "\n" + "\n" + "
\n" + "%3$s
\n" + "%4$s
\n" + " \n" + "