diff --git a/jinhokim/Makefile b/jinhokim/Makefile index 429f153..32aa5f8 100644 --- a/jinhokim/Makefile +++ b/jinhokim/Makefile @@ -1,39 +1,85 @@ -CC = c++ +# Define the compiler and flags -CFLAGS = -std=c++98 -Wall -Wextra -Werror -pedantic +CXX := c++ +CXXFLAGS := -Wall -Wextra -Werror -std=c++98 -pedantic -march=native -O2 -pipe -GREEN = \033[32m -YELLOW = \033[33m -RESET = \033[0m +# Define the directories -SERVER_SRCS = src/Server.cpp \ - src/server_main.cpp +SRC_DIR := src +BUILD_DIR := build +INC_DIR := include -CLIENT_SRCS = src/Client.cpp \ - src/client_main.cpp +# Define the source files -all: server client +SERVER_SRCS := $(addprefix $(SRC_DIR)/, server_main.cpp Server.cpp Response.cpp) +CLIENT_SRCS := $(addprefix $(SRC_DIR)/, client_main.cpp Client.cpp) +SERVER_OBJS := $(patsubst %.cpp, $(BUILD_DIR)/%.o, $(SERVER_SRCS)) +CLIENT_OBJS := $(patsubst %.cpp, $(BUILD_DIR)/%.o, $(CLIENT_SRCS)) +DEPS := $(patsubst %.cpp, $(BUILD_DIR)/%.d, $(SERVER_SRCS) $(CLIENT_SRCS)) -server: - @echo "$(YELLOW)Building $@...$(RESET)" - @$(CC) $(SERVER_SRCS) $(CFLAGS) -o $@ $< - @echo "$(GREEN)Done.$(RESET)" +# Define the variables for progress bar -client: - @echo "$(YELLOW)Building $@...$(RESET)" - @$(CC) $(CLIENT_SRCS) $(CFLAGS) -o $@ $< - @echo "$(GREEN)Done.$(RESET)" +TOTAL_FILES := $(shell find $(SRC_DIR) -type f -name '*.cpp' | wc -l) +COMPILED_FILES := 0 +STEP := 100 + +# Define the name + +SERVER := server +CLIENT := client + +# Define the rules + +all: + @$(MAKE) -j $(SERVER) + @$(MAKE) -j $(CLIENT) + +$(SERVER) : $(SERVER_OBJS) + @$(CXX) $(CXXFLAGS) $^ -o $@ + @printf "\n$(MAGENTA)[WEBSERV] Linking SERVER Success\n$(DEF_COLOR)" + +$(CLIENT) : $(CLIENT_OBJS) + @$(CXX) $(CXXFLAGS) $^ -o $@ + @printf "\n$(MAGENTA)[WEBSERV] Linking CLIENT Success\n$(DEF_COLOR)" + +$(BUILD_DIR)/%.o : %.cpp | dir_guard + @$(CXX) $(CXXFLAGS) -I $(INC_DIR) -c $^ -o $@ + $(eval COMPILED_FILES = $(shell expr $(COMPILED_FILES) + 1)) + $(eval PROGRESS = $(shell expr $(COMPILED_FILES) "*" $(STEP) / $(TOTAL_FILES))) + @printf " \r" + @printf "$(YELLOW)[WEBSERV] [%02d/%02d] ( %3d %%) Compiling $<\r$(DEF_COLOR)" $(COMPILED_FILES) $(TOTAL_FILES) $(PROGRESS) + +dir_guard: + @mkdir -p $(addprefix $(BUILD_DIR)/, $(SRC_DIR)) clean: - @echo "$(YELLOW)Cleaning up...$(RESET)" - @echo "$(GREEN)Done.$(RESET)" + @$(RM) -r $(BUILD_DIR) + @printf "$(BLUE)[WEBSERV]:\tobj. dep. files$(DEF_COLOR)$(GREEN) => Cleaned!\n$(DEF_COLOR)" + +fclean: clean + @$(RM) $(SERVER) $(CLIENT) + @printf "$(CYAN)[WEBSERV]:\texec. files$(DEF_COLOR)$(GREEN) => Cleaned!\n$(DEF_COLOR)" + +re: fclean + @$(MAKE) all + @printf "$(GREEN)[WEBSERV]Cleaned and Rebuilt everything\n$(DEF_COLOR)" + +.PHONY: all clean fclean re dir_guard norm + +norm: + @(norminette | grep Error) || (printf "$(GREEN)[WEBSERV]:\tNorminette Success\n$(DEF_COLOR)") -fclean: - make clean - @rm -f server client +# Colors -re: - make fclean - make all +RESET = \033[1;39m +YELLOW = \033[1;33m +GRAY = \033[1;90m +RED = \033[1;91m +GREEN = \033[1;92m +YELLOW = \033[1;93m +BLUE = \033[1;94m +MAGENTA = \033[1;95m +CYAN = \033[1;96m +WHITE = \033[1;97m -.PHONY: all clean fclean re +-include $(DEPS) diff --git a/jinhokim/favicon.ico b/jinhokim/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/jinhokim/html/200.png b/jinhokim/html/200.png new file mode 100644 index 0000000..504bd49 Binary files /dev/null and b/jinhokim/html/200.png differ diff --git a/jinhokim/html/404.html b/jinhokim/html/404.html new file mode 100644 index 0000000..5651299 --- /dev/null +++ b/jinhokim/html/404.html @@ -0,0 +1,14 @@ + + + + 404 Status Page + + + +

