diff --git a/common/socket_helper.h b/common/socket_helper.h index 12ea677..cb19af3 100644 --- a/common/socket_helper.h +++ b/common/socket_helper.h @@ -8,6 +8,7 @@ #define BUFFER_SIZE 1024 #define HEADER_CONTENT_LENGTH "Content-Length" #define HEADER_TRANSFER_ENCODING "Transfer-Encoding" +#define HEADER_CONNECTION "Connection" enum line_type {RESULT_START_LINE, RESULT_HEADER, RESULT_BLANK_LINE, RESULT_NO_LINE}; enum socket_result {RESULT_OK, RESULT_MALFORMED, RESULT_READ_ERROR, RESULT_PROCESSING_ERROR, RESULT_WRITE_ERROR}; diff --git a/server/socket_server.c b/server/socket_server.c index 11504f3..a923b58 100644 --- a/server/socket_server.c +++ b/server/socket_server.c @@ -1,6 +1,8 @@ #include "socket_server.h" #include "../common/http_types.h" #include "../common/socket_helper.h" +#include "../common/buffer_helper.h" +#include "status_codes.h" #include #include #include @@ -71,6 +73,71 @@ struct server_info setup(int port) { return info; } +/** + * Sends the headers and status line of the response + * + * @param status_code The status code to send + * @param client_socket The socket to send the message on + * @param content_length The content length of the file-to-send + * + * @return -1 on error, 0 otherwise. + */ +static int send_headers(int status_code, int client_socket, int content_length) { + const char *status_code_message = get_message_from_status_code(status_code); + if (status_code_message == NULL) { + return -1; + } + // All status codes are three digits, plus the space for the null term. + char *status_code_string = malloc(4 * sizeof(char)); + snprintf(status_code_string, 4, "%d", status_code); + const char *status_line_components[] = {HTTP_VERSION, " ", status_code_string, " ", status_code_message, "\r\n"}; + int status_line_buffer_size = get_buffer_size(status_line_components, 6) + 1; + char *status_line_buffer = malloc(status_line_buffer_size * sizeof(char)); + sprintf(status_line_buffer, "%s %s %s\r\n", HTTP_VERSION, status_code_string, status_code_message); + free(status_code_string); + + // RFC2616 states that if we don't intend to persist the connection, we must send "Connection: close" + const char *connection_header_components[] = {HEADER_CONNECTION, ": close\r\n"}; + char *content_length_header_buffer = ""; + int header_buffer_size = get_buffer_size(connection_header_components, 2); + if (content_length > 0) { + // Get the number of chars in the port + int content_length_length = floor(log10(content_length)) + 1; + char *content_length_string = malloc((content_length_length + 1) * sizeof(char)); + sprintf(content_length_string, "%d", content_length); + const char *content_length_header_components[] = {HEADER_CONTENT_LENGTH, ": ", content_length_string, "\r\n"}; + int content_length_header_length = get_buffer_size(content_length_header_components, 4); + header_buffer_size += content_length_header_length; + content_length_header_buffer = malloc(content_length_header_length * sizeof(char)); + sprintf(content_length_header_buffer, "%s: %s\r\n", HEADER_CONTENT_LENGTH, content_length_string); + free(content_length_string); + } + + // Include space for the null term and the CRLF + char *header_buffer = malloc((header_buffer_size + 3) * sizeof(char)); + sprintf(header_buffer, "%s: close\r\n%s\r\n", HEADER_CONNECTION, content_length_header_buffer); + if (content_length > 0) { + free(content_length_header_buffer); + } + + int send_result = send(client_socket, status_line_buffer, status_line_buffer_size - 1, 0); + if (send_result == -1) { + free(status_line_buffer); + free(header_buffer); + return -1; + } + send_result = send(client_socket, header_buffer, header_buffer_size - 1, 0); + if (send_result == -1) { + free(status_line_buffer); + free(header_buffer); + return -1; + } + free(status_line_buffer); + free(header_buffer); + + return 0; +} + enum socket_result serve_one_request(int sock_fd) { int client_fd = accept(sock_fd, NULL, NULL); if (client_fd == -1) { diff --git a/server/socket_server.h b/server/socket_server.h index 02f8012..b88a2d9 100644 --- a/server/socket_server.h +++ b/server/socket_server.h @@ -4,6 +4,7 @@ #include "../common/socket_helper.h" #define BACKLOG_SIZE 8; +#define HTTP_VERSION "HTTP/1.1" enum server_status {STATUS_LISTENING, STATUS_CLOSED, STATUS_ERROR}; struct server_info { diff --git a/server/status_codes.c b/server/status_codes.c index 64033b7..35878a3 100644 --- a/server/status_codes.c +++ b/server/status_codes.c @@ -1,6 +1,58 @@ #include "status_codes.h" #include +const char *STATUS_CODES_100[] = { + "Continue", + "Switching Protocols" +}; + +const char *STATUS_CODES_200[] = { + "OK", + "Created", + "Accepted", + "Non-Authoritative Information", + "No Content", + "Reset Content", + "Partial Content", +}; + +const char *STATUS_CODES_300[] = { + "Multiple Choices", + "Moved Permanently", + "Found", + "See Other", + "Not Modified", + "Use Proxy", + NULL, // There is no status code 306 + "Temporary Redirect", +}; + +const char *STATUS_CODES_400[] = { + "Bad Request", + "Unauthorized", + "Payment Required", + "Forbidden", + "Not Found", + "Method Not Allowed", + "Not Acceptable", + "Proxy Authentication Required", + "Request Time-out", + "Conflict", + "Gone", + "Length Required", + "Precondition Failed", + "Request Entity Too Large", + "Request-URI Too Large", + "Unsupported Media Type", + "Requested range not satisfiable", + "Expectation Failed", +}; + +const char *STATUS_CODES_500[] = { + "Internal Server Error", + "Not Implemented", +}; + const char *get_message_from_status_code(int status_code) { if (status_code > 100 && status_code < 100 + NUM_100_STATUS_CODES) { return STATUS_CODES_100[status_code - 100]; diff --git a/server/status_codes.h b/server/status_codes.h index 83c6ecd..ef93a15 100644 --- a/server/status_codes.h +++ b/server/status_codes.h @@ -9,61 +9,11 @@ #define NUM_400_STATUS_CODES 18 #define NUM_500_STATUS_CODES 6 -const char *STATUS_CODES_100[] = { - "Continue", - "Switching Protocols" -}; - -const char *STATUS_CODES_200[] = { - "OK", - "Created", - "Accepted", - "Non-Authoritative Information", - "No Content", - "Reset Content", - "Partial Content", -}; - -const char *STATUS_CODES_300[] = { - "Multiple Choices", - "Moved Permanently", - "Found", - "See Other", - "Not Modified", - "Use Proxy", - NULL, // There is no status code 306 - "Temporary Redirect", -}; - -const char *STATUS_CODES_400[] = { - "Bad Request", - "Unauthorized", - "Payment Required", - "Forbidden", - "Not Found", - "Method Not Allowed", - "Not Acceptable", - "Proxy Authentication Required", - "Request Time-out", - "Conflict", - "Gone", - "Length Required", - "Precondition Failed", - "Request Entity Too Large", - "Request-URI Too Large", - "Unsupported Media Type", - "Requested range not satisfiable", - "Expectation Failed", -}; - -const char *STATUS_CODES_500[] = { - "Internal Server Error", - "Not Implemented", - "Bad Gateway", - "Service Unavailable", - "Gateway Time-out", - "HTTP Version not supported" -}; +extern const char *STATUS_CODES_100[]; +extern const char *STATUS_CODES_200[]; +extern const char *STATUS_CODES_300[]; +extern const char *STATUS_CODES_400[]; +extern const char *STATUS_CODES_500[]; const char *get_message_from_status_code(int status_code);