<commit_before>#include "server.h"

void clienterror(int fd, char *cause, char *errnum, char *shortmsg,
        char *longmsg);
void serve_dynamic(int fd, char *filename, char *cgiargs);
void serve_static(int fd, char *filename, int filesize);
void handle_get(int incoming_socket, http_request* request);

/* Initialize socket for proxy server to listen on.
 *
 * Modifies server->socket.
 * Returns 0 if sucessful.
 */
int initialize_listen_socket(dirt_server* server) {
    int result;
    int on = 1;
    char port[MAX_PORT_LENGTH];
    sprintf(port, "%d", server->port);
    struct addrinfo* serv = NULL, hints;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    result = getaddrinfo(NULL, port, &hints, &serv);
    if(result < 0) {
        log4c_category_log(log4c_category_get("dirt"), LOG4C_PRIORITY_DEBUG,
                "ERROR: getaddrinfo failed with error %d: %s", 
                result, gai_strerror(result));
        return result;
    }

    server->socket = 
        socket(serv->ai_family, serv->ai_socktype, serv->ai_protocol);
    if(check_error(server->socket, "socket", &server->stdout_mutex)) {
        return server->socket;
    }

    result = setsockopt(server->socket, SOL_SOCKET,
                            SO_REUSEADDR, &on, sizeof(on));
    if(check_error(result, "setsockopt", &server->stdout_mutex)) {
        freeaddrinfo(serv);
        return result;
    }

    result = bind(server->socket, serv->ai_addr, serv->ai_addrlen);
    if(check_error(result, "bind", &server->stdout_mutex)) {
        freeaddrinfo(serv);
        return result;
    }
    freeaddrinfo(serv);

    result = listen(server->socket, MAX_CONNECTION_QUEUE);
    if(check_error(result, "listen", &server->stdout_mutex)) {
        return result;
    }
    
    return 0;
}

int initialize_server(dirt_server* server, unsigned int port) {
    server->port = port;

    pthread_attr_init(&server->thread_attr);
    pthread_attr_setstacksize(&server->thread_attr, 1024*1024);
    pthread_attr_setdetachstate(&server->thread_attr, PTHREAD_CREATE_DETACHED);
    pthread_mutex_init(&server->stdout_mutex, NULL);

    if(initialize_listen_socket(server)) {
        return -1;
    }

    log4c_category_log(log4c_category_get("dirt"), LOG4C_PRIORITY_INFO,
            "Starting server on port %d", server->port);
    return 0;
}

/* Read a line from the rio buffer. Just a wrapper to read the line, print
 * a log statement and check that it ends in a newline if necessary.
 *
 * Modifies rio, buf.
 * Returns the number of bytes read, or a negative number if an error.
 */
int read_line(rio_t* rio, char* buf, dirt_server* server) {
    int bytes_read = rio_readlineb(rio, buf, MAXLINE);
    if(bytes_read > 0 && buf[bytes_read - 1] != '\n') {
        log4c_category_log(log4c_category_get("dirt"), LOG4C_PRIORITY_WARN,
                "HTTP request line:\n%s with length %d longer than MAXLINE %d", 
                buf, bytes_read, MAXLINE);
    }
    return bytes_read;
}

/* Read HTTP headers from the rio buffer until CRLF is found or there are no
 * more bytes read.
 *
 * Only stores valid headers.
 *
 * Modifies rio, message.
 */
void read_http_headers(rio_t* rio, http_message* message, 
        dirt_server* server) {
    if(rio->rio_cnt) {
        char header_string[MAXLINE];
        int bytes_read = read_line(rio, header_string, server);
        while(bytes_read > 0 && header_string[0] != '\n' 
                && header_string[0] != '\r') {
            http_header header = parse_http_header(header_string);
            if(header.valid) {
                message->headers[message->header_count++] = header;
                bytes_read = read_line(rio, header_string, server);
            }
        }
    }
}

/* Read the HTTP response status line from the rio buffer.
 * Returns the parsed HTTP response, which may or may not be have
 * response.valid.
 * 
 * Modifies rio.
 */
http_response read_http_response(rio_t* rio, dirt_server* server) {
    char message_string[MAXLINE];
    http_response response;
    response.message.valid = 0;
    if(read_line(rio, message_string, server) > 0) {
        response = parse_http_response(message_string);
        read_http_headers(rio, &response.message, server);
    }
    return response;
}