+

404 Not Found


+ 404 cat
+

+ + + \ No newline at end of file diff --git a/jinhokim/html/404.png b/jinhokim/html/404.png new file mode 100644 index 0000000..beab571 Binary files /dev/null and b/jinhokim/html/404.png differ diff --git a/jinhokim/html/500.html b/jinhokim/html/500.html new file mode 100644 index 0000000..a24f305 --- /dev/null +++ b/jinhokim/html/500.html @@ -0,0 +1,14 @@ + + + + 500 Status Page + + + +

+

500 Internal Server Error


+ 500 cat
+

+ + + \ No newline at end of file diff --git a/jinhokim/html/500.png b/jinhokim/html/500.png new file mode 100644 index 0000000..266681f Binary files /dev/null and b/jinhokim/html/500.png differ diff --git a/jinhokim/html/index.html b/jinhokim/html/index.html new file mode 100644 index 0000000..1fb1d41 --- /dev/null +++ b/jinhokim/html/index.html @@ -0,0 +1,15 @@ + + + + 200 Status Page + + + +

+

Webserv...


+ 200 cat
+ 200 Cat +

+ + + \ No newline at end of file diff --git a/jinhokim/include/Client.hpp b/jinhokim/include/Client.hpp index 7d4734a..e9df367 100644 --- a/jinhokim/include/Client.hpp +++ b/jinhokim/include/Client.hpp @@ -1,28 +1,28 @@ #ifndef CLIENT_HPP -# define CLIENT_HPP +#define CLIENT_HPP -# include -# include -# include -# include -# include -# include -# include +#include +#include +#include + +#include class Client { - public: - Client(int port); - virtual ~Client(void); + public: + Client(int port); + virtual ~Client(void); + + int Set(void); + int Run(void); - int Set(void); - int Run(void); - private: - const int port_; - int client_fd_; - sockaddr_in server_address_; - std::string response_; + private: + const int port_; + int client_fd_; + sockaddr_in server_address_; + std::string response_; }; +void CheckArgument(int ac, char** av); int PrintError(const std::string str); #endif // CLIENT_HPP diff --git a/jinhokim/include/Response.hpp b/jinhokim/include/Response.hpp new file mode 100644 index 0000000..c05b9cb --- /dev/null +++ b/jinhokim/include/Response.hpp @@ -0,0 +1,33 @@ +#ifndef RESPONSE_HPP +#define RESPONSE_HPP + +#include +#include +#include + +#include + +#define BUFSIZE 10240 + +class Response { + public: + Response(char *request); + ~Response(void); + + void ResponseHandler(void); + std::string FindMime(std::string uri); + void FillHeader(int status, long len, std::string type); + void Handle200(int ct_len, const char *local_uri); + void Handle404(void); + void Handle500(void); + + void ComposeResponse(int status, const char *status_text, int len, + std::string type); + std::string GetResponse(void); + + private: + std::string request_; + std::string response_; +}; + +#endif // RESPONSE_HPP diff --git a/jinhokim/include/Server.hpp b/jinhokim/include/Server.hpp index 7369135..fffbab3 100644 --- a/jinhokim/include/Server.hpp +++ b/jinhokim/include/Server.hpp @@ -1,56 +1,43 @@ #ifndef SERVER_HPP -# define SERVER_HPP - -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include - -# define BACKLOG 1024 -# define BUFSIZE 1024 -# define HEADER_FORMAT "HTTP/1.1 %d %s\nContent-Length: %ld\nContent-Type: %s\n\n" -# define NOT_FOUND_CONTENT "

404 Not Found

\n" -# define SERVER_ERROR_CONTENT "

500 Internal Server Error

