Implemented http responses
This commit is contained in:
31
src/client.c
31
src/client.c
@ -33,7 +33,7 @@ int client_websocket_handler() {
|
|||||||
int client_request_handler(sock *client, int req_num) {
|
int client_request_handler(sock *client, int req_num) {
|
||||||
struct timespec begin, end;
|
struct timespec begin, end;
|
||||||
int ret, client_keep_alive;
|
int ret, client_keep_alive;
|
||||||
char buf[16];
|
char buf[64];
|
||||||
char *host, *hdr_connection;
|
char *host, *hdr_connection;
|
||||||
char *msg = "HTTP/1.1 501 Not Implemented\r\n"
|
char *msg = "HTTP/1.1 501 Not Implemented\r\n"
|
||||||
"Connection: keep-alive\r\n"
|
"Connection: keep-alive\r\n"
|
||||||
@ -59,6 +59,11 @@ int client_request_handler(sock *client, int req_num) {
|
|||||||
return ret;
|
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");
|
hdr_connection = http_get_header_field(&req.hdr, "Connection");
|
||||||
client_keep_alive = hdr_connection != NULL && strncmp(hdr_connection, "keep-alive", 10) == 0;
|
client_keep_alive = hdr_connection != NULL && strncmp(hdr_connection, "keep-alive", 10) == 0;
|
||||||
host = http_get_header_field(&req.hdr, "Host");
|
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);
|
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:
|
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);
|
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||||
unsigned long micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000;
|
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_req(&req);
|
||||||
|
http_free_res(&res);
|
||||||
return !client_keep_alive;
|
return !client_keep_alive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,14 @@
|
|||||||
#define HTTP_STR "\x1B[1;31m"
|
#define HTTP_STR "\x1B[1;31m"
|
||||||
#define HTTPS_STR "\x1B[1;32m"
|
#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];
|
int SOCKETS[NUM_SOCKETS];
|
||||||
pid_t CHILDREN[MAX_CHILDREN];
|
pid_t CHILDREN[MAX_CHILDREN];
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ void http_to_camel_case(char *str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void http_free_hdr(http_hdr *hdr) {
|
void http_free_hdr(http_hdr *hdr) {
|
||||||
for (int i = 0; i < hdr->field_num; i++) {
|
for (int i = 0; i < hdr->field_num; i++) {
|
||||||
free(hdr->fields[i][0]);
|
free(hdr->fields[i][0]);
|
||||||
@ -36,6 +35,10 @@ void http_free_req(http_req *req) {
|
|||||||
http_free_hdr(&req->hdr);
|
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) {
|
int http_receive_request(sock *client, http_req *req) {
|
||||||
ssize_t rcv_len, len;
|
ssize_t rcv_len, len;
|
||||||
char *ptr, *pos0, *pos1, *pos2;
|
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);
|
size_t len = strlen(field_name);
|
||||||
char *_field_name = malloc(len + 1);
|
char *_field_name = malloc(len + 1);
|
||||||
sprintf(_field_name, "%s", field_name);
|
sprintf(_field_name, "%s", field_name);
|
||||||
http_to_camel_case(_field_name);
|
http_to_camel_case(_field_name);
|
||||||
for (int i = 0; i < hdr->field_num; i++) {
|
for (int i = 0; i < hdr->field_num; i++) {
|
||||||
if (strncmp(hdr->fields[i][0], _field_name, len) == 0) {
|
if (strncmp(hdr->fields[i][0], _field_name, len) == 0) {
|
||||||
|
free(_field_name);
|
||||||
return hdr->fields[i][1];
|
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;
|
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);
|
||||||
|
}
|
||||||
|
@ -8,6 +8,12 @@
|
|||||||
#ifndef NECRONDA_SERVER_HTTP_H
|
#ifndef NECRONDA_SERVER_HTTP_H
|
||||||
#define NECRONDA_SERVER_HTTP_H
|
#define NECRONDA_SERVER_HTTP_H
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned short code;
|
||||||
|
char type[16];
|
||||||
|
char msg[32];
|
||||||
|
} http_status;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char field_num;
|
char field_num;
|
||||||
char *fields[64][2];
|
char *fields[64][2];
|
||||||
@ -20,6 +26,81 @@ typedef struct {
|
|||||||
http_hdr hdr;
|
http_hdr hdr;
|
||||||
} http_req;
|
} 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);
|
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
|
#endif //NECRONDA_SERVER_HTTP_H
|
||||||
|
Reference in New Issue
Block a user