Added http error messages
This commit is contained in:
58
src/client.c
58
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;
|
||||
|
@ -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];
|
||||
|
@ -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) {
|
||||
|
@ -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 =
|
||||
"<!DOCTYPE html>\n"
|
||||
"<html lang=\"en\">\n"
|
||||
"<head>\n"
|
||||
" <title>%1$i %2$s</title>\n"
|
||||
" <meta charset=\"UTF-8\"/>\n"
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>\n"
|
||||
" <style>\n"
|
||||
" html {\n"
|
||||
" font-family: \"Arial\", sans-serif;\n"
|
||||
" --error: #C00000;\n"
|
||||
" --info: #E0C000;\n"
|
||||
" --color: var(--%5$s);\n"
|
||||
" }\n"
|
||||
" body {\n"
|
||||
" background-color: #F0F0F0;\n"
|
||||
" margin: 0.5em;\n"
|
||||
" }\n"
|
||||
" main {\n"
|
||||
" max-width: 600px;\n"
|
||||
" margin: 2em auto;\n"
|
||||
" background-color: #FFFFFF;\n"
|
||||
" border: 1px solid var(--color);\n"
|
||||
" border-radius: 4px;\n"
|
||||
" padding: 1em 2em;\n"
|
||||
" }\n"
|
||||
" h1, h2, h3, h4, h5, h6, h7 {\n"
|
||||
" text-align: center;\n"
|
||||
" color: var(--color);\n"
|
||||
" }\n"
|
||||
" p {\n"
|
||||
" text-align: center;\n"
|
||||
" }\n"
|
||||
" div.footer {\n"
|
||||
" color: #808080;\n"
|
||||
" font-size: 0.75em;\n"
|
||||
" text-align: center;\n"
|
||||
" }\n"
|
||||
" </style>\n"
|
||||
"</head>\n"
|
||||
"<body>\n"
|
||||
" <main>\n"
|
||||
" <h1>%1$i %2$s</h1>\n"
|
||||
" <p>%3$s</p>\n"
|
||||
" <p>%4$s</p>\n"
|
||||
" <div class=\"footer\">Necronda web server " NECRONDA_VERSION "</div>\n"
|
||||
" </main>\n"
|
||||
"</body>\n"
|
||||
"</html>\n";
|
||||
|
||||
|
||||
void http_to_camel_case(char *str);
|
||||
|
||||
@ -97,6 +178,10 @@ void http_add_header_field(http_hdr *hdr, const char *field_name, const char *fi
|
||||
|
||||
int http_send_response(sock *client, http_res *res);
|
||||
|
||||
http_status *http_get_status(unsigned short status_code);
|
||||
|
||||
http_error_msg *http_get_error_msg(unsigned short status_code);
|
||||
|
||||
const char *http_get_status_color(http_status *status);
|
||||
|
||||
char *http_format_date(time_t time, char *buf, size_t size);
|
||||
|
32
src/uri.c
32
src/uri.c
@ -6,3 +6,35 @@
|
||||
*/
|
||||
|
||||
#include "uri.h"
|
||||
|
||||
|
||||
int uri_init(http_uri *uri, const char *webroot, const char *uri_str) {
|
||||
uri->webroot = malloc(strlen(webroot) + 1);
|
||||
strcpy(uri->webroot, webroot);
|
||||
|
||||
char* query = strchr(uri_str, '?');
|
||||
if (query == NULL) {
|
||||
uri->query = NULL;
|
||||
} else {
|
||||
query[0] = 0;
|
||||
query++;
|
||||
ssize_t size = strlen(query) + 1;
|
||||
uri->query = malloc(size);
|
||||
url_decode(query, uri->query, &size);
|
||||
}
|
||||
|
||||
ssize_t size = strlen(uri_str) + 1;
|
||||
char *uri_dec = malloc(size);
|
||||
url_decode(uri_str, uri_dec, &size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uri_free(http_uri *uri) {
|
||||
free(uri->webroot);
|
||||
free(uri->path);
|
||||
free(uri->pathinfo);
|
||||
if (uri->query != NULL) free(uri->query);
|
||||
free(uri->filename);
|
||||
free(uri->uri);
|
||||
}
|
||||
|
@ -16,8 +16,15 @@ typedef struct {
|
||||
char *pathinfo;
|
||||
char *query;
|
||||
char *filename;
|
||||
char *filename_comp;
|
||||
char *uri;
|
||||
struct stat stat;
|
||||
} uri;
|
||||
int is_static:1;
|
||||
} http_uri;
|
||||
|
||||
|
||||
int uri_init(http_uri *uri, const char *webroot, const char *uri_str);
|
||||
|
||||
void uri_free(http_uri *uri);
|
||||
|
||||
#endif //NECRONDA_SERVER_URI_H
|
||||
|
Reference in New Issue
Block a user