#include #include #include #include #include #include #include #include #include #include "request.h" #include "response.h" #include "server.h" #define MAX_BUFFER_SIZE 2048 int sockfd; struct Server server_constructor(const char *port, const char *static_path) { struct Server server; server.port = port; server.static_path = static_path; return server; } void sigchld_handler(int s) { int saved_errno = errno; while (waitpid(-1, NULL, WNOHANG) > 0) ; errno = saved_errno; } void path_from_target(struct Server *server, char *target, char *path) { strcpy(path, server->static_path); strcat(path, target); char *question = strchr(path, '?'); if (question) *question = '\0'; if (path[strlen(path) - 1] == '/') strcat(path, "index.html"); char *dot = strchr(path, '.'); if (!dot) strcat(path, ".html"); } void get_mime_type(char *path, char *mime) { char *dot = strchr(path, '.'); if (!dot) strcpy(mime, "text/html"); else if (strcmp(dot, ".html") == 0) strcpy(mime, "text/html"); else if (strcmp(dot, ".css") == 0) strcpy(mime, "text/css"); else if (strcmp(dot, ".js") == 0) strcpy(mime, "application/js"); else if (strcmp(dot, ".jpg") == 0) strcpy(mime, "image/jpeg"); else if (strcmp(dot, ".png") == 0) strcpy(mime, "image/png"); else if (strcmp(dot, ".gif") == 0) strcpy(mime, "image/gif"); else if (strcmp(dot, ".ico") == 0) strcpy(mime, "image/x-icon"); else strcpy(mime, "text/html"); } int sendall(int s, char *buf, int *len) { int total = 0; // how many bytes we've sent int bytesleft = *len; // how many we have left to send int n; while(total < *len) { n = send(s, buf+total, bytesleft, 0); if (n == -1) { break; } total += n; bytesleft -= n; } *len = total; // return number actually sent here return n==-1?-1:0; // return -1 on failure, 0 on success } void handle(struct Server *server, int client_fd) { char *buf = (char *)malloc(MAX_BUFFER_SIZE * sizeof(char)); recv(client_fd, buf, MAX_BUFFER_SIZE, 0); struct Request request = request_constructor(buf); char *path = (char *)malloc(1024 * sizeof(char)); path_from_target(server, request.target, path); FILE *fp = fopen(path, "rb"); if (fp == NULL) { const char response[] = "HTTP/1.1 404 Not Found\r\n\r\nNot Found"; send(client_fd, response, sizeof(response), 0); printf("[%s] %s -> %s %s\n", request.method, request.target, path, "404"); } else { fseek(fp, 0, SEEK_END); size_t file_size = ftell(fp); rewind(fp); char mime_type[32]; get_mime_type(path, mime_type); char res_header[1024]; sprintf(res_header, "HTTP/1.1 200 OK\r\nContent-Type: %s\r\n\r\n", mime_type); int header_size = strlen(res_header); char *res_buffer = (char *)malloc((file_size + header_size) * sizeof(char)); strcpy(res_buffer, res_header); fread(res_buffer + header_size, 1, file_size, fp); int len = file_size + header_size; if (sendall(client_fd, res_buffer, &len) == -1) { perror("send content"); exit(1); } printf("[%s] %s -> %s %s\n", request.method, request.target, path, "200"); free(res_buffer); }; fclose(fp); } void launch(struct Server *server) { struct sockaddr_storage client_addr; socklen_t client_addr_len = sizeof client_addr; struct addrinfo hints, *res, *p; memset(&hints, 0, sizeof hints); hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; int gairv; if ((gairv = getaddrinfo(NULL, server->port, &hints, &res)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gairv)); exit(1); } for (p = res; p != NULL; p = p->ai_next) { int yes = 1; if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("server: socket"); continue; } if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); continue; } if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { perror("server: bind"); continue; } break; } freeaddrinfo(res); if (p == NULL) { fprintf(stderr, "server: failed to bind to any available socket"); exit(1); } if (listen(sockfd, BACKLOG) == -1) { perror("server: listen"); exit(1); } struct sigaction sa; sa.sa_handler = sigchld_handler; // reap all dead processes sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(SIGCHLD, &sa, NULL) == -1) { perror("sigaction"); exit(1); } int client_fd; while (1) { if ((client_fd = accept(sockfd, (struct sockaddr *)&client_addr, &client_addr_len)) == -1) { perror("accept"); continue; } if (!fork()) { close(sockfd); handle(server, client_fd); close(client_fd); exit(0); } close(client_fd); // parent doesnt need this } }