/* Read the HTTP request from the rio buffer, including any headers.
 *
 * Does not read content following the headers.
 *
 * Modifies rio.
 * Returns the parsed request, which may or may not have request.valid.
 */
http_request read_http_request(rio_t* rio, dirt_server* server) {
    char message_string[MAXLINE];
    http_request request;
    request.message.valid = 0;
    if(read_line(rio, message_string, server) > 0) {
        log4c_category_log(log4c_category_get("dirt"), LOG4C_PRIORITY_DEBUG,
                "%s", message_string);
        request = parse_http_request(message_string);
        read_http_headers(rio, &request.message, server);
    }
    return request;
}

/* Receiving thread primary function. Receives client request and any content,
 * generates a response and returns it back to the client.
 *
 */
void receive(receive_args* args) {
    rio_t rio_client;
    rio_readinitb(&rio_client, args->incoming_socket);
    http_request request = read_http_request(&rio_client, args->server);
    // TODO http_response response, *response_pointer;
    if(request.message.valid) {
        switch(request.method) {
            case HTTP_METHOD_GET:
                handle_get(args->incoming_socket, &request);
                break;
            default:
                clienterror(args->incoming_socket,
                        http_method_to_string(request.method),
                        "501",
                        "Not Implemented",
                        "Tiny does not implement this method");
        }
    }
    log4c_category_log(log4c_category_get("dirt"), LOG4C_PRIORITY_DEBUG,
            "closing socket %d", args->incoming_socket);
    close(args->incoming_socket);
}

