#include #include #include #include #include #include #include "log.h" #include "server.h" #define BUF_CAP 65536 #define COMMON_CAP 1024 #define REQ_PATH_CAP 512 #define PORT "8079" #define MIN(a,b) (((a)<(b))?(a):(b)) #define _SEND_BUF_ADD(str) do { \ size_t n = MIN(strlen(str), BUF_CAP - (size_t)send_buf_sz); \ memcpy(&send_buf[send_buf_sz], str, n); \ send_buf_sz += n; \ } while(0) #define _SEND_BUF_ADD_LINE(str) do { \ _SEND_BUF_ADD(str); \ _SEND_BUF_ADD("\r\n"); \ } while(0) #define SEND_BUF_ADD(str) do { \ size_t n = MIN(strlen(str), BUF_CAP - (size_t)*send_buf_sz); \ memcpy(&send_buf[*send_buf_sz], str, strlen(str)); \ *send_buf_sz += n; \ } while(0) #define SEND_BUF_ADD_LINE(str) do { \ SEND_BUF_ADD(str); \ SEND_BUF_ADD("\r\n"); \ } while(0) #define SEND_BUF_ADD_LINE_LONG(lon) do { \ char str[COMMON_CAP]; \ snprintf(str, COMMON_CAP, "%ld", lon); \ SEND_BUF_ADD_LINE(str); \ } while(0) #ifdef USE_LIBMAGIC #include static int get_content_type(char *file_path, char *content_type) { int ret = 1; magic_t magic; if((magic = magic_open(MAGIC_MIME)) == NULL) { err("magic_open: %s", magic_error(magic)); return ret; } if(magic_load(magic, NULL) != 0) { err("magic_load: %s", magic_error(magic)); goto close; } const char *mime = magic_file(magic, file_path); if(mime == NULL) { err("magic_file: %s", magic_error(magic)); goto close; } if(strlen(mime) >= COMMON_CAP) { err("get_content_type: mime info too long"); goto close; } memcpy(content_type, mime, strlen(mime)); ret = 0; close: magic_close(magic); return ret; } #else static int get_content_type(char *file_path, char *content_type) { (void)file_path; (void)content_type; return 0; } #endif static int try_file(char *req_path, FILE **fp, char *content_type) { // req_path error check 0 if(strlen(req_path) > REQ_PATH_CAP) { err("try_file: requested path longer than %d characters", REQ_PATH_CAP); return 1; } // req_path error check 1 for(size_t i = 1; i < strlen(req_path); i++) { if(req_path[i-1] == '.' && req_path[i] == '.') { err("try_file: the requested path %s includes ..", req_path); return 1; } } char file_path[COMMON_CAP]; snprintf(file_path, COMMON_CAP, "%s%s", FILES, (strlen(req_path) == 1) ? "/index.html" : req_path); *fp = fopen(file_path, "r"); if(!(*fp)) { err("fopen: file %s: %s", file_path, strerror(errno)); return 1; } info("fopen: file %s was opened", file_path); if(get_content_type(file_path, content_type) != 0) { err("get_content_type: failed"); fclose(*fp); return 1; } return 0; } static int on_get(char *send_buf, ssize_t *send_buf_sz, char *req_path) { int ret = 1; char content_type[COMMON_CAP] = {0}; FILE *fp; if(try_file(req_path, &fp, content_type) != 0) { if(errno == 2) { // 2 is 'No such file or directory' SEND_BUF_ADD_LINE("404"); if(try_file("/404.html", &fp, content_type) != 0) return ret; } else { SEND_BUF_ADD_LINE("500"); if(try_file("/500.html", &fp, content_type) != 0) return ret; } } else { SEND_BUF_ADD_LINE("200"); } SEND_BUF_ADD_LINE("Server: potato"); SEND_BUF_ADD("Content-Type: "); SEND_BUF_ADD_LINE(content_type); if(fseek(fp, 0, SEEK_END) != 0) { err("fseek: %s", strerror(errno)); goto close; } size_t file_sz = ftell(fp); if(file_sz == 0) { err("ftell: %s", strerror(errno)); goto close; } rewind(fp); SEND_BUF_ADD("Content-Lenght: "); SEND_BUF_ADD_LINE_LONG(file_sz); SEND_BUF_ADD("\r\n"); fread(&send_buf[*send_buf_sz], sizeof(char), file_sz, fp); *send_buf_sz += file_sz; ret = 0; close: fclose(fp); return ret; } static int handle_connection(sock_t *conn) { char recv_buf[BUF_CAP] = {0}; ssize_t recv_buf_len = server_recv(conn, recv_buf, BUF_CAP); if(recv_buf_len == 0) { return 0; } else if(recv_buf_len < 0) { err("server_recv: %s", strerror(errno)); return 1; } /* HANDLE REQUEST */ char send_buf[BUF_CAP]; ssize_t send_buf_sz = 0; _SEND_BUF_ADD("HTTP/1.1 "); char recv_buf_cpy[BUF_CAP]; memcpy(recv_buf_cpy, recv_buf, recv_buf_len); char *req_method = strtok(recv_buf_cpy, " "); if(strcmp(req_method, "GET") == 0) { if(on_get(send_buf, &send_buf_sz, strtok(NULL, " "))) { err("on_get: failed"); return 1; } } else { err("request method %s has not been implemented", req_method); return 1; } ssize_t sent_sz = server_send(conn, send_buf, send_buf_sz); if(sent_sz < 0) { err("server_send: %s", strerror(errno)); return 1; } else if(sent_sz != send_buf_sz) { err("server_send: sent %ld out of %ld bytes", sent_sz, send_buf_sz); } return 0; } int main(void) { signal(SIGCHLD, SIG_IGN); // now i dont have to wait() sock_t *sock = server_sock_create(); if(server_start(PORT, sock) != 0) { err("server_init: failed"); return 1; } for(int i = 0; ; i++) { sock_t *conn = server_sock_create(); if(server_accept(sock, conn) != 0) { err("server_accept: failed"); } info("server: got connection with %s", server_connection_ip(conn)); pid_t p = fork(); if(p == 0) { server_sock_close(sock); if(handle_connection(conn) != 0) { err("handle_connection: failed"); } else { info("server: got disconnected with %s", server_connection_ip(conn)); } server_sock_close(conn); return 0; } else if (p < 0){ err("fork: %s", strerror(errno)); return 1; } server_sock_close(conn); } server_sock_close(sock); return 0; }