\n" - -/** - * @brief - * Server class, use socket communication for echo server - */ +#define SERVER_HPP + +#include +#include +#include + +#include +#include +#include +#include + +#include "Response.hpp" + class Server { - public: - Server(int port); - virtual ~Server(void); - - std::string GetIp(void); - void ChangeEvents(std::vector& change_list, int socket, int16_t filter, - uint16_t flags, uint32_t fflags, intptr_t data, void *udata); - void DisconnectClient(int client_fd, std::map& clients); - - int Set(void); - void SetResponse(void); - int Run(void); - private: - int port_; - int server_fd_; - sockaddr_in address_; - std::string request_; - std::string response_; + public: + Server(int port); + virtual ~Server(void); + + void ChangeEvents(int socket, int16_t filter, uint16_t flags, uint32_t fflags, + intptr_t data, void* udata); + void DisconnectClient(int client_fd); + + int Set(void); + int Run(void); + + int GetServerIdx(int sock); + int IsServer(uintptr_t ident); + int HandleErrorEvent(uintptr_t ident); + int HandleReadEvent(uintptr_t ident); + + private: + int port_; + std::vector server_fds_; + std::vector address_; + std::map clients_; + std::vector change_list_; }; +void CheckArgument(int ac, char** av); int PrintError(const std::string str); -void fill_header(char *header, int status, long len, std::string type); -void handle_404(int asock); -void handle_500(int asock); -void find_mime(char *ct_type, char *uri); -void http_handler(int asock); - #endif // SERVER_HPP diff --git a/jinhokim/index.html b/jinhokim/index.html deleted file mode 100644 index 4e4ea6e..0000000 --- a/jinhokim/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - Test Page - - -

- Webserv... -