void handle_get(int incoming_socket, http_request* request) {
    clienterror(incoming_socket, request->uri.path, "404",
            "Not found", "Tiny couldn't find this file");
    struct stat sbuf;
    if(stat(request->uri.path, &sbuf) < 0) {                     
        clienterror(incoming_socket, request->uri.path, "404", "Not found",
                "Tiny couldn't find this file");
        return;
    }                                                    <commit_msg>Remove stray clienterror.<commit_after>#include "server.h"

void clienterror(int fd, char *cause, char *errnum, char *shortmsg,
        char *longmsg);
void serve_dynamic(int fd, char *filename, char *cgiargs);
void serve_static(int fd, char *filename, int filesize);
void handle_get(int incoming_socket, http_request* request);

/* Initialize socket for proxy server to listen on.
 *
 * Modifies server->socket.
 * Returns 0 if sucessful.
 */
int initialize_listen_socket(dirt_server* server) {
    int result;
    int on = 1;
    char port[MAX_PORT_LENGTH];
    sprintf(port, "%d", server->port);
    struct addrinfo* serv = NULL, hints;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    result = getaddrinfo(NULL, port, &hints, &serv);
    if(result < 0) {
        log4c_category_log(log4c_category_get("dirt"), LOG4C_PRIORITY_DEBUG,
                "ERROR: getaddrinfo failed with error %d: %s", 
                result, gai_strerror(result));
        return result;
    }

    server->socket = 
        socket(serv->ai_family, serv->ai_socktype, serv->ai_protocol);
    if(check_error(server->socket, "socket", &server->stdout_mutex)) {
        return server->socket;
    }

    result = setsockopt(server->socket, SOL_SOCKET,
                            SO_REUSEADDR, &on, sizeof(on));
    if(check_error(result, "setsockopt", &server->stdout_mutex)) {
        freeaddrinfo(serv);
        return result;
    }

    result = bind(server->socket, serv->ai_addr, serv->ai_addrlen);
    if(check_error(result, "bind", &server->stdout_mutex)) {
        freeaddrinfo(serv);
        return result;
    }
    freeaddrinfo(serv);

    result = listen(server->socket, MAX_CONNECTION_QUEUE);
    if(check_error(result, "listen", &server->stdout_mutex)) {
        return result;
    }
    
    return 0;
}

int initialize_server(dirt_server* server, unsigned int port) {
    server->port = port;

    pthread_attr_init(&server->thread_attr);
    pthread_attr_setstacksize(&server->thread_attr, 1024*1024);
    pthread_attr_setdetachstate(&server->thread_attr, PTHREAD_CREATE_DETACHED);
    pthread_mutex_init(&server->stdout_mutex, NULL);

    if(initialize_listen_socket(server)) {
        return -1;
    }

    log4c_category_log(log4c_category_get("dirt"), LOG4C_PRIORITY_INFO,
            "Starting server on port %d", server->port);
    return 0;
}

/* Read a line from the rio buffer. Just a wrapper to read the line, print
 * a log statement and check that it ends in a newline if necessary.
 *
 * Modifies rio, buf.
 * Returns the number of bytes read, or a negative number if an error.
 */
int read_line(rio_t* rio, char* buf, dirt_server* server) {
    int bytes_read = rio_readlineb(rio, buf, MAXLINE);
    if(bytes_read > 0 && buf[bytes_read - 1] != '\n') {
        log4c_category_log(log4c_category_get("dirt"), LOG4C_PRIORITY_WARN,
                "HTTP request line:\n%s with length %d longer than MAXLINE %d", 
                buf, bytes_read, MAXLINE);
    }
    return bytes_read;
}

/* Read HTTP headers from the rio buffer until CRLF is found or there are no
 * more bytes read.
 *
 * Only stores valid headers.
 *
 * Modifies rio, message.
 */
void read_http_headers(rio_t* rio, http_message* message, 
        dirt_server* server) {
    if(rio->rio_cnt) {
        char header_string[MAXLINE];
        int bytes_read = read_line(rio, header_string, server);
        while(bytes_read > 0 && header_string[0] != '\n' 
                && header_string[0] != '\r') {
            http_header header = parse_http_header(header_string);
            if(header.valid) {
                message->headers[message->header_count++] = header;
                bytes_read = read_line(rio, header_string, server);
            }
        }
    }
}

/* Read the HTTP response status line from the rio buffer.
 * Returns the parsed HTTP response, which may or may not be have
 * response.valid.
 * 
 * Modifies rio.
 */
http_response read_http_response(rio_t* rio, dirt_server* server) {
    char message_string[MAXLINE];
    http_response response;
    response.message.valid = 0;
    if(read_line(rio, message_string, server) > 0) {
        response = parse_http_response(message_string);
        read_http_headers(rio, &response.message, server);
    }
    return response;
}

/* Read the HTTP request from the rio buffer, including any headers.
 *
 * Does not read content following the headers.
 *
 * Modifies rio.
 * Returns the parsed request, which may or may not have request.valid.
 */
http_request read_http_request(rio_t* rio, dirt_server* server) {
    char message_string[MAXLINE];
    http_request request;
    request.message.valid = 0;
    if(read_line(rio, message_string, server) > 0) {
        log4c_category_log(log4c_category_get("dirt"), LOG4C_PRIORITY_DEBUG,
                "%s", message_string);
        request = parse_http_request(message_string);
        read_http_headers(rio, &request.message, server);
    }
    return request;
}

/* Receiving thread primary function. Receives client request and any content,
 * generates a response and returns it back to the client.
 *
 */
void receive(receive_args* args) {
    rio_t rio_client;
    rio_readinitb(&rio_client, args->incoming_socket);
    http_request request = read_http_request(&rio_client, args->server);
    // TODO http_response response, *response_pointer;
    if(request.message.valid) {
        switch(request.method) {
            case HTTP_METHOD_GET:
                handle_get(args->incoming_socket, &request);
                break;
            default:
                clienterror(args->incoming_socket,
                        http_method_to_string(request.method),
                        "501",
                        "Not Implemented",
                        "Tiny does not implement this method");
        }
    }
    log4c_category_log(log4c_category_get("dirt"), LOG4C_PRIORITY_DEBUG,
            "closing socket %d", args->incoming_socket);
    close(args->incoming_socket);
}

void handle_get(int incoming_socket, http_request* request) {
    struct stat sbuf;
    if(stat(request->uri.path, &sbuf) < 0) {                     
        clienterror(incoming_socket, request->uri.path, "404", "Not found",
                "Tiny couldn't find this file");
        return;
    }                                                    
{'fraction_non_alphanumeric': 0.1082382172528923, 'fraction_numerical': 0.007221057535522944, 'mean_word_length': 2.8038984051978737, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 80, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5059565', 'n_tokens_mistral': 4057, 'n_tokens_neox': 3920, 'n_words': 974}