2018-09-08 19:55:19 +00:00
|
|
|
#include "socket_server.h"
|
2018-09-09 23:46:12 +00:00
|
|
|
#include "request_handling.h"
|
2018-09-08 21:26:34 +00:00
|
|
|
#include "../common/http_types.h"
|
|
|
|
#include "../common/socket_helper.h"
|
2018-09-09 21:34:26 +00:00
|
|
|
#include "../common/buffer_helper.h"
|
|
|
|
#include "status_codes.h"
|
2018-09-09 23:46:12 +00:00
|
|
|
#include <errno.h>
|
2018-09-08 19:55:19 +00:00
|
|
|
#include <math.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2018-09-08 21:26:34 +00:00
|
|
|
#include <string.h>
|
2018-09-08 19:55:19 +00:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the address info for the given port, such that the server will listen on 0.0.0.
|
|
|
|
*
|
|
|
|
* @param port The port to listen on.
|
|
|
|
* @param info A pointer to a location where getaddrinfo can store data.
|
|
|
|
*
|
|
|
|
* @return The result of getaddrinfo. See `man getaddrinfo` for details.
|
|
|
|
*/
|
|
|
|
static int build_addr_info(int port, struct addrinfo **info) {
|
|
|
|
struct addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM};
|
|
|
|
// Get the number of chars in the port
|
|
|
|
int port_length = floor(log10(port)) + 1;
|
|
|
|
char *port_string = malloc((port_length + 1) * sizeof(char));
|
|
|
|
sprintf(port_string, "%d", port);
|
|
|
|
|
|
|
|
int addr_status = getaddrinfo(NULL, port_string, &hints, info);
|
|
|
|
free(port_string);
|
|
|
|
|
|
|
|
return addr_status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Setup the server's socket for listening.
|
|
|
|
*
|
|
|
|
* @param port The port to listen on.
|
|
|
|
*
|
|
|
|
* @return A server_info representing the result of setting up the socket.
|
|
|
|
*/
|
|
|
|
struct server_info setup(int port) {
|
|
|
|
struct server_info info = {.sock_fd = -1};
|
|
|
|
struct addrinfo *addr_info;
|
|
|
|
int addr_status = build_addr_info(port, &addr_info);
|
|
|
|
if (addr_status != 0) {
|
|
|
|
info.status = STATUS_ERROR;
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
info.sock_fd = socket(addr_info->ai_family, addr_info->ai_socktype, addr_info->ai_protocol);
|
|
|
|
if (info.sock_fd == -1) {
|
|
|
|
info.status = STATUS_ERROR;
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bind_result = bind(info.sock_fd, addr_info->ai_addr, addr_info->ai_addrlen);
|
|
|
|
if (bind_result == -1) {
|
|
|
|
close(info.sock_fd);
|
|
|
|
info.sock_fd = -1;
|
|
|
|
info.status = STATUS_ERROR;
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2018-09-08 21:26:34 +00:00
|
|
|
int listen_result = listen(info.sock_fd, 8); if (listen_result == -1) {
|
2018-09-08 19:55:19 +00:00
|
|
|
close(info.sock_fd);
|
|
|
|
info.sock_fd = -1;
|
|
|
|
info.status = STATUS_ERROR;
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
info.status = STATUS_LISTENING;
|
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
2018-09-08 21:26:34 +00:00
|
|
|
|
2018-09-09 21:34:26 +00:00
|
|
|
/**
|
|
|
|
* 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 = "";
|
2018-09-09 23:46:12 +00:00
|
|
|
// Include space for the CRLF and null term
|
|
|
|
int header_buffer_size = get_buffer_size(connection_header_components, 2) + 3;
|
2018-09-09 21:34:26 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-09-09 23:46:12 +00:00
|
|
|
char *header_buffer = malloc(header_buffer_size * sizeof(char));
|
2018-09-09 21:34:26 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-09-09 04:18:56 +00:00
|
|
|
enum socket_result serve_one_request(int sock_fd) {
|
2018-09-08 21:26:34 +00:00
|
|
|
int client_fd = accept(sock_fd, NULL, NULL);
|
|
|
|
if (client_fd == -1) {
|
|
|
|
return RESULT_WRITE_ERROR;
|
|
|
|
}
|
|
|
|
struct http_message message = {};
|
|
|
|
get_all_remote_parts(client_fd, TYPE_SERVER, &message);
|
|
|
|
printf("%s\n", message.contents);
|
2018-09-09 18:52:39 +00:00
|
|
|
char *msg = "HTTP/1.1 501 Not Imlemented Yet ;p\r\n\r\n";
|
2018-09-08 21:26:34 +00:00
|
|
|
send(client_fd, msg, strlen(msg), 0);
|
|
|
|
shutdown(client_fd, SHUT_RDWR);
|
|
|
|
close(client_fd);
|
|
|
|
return RESULT_OK;
|
|
|
|
}
|