- - - - diff --git a/jinhokim/src/Client.cpp b/jinhokim/src/Client.cpp index 1f470f7..35ef3fe 100644 --- a/jinhokim/src/Client.cpp +++ b/jinhokim/src/Client.cpp @@ -21,66 +21,65 @@ EX) 7. 커낵션을 닫는다(close) */ -#include "../include/Client.hpp" +#include "Client.hpp" Client::Client(int port) : port_(port) {} -Client::~Client(void) { - close(client_fd_); -} +Client::~Client(void) { close(client_fd_); } -int Client::Set(void) { - // 소켓 생성 - client_fd_ = socket(AF_INET, SOCK_STREAM, 0); - if (client_fd_ < 0) - return (PrintError("Failed to create socket")); +int Client::Set(void) { + client_fd_ = socket(AF_INET, SOCK_STREAM, 0); + if (client_fd_ < 0) throw std::runtime_error("Failed to create socket"); - server_address_.sin_family = AF_INET; - server_address_.sin_port = htons(port_); - server_address_.sin_addr.s_addr = inet_addr("127.0.0.1"); + server_address_.sin_family = AF_INET; + server_address_.sin_port = htons(port_); + server_address_.sin_addr.s_addr = inet_addr("127.0.0.1"); - // 서버와 연결 - int connect_result = connect(client_fd_, (sockaddr*)&server_address_, \ - sizeof(server_address_)); - if (connect_result < 0) - return (PrintError("Failed to connect to server")); + int connect_result = + connect(client_fd_, (sockaddr*)&server_address_, sizeof(server_address_)); + if (connect_result < 0) + throw std::runtime_error("Failed to connect to server"); - std::cout << "Connected to server" << std::endl; + std::cout << "Connected to server" << std::endl; - return 0; + return 0; } -int Client::Run(void) { - char buffer[1024]; - - while (42) { - // request 입력 받기 - std::string message; - std::getline(std::cin, message); - if (std::cin.eof() || !message.compare("exit")) { - std::cout << "Client Bye!" << std::endl; - break ; - } - - // request 보내기 - ssize_t bytes_sent = send(client_fd_, message.c_str(), message.size(), 0); - if (bytes_sent < 0) - return (PrintError("Failed to send data to server")); - - // response 받기 - ssize_t bytes_received = recv(client_fd_, buffer, sizeof(buffer), 0); - if (bytes_received < 0) - return (PrintError("Failed to receive data from server")); - else if (bytes_received == 0) - return (PrintError("Server disconnected")); - - response_ = std::string(buffer, bytes_received); - std::cout << "Received from server: " << response_ << std::endl; +int Client::Run(void) { + char buffer[1024]; + + while (42) { + std::string message; + std::getline(std::cin, message); + if (std::cin.eof() || !message.compare("exit")) { + std::cout << "Client Bye!" << std::endl; + break; } - return 0; + + ssize_t bytes_sent = send(client_fd_, message.c_str(), message.size(), 0); + if (bytes_sent < 0) + throw std::runtime_error("Failed to send data to server"); + + ssize_t bytes_received = recv(client_fd_, buffer, sizeof(buffer), 0); + if (bytes_received < 0) + throw std::runtime_error("Failed to receive data from server"); + else if (bytes_received == 0) + throw std::runtime_error("Server disconnected"); + + response_ = std::string(buffer, bytes_received); + std::cout << "Received from server: " << response_ << std::endl; + } + return 0; +} + +void CheckArgument(int ac, char** av) { + if (ac < 2) throw std::runtime_error("Few argument error"); + for (std::size_t i = 0; av[1][i] != 0; i++) { + if (!std::isdigit(av[1][i])) throw std::runtime_error("Port is not number"); + } } int PrintError(const std::string str) { - std::cerr << str << std::endl; - return 1; -} + std::cerr << str << std::endl; + return 1; +} diff --git a/jinhokim/src/Response.cpp b/jinhokim/src/Response.cpp new file mode 100644 index 0000000..1545410 --- /dev/null +++ b/jinhokim/src/Response.cpp @@ -0,0 +1,132 @@ +#include "Response.hpp" + +Response::Response(char* request) : request_(std::string(request)) {} + +Response::~Response(void) {} + +void Response::ResponseHandler(void) { + if (request_.size() == 0) { + std::cerr << "[ERROR] Failed to read request" << std::endl; + Handle500(); + return; + } + + std::size_t method_end_idx = request_.find_first_of(" "); + std::string method = request_.substr(0, method_end_idx); + std::size_t uri_end_idx = request_.find_first_of(" ", method_end_idx + 1); + std::string uri = + request_.substr(method_end_idx + 1, uri_end_idx - method_end_idx - 1); + + if (method.size() == 0 || uri.size() == 0) { + std::cerr << "[ERROR] Failed to identify method, URI" << std::endl; + Handle500(); + return; + } + + std::cout << "[INFO] Request: method=" << method << ", URI=" << uri + << std::endl; + + std::string local_uri; + if (!uri.compare("/")) + local_uri = "html/index.html"; + else + local_uri = uri.substr(1, uri.size()); + + struct stat st; + + if (stat(local_uri.c_str(), &st) < 0) { + std::cerr << "[WARN] No file found matching URI" << std::endl; + Handle404(); + return; + } + + Handle200(st.st_size, local_uri.c_str()); + return; +} + +void Response::Handle200(int ct_len, const char* local_uri) { + std::string ct_type; + char buf[BUFSIZE]; + int r; + int fd = open(local_uri, O_RDONLY); + + ct_type = FindMime(local_uri); + FillHeader(200, ct_len, ct_type); + std::cout << "local_uri: " << local_uri << std::endl; + std::cout << "response header: " << response_ << std::endl; + while ((r = read(fd, buf, BUFSIZE)) > 0) response_.append(buf); +} + +void Response::Handle404(void) { + struct stat st; + std::string ct_type; + char buf[BUFSIZE]; + char uri[14] = "html/404.html"; + int r; + int fd = open(uri, O_RDONLY); + + stat(uri, &st); + ct_type = FindMime(uri); + FillHeader(404, st.st_size, ct_type); + while ((r = read(fd, buf, BUFSIZE)) > 0) response_.append(buf); +} + +void Response::Handle500(void) { + struct stat st; + std::string ct_type; + char buf[BUFSIZE]; + char uri[14] = "html/500.html"; + int r; + int fd = open(uri, O_RDONLY); + + stat(uri, &st); + ct_type = FindMime(uri); + FillHeader(500, st.st_size, ct_type); + while ((r = read(fd, buf, BUFSIZE)) > 0) response_.append(buf); +} + +std::string Response::FindMime(std::string uri) { + std::size_t dot_idx = uri.find_last_of("."); + std::string mime_type = uri.substr(dot_idx, uri.size()); + + if (!mime_type.compare(".html")) + return "text/html"; + else if (!mime_type.compare(".jpg") || !mime_type.compare(".jpeg")) + return "image/jpeg"; + else if (!mime_type.compare(".png")) + return "image/png"; + else if (!mime_type.compare(".ico")) + return "image/x-icon"; + else if (!mime_type.compare(".css")) + return "text/css"; + else if (!mime_type.compare(".js")) + return "text/javascript"; + else + return "text/plain"; +} + +void Response::FillHeader(int status, long len, std::string type) { + switch (status) { + case 200: + ComposeResponse(200, "OK", len, type); + break; + case 404: + ComposeResponse(404, "Not Found", len, type); + break; + case 500: + ComposeResponse(500, "Internal Server Error", len, type); + break; + default: + break; + } + return; +} + +void Response::ComposeResponse(int status, const char* status_text, int len, + std::string type) { + response_ = "HTTP/1.1" + std::to_string(status) + " " + status_text + + "\r\nContent-Length: " + std::to_string(len) + + "\nContent-Type: " + type + "\r\n\r\n"; +} + +std::string Response::GetResponse(void) { return response_; } diff --git a/jinhokim/src/Server.cpp b/jinhokim/src/Server.cpp index 5d4ce7e..55563ef 100644 --- a/jinhokim/src/Server.cpp +++ b/jinhokim/src/Server.cpp @@ -1,6 +1,6 @@ /* socket() -> 연결되지 않은 새로운 소켓 생성 -bind() -> socket에 로컬 포트 번호 할당 +bind() -> socket에 로컬 포트 번호 할당 listen() -> 커넥션을 받기 위해 로컬 소켓에 허용함을 표시 kqueue(), kevent() -> 여러 클라이언트를 이벤트(recv, send) 기반으로 커널 딴에서 처리함 @@ -24,270 +24,158 @@ EX) 8. 커낵션을 닫는다(close) */ -#include "../include/Server.hpp" +#include "Server.hpp" Server::Server(int port) : port_(port) {} Server::~Server(void) {} -std::string Server::GetIp(void) { - std::string ip = std::string(inet_ntoa(address_.sin_addr)); - std::string port = std::to_string(ntohs(address_.sin_port)); - return ip + ":" + port; -} - -void Server::ChangeEvents(std::vector& change_list, int socket, int16_t filter, - uint16_t flags, uint32_t fflags, intptr_t data, void *udata) { - struct kevent temp_event; +int Server::GetServerIdx(int sock) { + std::vector::iterator it = + std::find(server_fds_.begin(), server_fds_.end(), sock); + int i = it - server_fds_.begin(); - EV_SET(&temp_event, socket, filter, flags, fflags, data, udata); - change_list.push_back(temp_event); + return i; } -void Server::DisconnectClient(int client_fd, std::map& clients) { - std::cerr << "Client disconnected" << std::endl; - close(client_fd); - clients.erase(client_fd); +void Server::ChangeEvents(int socket, int16_t filter, uint16_t flags, + uint32_t fflags, intptr_t data, void* udata) { + struct kevent temp_event; + + EV_SET(&temp_event, socket, filter, flags, fflags, data, udata); + change_list_.push_back(temp_event); } -void Server::SetResponse(void) { - if (!request_.compare("GET")) - response_ = "GET!"; - else if (!request_.compare("POST")) - response_ = "POST!!"; - else if (!request_.compare("DELETE")) - response_ = "DELETE!!!"; - else - response_ = "?????"; - - return ; +void Server::DisconnectClient(int client_fd) { + std::cerr << "Client disconnected" << std::endl; + close(client_fd); + clients_.erase(client_fd); } int Server::Set(void) { - // 소켓 생성 - server_fd_ = socket(AF_INET, SOCK_STREAM, 0); - if (server_fd_ < 0) - return (PrintError("Failed to create socket")); + int optval = 1; + int sock; + sockaddr_in address; + + for (int i = 0; i < 5; ++i) { + sock = socket(AF_INET, SOCK_STREAM, 0); - address_.sin_family = AF_INET; - address_.sin_port = htons(port_); - address_.sin_addr.s_addr = inet_addr("127.0.0.1"); + if (sock < 0) throw std::runtime_error("Failed to create socket"); - int optval = 1; - setsockopt(server_fd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + address.sin_family = AF_INET; + address.sin_port = htons(port_ + i); + address.sin_addr.s_addr = inet_addr("127.0.0.1"); - // 소켓에 로컬 포트 번호 할당 - int bind_result = bind(server_fd_, (sockaddr*)&address_, sizeof(address_)); + server_fds_.push_back(sock); + address_.push_back(address); + + fcntl(sock, F_SETFL, O_NONBLOCK); + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + + int bind_result = bind(sock, (sockaddr*)&address, sizeof(address)); if (bind_result < 0) - return (PrintError("Failed to bind socket to address")); + throw std::runtime_error("Failed to bind socket to address"); - // 커넥션을 받기 위해 로컬 소켓에 허용함을 표시 - int listen_result = listen(server_fd_, BACKLOG); + int listen_result = listen(sock, 1024); if (listen_result < 0) - return (PrintError("Failed to listen on socket")); - - return 0; + throw std::runtime_error("Failed to listen on socket"); + } + return 0; } int Server::Run(void) { - // init kqueue - int kq = kqueue(); - if (kq == -1) - return (PrintError("Failed kqueue init")); - - std::map clients; - std::vector change_list; - struct kevent event_list[128]; - - // add event for server socket - ChangeEvents(change_list, server_fd_, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL); - - int new_events = 1; - struct kevent* curr_event; - - while (42) { - // apply changes and return new events(pending events - new_events = kevent(kq, &change_list[0], change_list.size(), event_list, 8, NULL); - if (new_events == -1) - return (PrintError("Failed kevent()")); - - change_list.clear(); - - for (int i = 0; i < new_events; ++i) { - curr_event = &event_list[i]; - - // check event error - if (curr_event->flags & EV_ERROR) { - if (curr_event->ident == static_cast(server_fd_)) - return (PrintError("Server socket error")); - else - DisconnectClient(curr_event->ident, clients); - } - else if (curr_event->filter == EVFILT_READ) { - if (curr_event->ident == static_cast(server_fd_)) { - // 클라이언트의 커낵션을 기다림 - socklen_t client_len = sizeof(address_); - int client_fd = accept(server_fd_, (sockaddr*)&address_, &client_len); - if (client_fd == -1) - PrintError("Failed to accept incoming connection"); - // 소켓 nonblocking 설정 - fcntl(client_fd, F_SETFL, O_NONBLOCK); - - std::cout << "Accepted connection from " << GetIp() << std::endl; - - // 클라이언트 소켓 이벤트 추가(recv, send) - ChangeEvents(change_list, client_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL); - ChangeEvents(change_list, client_fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, NULL); - clients[client_fd] = ""; - } - else if (clients.find(curr_event->ident) != clients.end()) { - http_handler(curr_event->ident); - // // request 받기 - // char buf[1024]; - // ssize_t bytes_received = recv(curr_event->ident, buf, sizeof(buf), 0); - // if (bytes_received <= 0) { - // if (bytes_received < 0) - // std::cerr << "Failed to receive data from client" << std::endl; - // DisconnectClient(curr_event->ident, clients); - // } - // else { - // buf[bytes_received] = '\0'; - // clients[curr_event->ident] += buf; - // request_ = std::string(buf, bytes_received); - // std::cout << "request from " << GetIp() << ": " << request_ << std::endl; - - // // response 보내기 - // SetResponse(); - // std::map::iterator it = clients.find(curr_event->ident); - // if (it != clients.end()) { - // if (clients[curr_event->ident] != "") { - // ssize_t bytes_sent = send(curr_event->ident, response_.c_str(), response_.size(), 0); - // if (bytes_sent < 0) - // std::cerr << "Failed to send data to client" << std::endl; - // else - // clients[curr_event->ident].clear(); - // } - // } - // } - } - // else if (curr_event->filter == EVFILT_WRITE) { - // // response 보내기 - // SetResponse(); - // std::cout << "response: " << response_ << std::endl; - // std::map::iterator it = clients.find(curr_event->ident); - // if (it != clients.end()) { - // if (clients[curr_event->ident] != "") { - // ssize_t bytes_sent = send(curr_event->ident, response_.c_str(), response_.size(), 0); - // if (bytes_sent < 0) - // std::cerr << "Failed to send data to client" << std::endl; - // else - // clients[curr_event->ident].clear(); - // } - // } - // } - } - } - } - return 0; -} + int kq = kqueue(); + if (kq == -1) throw std::runtime_error("Failed to init kqueue"); -int PrintError(const std::string str) { - std::cerr << str << std::endl; - return 1; -} - -void fill_header(char *header, int status, long len, std::string type) { - char status_text[40]; - switch (status) { - case 200: - strcpy(status_text, "OK"); break; - case 404: - strcpy(status_text, "Not Found"); break; - case 500: - default: - strcpy(status_text, "Internal Server Error"); break; - } - sprintf(header, HEADER_FORMAT, status, status_text, len, type.c_str()); -} + struct kevent event_list[128]; -void handle_404(int asock) { - char header[BUFSIZE]; - std::string t("text/html"); - fill_header(header, 404, sizeof(NOT_FOUND_CONTENT), t); + for (std::size_t i = 0; i < server_fds_.size(); ++i) + ChangeEvents(server_fds_[i], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL); - write(asock, header, strlen(header)); - write(asock, NOT_FOUND_CONTENT, sizeof(NOT_FOUND_CONTENT)); -} + int new_events = 1; + struct kevent* curr_event; -void handle_500(int asock) { - char header[1024]; - std::string t("text/html"); - fill_header(header, 500, sizeof(SERVER_ERROR_CONTENT), t); + while (42) { + new_events = + kevent(kq, &change_list_[0], change_list_.size(), event_list, 8, NULL); + if (new_events == -1) throw std::runtime_error("Failed kevent()"); - write(asock, header, strlen(header)); - write(asock, SERVER_ERROR_CONTENT, sizeof(SERVER_ERROR_CONTENT)); -} + change_list_.clear(); -void find_mime(char *ct_type, char *uri) { - char *ext = strrchr(uri, '.'); - if (!strcmp(ext, ".html")) - strcpy(ct_type, "text/html"); - else if (!strcmp(ext, ".jpg") || !strcmp(ext, ".jpeg")) - strcpy(ct_type, "image/jpeg"); - else if (!strcmp(ext, ".png")) - strcpy(ct_type, "image/png"); - else if (!strcmp(ext, ".css")) - strcpy(ct_type, "text/css"); - else if (!strcmp(ext, ".js")) - strcpy(ct_type, "text/javascript"); - else strcpy(ct_type, "text/plain"); -} - -void http_handler(int asock) { - char header[BUFSIZE]; - char buf[BUFSIZE]; + for (int i = 0; i < new_events; ++i) { + curr_event = &event_list[i]; - if (read(asock, buf, BUFSIZE) < 0) { - perror("[ERR] Failed to read request.\n"); - handle_500(asock); return; - } - - char *method = strtok(buf, " "); - char *uri = strtok(NULL, " "); - if (method == NULL || uri == NULL) { - perror("[ERR] Failed to identify method, URI.\n"); - handle_500(asock); return; + if (curr_event->flags & EV_ERROR) + HandleErrorEvent(curr_event->ident); + else if (curr_event->filter == EVFILT_READ) + HandleReadEvent(curr_event->ident); } + } + return 0; +} - printf("[INFO] Handling Request: method=%s, URI=%s\n", method, uri); +int Server::IsServer(uintptr_t ident) { + for (std::size_t i = 0; i < server_fds_.size(); ++i) { + if (ident == static_cast(server_fds_[i])) return 1; + } + return 0; +} - char safe_uri[BUFSIZE]; - char *local_uri; - struct stat st; +int Server::HandleErrorEvent(uintptr_t ident) { + if (IsServer(ident)) + throw std::runtime_error("Server socket error"); + else + DisconnectClient(ident); + return 0; +} - strcpy(safe_uri, uri); - if (!strcmp(safe_uri, "/")) - strcpy(safe_uri, "/index.html"); +int Server::HandleReadEvent(uintptr_t ident) { + if (IsServer(ident)) { + int i = GetServerIdx(ident); + socklen_t client_len = sizeof(address_[i]); + int client_fd = + accept(server_fds_[i], (sockaddr*)&address_[i], &client_len); + if (client_fd == -1) + return (PrintError("Failed to accept incoming connection")); - local_uri = safe_uri + 1; - if (stat(local_uri, &st) < 0) { - perror("[WARN] No file found matching URI.\n"); - handle_404(asock); return; - } + fcntl(client_fd, F_SETFL, O_NONBLOCK); - int fd = open(local_uri, O_RDONLY); - if (fd < 0) { - perror("[ERR] Failed to open file.\n"); - handle_500(asock); return; + ChangeEvents(client_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL); + clients_[client_fd] = ""; + } else if (clients_.find(ident) != clients_.end()) { + char buf[BUFSIZE]; + ssize_t bytes_received = recv(ident, buf, sizeof(buf), 0); + if (bytes_received <= 0) { + if (bytes_received < 0) + std::cerr << "Failed to receive data from client" << std::endl; + DisconnectClient(ident); + return 1; + } else { + buf[bytes_received] = '\0'; + clients_[ident] += buf; + std::map::iterator it = clients_.find(ident); + if (it != clients_.end()) { + if (clients_[ident] != "") { + Response response(buf); + response.ResponseHandler(); + std::string response_str = response.GetResponse(); + send(ident, response_str.c_str(), response_str.size(), 0); + return 1; + } + } } + } + return 0; +} - int ct_len = st.st_size; - char ct_type[40]; - find_mime(ct_type, local_uri); - fill_header(header, 200, ct_len, ct_type); - write(asock, header, strlen(header)); +void CheckArgument(int ac, char** av) { + if (ac < 2) throw std::runtime_error("Few argument error"); + for (std::size_t i = 0; av[1][i] != 0; i++) { + if (!std::isdigit(av[1][i])) throw std::runtime_error("Port is not number"); + } +} - int cnt; - while ((cnt = read(fd, buf, BUFSIZE)) > 0) - write(asock, buf, cnt); +int PrintError(const std::string str) { + std::cerr << str << std::endl; + return 1; } diff --git a/jinhokim/src/client_main.cpp b/jinhokim/src/client_main.cpp index 4bce144..44113fa 100644 --- a/jinhokim/src/client_main.cpp +++ b/jinhokim/src/client_main.cpp @@ -1,19 +1,16 @@ -#include "../include/Client.hpp" +#include "Client.hpp" int main(int ac, char** av) { - if (ac < 2) - return (PrintError("Few argument error")); + try { + CheckArgument(ac, av); - for (std::size_t i = 0; av[1][i] != 0; i++) { - if (!std::isdigit(av[1][i])) - return (PrintError("Port is not number")); - } + Client client(atoi(av[1])); - Client client(atoi(av[1])); - - if (client.Set() || client.Run()) { - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; + client.Set(); + client.Run(); + } catch (const std::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; } diff --git a/jinhokim/src/server_main.cpp b/jinhokim/src/server_main.cpp index c34e2ce..48adec8 100644 --- a/jinhokim/src/server_main.cpp +++ b/jinhokim/src/server_main.cpp @@ -1,19 +1,16 @@ -#include "../include/Server.hpp" +#include "Server.hpp" -int main(int ac, char **av) { - if (ac < 2) - return (PrintError("Few argument error")); +int main(int ac, char** av) { + try { + CheckArgument(ac, av); - for (std::size_t i = 0; av[1][i] != 0; i++) { - if (!std::isdigit(av[1][i])) - return (PrintError("Port is not number")); - } + Server server(atoi(av[1])); - Server server(atoi(av[1])); - - if (server.Set() || server.Run()) { - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; + server.Set(); + server.Run(); + } catch (const std::exception& e) { + std::cerr << e.what() << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; } diff --git a/jinhokim/test.cpp b/jinhokim/test.cpp index 37f4ecb..92c293e 100644 --- a/jinhokim/test.cpp +++ b/jinhokim/test.cpp @@ -1,177 +1,21 @@ -#define BUF_SIZE 1000 -#define HEADER_FORMAT "HTTP/1.1 %d %s\nContent-Length: %ld\nContent-Type: %s\n\n" - -#define NOT_FOUND_CONTENT "

