diff --git a/src/client.c b/src/client.c index 3671987..db302a0 100644 --- a/src/client.c +++ b/src/client.c @@ -33,7 +33,7 @@ int client_websocket_handler() { int client_request_handler(sock *client, int req_num) { struct timespec begin, end; int ret, client_keep_alive; - char buf[16]; + char buf[64]; char *host, *hdr_connection; char *msg = "HTTP/1.1 501 Not Implemented\r\n" "Connection: keep-alive\r\n" @@ -59,6 +59,11 @@ int client_request_handler(sock *client, int req_num) { return ret; } + http_res res; + sprintf(res.version, "1.1"); + res.status = http_get_status(501); + res.hdr.field_num = 0; + 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"); @@ -71,18 +76,28 @@ int client_request_handler(sock *client, int req_num) { print(BLD_STR "%s %s" CLR_STR, req.method, req.uri); - if (client->enc) { - SSL_write(client->ssl, msg, (int) strlen(msg)); - } else { - send(client->socket, msg, strlen(msg), 0); - } - 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"); + + } else { + http_add_header_field(&res.hdr, "Connection", "close"); + } + http_add_header_field(&res.hdr, "Content-Length", "0"); + + res.status = http_get_status(501); + + http_send_response(client, &res); + clock_gettime(CLOCK_MONOTONIC, &end); unsigned long micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000; - print(ERR_STR "501 Not Implemented (%s)" CLR_STR, format_duration(micros, buf)); + print("%s%03i %s (%s)%s", http_get_status_color(res.status), res.status->code, res.status->msg, format_duration(micros, buf), CLR_STR); http_free_req(&req); + http_free_res(&res); return !client_keep_alive; } diff --git a/src/necronda-server.h b/src/necronda-server.h index c096b66..4c78516 100644 --- a/src/necronda-server.h +++ b/src/necronda-server.h @@ -32,6 +32,14 @@ #define HTTP_STR "\x1B[1;31m" #define HTTPS_STR "\x1B[1;32m" +#define HTTP_1XX_STR "\x1B[1;32m" +#define HTTP_2XX_STR "\x1B[1;32m" +#define HTTP_3XX_STR "\x1B[1;33m" +#define HTTP_4XX_STR "\x1B[1;31m" +#define HTTP_5XX_STR "\x1B[1;31m" + +#define SERVER_STR "Necronda/4.0" + int SOCKETS[NUM_SOCKETS]; pid_t CHILDREN[MAX_CHILDREN]; diff --git a/src/net/http.c b/src/net/http.c index af19454..1543bb2 100644 --- a/src/net/http.c +++ b/src/net/http.c @@ -23,7 +23,6 @@ void http_to_camel_case(char *str) { } } - void http_free_hdr(http_hdr *hdr) { for (int i = 0; i < hdr->field_num; i++) { free(hdr->fields[i][0]); @@ -36,6 +35,10 @@ void http_free_req(http_req *req) { http_free_hdr(&req->hdr); } +void http_free_res(http_res *res) { + http_free_hdr(&res->hdr); +} + int http_receive_request(sock *client, http_req *req) { ssize_t rcv_len, len; char *ptr, *pos0, *pos1, *pos2; @@ -150,15 +153,87 @@ int http_receive_request(sock *client, http_req *req) { } } -char *http_get_header_field(http_hdr *hdr, const char *field_name) { +char *http_get_header_field(http_hdr *hdr, char *field_name) { size_t len = strlen(field_name); char *_field_name = malloc(len + 1); sprintf(_field_name, "%s", field_name); http_to_camel_case(_field_name); for (int i = 0; i < hdr->field_num; i++) { if (strncmp(hdr->fields[i][0], _field_name, len) == 0) { + free(_field_name); return hdr->fields[i][1]; } } + free(_field_name); + return NULL; +} + +void http_add_header_field(http_hdr *hdr, char *field_name, char *field_value) { + size_t len_name = strlen(field_name); + size_t len_value = strlen(field_value); + char *_field_name = malloc(len_name + 1); + char *_field_value = malloc(len_value + 1); + sprintf(_field_name, "%s", field_name); + sprintf(_field_value, "%s", field_value); + http_to_camel_case(_field_name); + hdr->fields[hdr->field_num][0] = _field_name; + hdr->fields[hdr->field_num][1] = _field_value; + hdr->field_num++; +} + +int http_send_response(sock *client, http_res *res) { + char *buf = malloc(CLIENT_MAX_HEADER_SIZE); + int len = 0; + int snd_len = 0; + + len += sprintf(buf + len, "HTTP/%s %03i %s\r\n", res->version, res->status->code, res->status->msg); + for (int i = 0; i < res->hdr.field_num; i++) { + len += sprintf(buf + len, "%s: %s\r\n", res->hdr.fields[i][0], res->hdr.fields[i][1]); + } + len += sprintf(buf + len, "\r\n"); + + if (client->enc) { + snd_len = SSL_write(client->ssl, buf, len); + } else { + snd_len = send(client->socket, buf, len, 0); + } + free(buf); + return 0; +} + +http_status *http_get_status(unsigned short status_code) { + for (int i = 0; i < sizeof(http_statuses) / sizeof(http_status); i++) { + if (http_statuses[i].code == status_code) { + return &http_statuses[i]; + } + } return NULL; } + +const char *http_get_status_color(http_status *status) { + unsigned short code = status->code; + if (code >= 100 && code < 200) { + return HTTP_1XX_STR; + } else if (code >= 200 && code < 300 || code == 304) { + return HTTP_2XX_STR; + } else if (code >= 300 && code < 400) { + return HTTP_3XX_STR; + } else if (code >= 400 && code < 500) { + return HTTP_4XX_STR; + } else if (code >= 500 && code < 600) { + return HTTP_5XX_STR; + } + return ""; +} + +char *http_format_date(time_t time, char *buf, size_t size) { + struct tm *timeinfo = gmtime(&time); + strftime(buf, size, "%a, %d %b %Y %H:%M:%S GMT", timeinfo); + return buf; +} + +char *http_get_date(char *buf, size_t size) { + time_t rawtime; + time(&rawtime); + return http_format_date(rawtime, buf, size); +} diff --git a/src/net/http.h b/src/net/http.h index decfa71..326e139 100644 --- a/src/net/http.h +++ b/src/net/http.h @@ -8,6 +8,12 @@ #ifndef NECRONDA_SERVER_HTTP_H #define NECRONDA_SERVER_HTTP_H +typedef struct { + unsigned short code; + char type[16]; + char msg[32]; +} http_status; + typedef struct { char field_num; char *fields[64][2]; @@ -20,6 +26,81 @@ typedef struct { http_hdr hdr; } http_req; +typedef struct { + http_status *status; + char version[3]; + http_hdr hdr; +} http_res; + +http_status http_statuses[] = { + {100, "Informational", "Continue"}, + {101, "Informational", "Switching Protocols"}, + + {200, "Success", "OK"}, + {201, "Success", "Created"}, + {202, "Success", "Accepted"}, + {203, "Success", "Non-Authoritative Information"}, + {204, "Success", "No Content"}, + {205, "Success", "Reset Content"}, + {206, "Success", "Partial Content"}, + + {300, "Redirection", "Multiple Choices"}, + {301, "Redirection", "Moved Permanently"}, + {302, "Redirection", "Found"}, + {303, "Redirection", "See Other"}, + {304, "Redirection", "Not Modified"}, + {305, "Redirection", "Use Proxy"}, + {307, "Redirection", "Temporary Redirect"}, + {308, "Redirection", "Permanent Redirect"}, + + {400, "Client Error", "Bad Request"}, + {401, "Client Error", "Unauthorized"}, + {402, "Client Error", "Payment Required"}, + {403, "Client Error", "Forbidden"}, + {404, "Client Error", "Not Found"}, + {405, "Client Error", "Method Not Allowed"}, + {406, "Client Error", "Not Acceptable"}, + {407, "Client Error", "Proxy Authentication Required"}, + {408, "Client Error", "Request Timeout"}, + {409, "Client Error", "Conflict"}, + {410, "Client Error", "Gone"}, + {411, "Client Error", "Length Required"}, + {412, "Client Error", "Precondition Failed"}, + {413, "Client Error", "Request Entity Too Large"}, + {414, "Client Error", "Request-URI Too Long"}, + {415, "Client Error", "Unsupported Media Type"}, + {416, "Client Error", "Requested Range Not Satisfiable"}, + {417, "Client Error", "Expectation Failed"}, + + {500, "Server Error", "Internal Server Error"}, + {501, "Server Error", "Not Implemented"}, + {502, "Server Error", "Bad Gateway"}, + {503, "Server Error", "Service Unavailable"}, + {504, "Server Error", "Gateway Timeout"}, + {505, "Server Error", "HTTP Version Not Supported"}, +}; + + +void http_to_camel_case(char *str); + +void http_free_hdr(http_hdr *hdr); + +void http_free_req(http_req *req); + +void http_free_res(http_res *res); + int http_receive_request(sock *client, http_req *req); +char *http_get_header_field(http_hdr *hdr, char *field_name); + +void http_add_header_field(http_hdr *hdr, char *field_name, char *field_value); + +int http_send_response(sock *client, http_res *res); + +const char *http_get_status_color(http_status *status); + +char *http_format_date(time_t time, char *buf, size_t size); + +char *http_get_date(char *buf, size_t size); + #endif //NECRONDA_SERVER_HTTP_H