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) {
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user