404 Not Found

\n" -#define SERVER_ERROR_CONTENT "

500 Internal Server Error

\n" - -#include -#include -#include -#include -#include #include -#include -#include #include -#include -#include - -int bind_lsock(int lsock, int port) { - struct sockaddr_in sin; - - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_port = htons(port); - - return bind(lsock, (struct sockaddr *)&sin, sizeof(sin)); -} - -void fill_header(char *header, int status, long len, std::string type) { - char status_text[40]; - switch (status) { - case 200: - strcpy(status_text, "OK"); break; - case 404: - strcpy(status_text, "Not Found"); break; - case 500: - default: - strcpy(status_text, "Internal Server Error"); break; - } - sprintf(header, HEADER_FORMAT, status, status_text, len, type.c_str()); -} - -void handle_404(int asock) { - char header[BUF_SIZE]; - std::string t("text/html"); - fill_header(header, 404, sizeof(NOT_FOUND_CONTENT), t); - - write(asock, header, strlen(header)); - write(asock, NOT_FOUND_CONTENT, sizeof(NOT_FOUND_CONTENT)); -} - -void handle_500(int asock) { - char header[1024]; - std::string t("text/html"); - fill_header(header, 500, sizeof(SERVER_ERROR_CONTENT), t); - - write(asock, header, strlen(header)); - write(asock, SERVER_ERROR_CONTENT, sizeof(SERVER_ERROR_CONTENT)); -} - -void find_mime(char *ct_type, char *uri) { - char *ext = strrchr(uri, '.'); - if (!strcmp(ext, ".html")) - strcpy(ct_type, "text/html"); - else if (!strcmp(ext, ".jpg") || !strcmp(ext, ".jpeg")) - strcpy(ct_type, "image/jpeg"); - else if (!strcmp(ext, ".png")) - strcpy(ct_type, "image/png"); - else if (!strcmp(ext, ".css")) - strcpy(ct_type, "text/css"); - else if (!strcmp(ext, ".js")) - strcpy(ct_type, "text/javascript"); - else strcpy(ct_type, "text/plain"); -} - -void http_handler(int asock) { - char header[BUF_SIZE]; - char buf[BUF_SIZE]; - - if (read(asock, buf, BUF_SIZE) < 0) { - perror("[ERR] Failed to read request.\n"); - handle_500(asock); return; - } - - char *method = strtok(buf, " "); - char *uri = strtok(NULL, " "); - if (method == NULL || uri == NULL) { - perror("[ERR] Failed to identify method, URI.\n"); - handle_500(asock); return; - } - - printf("[INFO] Handling Request: method=%s, URI=%s\n", method, uri); - - char safe_uri[BUF_SIZE]; - char *local_uri; - struct stat st; - - strcpy(safe_uri, uri); - if (!strcmp(safe_uri, "/")) - strcpy(safe_uri, "/index.html"); - - local_uri = safe_uri + 1; - if (stat(local_uri, &st) < 0) { - perror("[WARN] No file found matching URI.\n"); - handle_404(asock); return; - } - - int fd = open(local_uri, O_RDONLY); - if (fd < 0) { - perror("[ERR] Failed to open file.\n"); - handle_500(asock); return; - } - - int ct_len = st.st_size; - char ct_type[40]; - find_mime(ct_type, local_uri); - fill_header(header, 200, ct_len, ct_type); - write(asock, header, strlen(header)); - - int cnt; - while ((cnt = read(fd, buf, BUF_SIZE)) > 0) - write(asock, buf, cnt); -} - -int main(int argc, char **argv) { - int port, pid; - int lsock, asock; - - struct sockaddr_in remote_sin; - socklen_t remote_sin_len; - - if (argc < 2) { - printf("Usage: \n"); - printf("\t%s {port}: runs mini HTTP server.\n", argv[0]); - exit(0); - } - - port = atoi(argv[1]); - printf("[INFO] The server will listen to port: %d.\n", port); - - lsock = socket(AF_INET, SOCK_STREAM, 0); - if (lsock < 0) { - perror("[ERR] failed to create lsock.\n"); - exit(1); - } - - if (bind_lsock(lsock, port) < 0) { - perror("[ERR] failed to bind lsock.\n"); - exit(1); - } +#include +#include - if (listen(lsock, 10) < 0) { - perror("[ERR] failed to listen lsock.\n"); - exit(1); - } +#include - // to handle zombie process - signal(SIGCHLD, SIG_IGN); +int main(void) { + int fd = open("html/200.png", O_RDONLY); + if (fd < 0) return write(2, "File Error", 10); - while (1) { - printf("[INFO] waiting...\n"); - asock = accept(lsock, (struct sockaddr *)&remote_sin, &remote_sin_len); - if (asock < 0) { - perror("[ERR] failed to accept.\n"); - continue; - } + off_t file_size = lseek(fd, 0, SEEK_END); + if (file_size < 0) return write(2, "Lseek Error", 11); - pid = fork(); - if (pid == 0) { - close(lsock); http_handler(asock); close(asock); - exit(0); - } + std::cout << "lseek file size: " << file_size << std::endl; - if (pid != 0) { close(asock); } - if (pid < 0) { perror("[ERR] failed to fork.\n"); } - } + char uri[13] = "html/200.png"; + struct stat st; + stat(uri, &st); + std::cout << "stat file size: " << st.st_size << std::endl; } \ No newline at end of file