From d30555a9cc01d20ea4602a11277b4e29d36db72f Mon Sep 17 00:00:00 2001 From: jinhokim Date: Thu, 12 Jan 2023 05:15:46 +0900 Subject: [PATCH 01/16] =?UTF-8?q?WIP:=20=EC=9E=91=EC=9D=80=20http=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=20=EA=B5=AC=ED=98=84=ED=95=B4=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jinhokim/Makefile | 5 +- jinhokim/include/Client.hpp | 3 +- jinhokim/include/Http.hpp | 35 ++++++ jinhokim/include/Server.hpp | 26 +---- jinhokim/index.html | 3 +- jinhokim/src/Client.cpp | 24 ++-- jinhokim/src/Http.cpp | 108 +++++++++++++++++ jinhokim/src/Server.cpp | 218 ++++++++--------------------------- jinhokim/src/client_main.cpp | 18 ++- jinhokim/src/server_main.cpp | 18 ++- 10 files changed, 235 insertions(+), 223 deletions(-) create mode 100644 jinhokim/include/Http.hpp create mode 100644 jinhokim/src/Http.cpp diff --git a/jinhokim/Makefile b/jinhokim/Makefile index 429f153..888e4c6 100644 --- a/jinhokim/Makefile +++ b/jinhokim/Makefile @@ -7,10 +7,11 @@ YELLOW = \033[33m RESET = \033[0m SERVER_SRCS = src/Server.cpp \ - src/server_main.cpp + src/http.cpp \ + src/server_main.cpp \ CLIENT_SRCS = src/Client.cpp \ - src/client_main.cpp + src/client_main.cpp \ all: server client diff --git a/jinhokim/include/Client.hpp b/jinhokim/include/Client.hpp index 7d4734a..fcfbe42 100644 --- a/jinhokim/include/Client.hpp +++ b/jinhokim/include/Client.hpp @@ -23,6 +23,7 @@ class Client { std::string response_; }; -int PrintError(const std::string str); +void CheckArgument(int ac, char** av); +int PrintError(const std::string str); #endif // CLIENT_HPP diff --git a/jinhokim/include/Http.hpp b/jinhokim/include/Http.hpp new file mode 100644 index 0000000..c3b1e3e --- /dev/null +++ b/jinhokim/include/Http.hpp @@ -0,0 +1,35 @@ +#ifndef HTTP_HPP +# define HTTP_HPP + +# include +# include +# include +# include +# include + +# define BUFSIZE 1024 +# define HEADER_FORMAT "HTTP/1.1 %d %s\r\nContent-Length: %ld\nContent-Type: %s\r\n" +# define NOT_FOUND_CONTENT "

404 Not Found

\n" +# define SERVER_ERROR_CONTENT "

500 Internal Server Error

\n" + +class Http { + public: + Http(char* request); + ~Http(void); + + void FillHeader(long len, std::string type); + void Handle404(void); + void Handle500(void); + void FindMime(char* ct_type, char* uri); + void HttpHandler(void); + + std::string GetHtml(void) const; + std::string GetHeader(void) const; + private: + std::string request_; + std::string header_; + int status_; + int fd_; +}; + +#endif // HTTP_HPP diff --git a/jinhokim/include/Server.hpp b/jinhokim/include/Server.hpp index 7369135..daba337 100644 --- a/jinhokim/include/Server.hpp +++ b/jinhokim/include/Server.hpp @@ -11,19 +11,13 @@ # include # include # include -# include # include +# include "Http.hpp" + # 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 - */ + + class Server { public: Server(int port); @@ -35,22 +29,14 @@ class Server { 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_; }; -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); +void CheckArgument(int ac, char** av); +int PrintError(const std::string str); #endif // SERVER_HPP diff --git a/jinhokim/index.html b/jinhokim/index.html index 4e4ea6e..1b6b408 100644 --- a/jinhokim/index.html +++ b/jinhokim/index.html @@ -9,5 +9,4 @@

- - + \ No newline at end of file diff --git a/jinhokim/src/Client.cpp b/jinhokim/src/Client.cpp index 1f470f7..e50e104 100644 --- a/jinhokim/src/Client.cpp +++ b/jinhokim/src/Client.cpp @@ -30,20 +30,18 @@ Client::~Client(void) { } int Client::Set(void) { - // 소켓 생성 client_fd_ = socket(AF_INET, SOCK_STREAM, 0); if (client_fd_ < 0) - return (PrintError("Failed to create socket")); + 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"); - // 서버와 연결 int connect_result = connect(client_fd_, (sockaddr*)&server_address_, \ sizeof(server_address_)); if (connect_result < 0) - return (PrintError("Failed to connect to server")); + throw std::runtime_error("Failed to connect to server"); std::cout << "Connected to server" << std::endl; @@ -54,7 +52,6 @@ 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")) { @@ -62,17 +59,15 @@ int Client::Run(void) { 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")); + throw std::runtime_error("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")); + throw std::runtime_error("Failed to receive data from server"); else if (bytes_received == 0) - return (PrintError("Server disconnected")); + throw std::runtime_error("Server disconnected"); response_ = std::string(buffer, bytes_received); std::cout << "Received from server: " << response_ << std::endl; @@ -80,6 +75,15 @@ int Client::Run(void) { 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; diff --git a/jinhokim/src/Http.cpp b/jinhokim/src/Http.cpp new file mode 100644 index 0000000..0a4f8e0 --- /dev/null +++ b/jinhokim/src/Http.cpp @@ -0,0 +1,108 @@ +#include "../include/Http.hpp" + +Http::Http(char* request) : request_(std::string(request)) {} + +Http::~Http(void) {} + +void Http::FillHeader(long len, std::string type) { + char header[BUFSIZE]; + 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()); + header_ = std::string(header); +} + +void Http::Handle404(void) { + status_ = 404; + std::string t("text/html"); + FillHeader(sizeof(NOT_FOUND_CONTENT), t); +} + +void Http::Handle500(void) { + status_ = 500; + std::string t("text/html"); + FillHeader(sizeof(SERVER_ERROR_CONTENT), t); +} + +void Http::FindMime(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::HttpHandler(void) { + char safe_uri[BUFSIZE]; + char* local_uri; + struct stat st; + + strcpy(safe_uri, "/index.html"); + + local_uri = safe_uri + 1; + if (stat(local_uri, &st) < 0) { + perror("[WARN] No file found matching URI.\n"); + Handle404(); return; + } + + fd_ = open(local_uri, O_RDONLY); + if (fd_ < 0) { + perror("[ERR] Failed to open file.\n"); + Handle500(); return; + } + + int ct_len = st.st_size; + char ct_type[40]; + FindMime(ct_type, local_uri); + status_ = 200; + FillHeader(ct_len, ct_type); +} + +std::string Http::GetHtml(void) const { + if (status_ == 200) { + // char buf[BUFSIZE]; + // int r; + // std::string res; + // while ((r = read(fd_, buf, BUFSIZE)) > 0) { + // res.append(buf); + // res.append("\n"); + // } + // res.pop_back(); + std::string res("\n\ + \n\ + \n\ + Test Page\n\ + \n\ + \n\ +

\n\ + Webserv...\n\ +

\n\ + \n\ +"); + return res; + } + else if (status_ == 404) + return std::string(NOT_FOUND_CONTENT); + else if (status_ == 500) + return std::string(SERVER_ERROR_CONTENT); + return std::string("Error"); +} + +std::string Http::GetHeader(void) const { + return header_; +} diff --git a/jinhokim/src/Server.cpp b/jinhokim/src/Server.cpp index 5d4ce7e..d725142 100644 --- a/jinhokim/src/Server.cpp +++ b/jinhokim/src/Server.cpp @@ -50,24 +50,10 @@ void Server::DisconnectClient(int client_fd, std::map& clie clients.erase(client_fd); } -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 ; -} - int Server::Set(void) { - // 소켓 생성 server_fd_ = socket(AF_INET, SOCK_STREAM, 0); if (server_fd_ < 0) - return (PrintError("Failed to create socket")); + throw std::runtime_error("Failed to create socket"); address_.sin_family = AF_INET; address_.sin_port = htons(port_); @@ -76,218 +62,114 @@ int Server::Set(void) { int optval = 1; setsockopt(server_fd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); - // 소켓에 로컬 포트 번호 할당 int bind_result = bind(server_fd_, (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); if (listen_result < 0) - return (PrintError("Failed to listen on socket")); + 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")); + throw std::runtime_error("Failed to init kqueue"); 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()")); + throw std::runtime_error("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")); + throw std::runtime_error("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(); - // } - // } - // } + // request 받기 + char buf[BUFSIZE]; + 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; + std::cout << "request from " << GetIp() << ": " << buf << std::endl; + + // response 보내기 + std::map::iterator it = clients.find(curr_event->ident); + if (it != clients.end()) { + if (clients[curr_event->ident] != "") { + Http http(buf); + http.HttpHandler(); + std::string response = http.GetHeader(); + 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(); + clients[curr_event->ident].clear(); + ChangeEvents(change_list, server_fd_, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL); + std::string html_str = http.GetHtml(); + std::cout << html_str << std::endl; + bytes_sent = send(curr_event->ident, html_str.c_str(), html_str.size(), 0); + 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; } +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; } - -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[BUFSIZE]; - 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[BUFSIZE]; - char buf[BUFSIZE]; - - 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; - } - - printf("[INFO] Handling Request: method=%s, URI=%s\n", method, uri); - - char safe_uri[BUFSIZE]; - 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, BUFSIZE)) > 0) - write(asock, buf, cnt); -} diff --git a/jinhokim/src/client_main.cpp b/jinhokim/src/client_main.cpp index 4bce144..19a6dd0 100644 --- a/jinhokim/src/client_main.cpp +++ b/jinhokim/src/client_main.cpp @@ -1,19 +1,17 @@ #include "../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()) { + 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..13e475e 100644 --- a/jinhokim/src/server_main.cpp +++ b/jinhokim/src/server_main.cpp @@ -1,19 +1,17 @@ #include "../include/Server.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")); - } - - Server server(atoi(av[1])); + Server server(atoi(av[1])); - if (server.Set() || server.Run()) { + server.Set(); + server.Run(); + } + catch(const std::exception& e) { + std::cerr << e.what() << std::endl; return EXIT_FAILURE; } - return EXIT_SUCCESS; } From 54eb09fd4f6cff05d4b2b7f954b3a9d86d6c8627 Mon Sep 17 00:00:00 2001 From: jinhokim Date: Thu, 12 Jan 2023 18:43:00 +0900 Subject: [PATCH 02/16] =?UTF-8?q?WIP:=20http=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jinhokim/include/Http.hpp | 2 +- jinhokim/index.html | 8 +++++--- jinhokim/src/Http.cpp | 28 ++++++++-------------------- jinhokim/src/Server.cpp | 38 ++++++++++++++++++-------------------- 4 files changed, 32 insertions(+), 44 deletions(-) diff --git a/jinhokim/include/Http.hpp b/jinhokim/include/Http.hpp index c3b1e3e..36ed84a 100644 --- a/jinhokim/include/Http.hpp +++ b/jinhokim/include/Http.hpp @@ -23,7 +23,7 @@ class Http { void FindMime(char* ct_type, char* uri); void HttpHandler(void); - std::string GetHtml(void) const; + std::string GetHtml(int socket) const; std::string GetHeader(void) const; private: std::string request_; diff --git a/jinhokim/index.html b/jinhokim/index.html index 1b6b408..e83a1e0 100644 --- a/jinhokim/index.html +++ b/jinhokim/index.html @@ -1,4 +1,7 @@ - +HTTP/1.1 200 OK +Content-Length: 174 +Content-Type: text/html + Test Page @@ -7,6 +10,5 @@

Webserv...

- - \ No newline at end of file + diff --git a/jinhokim/src/Http.cpp b/jinhokim/src/Http.cpp index 0a4f8e0..4260755 100644 --- a/jinhokim/src/Http.cpp +++ b/jinhokim/src/Http.cpp @@ -73,27 +73,15 @@ void Http::HttpHandler(void) { FillHeader(ct_len, ct_type); } -std::string Http::GetHtml(void) const { +std::string Http::GetHtml(int socket) const { if (status_ == 200) { - // char buf[BUFSIZE]; - // int r; - // std::string res; - // while ((r = read(fd_, buf, BUFSIZE)) > 0) { - // res.append(buf); - // res.append("\n"); - // } - // res.pop_back(); - std::string res("\n\ - \n\ - \n\ - Test Page\n\ - \n\ - \n\ -

\n\ - Webserv...\n\ -

\n\ - \n\ -"); + char buf[BUFSIZE]; + int r; + std::string res; + while ((r = read(fd_, buf, BUFSIZE)) > 0) { + write(socket, buf, r); + res.append(buf); + } return res; } else if (status_ == 404) diff --git a/jinhokim/src/Server.cpp b/jinhokim/src/Server.cpp index d725142..662945d 100644 --- a/jinhokim/src/Server.cpp +++ b/jinhokim/src/Server.cpp @@ -133,26 +133,24 @@ int Server::Run(void) { std::cout << "request from " << GetIp() << ": " << buf << std::endl; // response 보내기 - std::map::iterator it = clients.find(curr_event->ident); - if (it != clients.end()) { - if (clients[curr_event->ident] != "") { - Http http(buf); - http.HttpHandler(); - std::string response = http.GetHeader(); - 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(); - clients[curr_event->ident].clear(); - ChangeEvents(change_list, server_fd_, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL); - std::string html_str = http.GetHtml(); - std::cout << html_str << std::endl; - bytes_sent = send(curr_event->ident, html_str.c_str(), html_str.size(), 0); - clients[curr_event->ident].clear(); - } - } - } + // Http http(buf); + // http.HttpHandler(); + // std::string response = http.GetHeader(); + // 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(); + // std::string html_str = http.GetHtml(curr_event->ident); + // std::cout << html_str << std::endl; + //send(curr_event->ident, html_str.c_str(), html_str.size(), 0); + buf[0] = '\0'; + int f = open("./index.html", O_RDONLY); + int r; + while ((r = read(f, buf, BUFSIZE)) > 0) + send(curr_event->ident, buf, r, 0); + clients[curr_event->ident].clear(); + } } } } From d325ab622a79a70006798948a2be471aeb86d535 Mon Sep 17 00:00:00 2001 From: jinhokim Date: Sun, 15 Jan 2023 20:13:23 +0900 Subject: [PATCH 03/16] =?UTF-8?q?WIP:=20http=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=9D=BC=EB=8B=A8=20=EC=84=B1=EA=B3=B5?= =?UTF-8?q?=EC=9D=80=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jinhokim/include/Http.hpp | 2 +- jinhokim/index.html | 6 +----- jinhokim/index2.html | 14 ++++++++++++++ jinhokim/src/Http.cpp | 23 +++++++++++++---------- jinhokim/src/Server.cpp | 32 +++++++++++++------------------- 5 files changed, 42 insertions(+), 35 deletions(-) create mode 100644 jinhokim/index2.html diff --git a/jinhokim/include/Http.hpp b/jinhokim/include/Http.hpp index 36ed84a..59b1be1 100644 --- a/jinhokim/include/Http.hpp +++ b/jinhokim/include/Http.hpp @@ -23,7 +23,7 @@ class Http { void FindMime(char* ct_type, char* uri); void HttpHandler(void); - std::string GetHtml(int socket) const; + std::string GetHtml(void); std::string GetHeader(void) const; private: std::string request_; diff --git a/jinhokim/index.html b/jinhokim/index.html index e83a1e0..572bacb 100644 --- a/jinhokim/index.html +++ b/jinhokim/index.html @@ -1,7 +1,3 @@ -HTTP/1.1 200 OK -Content-Length: 174 -Content-Type: text/html - Test Page @@ -11,4 +7,4 @@ Webserv...

- + \ No newline at end of file diff --git a/jinhokim/index2.html b/jinhokim/index2.html new file mode 100644 index 0000000..e83a1e0 --- /dev/null +++ b/jinhokim/index2.html @@ -0,0 +1,14 @@ +HTTP/1.1 200 OK +Content-Length: 174 +Content-Type: text/html + + + + Test Page + + +

+ Webserv... +

+ + diff --git a/jinhokim/src/Http.cpp b/jinhokim/src/Http.cpp index 4260755..8963791 100644 --- a/jinhokim/src/Http.cpp +++ b/jinhokim/src/Http.cpp @@ -71,23 +71,26 @@ void Http::HttpHandler(void) { FindMime(ct_type, local_uri); status_ = 200; FillHeader(ct_len, ct_type); + header_.append("\r\n"); } -std::string Http::GetHtml(int socket) const { +std::string Http::GetHtml(void){ if (status_ == 200) { char buf[BUFSIZE]; int r; - std::string res; - while ((r = read(fd_, buf, BUFSIZE)) > 0) { - write(socket, buf, r); - res.append(buf); - } - return res; + while ((r = read(fd_, buf, BUFSIZE)) > 0) + header_.append(buf); + return header_; + } + else if (status_ == 404) { + header_.append("\r\n"); + header_.append(NOT_FOUND_CONTENT); + return header_; } - else if (status_ == 404) - return std::string(NOT_FOUND_CONTENT); else if (status_ == 500) - return std::string(SERVER_ERROR_CONTENT); + header_.append("\r\n"); + header_.append(SERVER_ERROR_CONTENT); + return header_; return std::string("Error"); } diff --git a/jinhokim/src/Server.cpp b/jinhokim/src/Server.cpp index 662945d..9d1fd21 100644 --- a/jinhokim/src/Server.cpp +++ b/jinhokim/src/Server.cpp @@ -119,7 +119,6 @@ int Server::Run(void) { clients[client_fd] = ""; } else if (clients.find(curr_event->ident) != clients.end()) { - // request 받기 char buf[BUFSIZE]; ssize_t bytes_received = recv(curr_event->ident, buf, sizeof(buf), 0); if (bytes_received <= 0) { @@ -132,24 +131,19 @@ int Server::Run(void) { clients[curr_event->ident] += buf; std::cout << "request from " << GetIp() << ": " << buf << std::endl; - // response 보내기 - // Http http(buf); - // http.HttpHandler(); - // std::string response = http.GetHeader(); - // 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(); - // std::string html_str = http.GetHtml(curr_event->ident); - // std::cout << html_str << std::endl; - //send(curr_event->ident, html_str.c_str(), html_str.size(), 0); - buf[0] = '\0'; - int f = open("./index.html", O_RDONLY); - int r; - while ((r = read(f, buf, BUFSIZE)) > 0) - send(curr_event->ident, buf, r, 0); - clients[curr_event->ident].clear(); + std::map::iterator it = clients.find(curr_event->ident); + if (it != clients.end()) { + if (clients[curr_event->ident] != "") { + Http http(buf); + http.HttpHandler(); + std::string html_str = http.GetHtml(); + std::cout << "response: " << html_str << std::endl; + send(curr_event->ident, html_str.c_str(), html_str.size(), 0); + } + //DisconnectClient(curr_event->ident, clients); + //clients[curr_event->ident].clear(); + } + } } } From 590baef7e039860856930be4ed5dfc1f94bd8345 Mon Sep 17 00:00:00 2001 From: jinhokim Date: Mon, 16 Jan 2023 00:41:22 +0900 Subject: [PATCH 04/16] =?UTF-8?q?WIP:=20http=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jinhokim/Makefile | 101 +++++-- jinhokim/css/animal-face.css | 553 ++++++++++++++++++++++++++++++++++ jinhokim/css/dog-cat.css | 401 ++++++++++++++++++++++++ jinhokim/css/index.css | 401 ++++++++++++++++++++++++ jinhokim/include/Http.hpp | 35 --- jinhokim/include/Response.hpp | 37 +++ jinhokim/include/Server.hpp | 2 +- jinhokim/index.html | 257 +++++++++++++++- jinhokim/src/Http.cpp | 99 ------ jinhokim/src/Response.cpp | 118 ++++++++ jinhokim/src/Server.cpp | 13 +- 11 files changed, 1838 insertions(+), 179 deletions(-) create mode 100644 jinhokim/css/animal-face.css create mode 100644 jinhokim/css/dog-cat.css create mode 100644 jinhokim/css/index.css delete mode 100644 jinhokim/include/Http.hpp create mode 100644 jinhokim/include/Response.hpp delete mode 100644 jinhokim/src/Http.cpp create mode 100644 jinhokim/src/Response.cpp diff --git a/jinhokim/Makefile b/jinhokim/Makefile index 888e4c6..c3da349 100644 --- a/jinhokim/Makefile +++ b/jinhokim/Makefile @@ -1,40 +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/http.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) $(SERVER) + @$(MAKE) $(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/css/animal-face.css b/jinhokim/css/animal-face.css new file mode 100644 index 0000000..55460e8 --- /dev/null +++ b/jinhokim/css/animal-face.css @@ -0,0 +1,553 @@ +body { + font-family: 'Jua', sans-serif; + background-color: #ffffff; + box-sizing: border-box; + color: #35465d; +} + +.file-upload { + padding: 15% 3%; + margin: 0 auto; + border-radius: 10px; + border: solid 1.5px #f6f7fa; + background-color: #f6f7fa; +} + +.file-upload-btn { + width: 100%; + margin: 0; + color: #fff; + background: #1FB264; + border: none; + padding: 10px; + border-radius: 4px; + border-bottom: 4px solid #15824B; + transition: all .2s ease; + outline: none; + text-transform: uppercase; + font-weight: 700; +} + +.file-upload-btn:hover { + background: #1AA059; + color: #ffffff; + transition: all .2s ease; + cursor: pointer; +} + +.file-upload-btn:active { + border: 0; + transition: all .2s ease; +} + +.file-upload-content { + display: none; + text-align: center; +} + +#loading { + display: none; +} + +.file-upload-input { + position: absolute; + margin: 0; + padding: 0; + width: 100%; + height: 100%; + outline: none; + opacity: 0; + cursor: pointer; +} + +.image-upload-wrap { + width: 60%; + margin: 0 auto; + position: relative; + object-fit: contain; + border-radius: 10px; + border: dashed 1.5px #35465d; + background-color: #ffffff; +} + +.image-dropping, +.image-upload-wrap:hover { + background-color: #35465d82; + border: 1.5px dashed #ffffff; +} + +.image-title-wrap { + padding: 0 15px 15px 15px; + color: #222; +} + +.drag-text { + text-align: center; +} + +.drag-text h3 { + font-weight: 500; + text-transform: uppercase; +} + +.file-upload-image { + max-height: 60%; + max-width: 60%; + margin: auto; + padding: 10px; +} + +.try-again-btn { + border-radius: 40px; + border: solid 1.5px #35465d; + background-color: #35465d; +} + +.try-again-text { + display: block; + font-size: 1.5rem; + text-align: center; + color: #ffffff; + padding: 4px 15px; +} + +.remove-image:hover { + background: #c13b2a; + color: #ffffff; + transition: all .2s ease; + cursor: pointer; +} + +.remove-image:active { + border: 0; + transition: all .2s ease; +} + +.navbar-light .navbar-brand { + line-height: 1.68; + text-align: left; + color: #35465d; +} + +.navbar-light .navbar-toggler { + border-color: #ffffff; +} + +.section { + margin-top: 50px; +} + +.title { + line-height: 1.67; + text-align: center; + color: #d8a3b9; +} + +.subtitle { + line-height: 1.53; + text-align: center; + color: #35465d; +} + + +/* On screens that are 600px or less, set the background color to olive */ + +@media screen and (max-width: 600px) { + html { + font-size: 10px; + } + .navbar-brand { + font-size: 2rem; + } +} + +.youtube-link { + font-size: 1.5rem; + line-height: 1.71; + text-align: center; + color: #f73737; + text-decoration: underline; +} + +.youtube-link:hover { + font-size: 1.5rem; + line-height: 1.71; + text-align: center; + color: #f73737; + text-decoration: underline; +} + +.youtube-icon { + width: 30px; + height: 27.262px; +} + +.upload { + width: 20%; +} + +.upload-text { + width: 58%; + font-size: 1.5rem; + line-height: 1.53; + text-align: center; + color: #35465d; + margin: 0 auto; +} + +footer { + text-align: center; + padding: 0px 0px; + /* background-color: #353535; */ + width: 100%; + height: auto; + position: relative; +} + +.footer_table { + color: rgb(58, 29, 29); + width: 30%; +} + +.footer_table .footer-txt { + font-weight: bold; + color: #747474; +} + +.footer_table img { + width: 23px; + height: 23px +} + +.footer_table img:hover { + opacity: 0.5; + cursor: pointer; +} + +.footer-logo { + width: 80; + height: 80; +} + +.email { + color: #A566FF; +} + +.copy { + font-weight: bold; + color: #747474; +} + +.bar-container { + height: 2.7rem; +} + +.dinosaur-box { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + width: 100%; + background-color: rgba(117, 204, 84, 0.2); +} + +.dinosaur-bar { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + background-color: rgba(117, 204, 84, 1); +} + +.deer-box { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + width: 100%; + background-color: rgba(117, 204, 84, 0.2); +} + +.deer-bar { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + background-color: rgba(117, 204, 84, 1); +} + +.dog-box { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + width: 100%; + background-color: rgba(27, 175, 234, 0.2); +} + +.dog-bar { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + background-color: rgba(27, 175, 234, 1); +} + +.cat-box { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + width: 100%; + background-color: rgba(251, 176, 59, 0.2); +} + +.cat-bar { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + background-color: rgba(251, 176, 59, 1); +} + +.bear-box { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + width: 100%; + background-color: rgba(195, 140, 102, 0.2); +} + +.bear-bar { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + background-color: rgba(195, 140, 102, 1); +} + +.fox-box { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + width: 100%; + background-color: rgba(49, 132, 155, 0.2); +} + +.fox-bar { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + background-color: rgba(49, 132, 155, 1); +} + +.rabbit-box { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + width: 100%; + background-color: rgba(235, 166, 190, 0.2); +} + +.rabbit-bar { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + background-color: rgba(235, 166, 190, 1); +} + +.percent-text { + font-size: 1rem; + color: #ffffff; +} + +.animal-label { + width: 20%; + text-align: left; + height: 2rem; +} + +#label-container { + width: 80%; + margin: 0 auto; +} + +.result-message { + font-size: 2rem; +} + +.animal-explain { + font-size: 1.5rem; +} + +.dinosaur-animal-title { + color: #3ba363; +} + +.dinosaur-animal-celeb { + color: #3ba363; + font-size: 1.2rem; +} + +.deer-animal-title { + color: #3ba363; +} + +.deer-animal-celeb { + color: #3ba363; + font-size: 1.2rem; +} + +.cat-animal-title { + color: #ff6c0a; +} + +.cat-animal-celeb { + color: #ff6c0a; + font-size: 1.2rem; +} + +.dog-animal-title { + color: #548dd4; +} + +.dog-animal-celeb { + color: #548dd4; + font-size: 1.2rem; +} + +.rabbit-animal-title { + color: #e56995; +} + +.rabbit-animal-celeb { + color: #e56995; + font-size: 1.2rem; +} + +.bear-animal-title { + color: #9b6b43; +} + +.bear-animal-celeb { + color: #9b6b43; + font-size: 1.2rem; +} + +.fox-animal-title { + color: #31849b; +} + +.fox-animal-celeb { + color: #31849b; + font-size: 1.2rem; +} + + +/* gender */ + +input#gender { + display: none; +} + +label { + cursor: pointer; + display: inline-block; + width: 90px; + height: 41px; + box-shadow: 0 0 0 8px #e5e9ea inset; + border-radius: 30px; + position: relative; +} + +.knob { + position: absolute; + width: 41px; + top: 0; + left: 0; + height: 41px; + border-radius: 50%; + box-shadow: 0 0 0 8px #f470a7 inset; + background-color: #fbd4e6; + transition: 0.3s; +} + +.knob>i { + position: absolute; + width: 4px; + height: 18px; + top: 100%; + background-color: #f470a7; + left: calc(50% - 2px); +} + +.knob>i:before, +.knob>i:after { + width: 6px; + position: absolute; + top: 42%; + content: ""; + height: 4px; + background-color: #f470a7; +} + +.knob>i:before { + left: 4px; +} + +.knob>i:after { + left: -6px; +} + +input:checked+label>.knob { + box-shadow: 0 0 0 8px #a1c6dd inset; + background-color: #e2ecf4; + transform: translateX(49px) rotate(-140deg); +} + +input:checked+label>.knob>i { + background-color: #a1c6dd; +} + +input:checked+label>.knob>i:after, +input:checked+label>.knob>i:before { + background-color: #a1c6dd; +} + +input:checked+label>.knob>i:after { + top: 14.5px; + width: 12px; + left: -7px; + transform: rotate(45deg); +} + +input:checked+label>.knob>i:before { + top: 13px; + width: 12px; + left: 0px; + transform: rotate(134deg); +} + +.ad-banner { + width: 320px; + margin: 0 auto; +} \ No newline at end of file diff --git a/jinhokim/css/dog-cat.css b/jinhokim/css/dog-cat.css new file mode 100644 index 0000000..ded0f23 --- /dev/null +++ b/jinhokim/css/dog-cat.css @@ -0,0 +1,401 @@ +body { + font-family: 'Jua', sans-serif; + background-color: #ffffff; + box-sizing: border-box; + color: #35465d; +} + +.file-upload { + padding: 15% 3%; + margin: 0 auto; + border-radius: 10px; + border: solid 1.5px #f6f7fa; + background-color: #f6f7fa; +} + +.file-upload-btn { + width: 100%; + margin: 0; + color: #fff; + background: #1FB264; + border: none; + padding: 10px; + border-radius: 4px; + border-bottom: 4px solid #15824B; + transition: all .2s ease; + outline: none; + text-transform: uppercase; + font-weight: 700; +} + +.file-upload-btn:hover { + background: #1AA059; + color: #ffffff; + transition: all .2s ease; + cursor: pointer; +} + +.file-upload-btn:active { + border: 0; + transition: all .2s ease; +} + +.file-upload-content { + display: none; + text-align: center; +} + +#loading { + display: none; +} + +.file-upload-input { + position: absolute; + margin: 0; + padding: 0; + width: 100%; + height: 100%; + outline: none; + opacity: 0; + cursor: pointer; +} + +.image-upload-wrap { + width: 60%; + margin: 0 auto; + position: relative; + object-fit: contain; + border-radius: 10px; + border: dashed 1.5px #35465d; + background-color: #ffffff; +} + +.image-dropping, +.image-upload-wrap:hover { + background-color: #35465d82; + border: 1.5px dashed #ffffff; +} + +.image-title-wrap { + padding: 0 15px 15px 15px; + color: #222; +} + +.drag-text { + text-align: center; +} + +.drag-text h3 { + font-weight: 500; + text-transform: uppercase; +} + +.file-upload-image { + max-height: 60%; + max-width: 60%; + margin: auto; + padding: 10px; +} + +.try-again-btn { + border-radius: 40px; + border: solid 1.5px #35465d; + background-color: #35465d; +} + +.try-again-text { + display: block; + font-size: 1.5rem; + text-align: center; + color: #ffffff; + padding: 4px 15px; +} + +.remove-image:hover { + background: #c13b2a; + color: #ffffff; + transition: all .2s ease; + cursor: pointer; +} + +.remove-image:active { + border: 0; + transition: all .2s ease; +} + +.navbar-light .navbar-brand { + line-height: 1.68; + text-align: left; + color: #35465d; +} + +.navbar-light .navbar-toggler { + border-color: #ffffff; +} + +.section { + margin-top: 50px; +} + +.title { + line-height: 1.67; + text-align: center; + color: #a1c6dd; +} + +.subtitle { + line-height: 1.53; + text-align: center; + color: #35465d; +} + +@media screen and (max-width: 600px) { + html { + font-size: 10px; + } + .navbar-brand { + font-size: 2rem; + } +} + +.upload { + width: 20%; +} + +.upload-text { + width: 58%; + font-size: 1.5rem; + line-height: 1.53; + text-align: center; + color: #35465d; + margin: 0 auto; +} + + +/* .footer { + background-color: initial; + color: initial; +} */ + +footer { + text-align: center; + padding: 0px 0px; + /* background-color: #353535; */ + width: 100%; + height: auto; + position: relative; +} + +.footer_table { + color: rgb(58, 29, 29); + width: 30%; +} + +.footer_table .footer-txt { + font-weight: bold; + color: #747474; +} + +.footer_table img { + width: 23px; + height: 23px +} + +.footer_table img:hover { + opacity: 0.5; + cursor: pointer; +} + +.footer-logo { + width: 80; + height: 80; +} + +.email { + color: #A566FF; +} + +.copy { + font-weight: bold; + color: #747474; +} + +.bar-container { + height: 2.7rem; +} + +.Dog-box { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + width: 100%; + background-color: rgba(27, 175, 234, 0.2); +} + +.Dog-bar { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + background-color: rgba(27, 175, 234, 1); +} + +.Cat-box { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + width: 100%; + background-color: rgba(251, 176, 59, 0.2); +} + +.Cat-bar { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + background-color: rgba(251, 176, 59, 1); +} + +.Else-box { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + width: 100%; + background-color: rgba(235, 90, 210, 0.2); +} + +.Else-bar { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + background-color: rgba(235, 90, 210, 1); +} + +.percent-text { + font-size: 1rem; + color: #ffffff; +} + +.animal-label { + width: 20%; + text-align: left; + height: 2rem; +} + +#label-container { + width: 80%; + margin: 0 auto; +} + +.result-message { + font-size: 2rem; +} + +.animal-explain { + font-size: 1.5rem; +} + +.Dog-animal-title { + color: #548dd4; +} + +.Cat-animal-title { + color: #ff6c0a; +} + +.Else-animal-title { + color: #bb65ddf1; +} + +label { + cursor: pointer; + display: inline-block; + width: 90px; + height: 41px; + box-shadow: 0 0 0 8px #e5e9ea inset; + border-radius: 30px; + position: relative; +} + +.knob { + position: absolute; + width: 41px; + top: 0; + left: 0; + height: 41px; + border-radius: 50%; + box-shadow: 0 0 0 8px #f470a7 inset; + background-color: #fbd4e6; + transition: 0.3s; +} + +.knob>i { + position: absolute; + width: 4px; + height: 18px; + top: 100%; + background-color: #f470a7; + left: calc(50% - 2px); +} + +.knob>i:before, +.knob>i:after { + width: 6px; + position: absolute; + top: 42%; + content: ""; + height: 4px; + background-color: #f470a7; +} + +.knob>i:before { + left: 4px; +} + +.knob>i:after { + left: -6px; +} + +input:checked+label>.knob { + box-shadow: 0 0 0 8px #a1c6dd inset; + background-color: #e2ecf4; + transform: translateX(49px) rotate(-140deg); +} + +input:checked+label>.knob>i { + background-color: #a1c6dd; +} + +input:checked+label>.knob>i:after, +input:checked+label>.knob>i:before { + background-color: #a1c6dd; +} + +input:checked+label>.knob>i:after { + top: 14.5px; + width: 12px; + left: -7px; + transform: rotate(45deg); +} + +input:checked+label>.knob>i:before { + top: 13px; + width: 12px; + left: 0px; + transform: rotate(134deg); +} + +.ad-banner { + width: 320px; + margin: 0 auto; +} \ No newline at end of file diff --git a/jinhokim/css/index.css b/jinhokim/css/index.css new file mode 100644 index 0000000..ded0f23 --- /dev/null +++ b/jinhokim/css/index.css @@ -0,0 +1,401 @@ +body { + font-family: 'Jua', sans-serif; + background-color: #ffffff; + box-sizing: border-box; + color: #35465d; +} + +.file-upload { + padding: 15% 3%; + margin: 0 auto; + border-radius: 10px; + border: solid 1.5px #f6f7fa; + background-color: #f6f7fa; +} + +.file-upload-btn { + width: 100%; + margin: 0; + color: #fff; + background: #1FB264; + border: none; + padding: 10px; + border-radius: 4px; + border-bottom: 4px solid #15824B; + transition: all .2s ease; + outline: none; + text-transform: uppercase; + font-weight: 700; +} + +.file-upload-btn:hover { + background: #1AA059; + color: #ffffff; + transition: all .2s ease; + cursor: pointer; +} + +.file-upload-btn:active { + border: 0; + transition: all .2s ease; +} + +.file-upload-content { + display: none; + text-align: center; +} + +#loading { + display: none; +} + +.file-upload-input { + position: absolute; + margin: 0; + padding: 0; + width: 100%; + height: 100%; + outline: none; + opacity: 0; + cursor: pointer; +} + +.image-upload-wrap { + width: 60%; + margin: 0 auto; + position: relative; + object-fit: contain; + border-radius: 10px; + border: dashed 1.5px #35465d; + background-color: #ffffff; +} + +.image-dropping, +.image-upload-wrap:hover { + background-color: #35465d82; + border: 1.5px dashed #ffffff; +} + +.image-title-wrap { + padding: 0 15px 15px 15px; + color: #222; +} + +.drag-text { + text-align: center; +} + +.drag-text h3 { + font-weight: 500; + text-transform: uppercase; +} + +.file-upload-image { + max-height: 60%; + max-width: 60%; + margin: auto; + padding: 10px; +} + +.try-again-btn { + border-radius: 40px; + border: solid 1.5px #35465d; + background-color: #35465d; +} + +.try-again-text { + display: block; + font-size: 1.5rem; + text-align: center; + color: #ffffff; + padding: 4px 15px; +} + +.remove-image:hover { + background: #c13b2a; + color: #ffffff; + transition: all .2s ease; + cursor: pointer; +} + +.remove-image:active { + border: 0; + transition: all .2s ease; +} + +.navbar-light .navbar-brand { + line-height: 1.68; + text-align: left; + color: #35465d; +} + +.navbar-light .navbar-toggler { + border-color: #ffffff; +} + +.section { + margin-top: 50px; +} + +.title { + line-height: 1.67; + text-align: center; + color: #a1c6dd; +} + +.subtitle { + line-height: 1.53; + text-align: center; + color: #35465d; +} + +@media screen and (max-width: 600px) { + html { + font-size: 10px; + } + .navbar-brand { + font-size: 2rem; + } +} + +.upload { + width: 20%; +} + +.upload-text { + width: 58%; + font-size: 1.5rem; + line-height: 1.53; + text-align: center; + color: #35465d; + margin: 0 auto; +} + + +/* .footer { + background-color: initial; + color: initial; +} */ + +footer { + text-align: center; + padding: 0px 0px; + /* background-color: #353535; */ + width: 100%; + height: auto; + position: relative; +} + +.footer_table { + color: rgb(58, 29, 29); + width: 30%; +} + +.footer_table .footer-txt { + font-weight: bold; + color: #747474; +} + +.footer_table img { + width: 23px; + height: 23px +} + +.footer_table img:hover { + opacity: 0.5; + cursor: pointer; +} + +.footer-logo { + width: 80; + height: 80; +} + +.email { + color: #A566FF; +} + +.copy { + font-weight: bold; + color: #747474; +} + +.bar-container { + height: 2.7rem; +} + +.Dog-box { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + width: 100%; + background-color: rgba(27, 175, 234, 0.2); +} + +.Dog-bar { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + background-color: rgba(27, 175, 234, 1); +} + +.Cat-box { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + width: 100%; + background-color: rgba(251, 176, 59, 0.2); +} + +.Cat-bar { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + background-color: rgba(251, 176, 59, 1); +} + +.Else-box { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + width: 100%; + background-color: rgba(235, 90, 210, 0.2); +} + +.Else-bar { + position: absolute; + top: 0; + left: 0; + height: 2rem; + border-radius: 10px; + background-color: rgba(235, 90, 210, 1); +} + +.percent-text { + font-size: 1rem; + color: #ffffff; +} + +.animal-label { + width: 20%; + text-align: left; + height: 2rem; +} + +#label-container { + width: 80%; + margin: 0 auto; +} + +.result-message { + font-size: 2rem; +} + +.animal-explain { + font-size: 1.5rem; +} + +.Dog-animal-title { + color: #548dd4; +} + +.Cat-animal-title { + color: #ff6c0a; +} + +.Else-animal-title { + color: #bb65ddf1; +} + +label { + cursor: pointer; + display: inline-block; + width: 90px; + height: 41px; + box-shadow: 0 0 0 8px #e5e9ea inset; + border-radius: 30px; + position: relative; +} + +.knob { + position: absolute; + width: 41px; + top: 0; + left: 0; + height: 41px; + border-radius: 50%; + box-shadow: 0 0 0 8px #f470a7 inset; + background-color: #fbd4e6; + transition: 0.3s; +} + +.knob>i { + position: absolute; + width: 4px; + height: 18px; + top: 100%; + background-color: #f470a7; + left: calc(50% - 2px); +} + +.knob>i:before, +.knob>i:after { + width: 6px; + position: absolute; + top: 42%; + content: ""; + height: 4px; + background-color: #f470a7; +} + +.knob>i:before { + left: 4px; +} + +.knob>i:after { + left: -6px; +} + +input:checked+label>.knob { + box-shadow: 0 0 0 8px #a1c6dd inset; + background-color: #e2ecf4; + transform: translateX(49px) rotate(-140deg); +} + +input:checked+label>.knob>i { + background-color: #a1c6dd; +} + +input:checked+label>.knob>i:after, +input:checked+label>.knob>i:before { + background-color: #a1c6dd; +} + +input:checked+label>.knob>i:after { + top: 14.5px; + width: 12px; + left: -7px; + transform: rotate(45deg); +} + +input:checked+label>.knob>i:before { + top: 13px; + width: 12px; + left: 0px; + transform: rotate(134deg); +} + +.ad-banner { + width: 320px; + margin: 0 auto; +} \ No newline at end of file diff --git a/jinhokim/include/Http.hpp b/jinhokim/include/Http.hpp deleted file mode 100644 index 59b1be1..0000000 --- a/jinhokim/include/Http.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef HTTP_HPP -# define HTTP_HPP - -# include -# include -# include -# include -# include - -# define BUFSIZE 1024 -# define HEADER_FORMAT "HTTP/1.1 %d %s\r\nContent-Length: %ld\nContent-Type: %s\r\n" -# define NOT_FOUND_CONTENT "

404 Not Found

\n" -# define SERVER_ERROR_CONTENT "

500 Internal Server Error

\n" - -class Http { - public: - Http(char* request); - ~Http(void); - - void FillHeader(long len, std::string type); - void Handle404(void); - void Handle500(void); - void FindMime(char* ct_type, char* uri); - void HttpHandler(void); - - std::string GetHtml(void); - std::string GetHeader(void) const; - private: - std::string request_; - std::string header_; - int status_; - int fd_; -}; - -#endif // HTTP_HPP diff --git a/jinhokim/include/Response.hpp b/jinhokim/include/Response.hpp new file mode 100644 index 0000000..549223b --- /dev/null +++ b/jinhokim/include/Response.hpp @@ -0,0 +1,37 @@ +#ifndef RESPONSE_HPP +# define RESPONSE_HPP + +# include +# include +# include + +# include +# include + +# include +# include + +# define BUFSIZE 1024 +# define HEADER_FORMAT "HTTP/1.1 %d %s\r\nContent-Length: %ld\nContent-Type: %s\r\n" +# define NOT_FOUND_CONTENT "

404 Not Found

\n" +# define SERVER_ERROR_CONTENT "

500 Internal Server Error

\n" + +class Response { + public: + Response(char* request); + ~Response(void); + + void ResponseHandler(void); + void FindMime(char* ct_type, char* uri); + void FillHeader(int status, long len, std::string type); + void Handle200(int ct_len, char* local_uri); + void Handle404(void); + void Handle500(void); + + 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 daba337..15abd5c 100644 --- a/jinhokim/include/Server.hpp +++ b/jinhokim/include/Server.hpp @@ -13,7 +13,7 @@ # include # include -# include "Http.hpp" +# include "Response.hpp" # define BACKLOG 1024 diff --git a/jinhokim/index.html b/jinhokim/index.html index 572bacb..d320b3d 100644 --- a/jinhokim/index.html +++ b/jinhokim/index.html @@ -1,10 +1,249 @@ - - - Test Page - - -

- Webserv... -

- + + + + + + + + + + 진호의 실험실 + + + + + + + + + + + + + + + + + + + + +
+

강아지 고양이 분류기

+

웹 공부하는 겸 만들어본 사이트!

+

나는 어떤 동물과 닮았을까? 나의 동물상 찾기를 해보세요!

+

대표 동물상 연예인 사진 데이터로 학습한 인공지능이 나의 얼굴로 동물상과 특징을 찾아드립니다.

+

본 서비스는 Google의 인공지능 teachable machine 2.0을 활용하였습니다.

+

얼굴로 보는 인공지능 동물상 테스트, 나와 닮은 동물상을 찾아보세요! 대표 동물상 연예인 사진 데이터로 학습한 인공지능이 나의 얼굴로 동물상과 특징을 찾아드립니다. 회원가입도 필요없이 화면에서 바로 확인해보세요! 사진 데이터는 그 어디에도 전송되지 않습니다. 인공지능이 보는 나의 동물상 테스트 한번 해보세요! 강아지상? 고양이상? 여우상? 사슴상? 토끼상? 곰상? 공룡상? 얼굴상 테스트를 통해 나와 닮은 동물 찾기를 할 수 있습니다.

+
+ +

강아지와 고양이 중 더 좋아하는 동물의 사진을 올려보세요!

+ + +
+
+ +
+ +

강아지, 고양이 사진을 올려놓거나 눌러서 업로드하세요!

+
+
+
+ your image +
+
+ Loading... +
+

AI가 당신의 사진을 분류 중입니다.

+
+

+
+
+ +
+ +
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + \ No newline at end of file diff --git a/jinhokim/src/Http.cpp b/jinhokim/src/Http.cpp deleted file mode 100644 index 8963791..0000000 --- a/jinhokim/src/Http.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "../include/Http.hpp" - -Http::Http(char* request) : request_(std::string(request)) {} - -Http::~Http(void) {} - -void Http::FillHeader(long len, std::string type) { - char header[BUFSIZE]; - 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()); - header_ = std::string(header); -} - -void Http::Handle404(void) { - status_ = 404; - std::string t("text/html"); - FillHeader(sizeof(NOT_FOUND_CONTENT), t); -} - -void Http::Handle500(void) { - status_ = 500; - std::string t("text/html"); - FillHeader(sizeof(SERVER_ERROR_CONTENT), t); -} - -void Http::FindMime(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::HttpHandler(void) { - char safe_uri[BUFSIZE]; - char* local_uri; - struct stat st; - - strcpy(safe_uri, "/index.html"); - - local_uri = safe_uri + 1; - if (stat(local_uri, &st) < 0) { - perror("[WARN] No file found matching URI.\n"); - Handle404(); return; - } - - fd_ = open(local_uri, O_RDONLY); - if (fd_ < 0) { - perror("[ERR] Failed to open file.\n"); - Handle500(); return; - } - - int ct_len = st.st_size; - char ct_type[40]; - FindMime(ct_type, local_uri); - status_ = 200; - FillHeader(ct_len, ct_type); - header_.append("\r\n"); -} - -std::string Http::GetHtml(void){ - if (status_ == 200) { - char buf[BUFSIZE]; - int r; - while ((r = read(fd_, buf, BUFSIZE)) > 0) - header_.append(buf); - return header_; - } - else if (status_ == 404) { - header_.append("\r\n"); - header_.append(NOT_FOUND_CONTENT); - return header_; - } - else if (status_ == 500) - header_.append("\r\n"); - header_.append(SERVER_ERROR_CONTENT); - return header_; - return std::string("Error"); -} - -std::string Http::GetHeader(void) const { - return header_; -} diff --git a/jinhokim/src/Response.cpp b/jinhokim/src/Response.cpp new file mode 100644 index 0000000..799180d --- /dev/null +++ b/jinhokim/src/Response.cpp @@ -0,0 +1,118 @@ +#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; + } + + char* method = strtok(const_cast(request_.c_str()), " "); + char* uri = strtok(NULL, " "); + if (method == NULL || uri == NULL) { + std::cerr << "[ERROR] Failed to identify method, URI" << std::endl; + Handle500(); + return; + } + + printf("[INFO] Handling Request: method=%s, URI=%s\n", method, uri); + + char safe_uri[BUFSIZE]; + 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"); + Handle404(); + return ; + } + + Handle200(st.st_size, local_uri); + return ; +} + +void Response::FindMime(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 Response::FillHeader(int status, long len, std::string type) { + char header[BUFSIZE]; + char status_text[40]; + std::string status_text; + std::stringstream stream; + std::string status_str; + + switch (status) { + case 200: + stream << 200; + status_text.append("OK"); + //strcpy(status_text, "OK"); + break; + case 404: + stream << 404; + strcpy(status_text, "Not Found"); + break; + case 500: + stream << 500; + strcpy(status_text, "Internal Server Error"); + break; + default: + break; + } + //sprintf(header, HEADER_FORMAT, status, status_text, len, type.c_str()); + stream >> status_str; + std::string str = "HTTP/1.1" + status_str; + //%s\r\nContent-Length: %ld\nContent-Type: %s\r\n" + response_ = std::string(header); +} + +void Response::Handle200(int ct_len, char* local_uri) { + char ct_type[40]; + int r; + int fd = open(local_uri, O_RDONLY); + char buf[BUFSIZE]; + + FindMime(ct_type, local_uri); + FillHeader(200, ct_len, ct_type); + while ((r = read(fd, buf, BUFSIZE)) > 0) + response_.append(buf); +} + +void Response::Handle404(void) { + std::string t("text/html"); + + FillHeader(404, sizeof(NOT_FOUND_CONTENT), t); + response_.append(NOT_FOUND_CONTENT); +} + +void Response::Handle500(void) { + std::string t("text/html"); + + FillHeader(500, sizeof(SERVER_ERROR_CONTENT), t); + response_.append(SERVER_ERROR_CONTENT); +} + +std::string Response::GetResponse(void){ + return response_; +} diff --git a/jinhokim/src/Server.cpp b/jinhokim/src/Server.cpp index 9d1fd21..2aff8aa 100644 --- a/jinhokim/src/Server.cpp +++ b/jinhokim/src/Server.cpp @@ -24,7 +24,7 @@ EX) 8. 커낵션을 닫는다(close) */ -#include "../include/Server.hpp" +#include "Server.hpp" Server::Server(int port) : port_(port) {} @@ -134,16 +134,15 @@ int Server::Run(void) { std::map::iterator it = clients.find(curr_event->ident); if (it != clients.end()) { if (clients[curr_event->ident] != "") { - Http http(buf); - http.HttpHandler(); - std::string html_str = http.GetHtml(); - std::cout << "response: " << html_str << std::endl; - send(curr_event->ident, html_str.c_str(), html_str.size(), 0); + Response response(buf); + response.ResponseHandler(); + std::string response_str = response.GetResponse(); + std::cout << "response: " << response_str << std::endl; + send(curr_event->ident, response_str.c_str(), response_str.size(), 0); } //DisconnectClient(curr_event->ident, clients); //clients[curr_event->ident].clear(); } - } } } From 3c524230efda25815e894985c1297c857dfca234 Mon Sep 17 00:00:00 2001 From: jinhokim Date: Mon, 16 Jan 2023 02:25:56 +0900 Subject: [PATCH 05/16] =?UTF-8?q?WIP:=20http=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jinhokim/Makefile | 4 ++ jinhokim/include/Response.hpp | 2 +- jinhokim/index2.html | 4 -- jinhokim/src/Response.cpp | 83 +++++++++++++++++++---------------- jinhokim/src/Server.cpp | 1 - jinhokim/test.cpp | 1 - 6 files changed, 49 insertions(+), 46 deletions(-) diff --git a/jinhokim/Makefile b/jinhokim/Makefile index c3da349..b1b169c 100644 --- a/jinhokim/Makefile +++ b/jinhokim/Makefile @@ -49,6 +49,10 @@ $(BUILD_DIR)/%.o : %.cpp | dir_guard @printf " \r" @printf "$(YELLOW)[WEBSERV] [%02d/%02d] ( %3d %%) Compiling $<\r$(DEF_COLOR)" $(COMPILED_FILES) $(TOTAL_FILES) $(PROGRESS) +# $(BUILD_DIR)/%.d : %.cpp | dir_guard +# @$(CXX) -M $< > $@ +#@$(CXX) $(CXXFLAGS) -I $(INC_DIR) -c $^ -o $@ + dir_guard: @mkdir -p $(addprefix $(BUILD_DIR)/, $(SRC_DIR)) diff --git a/jinhokim/include/Response.hpp b/jinhokim/include/Response.hpp index 549223b..15ff853 100644 --- a/jinhokim/include/Response.hpp +++ b/jinhokim/include/Response.hpp @@ -12,7 +12,7 @@ # include # define BUFSIZE 1024 -# define HEADER_FORMAT "HTTP/1.1 %d %s\r\nContent-Length: %ld\nContent-Type: %s\r\n" +# define HEADER_FORMAT "HTTP/1.1 %d %s\r\nContent-Length: %ld\nContent-Type: %s\r\n\r\n" # define NOT_FOUND_CONTENT "

404 Not Found

\n" # define SERVER_ERROR_CONTENT "

500 Internal Server Error

\n" diff --git a/jinhokim/index2.html b/jinhokim/index2.html index e83a1e0..dad0f12 100644 --- a/jinhokim/index2.html +++ b/jinhokim/index2.html @@ -1,7 +1,3 @@ -HTTP/1.1 200 OK -Content-Length: 174 -Content-Type: text/html - Test Page diff --git a/jinhokim/src/Response.cpp b/jinhokim/src/Response.cpp index 799180d..2958abd 100644 --- a/jinhokim/src/Response.cpp +++ b/jinhokim/src/Response.cpp @@ -40,6 +40,32 @@ void Response::ResponseHandler(void) { return ; } +void Response::Handle200(int ct_len, char* local_uri) { + char ct_type[40900]; + int r; + int fd = open(local_uri, O_RDONLY); + char buf[BUFSIZE]; + + FindMime(ct_type, local_uri); + FillHeader(200, ct_len, ct_type); + while ((r = read(fd, buf, BUFSIZE)) > 0) + response_.append(buf); +} + +void Response::Handle404(void) { + std::string t("text/html"); + + FillHeader(404, sizeof(NOT_FOUND_CONTENT), t); + response_.append(NOT_FOUND_CONTENT); +} + +void Response::Handle500(void) { + std::string t("text/html"); + + FillHeader(500, sizeof(SERVER_ERROR_CONTENT), t); + response_.append(SERVER_ERROR_CONTENT); +} + void Response::FindMime(char* ct_type, char* uri) { char *ext = strrchr(uri, '.'); @@ -58,61 +84,40 @@ void Response::FindMime(char* ct_type, char* uri) { void Response::FillHeader(int status, long len, std::string type) { char header[BUFSIZE]; - char status_text[40]; - std::string status_text; - std::stringstream stream; - std::string status_str; + char status_text[42]; + // std::string status_text; + // std::stringstream stream; + // std::string status_str; + // std::string len_str; + // stream << len; + // stream >> len_str; switch (status) { case 200: - stream << 200; - status_text.append("OK"); - //strcpy(status_text, "OK"); + // stream << 200; + // status_text.append("OK"); + strcpy(status_text, "OK"); break; case 404: - stream << 404; + // stream << 404; + // status_text.append("Not Found"); strcpy(status_text, "Not Found"); break; case 500: - stream << 500; + // stream << 500; + // status_text.append("Internal Server Error"); strcpy(status_text, "Internal Server Error"); break; default: break; } - //sprintf(header, HEADER_FORMAT, status, status_text, len, type.c_str()); - stream >> status_str; - std::string str = "HTTP/1.1" + status_str; - //%s\r\nContent-Length: %ld\nContent-Type: %s\r\n" + sprintf(header, HEADER_FORMAT, status, status_text, len, type.c_str()); + //stream >> status_str; + //response_ = "HTTP/1.1" + status_str + " " + status_str + "\r\nContent-Length: " \ + //+ len_str + "\nContent-Type: " + type + "\r\n"; response_ = std::string(header); } -void Response::Handle200(int ct_len, char* local_uri) { - char ct_type[40]; - int r; - int fd = open(local_uri, O_RDONLY); - char buf[BUFSIZE]; - - FindMime(ct_type, local_uri); - FillHeader(200, ct_len, ct_type); - while ((r = read(fd, buf, BUFSIZE)) > 0) - response_.append(buf); -} - -void Response::Handle404(void) { - std::string t("text/html"); - - FillHeader(404, sizeof(NOT_FOUND_CONTENT), t); - response_.append(NOT_FOUND_CONTENT); -} - -void Response::Handle500(void) { - std::string t("text/html"); - - FillHeader(500, sizeof(SERVER_ERROR_CONTENT), t); - response_.append(SERVER_ERROR_CONTENT); -} - std::string Response::GetResponse(void){ return response_; } diff --git a/jinhokim/src/Server.cpp b/jinhokim/src/Server.cpp index 2aff8aa..55ea3d9 100644 --- a/jinhokim/src/Server.cpp +++ b/jinhokim/src/Server.cpp @@ -137,7 +137,6 @@ int Server::Run(void) { Response response(buf); response.ResponseHandler(); std::string response_str = response.GetResponse(); - std::cout << "response: " << response_str << std::endl; send(curr_event->ident, response_str.c_str(), response_str.size(), 0); } //DisconnectClient(curr_event->ident, clients); diff --git a/jinhokim/test.cpp b/jinhokim/test.cpp index 37f4ecb..c6ca85a 100644 --- a/jinhokim/test.cpp +++ b/jinhokim/test.cpp @@ -1,6 +1,5 @@ #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" From 2105fdf356f7dc38f55bb6288cbd22b7ddd51e21 Mon Sep 17 00:00:00 2001 From: kimjinho1 Date: Tue, 17 Jan 2023 15:27:00 +0900 Subject: [PATCH 06/16] =?UTF-8?q?WIP:=20http=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jinhokim/img/200.png | Bin 0 -> 27012 bytes jinhokim/index2.html | 1 + 2 files changed, 1 insertion(+) create mode 100644 jinhokim/img/200.png diff --git a/jinhokim/img/200.png b/jinhokim/img/200.png new file mode 100644 index 0000000000000000000000000000000000000000..504bd4922ceef7540174315749284395faecdc25 GIT binary patch literal 27012 zcmeFXbyQqU*C*Pz1b5fQT{>7ufZ*OpBVIs4S9`faPKy?6DmrC*x>B2`5dMF0v40Dyx01N>SAJO$uj zVqs%p;$UNA~T0x~J3OA)`J+E@^d~mWZ-l^Eh_f9joP1R5FB^e3#>T@5O z-Z;IU$nuNbke`bJjwbHU zLCxf3{FmO&hYOwZv27P){X2Wd{Gq9(P;7*A5NbVbK5*`@v|relk4vXUX1zpX;f+Qq)kP!^!n_HJWK@pqctNhO4%6TO;9~!XZ!-BdTHHbrnc!g&a_I zRTpSrm+3HCDe0&o;&nOADxxvYCWKnYgApg2L7sY@&-(5U7TQ|&J|Z6ST(deknSY#B z$Gp^Y+j&8NOV|aZuA|G3LqS@Fvs{ROl;h6+kqWJj4(M$p3 z31?RmzhYYUq(%YJ9ut#`;0o9T0;s{qU+-je=w!CO?-y-bF%(M%MUNhd)el=RtF*Od zYN8TMsm?Y%^vhhcyV+|j9dJFF_}OZelflCx6GVam#T}vv$fy6uZX8ef*IXRlIoX=N za7w2)j#5co7+;bQ@;|M{ChJ=m$rP>SQ#+n|F&R)YtZ95Xqsdoqz(?221G17O=5?|j zJsHtiP&-m^u%h*{$@uty-p}ul-@l8p{=La7aYc1rFL=Y;QJi+VzL9c%#Y-ZnA zKQbB)ZRJa=hbD2tMmT78FV7KU{KB2i{Crh$WdT`v34VD|PNQ<>FXy>btu+olmO^KZ zZ6Km*N2h{@u8unClk?r5j#ejLxqk1=TVn+q z0!er_wc%UDqlb?{mVcnJS|xtCcP*w^Q!nZi*bU92kn^xX)5RoWi{Jw{9v_A5 zL^_YHIO@m0(0!@Nj$RU{`?6EkpmE1n<$RMa)ucQp!RI03x2#h)^Wbe-|D>nxw2^mN zlPm1FdsVV+GTG2i9)TbPpZ6#`NI%EdU*JBvJ&B>c5tt<*4IMo-loas zs=@{{Ls6Y>*>BzxKiADST0Y`Ac4*F)HFdjK+B!Z@1H)0@AS?i;7S&uR0Bo@d!!$c-|M8)>e zOY`nOOb~jw-(MY_Q>D+{#6+bp=;({LbMdRN_o@$b4x8oBo%g#RoBK4|x6B^2rm10s zCOHk+m&1A`^Cop?RTdXKYT=Rmneti79_h(myQMoekA7&JJTH3x!RUr&QhZ@#29)e4 z)jsC($;N-FbHE+UqG#n~n8YN5Kx{j{H2#MJ56P>8=hEDVBHerIt1~VRSvhGWpgns7 zN($hS%w@~R`0ZuN#paqfsh1mu)|J98*%

MTTU_pv=M-w#)UBIfxDQMFQh`AH?z6 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 M(O*RF5#P`JA5&5oXaE2J literal 0 HcmV?d00001 diff --git a/jinhokim/index2.html b/jinhokim/index2.html index dad0f12..df7be61 100644 --- a/jinhokim/index2.html +++ b/jinhokim/index2.html @@ -5,6 +5,7 @@

Webserv... +

From 1b78de3a0e822457dfad6a52944091d3a9302d0d Mon Sep 17 00:00:00 2001 From: jinhokim Date: Tue, 17 Jan 2023 17:03:15 +0900 Subject: [PATCH 07/16] =?UTF-8?q?REFACT:=20http=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jinhokim/404.html | 11 + jinhokim/500.html | 11 + jinhokim/Makefile | 4 +- jinhokim/css/animal-face.css | 553 ---------------------------------- jinhokim/css/dog-cat.css | 401 ------------------------ jinhokim/css/index.css | 401 ------------------------ jinhokim/img/200.png | Bin 27012 -> 0 bytes jinhokim/include/Response.hpp | 62 ++-- jinhokim/include/Server.hpp | 17 +- jinhokim/index.html | 260 +--------------- jinhokim/index2.html | 11 - jinhokim/src/Response.cpp | 58 ++-- jinhokim/src/Server.cpp | 10 +- jinhokim/test.cpp | 2 +- 14 files changed, 109 insertions(+), 1692 deletions(-) create mode 100644 jinhokim/404.html create mode 100644 jinhokim/500.html delete mode 100644 jinhokim/css/animal-face.css delete mode 100644 jinhokim/css/dog-cat.css delete mode 100644 jinhokim/css/index.css delete mode 100644 jinhokim/img/200.png delete mode 100644 jinhokim/index2.html diff --git a/jinhokim/404.html b/jinhokim/404.html new file mode 100644 index 0000000..f61c4a1 --- /dev/null +++ b/jinhokim/404.html @@ -0,0 +1,11 @@ + + + 404 Status Page + + +

+

404 Not Found

+ 404 cat +

+ + diff --git a/jinhokim/500.html b/jinhokim/500.html new file mode 100644 index 0000000..7d9b284 --- /dev/null +++ b/jinhokim/500.html @@ -0,0 +1,11 @@ + + + 500 Status Page + + +

+

500 Internal Server Error

+ 500 cat +

+ + diff --git a/jinhokim/Makefile b/jinhokim/Makefile index b1b169c..3c12f99 100644 --- a/jinhokim/Makefile +++ b/jinhokim/Makefile @@ -31,8 +31,8 @@ CLIENT := client # Define the rules all: - @$(MAKE) $(SERVER) - @$(MAKE) $(CLIENT) + @$(MAKE) -j $(SERVER) + @$(MAKE) -j $(CLIENT) $(SERVER) : $(SERVER_OBJS) @$(CXX) $(CXXFLAGS) $^ -o $@ diff --git a/jinhokim/css/animal-face.css b/jinhokim/css/animal-face.css deleted file mode 100644 index 55460e8..0000000 --- a/jinhokim/css/animal-face.css +++ /dev/null @@ -1,553 +0,0 @@ -body { - font-family: 'Jua', sans-serif; - background-color: #ffffff; - box-sizing: border-box; - color: #35465d; -} - -.file-upload { - padding: 15% 3%; - margin: 0 auto; - border-radius: 10px; - border: solid 1.5px #f6f7fa; - background-color: #f6f7fa; -} - -.file-upload-btn { - width: 100%; - margin: 0; - color: #fff; - background: #1FB264; - border: none; - padding: 10px; - border-radius: 4px; - border-bottom: 4px solid #15824B; - transition: all .2s ease; - outline: none; - text-transform: uppercase; - font-weight: 700; -} - -.file-upload-btn:hover { - background: #1AA059; - color: #ffffff; - transition: all .2s ease; - cursor: pointer; -} - -.file-upload-btn:active { - border: 0; - transition: all .2s ease; -} - -.file-upload-content { - display: none; - text-align: center; -} - -#loading { - display: none; -} - -.file-upload-input { - position: absolute; - margin: 0; - padding: 0; - width: 100%; - height: 100%; - outline: none; - opacity: 0; - cursor: pointer; -} - -.image-upload-wrap { - width: 60%; - margin: 0 auto; - position: relative; - object-fit: contain; - border-radius: 10px; - border: dashed 1.5px #35465d; - background-color: #ffffff; -} - -.image-dropping, -.image-upload-wrap:hover { - background-color: #35465d82; - border: 1.5px dashed #ffffff; -} - -.image-title-wrap { - padding: 0 15px 15px 15px; - color: #222; -} - -.drag-text { - text-align: center; -} - -.drag-text h3 { - font-weight: 500; - text-transform: uppercase; -} - -.file-upload-image { - max-height: 60%; - max-width: 60%; - margin: auto; - padding: 10px; -} - -.try-again-btn { - border-radius: 40px; - border: solid 1.5px #35465d; - background-color: #35465d; -} - -.try-again-text { - display: block; - font-size: 1.5rem; - text-align: center; - color: #ffffff; - padding: 4px 15px; -} - -.remove-image:hover { - background: #c13b2a; - color: #ffffff; - transition: all .2s ease; - cursor: pointer; -} - -.remove-image:active { - border: 0; - transition: all .2s ease; -} - -.navbar-light .navbar-brand { - line-height: 1.68; - text-align: left; - color: #35465d; -} - -.navbar-light .navbar-toggler { - border-color: #ffffff; -} - -.section { - margin-top: 50px; -} - -.title { - line-height: 1.67; - text-align: center; - color: #d8a3b9; -} - -.subtitle { - line-height: 1.53; - text-align: center; - color: #35465d; -} - - -/* On screens that are 600px or less, set the background color to olive */ - -@media screen and (max-width: 600px) { - html { - font-size: 10px; - } - .navbar-brand { - font-size: 2rem; - } -} - -.youtube-link { - font-size: 1.5rem; - line-height: 1.71; - text-align: center; - color: #f73737; - text-decoration: underline; -} - -.youtube-link:hover { - font-size: 1.5rem; - line-height: 1.71; - text-align: center; - color: #f73737; - text-decoration: underline; -} - -.youtube-icon { - width: 30px; - height: 27.262px; -} - -.upload { - width: 20%; -} - -.upload-text { - width: 58%; - font-size: 1.5rem; - line-height: 1.53; - text-align: center; - color: #35465d; - margin: 0 auto; -} - -footer { - text-align: center; - padding: 0px 0px; - /* background-color: #353535; */ - width: 100%; - height: auto; - position: relative; -} - -.footer_table { - color: rgb(58, 29, 29); - width: 30%; -} - -.footer_table .footer-txt { - font-weight: bold; - color: #747474; -} - -.footer_table img { - width: 23px; - height: 23px -} - -.footer_table img:hover { - opacity: 0.5; - cursor: pointer; -} - -.footer-logo { - width: 80; - height: 80; -} - -.email { - color: #A566FF; -} - -.copy { - font-weight: bold; - color: #747474; -} - -.bar-container { - height: 2.7rem; -} - -.dinosaur-box { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - width: 100%; - background-color: rgba(117, 204, 84, 0.2); -} - -.dinosaur-bar { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - background-color: rgba(117, 204, 84, 1); -} - -.deer-box { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - width: 100%; - background-color: rgba(117, 204, 84, 0.2); -} - -.deer-bar { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - background-color: rgba(117, 204, 84, 1); -} - -.dog-box { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - width: 100%; - background-color: rgba(27, 175, 234, 0.2); -} - -.dog-bar { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - background-color: rgba(27, 175, 234, 1); -} - -.cat-box { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - width: 100%; - background-color: rgba(251, 176, 59, 0.2); -} - -.cat-bar { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - background-color: rgba(251, 176, 59, 1); -} - -.bear-box { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - width: 100%; - background-color: rgba(195, 140, 102, 0.2); -} - -.bear-bar { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - background-color: rgba(195, 140, 102, 1); -} - -.fox-box { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - width: 100%; - background-color: rgba(49, 132, 155, 0.2); -} - -.fox-bar { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - background-color: rgba(49, 132, 155, 1); -} - -.rabbit-box { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - width: 100%; - background-color: rgba(235, 166, 190, 0.2); -} - -.rabbit-bar { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - background-color: rgba(235, 166, 190, 1); -} - -.percent-text { - font-size: 1rem; - color: #ffffff; -} - -.animal-label { - width: 20%; - text-align: left; - height: 2rem; -} - -#label-container { - width: 80%; - margin: 0 auto; -} - -.result-message { - font-size: 2rem; -} - -.animal-explain { - font-size: 1.5rem; -} - -.dinosaur-animal-title { - color: #3ba363; -} - -.dinosaur-animal-celeb { - color: #3ba363; - font-size: 1.2rem; -} - -.deer-animal-title { - color: #3ba363; -} - -.deer-animal-celeb { - color: #3ba363; - font-size: 1.2rem; -} - -.cat-animal-title { - color: #ff6c0a; -} - -.cat-animal-celeb { - color: #ff6c0a; - font-size: 1.2rem; -} - -.dog-animal-title { - color: #548dd4; -} - -.dog-animal-celeb { - color: #548dd4; - font-size: 1.2rem; -} - -.rabbit-animal-title { - color: #e56995; -} - -.rabbit-animal-celeb { - color: #e56995; - font-size: 1.2rem; -} - -.bear-animal-title { - color: #9b6b43; -} - -.bear-animal-celeb { - color: #9b6b43; - font-size: 1.2rem; -} - -.fox-animal-title { - color: #31849b; -} - -.fox-animal-celeb { - color: #31849b; - font-size: 1.2rem; -} - - -/* gender */ - -input#gender { - display: none; -} - -label { - cursor: pointer; - display: inline-block; - width: 90px; - height: 41px; - box-shadow: 0 0 0 8px #e5e9ea inset; - border-radius: 30px; - position: relative; -} - -.knob { - position: absolute; - width: 41px; - top: 0; - left: 0; - height: 41px; - border-radius: 50%; - box-shadow: 0 0 0 8px #f470a7 inset; - background-color: #fbd4e6; - transition: 0.3s; -} - -.knob>i { - position: absolute; - width: 4px; - height: 18px; - top: 100%; - background-color: #f470a7; - left: calc(50% - 2px); -} - -.knob>i:before, -.knob>i:after { - width: 6px; - position: absolute; - top: 42%; - content: ""; - height: 4px; - background-color: #f470a7; -} - -.knob>i:before { - left: 4px; -} - -.knob>i:after { - left: -6px; -} - -input:checked+label>.knob { - box-shadow: 0 0 0 8px #a1c6dd inset; - background-color: #e2ecf4; - transform: translateX(49px) rotate(-140deg); -} - -input:checked+label>.knob>i { - background-color: #a1c6dd; -} - -input:checked+label>.knob>i:after, -input:checked+label>.knob>i:before { - background-color: #a1c6dd; -} - -input:checked+label>.knob>i:after { - top: 14.5px; - width: 12px; - left: -7px; - transform: rotate(45deg); -} - -input:checked+label>.knob>i:before { - top: 13px; - width: 12px; - left: 0px; - transform: rotate(134deg); -} - -.ad-banner { - width: 320px; - margin: 0 auto; -} \ No newline at end of file diff --git a/jinhokim/css/dog-cat.css b/jinhokim/css/dog-cat.css deleted file mode 100644 index ded0f23..0000000 --- a/jinhokim/css/dog-cat.css +++ /dev/null @@ -1,401 +0,0 @@ -body { - font-family: 'Jua', sans-serif; - background-color: #ffffff; - box-sizing: border-box; - color: #35465d; -} - -.file-upload { - padding: 15% 3%; - margin: 0 auto; - border-radius: 10px; - border: solid 1.5px #f6f7fa; - background-color: #f6f7fa; -} - -.file-upload-btn { - width: 100%; - margin: 0; - color: #fff; - background: #1FB264; - border: none; - padding: 10px; - border-radius: 4px; - border-bottom: 4px solid #15824B; - transition: all .2s ease; - outline: none; - text-transform: uppercase; - font-weight: 700; -} - -.file-upload-btn:hover { - background: #1AA059; - color: #ffffff; - transition: all .2s ease; - cursor: pointer; -} - -.file-upload-btn:active { - border: 0; - transition: all .2s ease; -} - -.file-upload-content { - display: none; - text-align: center; -} - -#loading { - display: none; -} - -.file-upload-input { - position: absolute; - margin: 0; - padding: 0; - width: 100%; - height: 100%; - outline: none; - opacity: 0; - cursor: pointer; -} - -.image-upload-wrap { - width: 60%; - margin: 0 auto; - position: relative; - object-fit: contain; - border-radius: 10px; - border: dashed 1.5px #35465d; - background-color: #ffffff; -} - -.image-dropping, -.image-upload-wrap:hover { - background-color: #35465d82; - border: 1.5px dashed #ffffff; -} - -.image-title-wrap { - padding: 0 15px 15px 15px; - color: #222; -} - -.drag-text { - text-align: center; -} - -.drag-text h3 { - font-weight: 500; - text-transform: uppercase; -} - -.file-upload-image { - max-height: 60%; - max-width: 60%; - margin: auto; - padding: 10px; -} - -.try-again-btn { - border-radius: 40px; - border: solid 1.5px #35465d; - background-color: #35465d; -} - -.try-again-text { - display: block; - font-size: 1.5rem; - text-align: center; - color: #ffffff; - padding: 4px 15px; -} - -.remove-image:hover { - background: #c13b2a; - color: #ffffff; - transition: all .2s ease; - cursor: pointer; -} - -.remove-image:active { - border: 0; - transition: all .2s ease; -} - -.navbar-light .navbar-brand { - line-height: 1.68; - text-align: left; - color: #35465d; -} - -.navbar-light .navbar-toggler { - border-color: #ffffff; -} - -.section { - margin-top: 50px; -} - -.title { - line-height: 1.67; - text-align: center; - color: #a1c6dd; -} - -.subtitle { - line-height: 1.53; - text-align: center; - color: #35465d; -} - -@media screen and (max-width: 600px) { - html { - font-size: 10px; - } - .navbar-brand { - font-size: 2rem; - } -} - -.upload { - width: 20%; -} - -.upload-text { - width: 58%; - font-size: 1.5rem; - line-height: 1.53; - text-align: center; - color: #35465d; - margin: 0 auto; -} - - -/* .footer { - background-color: initial; - color: initial; -} */ - -footer { - text-align: center; - padding: 0px 0px; - /* background-color: #353535; */ - width: 100%; - height: auto; - position: relative; -} - -.footer_table { - color: rgb(58, 29, 29); - width: 30%; -} - -.footer_table .footer-txt { - font-weight: bold; - color: #747474; -} - -.footer_table img { - width: 23px; - height: 23px -} - -.footer_table img:hover { - opacity: 0.5; - cursor: pointer; -} - -.footer-logo { - width: 80; - height: 80; -} - -.email { - color: #A566FF; -} - -.copy { - font-weight: bold; - color: #747474; -} - -.bar-container { - height: 2.7rem; -} - -.Dog-box { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - width: 100%; - background-color: rgba(27, 175, 234, 0.2); -} - -.Dog-bar { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - background-color: rgba(27, 175, 234, 1); -} - -.Cat-box { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - width: 100%; - background-color: rgba(251, 176, 59, 0.2); -} - -.Cat-bar { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - background-color: rgba(251, 176, 59, 1); -} - -.Else-box { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - width: 100%; - background-color: rgba(235, 90, 210, 0.2); -} - -.Else-bar { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - background-color: rgba(235, 90, 210, 1); -} - -.percent-text { - font-size: 1rem; - color: #ffffff; -} - -.animal-label { - width: 20%; - text-align: left; - height: 2rem; -} - -#label-container { - width: 80%; - margin: 0 auto; -} - -.result-message { - font-size: 2rem; -} - -.animal-explain { - font-size: 1.5rem; -} - -.Dog-animal-title { - color: #548dd4; -} - -.Cat-animal-title { - color: #ff6c0a; -} - -.Else-animal-title { - color: #bb65ddf1; -} - -label { - cursor: pointer; - display: inline-block; - width: 90px; - height: 41px; - box-shadow: 0 0 0 8px #e5e9ea inset; - border-radius: 30px; - position: relative; -} - -.knob { - position: absolute; - width: 41px; - top: 0; - left: 0; - height: 41px; - border-radius: 50%; - box-shadow: 0 0 0 8px #f470a7 inset; - background-color: #fbd4e6; - transition: 0.3s; -} - -.knob>i { - position: absolute; - width: 4px; - height: 18px; - top: 100%; - background-color: #f470a7; - left: calc(50% - 2px); -} - -.knob>i:before, -.knob>i:after { - width: 6px; - position: absolute; - top: 42%; - content: ""; - height: 4px; - background-color: #f470a7; -} - -.knob>i:before { - left: 4px; -} - -.knob>i:after { - left: -6px; -} - -input:checked+label>.knob { - box-shadow: 0 0 0 8px #a1c6dd inset; - background-color: #e2ecf4; - transform: translateX(49px) rotate(-140deg); -} - -input:checked+label>.knob>i { - background-color: #a1c6dd; -} - -input:checked+label>.knob>i:after, -input:checked+label>.knob>i:before { - background-color: #a1c6dd; -} - -input:checked+label>.knob>i:after { - top: 14.5px; - width: 12px; - left: -7px; - transform: rotate(45deg); -} - -input:checked+label>.knob>i:before { - top: 13px; - width: 12px; - left: 0px; - transform: rotate(134deg); -} - -.ad-banner { - width: 320px; - margin: 0 auto; -} \ No newline at end of file diff --git a/jinhokim/css/index.css b/jinhokim/css/index.css deleted file mode 100644 index ded0f23..0000000 --- a/jinhokim/css/index.css +++ /dev/null @@ -1,401 +0,0 @@ -body { - font-family: 'Jua', sans-serif; - background-color: #ffffff; - box-sizing: border-box; - color: #35465d; -} - -.file-upload { - padding: 15% 3%; - margin: 0 auto; - border-radius: 10px; - border: solid 1.5px #f6f7fa; - background-color: #f6f7fa; -} - -.file-upload-btn { - width: 100%; - margin: 0; - color: #fff; - background: #1FB264; - border: none; - padding: 10px; - border-radius: 4px; - border-bottom: 4px solid #15824B; - transition: all .2s ease; - outline: none; - text-transform: uppercase; - font-weight: 700; -} - -.file-upload-btn:hover { - background: #1AA059; - color: #ffffff; - transition: all .2s ease; - cursor: pointer; -} - -.file-upload-btn:active { - border: 0; - transition: all .2s ease; -} - -.file-upload-content { - display: none; - text-align: center; -} - -#loading { - display: none; -} - -.file-upload-input { - position: absolute; - margin: 0; - padding: 0; - width: 100%; - height: 100%; - outline: none; - opacity: 0; - cursor: pointer; -} - -.image-upload-wrap { - width: 60%; - margin: 0 auto; - position: relative; - object-fit: contain; - border-radius: 10px; - border: dashed 1.5px #35465d; - background-color: #ffffff; -} - -.image-dropping, -.image-upload-wrap:hover { - background-color: #35465d82; - border: 1.5px dashed #ffffff; -} - -.image-title-wrap { - padding: 0 15px 15px 15px; - color: #222; -} - -.drag-text { - text-align: center; -} - -.drag-text h3 { - font-weight: 500; - text-transform: uppercase; -} - -.file-upload-image { - max-height: 60%; - max-width: 60%; - margin: auto; - padding: 10px; -} - -.try-again-btn { - border-radius: 40px; - border: solid 1.5px #35465d; - background-color: #35465d; -} - -.try-again-text { - display: block; - font-size: 1.5rem; - text-align: center; - color: #ffffff; - padding: 4px 15px; -} - -.remove-image:hover { - background: #c13b2a; - color: #ffffff; - transition: all .2s ease; - cursor: pointer; -} - -.remove-image:active { - border: 0; - transition: all .2s ease; -} - -.navbar-light .navbar-brand { - line-height: 1.68; - text-align: left; - color: #35465d; -} - -.navbar-light .navbar-toggler { - border-color: #ffffff; -} - -.section { - margin-top: 50px; -} - -.title { - line-height: 1.67; - text-align: center; - color: #a1c6dd; -} - -.subtitle { - line-height: 1.53; - text-align: center; - color: #35465d; -} - -@media screen and (max-width: 600px) { - html { - font-size: 10px; - } - .navbar-brand { - font-size: 2rem; - } -} - -.upload { - width: 20%; -} - -.upload-text { - width: 58%; - font-size: 1.5rem; - line-height: 1.53; - text-align: center; - color: #35465d; - margin: 0 auto; -} - - -/* .footer { - background-color: initial; - color: initial; -} */ - -footer { - text-align: center; - padding: 0px 0px; - /* background-color: #353535; */ - width: 100%; - height: auto; - position: relative; -} - -.footer_table { - color: rgb(58, 29, 29); - width: 30%; -} - -.footer_table .footer-txt { - font-weight: bold; - color: #747474; -} - -.footer_table img { - width: 23px; - height: 23px -} - -.footer_table img:hover { - opacity: 0.5; - cursor: pointer; -} - -.footer-logo { - width: 80; - height: 80; -} - -.email { - color: #A566FF; -} - -.copy { - font-weight: bold; - color: #747474; -} - -.bar-container { - height: 2.7rem; -} - -.Dog-box { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - width: 100%; - background-color: rgba(27, 175, 234, 0.2); -} - -.Dog-bar { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - background-color: rgba(27, 175, 234, 1); -} - -.Cat-box { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - width: 100%; - background-color: rgba(251, 176, 59, 0.2); -} - -.Cat-bar { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - background-color: rgba(251, 176, 59, 1); -} - -.Else-box { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - width: 100%; - background-color: rgba(235, 90, 210, 0.2); -} - -.Else-bar { - position: absolute; - top: 0; - left: 0; - height: 2rem; - border-radius: 10px; - background-color: rgba(235, 90, 210, 1); -} - -.percent-text { - font-size: 1rem; - color: #ffffff; -} - -.animal-label { - width: 20%; - text-align: left; - height: 2rem; -} - -#label-container { - width: 80%; - margin: 0 auto; -} - -.result-message { - font-size: 2rem; -} - -.animal-explain { - font-size: 1.5rem; -} - -.Dog-animal-title { - color: #548dd4; -} - -.Cat-animal-title { - color: #ff6c0a; -} - -.Else-animal-title { - color: #bb65ddf1; -} - -label { - cursor: pointer; - display: inline-block; - width: 90px; - height: 41px; - box-shadow: 0 0 0 8px #e5e9ea inset; - border-radius: 30px; - position: relative; -} - -.knob { - position: absolute; - width: 41px; - top: 0; - left: 0; - height: 41px; - border-radius: 50%; - box-shadow: 0 0 0 8px #f470a7 inset; - background-color: #fbd4e6; - transition: 0.3s; -} - -.knob>i { - position: absolute; - width: 4px; - height: 18px; - top: 100%; - background-color: #f470a7; - left: calc(50% - 2px); -} - -.knob>i:before, -.knob>i:after { - width: 6px; - position: absolute; - top: 42%; - content: ""; - height: 4px; - background-color: #f470a7; -} - -.knob>i:before { - left: 4px; -} - -.knob>i:after { - left: -6px; -} - -input:checked+label>.knob { - box-shadow: 0 0 0 8px #a1c6dd inset; - background-color: #e2ecf4; - transform: translateX(49px) rotate(-140deg); -} - -input:checked+label>.knob>i { - background-color: #a1c6dd; -} - -input:checked+label>.knob>i:after, -input:checked+label>.knob>i:before { - background-color: #a1c6dd; -} - -input:checked+label>.knob>i:after { - top: 14.5px; - width: 12px; - left: -7px; - transform: rotate(45deg); -} - -input:checked+label>.knob>i:before { - top: 13px; - width: 12px; - left: 0px; - transform: rotate(134deg); -} - -.ad-banner { - width: 320px; - margin: 0 auto; -} \ No newline at end of file diff --git a/jinhokim/img/200.png b/jinhokim/img/200.png deleted file mode 100644 index 504bd4922ceef7540174315749284395faecdc25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27012 zcmeFXbyQqU*C*Pz1b5fQT{>7ufZ*OpBVIs4S9`faPKy?6DmrC*x>B2`5dMF0v40Dyx01N>SAJO$uj zVqs%p;$UNA~T0x~J3OA)`J+E@^d~mWZ-l^Eh_f9joP1R5FB^e3#>T@5O z-Z;IU$nuNbke`bJjwbHU zLCxf3{FmO&hYOwZv27P){X2Wd{Gq9(P;7*A5NbVbK5*`@v|relk4vXUX1zpX;f+Qq)kP!^!n_HJWK@pqctNhO4%6TO;9~!XZ!-BdTHHbrnc!g&a_I zRTpSrm+3HCDe0&o;&nOADxxvYCWKnYgApg2L7sY@&-(5U7TQ|&J|Z6ST(deknSY#B z$Gp^Y+j&8NOV|aZuA|G3LqS@Fvs{ROl;h6+kqWJj4(M$p3 z31?RmzhYYUq(%YJ9ut#`;0o9T0;s{qU+-je=w!CO?-y-bF%(M%MUNhd)el=RtF*Od zYN8TMsm?Y%^vhhcyV+|j9dJFF_}OZelflCx6GVam#T}vv$fy6uZX8ef*IXRlIoX=N za7w2)j#5co7+;bQ@;|M{ChJ=m$rP>SQ#+n|F&R)YtZ95Xqsdoqz(?221G17O=5?|j zJsHtiP&-m^u%h*{$@uty-p}ul-@l8p{=La7aYc1rFL=Y;QJi+VzL9c%#Y-ZnA zKQbB)ZRJa=hbD2tMmT78FV7KU{KB2i{Crh$WdT`v34VD|PNQ<>FXy>btu+olmO^KZ zZ6Km*N2h{@u8unClk?r5j#ejLxqk1=TVn+q z0!er_wc%UDqlb?{mVcnJS|xtCcP*w^Q!nZi*bU92kn^xX)5RoWi{Jw{9v_A5 zL^_YHIO@m0(0!@Nj$RU{`?6EkpmE1n<$RMa)ucQp!RI03x2#h)^Wbe-|D>nxw2^mN zlPm1FdsVV+GTG2i9)TbPpZ6#`NI%EdU*JBvJ&B>c5tt<*4IMo-loas zs=@{{Ls6Y>*>BzxKiADST0Y`Ac4*F)HFdjK+B!Z@1H)0@AS?i;7S&uR0Bo@d!!$c-|M8)>e zOY`nOOb~jw-(MY_Q>D+{#6+bp=;({LbMdRN_o@$b4x8oBo%g#RoBK4|x6B^2rm10s zCOHk+m&1A`^Cop?RTdXKYT=Rmneti79_h(myQMoekA7&JJTH3x!RUr&QhZ@#29)e4 z)jsC($;N-FbHE+UqG#n~n8YN5Kx{j{H2#MJ56P>8=hEDVBHerIt1~VRSvhGWpgns7 zN($hS%w@~R`0ZuN#paqfsh1mu)|J98*%

MTTU_pv=M-w#)UBIfxDQMFQh`AH?z6 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 M(O*RF5#P`JA5&5oXaE2J diff --git a/jinhokim/include/Response.hpp b/jinhokim/include/Response.hpp index 15ff853..7393c26 100644 --- a/jinhokim/include/Response.hpp +++ b/jinhokim/include/Response.hpp @@ -1,37 +1,33 @@ #ifndef RESPONSE_HPP -# define RESPONSE_HPP - -# include -# include -# include - -# include -# include - -# include -# include - -# define BUFSIZE 1024 -# define HEADER_FORMAT "HTTP/1.1 %d %s\r\nContent-Length: %ld\nContent-Type: %s\r\n\r\n" -# define NOT_FOUND_CONTENT "

404 Not Found

\n" -# define SERVER_ERROR_CONTENT "

500 Internal Server Error

\n" - -class Response { - public: - Response(char* request); - ~Response(void); - - void ResponseHandler(void); - void FindMime(char* ct_type, char* uri); - void FillHeader(int status, long len, std::string type); - void Handle200(int ct_len, char* local_uri); - void Handle404(void); - void Handle500(void); - - std::string GetResponse(void); - private: - std::string request_; - std::string response_; +#define RESPONSE_HPP + +#include +#include +#include +#include +#include + +#define BUFSIZE 10240 +#define HEADER_FORMAT "HTTP/1.1 %d %s\r\nContent-Length: %ld\nContent-Type: %s\r\n\r\n" + +class Response +{ +public: + Response(char *request); + ~Response(void); + + void ResponseHandler(void); + void FindMime(char *ct_type, char *uri); + void FillHeader(int status, long len, std::string type); + void Handle200(int ct_len, char *local_uri); + void Handle404(void); + void Handle500(void); + + 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 15abd5c..ff17de6 100644 --- a/jinhokim/include/Server.hpp +++ b/jinhokim/include/Server.hpp @@ -17,23 +17,22 @@ # define BACKLOG 1024 - class Server { public: Server(int port); virtual ~Server(void); - std::string GetIp(void); - void ChangeEvents(std::vector& change_list, int socket, int16_t filter, + 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); + void DisconnectClient(int client_fd, std::map& clients); - int Set(void); - int Run(void); + int Set(void); + int Run(void); private: - int port_; - int server_fd_; - sockaddr_in address_; + int port_; + int server_fd_; + sockaddr_in address_; }; void CheckArgument(int ac, char** av); diff --git a/jinhokim/index.html b/jinhokim/index.html index d320b3d..2c49f00 100644 --- a/jinhokim/index.html +++ b/jinhokim/index.html @@ -1,249 +1,11 @@ - - - - - - - - - - 진호의 실험실 - - - - - - - - - - - - - - - - - - - - -
-

강아지 고양이 분류기

-

웹 공부하는 겸 만들어본 사이트!

-

나는 어떤 동물과 닮았을까? 나의 동물상 찾기를 해보세요!

-

대표 동물상 연예인 사진 데이터로 학습한 인공지능이 나의 얼굴로 동물상과 특징을 찾아드립니다.

-

본 서비스는 Google의 인공지능 teachable machine 2.0을 활용하였습니다.

-

얼굴로 보는 인공지능 동물상 테스트, 나와 닮은 동물상을 찾아보세요! 대표 동물상 연예인 사진 데이터로 학습한 인공지능이 나의 얼굴로 동물상과 특징을 찾아드립니다. 회원가입도 필요없이 화면에서 바로 확인해보세요! 사진 데이터는 그 어디에도 전송되지 않습니다. 인공지능이 보는 나의 동물상 테스트 한번 해보세요! 강아지상? 고양이상? 여우상? 사슴상? 토끼상? 곰상? 공룡상? 얼굴상 테스트를 통해 나와 닮은 동물 찾기를 할 수 있습니다.

-
- -

강아지와 고양이 중 더 좋아하는 동물의 사진을 올려보세요!

- - -
-
- -
- -

강아지, 고양이 사진을 올려놓거나 눌러서 업로드하세요!

-
-
-
- your image -
-
- Loading... -
-

AI가 당신의 사진을 분류 중입니다.

-
-

-
-
- -
- -
-
-
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - - - - - - \ No newline at end of file + + + 200 Status Page + + +

+

Webserv...


+ 200 cat +

+ + diff --git a/jinhokim/index2.html b/jinhokim/index2.html deleted file mode 100644 index df7be61..0000000 --- a/jinhokim/index2.html +++ /dev/null @@ -1,11 +0,0 @@ - - - Test Page - - -

- Webserv... - -

- - diff --git a/jinhokim/src/Response.cpp b/jinhokim/src/Response.cpp index 2958abd..33e4f60 100644 --- a/jinhokim/src/Response.cpp +++ b/jinhokim/src/Response.cpp @@ -4,7 +4,7 @@ Response::Response(char* request) : request_(std::string(request)) {} Response::~Response(void) {} -void Response::ResponseHandler(void) { +void Response::ResponseHandler(void) { if (request_.size() == 0) { std::cerr << "[ERROR] Failed to read request" << std::endl; Handle500(); @@ -40,8 +40,8 @@ void Response::ResponseHandler(void) { return ; } -void Response::Handle200(int ct_len, char* local_uri) { - char ct_type[40900]; +void Response::Handle200(int ct_len, char* local_uri) { + char ct_type[40]; int r; int fd = open(local_uri, O_RDONLY); char buf[BUFSIZE]; @@ -52,25 +52,41 @@ void Response::Handle200(int ct_len, char* local_uri) { response_.append(buf); } -void Response::Handle404(void) { - std::string t("text/html"); +void Response::Handle404(void) { + char uri[9] = "404.html"; + struct stat st; + char ct_type[40]; + int r; + char buf[BUFSIZE]; + int fd = open(uri, O_RDONLY); - FillHeader(404, sizeof(NOT_FOUND_CONTENT), t); - response_.append(NOT_FOUND_CONTENT); + stat(uri, &st); + FindMime(ct_type, uri); + FillHeader(404, st.st_size, ct_type); + while ((r = read(fd, buf, BUFSIZE)) > 0) + response_.append(buf); } -void Response::Handle500(void) { - std::string t("text/html"); +void Response::Handle500(void) { + char uri[9] = "500.html"; + struct stat st; + char ct_type[40]; + int r; + char buf[BUFSIZE]; + int fd = open(uri, O_RDONLY); - FillHeader(500, sizeof(SERVER_ERROR_CONTENT), t); - response_.append(SERVER_ERROR_CONTENT); + stat(uri, &st); + FindMime(ct_type, uri); + FillHeader(500, st.st_size, ct_type); + while ((r = read(fd, buf, BUFSIZE)) > 0) + response_.append(buf); } -void Response::FindMime(char* ct_type, char* uri) { +void Response::FindMime(char* ct_type, char* uri) { char *ext = strrchr(uri, '.'); if (!strcmp(ext, ".html")) - strcpy(ct_type, "text/html"); + strcpy(ct_type, "text/html"); else if (!strcmp(ext, ".jpg") || !strcmp(ext, ".jpeg")) strcpy(ct_type, "image/jpeg"); else if (!strcmp(ext, ".png")) @@ -82,30 +98,18 @@ void Response::FindMime(char* ct_type, char* uri) { else strcpy(ct_type, "text/plain"); } -void Response::FillHeader(int status, long len, std::string type) { +void Response::FillHeader(int status, long len, std::string type) { char header[BUFSIZE]; char status_text[42]; - // std::string status_text; - // std::stringstream stream; - // std::string status_str; - // std::string len_str; - // stream << len; - // stream >> len_str; switch (status) { case 200: - // stream << 200; - // status_text.append("OK"); strcpy(status_text, "OK"); break; case 404: - // stream << 404; - // status_text.append("Not Found"); strcpy(status_text, "Not Found"); break; case 500: - // stream << 500; - // status_text.append("Internal Server Error"); strcpy(status_text, "Internal Server Error"); break; default: @@ -113,7 +117,7 @@ void Response::FillHeader(int status, long len, std::string type) { } sprintf(header, HEADER_FORMAT, status, status_text, len, type.c_str()); //stream >> status_str; - //response_ = "HTTP/1.1" + status_str + " " + status_str + "\r\nContent-Length: " \ + //response_ = "HTTP/1.1" + status_str + " " + status_text + "\r\nContent-Length: " \ //+ len_str + "\nContent-Type: " + type + "\r\n"; response_ = std::string(header); } diff --git a/jinhokim/src/Server.cpp b/jinhokim/src/Server.cpp index 55ea3d9..c7c0002 100644 --- a/jinhokim/src/Server.cpp +++ b/jinhokim/src/Server.cpp @@ -36,7 +36,7 @@ std::string Server::GetIp(void) { return ip + ":" + port; } -void Server::ChangeEvents(std::vector& change_list, int socket, int16_t filter, +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; @@ -44,13 +44,13 @@ void Server::ChangeEvents(std::vector& change_list, int socket change_list.push_back(temp_event); } -void Server::DisconnectClient(int client_fd, std::map& clients) { +void Server::DisconnectClient(int client_fd, std::map& clients) { std::cerr << "Client disconnected" << std::endl; close(client_fd); clients.erase(client_fd); } -int Server::Set(void) { +int Server::Set(void) { server_fd_ = socket(AF_INET, SOCK_STREAM, 0); if (server_fd_ < 0) throw std::runtime_error("Failed to create socket"); @@ -150,7 +150,7 @@ int Server::Run(void) { return 0; } -void CheckArgument(int ac, char** av) { +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++) { @@ -159,7 +159,7 @@ void CheckArgument(int ac, char** av) { } } -int PrintError(const std::string str) { +int PrintError(const std::string str) { std::cerr << str << std::endl; return 1; } diff --git a/jinhokim/test.cpp b/jinhokim/test.cpp index c6ca85a..b7539f4 100644 --- a/jinhokim/test.cpp +++ b/jinhokim/test.cpp @@ -96,7 +96,7 @@ void http_handler(int asock) { strcpy(safe_uri, uri); if (!strcmp(safe_uri, "/")) - strcpy(safe_uri, "/index.html"); + strcpy(safe_uri, "/index2.html"); local_uri = safe_uri + 1; if (stat(local_uri, &st) < 0) { From 53e0db15defb5450f09439c504179b38e8e699b8 Mon Sep 17 00:00:00 2001 From: kimjinho1 Date: Tue, 17 Jan 2023 17:41:40 +0900 Subject: [PATCH 08/16] =?UTF-8?q?REFACT:=20http=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jinhokim/parse.cpp | 14 ++++++++++++++ jinhokim/src/Response.cpp | 21 ++++++++++++++------- 2 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 jinhokim/parse.cpp diff --git a/jinhokim/parse.cpp b/jinhokim/parse.cpp new file mode 100644 index 0000000..23bbfc0 --- /dev/null +++ b/jinhokim/parse.cpp @@ -0,0 +1,14 @@ +#include + +int main(void) { + std::string request_("GET / Moved Permanently"); + + std::size_t method_end_idx = request_.find_first_of(" "); + std::cout << "method_end_idx: " << method_end_idx << std::endl; + std::string method = request_.substr(0, method_end_idx); + std::size_t uri_end_idx = request_.find_first_of(" ", method_end_idx + 1); + std::cout << "uri_end_idx: " << uri_end_idx << std::endl; + std::string uri = request_.substr(method_end_idx + 1, uri_end_idx - method_end_idx - 1); + + std::cout << "[INFO] Request: method=" << method << ", URI=" << uri << std::endl; +} \ No newline at end of file diff --git a/jinhokim/src/Response.cpp b/jinhokim/src/Response.cpp index 33e4f60..f2942ac 100644 --- a/jinhokim/src/Response.cpp +++ b/jinhokim/src/Response.cpp @@ -11,15 +11,22 @@ void Response::ResponseHandler(void) { return; } - char* method = strtok(const_cast(request_.c_str()), " "); - char* uri = strtok(NULL, " "); - if (method == NULL || uri == NULL) { + std::size_t method_end_idx = request_.find_first_of(" "); + std::cout << "method_end_idx: " << method_end_idx << std::endl; + std::string method = request_.substr(0, method_end_idx); + std::size_t uri_end_idx = request_.find_first_of(" ", method_end_idx + 1); + std::cout << "uri_end_idx: " << uri_end_idx << std::endl; + std::string uri = request_.substr(method_end_idx + 1, uri_end_idx - method_end_idx - 1); + + // char* method = strtok(const_cast(request_.c_str()), " "); + // char* uri = strtok(NULL, " "); + if (method.size() == 0 || uri.size() == 0) { std::cerr << "[ERROR] Failed to identify method, URI" << std::endl; Handle500(); return; } - printf("[INFO] Handling Request: method=%s, URI=%s\n", method, uri); + std::cout << "[INFO] Request: method=" << method << ", URI=" << uri << std::endl; char safe_uri[BUFSIZE]; char* local_uri; @@ -31,7 +38,7 @@ void Response::ResponseHandler(void) { local_uri = safe_uri + 1; if (stat(local_uri, &st) < 0) { - perror("[WARN] No file found matching URI.\n"); + std::cerr << "[WARN] No file found matching URI" << std::endl; Handle404(); return ; } @@ -116,8 +123,8 @@ void Response::FillHeader(int status, long len, std::string type) { break; } sprintf(header, HEADER_FORMAT, status, status_text, len, type.c_str()); - //stream >> status_str; - //response_ = "HTTP/1.1" + status_str + " " + status_text + "\r\nContent-Length: " \ + // stream >> status_str; + // response_ = "HTTP/1.1" + status_str + " " + status_text + "\r\nContent-Length: " \ //+ len_str + "\nContent-Type: " + type + "\r\n"; response_ = std::string(header); } From 4d79f03d13e7bcab9ac634ae8b5d01411f5c0a5e Mon Sep 17 00:00:00 2001 From: kimjinho1 Date: Tue, 17 Jan 2023 19:27:26 +0900 Subject: [PATCH 09/16] REFACT: CPP Google format refactoring --- jinhokim/include/Client.hpp | 42 ++--- jinhokim/include/Response.hpp | 47 +++--- jinhokim/include/Server.hpp | 65 ++++---- jinhokim/parse.cpp | 18 ++- jinhokim/src/Client.cpp | 93 +++++------ jinhokim/src/Response.cpp | 226 +++++++++++++------------- jinhokim/src/Server.cpp | 223 +++++++++++++------------- jinhokim/src/client_main.cpp | 21 ++- jinhokim/src/server_main.cpp | 23 ++- jinhokim/test.cpp | 294 ++++++++++++++++++---------------- 10 files changed, 532 insertions(+), 520 deletions(-) diff --git a/jinhokim/include/Client.hpp b/jinhokim/include/Client.hpp index fcfbe42..555242d 100644 --- a/jinhokim/include/Client.hpp +++ b/jinhokim/include/Client.hpp @@ -1,29 +1,31 @@ #ifndef CLIENT_HPP -# define CLIENT_HPP +#define CLIENT_HPP -# include -# include -# include -# 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); +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 index 7393c26..d198c1f 100644 --- a/jinhokim/include/Response.hpp +++ b/jinhokim/include/Response.hpp @@ -1,33 +1,34 @@ #ifndef RESPONSE_HPP #define RESPONSE_HPP -#include -#include #include #include +#include #include +#include + #define BUFSIZE 10240 -#define HEADER_FORMAT "HTTP/1.1 %d %s\r\nContent-Length: %ld\nContent-Type: %s\r\n\r\n" - -class Response -{ -public: - Response(char *request); - ~Response(void); - - void ResponseHandler(void); - void FindMime(char *ct_type, char *uri); - void FillHeader(int status, long len, std::string type); - void Handle200(int ct_len, char *local_uri); - void Handle404(void); - void Handle500(void); - - std::string GetResponse(void); - -private: - std::string request_; - std::string response_; +#define HEADER_FORMAT \ + "HTTP/1.1 %d %s\r\nContent-Length: %ld\nContent-Type: %s\r\n\r\n" + +class Response { + public: + Response(char *request); + ~Response(void); + + void ResponseHandler(void); + void FindMime(char *ct_type, char *uri); + void FillHeader(int status, long len, std::string type); + void Handle200(int ct_len, char *local_uri); + void Handle404(void); + void Handle500(void); + + std::string GetResponse(void); + + private: + std::string request_; + std::string response_; }; -#endif // RESPONSE_HPP +#endif // RESPONSE_HPP diff --git a/jinhokim/include/Server.hpp b/jinhokim/include/Server.hpp index ff17de6..dbea027 100644 --- a/jinhokim/include/Server.hpp +++ b/jinhokim/include/Server.hpp @@ -1,41 +1,44 @@ #ifndef SERVER_HPP -# define SERVER_HPP +#define SERVER_HPP -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include -# include +#include +#include +#include +#include +#include +#include +#include +#include -# include "Response.hpp" +#include +#include +#include -# define BACKLOG 1024 +#include "Response.hpp" + +#define BACKLOG 1024 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); - int Run(void); - private: - int port_; - int server_fd_; - sockaddr_in address_; + 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); + int Run(void); + + private: + int port_; + int server_fd_; + sockaddr_in address_; }; -void CheckArgument(int ac, char** av); -int PrintError(const std::string str); +void CheckArgument(int ac, char** av); +int PrintError(const std::string str); #endif // SERVER_HPP diff --git a/jinhokim/parse.cpp b/jinhokim/parse.cpp index 23bbfc0..f911905 100644 --- a/jinhokim/parse.cpp +++ b/jinhokim/parse.cpp @@ -1,14 +1,16 @@ #include int main(void) { - std::string request_("GET / Moved Permanently"); + std::string request_("GET / Moved Permanently"); - std::size_t method_end_idx = request_.find_first_of(" "); - std::cout << "method_end_idx: " << method_end_idx << std::endl; - std::string method = request_.substr(0, method_end_idx); - std::size_t uri_end_idx = request_.find_first_of(" ", method_end_idx + 1); - std::cout << "uri_end_idx: " << uri_end_idx << std::endl; - std::string uri = request_.substr(method_end_idx + 1, uri_end_idx - method_end_idx - 1); + std::size_t method_end_idx = request_.find_first_of(" "); + std::cout << "method_end_idx: " << method_end_idx << std::endl; + std::string method = request_.substr(0, method_end_idx); + std::size_t uri_end_idx = request_.find_first_of(" ", method_end_idx + 1); + std::cout << "uri_end_idx: " << uri_end_idx << std::endl; + std::string uri = + request_.substr(method_end_idx + 1, uri_end_idx - method_end_idx - 1); - std::cout << "[INFO] Request: method=" << method << ", URI=" << uri << std::endl; + std::cout << "[INFO] Request: method=" << method << ", URI=" << uri + << std::endl; } \ No newline at end of file diff --git a/jinhokim/src/Client.cpp b/jinhokim/src/Client.cpp index e50e104..9c6b043 100644 --- a/jinhokim/src/Client.cpp +++ b/jinhokim/src/Client.cpp @@ -25,66 +25,61 @@ EX) 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) - throw std::runtime_error("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) - throw std::runtime_error("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) { - std::string message; - std::getline(std::cin, message); - if (std::cin.eof() || !message.compare("exit")) { - std::cout << "Client Bye!" << std::endl; - break ; - } - - 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; +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"); - } + 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 index f2942ac..d607110 100644 --- a/jinhokim/src/Response.cpp +++ b/jinhokim/src/Response.cpp @@ -4,131 +4,129 @@ 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::cout << "method_end_idx: " << method_end_idx << std::endl; - std::string method = request_.substr(0, method_end_idx); - std::size_t uri_end_idx = request_.find_first_of(" ", method_end_idx + 1); - std::cout << "uri_end_idx: " << uri_end_idx << std::endl; - std::string uri = request_.substr(method_end_idx + 1, uri_end_idx - method_end_idx - 1); - - // char* method = strtok(const_cast(request_.c_str()), " "); - // char* uri = strtok(NULL, " "); - 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; - - char safe_uri[BUFSIZE]; - 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) { - std::cerr << "[WARN] No file found matching URI" << std::endl; - Handle404(); - return ; - } - - Handle200(st.st_size, local_uri); - return ; +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::cout << "method_end_idx: " << method_end_idx << std::endl; + std::string method = request_.substr(0, method_end_idx); + std::size_t uri_end_idx = request_.find_first_of(" ", method_end_idx + 1); + std::cout << "uri_end_idx: " << uri_end_idx << std::endl; + std::string uri = + request_.substr(method_end_idx + 1, uri_end_idx - method_end_idx - 1); + + // char* method = strtok(const_cast(request_.c_str()), " "); + // char* uri = strtok(NULL, " "); + 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; + + char safe_uri[BUFSIZE]; + char* local_uri; + struct stat st; + + strcpy(safe_uri, uri.c_str()); + if (!strcmp(safe_uri, "/")) strcpy(safe_uri, "/index.html"); + + local_uri = safe_uri + 1; + if (stat(local_uri, &st) < 0) { + std::cerr << "[WARN] No file found matching URI" << std::endl; + Handle404(); + return; + } + + Handle200(st.st_size, local_uri); + return; } -void Response::Handle200(int ct_len, char* local_uri) { - char ct_type[40]; - int r; - int fd = open(local_uri, O_RDONLY); - char buf[BUFSIZE]; +void Response::Handle200(int ct_len, char* local_uri) { + char ct_type[40]; + int r; + int fd = open(local_uri, O_RDONLY); + char buf[BUFSIZE]; - FindMime(ct_type, local_uri); - FillHeader(200, ct_len, ct_type); - while ((r = read(fd, buf, BUFSIZE)) > 0) - response_.append(buf); + FindMime(ct_type, local_uri); + FillHeader(200, ct_len, ct_type); + while ((r = read(fd, buf, BUFSIZE)) > 0) response_.append(buf); } -void Response::Handle404(void) { - char uri[9] = "404.html"; - struct stat st; - char ct_type[40]; - int r; - char buf[BUFSIZE]; - int fd = open(uri, O_RDONLY); - - stat(uri, &st); - FindMime(ct_type, uri); - FillHeader(404, st.st_size, ct_type); - while ((r = read(fd, buf, BUFSIZE)) > 0) - response_.append(buf); +void Response::Handle404(void) { + char uri[9] = "404.html"; + struct stat st; + char ct_type[40]; + int r; + char buf[BUFSIZE]; + int fd = open(uri, O_RDONLY); + + stat(uri, &st); + FindMime(ct_type, uri); + FillHeader(404, st.st_size, ct_type); + while ((r = read(fd, buf, BUFSIZE)) > 0) response_.append(buf); } -void Response::Handle500(void) { - char uri[9] = "500.html"; - struct stat st; - char ct_type[40]; - int r; - char buf[BUFSIZE]; - int fd = open(uri, O_RDONLY); - - stat(uri, &st); - FindMime(ct_type, uri); - FillHeader(500, st.st_size, ct_type); - while ((r = read(fd, buf, BUFSIZE)) > 0) - response_.append(buf); +void Response::Handle500(void) { + char uri[9] = "500.html"; + struct stat st; + char ct_type[40]; + int r; + char buf[BUFSIZE]; + int fd = open(uri, O_RDONLY); + + stat(uri, &st); + FindMime(ct_type, uri); + FillHeader(500, st.st_size, ct_type); + while ((r = read(fd, buf, BUFSIZE)) > 0) response_.append(buf); } -void Response::FindMime(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 Response::FindMime(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 Response::FillHeader(int status, long len, std::string type) { - char header[BUFSIZE]; - char status_text[42]; - - switch (status) { - case 200: - strcpy(status_text, "OK"); - break; - case 404: - strcpy(status_text, "Not Found"); - break; - case 500: - strcpy(status_text, "Internal Server Error"); - break; - default: - break; - } - sprintf(header, HEADER_FORMAT, status, status_text, len, type.c_str()); - // stream >> status_str; - // response_ = "HTTP/1.1" + status_str + " " + status_text + "\r\nContent-Length: " \ +void Response::FillHeader(int status, long len, std::string type) { + char header[BUFSIZE]; + char status_text[42]; + + switch (status) { + case 200: + strcpy(status_text, "OK"); + break; + case 404: + strcpy(status_text, "Not Found"); + break; + case 500: + strcpy(status_text, "Internal Server Error"); + break; + default: + break; + } + sprintf(header, HEADER_FORMAT, status, status_text, len, type.c_str()); + // stream >> status_str; + // response_ = "HTTP/1.1" + status_str + " " + status_text + + // "\r\nContent-Length: " \ //+ len_str + "\nContent-Type: " + type + "\r\n"; - response_ = std::string(header); + response_ = std::string(header); } -std::string Response::GetResponse(void){ - return response_; -} +std::string Response::GetResponse(void) { return response_; } diff --git a/jinhokim/src/Server.cpp b/jinhokim/src/Server.cpp index c7c0002..9318dd0 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) 기반으로 커널 딴에서 처리함 @@ -30,136 +30,135 @@ 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; +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; +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; - EV_SET(&temp_event, socket, filter, flags, fflags, data, udata); - change_list.push_back(temp_event); + EV_SET(&temp_event, socket, filter, flags, fflags, data, udata); + change_list.push_back(temp_event); } -void Server::DisconnectClient(int client_fd, std::map& clients) { - std::cerr << "Client disconnected" << std::endl; - close(client_fd); - clients.erase(client_fd); +void Server::DisconnectClient(int client_fd, + std::map& clients) { + 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) - throw std::runtime_error("Failed to create socket"); +int Server::Set(void) { + server_fd_ = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd_ < 0) throw std::runtime_error("Failed to create socket"); - address_.sin_family = AF_INET; - address_.sin_port = htons(port_); - address_.sin_addr.s_addr = inet_addr("127.0.0.1"); + address_.sin_family = AF_INET; + address_.sin_port = htons(port_); + address_.sin_addr.s_addr = inet_addr("127.0.0.1"); - int optval = 1; - setsockopt(server_fd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + int optval = 1; + setsockopt(server_fd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); - int bind_result = bind(server_fd_, (sockaddr*)&address_, sizeof(address_)); - if (bind_result < 0) - throw std::runtime_error("Failed to bind socket to address"); + int bind_result = bind(server_fd_, (sockaddr*)&address_, sizeof(address_)); + if (bind_result < 0) + throw std::runtime_error("Failed to bind socket to address"); - int listen_result = listen(server_fd_, BACKLOG); - if (listen_result < 0) - throw std::runtime_error("Failed to listen on socket"); + int listen_result = listen(server_fd_, BACKLOG); + if (listen_result < 0) throw std::runtime_error("Failed to listen on socket"); - return 0; + return 0; } int Server::Run(void) { - int kq = kqueue(); - if (kq == -1) - throw std::runtime_error("Failed to init kqueue"); - - std::map clients; - std::vector change_list; - struct kevent event_list[128]; - - ChangeEvents(change_list, server_fd_, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL); - - int new_events = 1; - struct kevent* curr_event; - - 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()"); - - change_list.clear(); - - for (int i = 0; i < new_events; ++i) { - curr_event = &event_list[i]; - - if (curr_event->flags & EV_ERROR) { - if (curr_event->ident == static_cast(server_fd_)) - throw std::runtime_error("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"); - - fcntl(client_fd, F_SETFL, O_NONBLOCK); - - std::cout << "Accepted connection from " << GetIp() << std::endl; - - 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()) { - char buf[BUFSIZE]; - 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; - std::cout << "request from " << GetIp() << ": " << buf << std::endl; - - std::map::iterator it = clients.find(curr_event->ident); - if (it != clients.end()) { - if (clients[curr_event->ident] != "") { - Response response(buf); - response.ResponseHandler(); - std::string response_str = response.GetResponse(); - send(curr_event->ident, response_str.c_str(), response_str.size(), 0); - } - //DisconnectClient(curr_event->ident, clients); - //clients[curr_event->ident].clear(); - } - } - } + int kq = kqueue(); + if (kq == -1) throw std::runtime_error("Failed to init kqueue"); + + std::map clients; + std::vector change_list; + struct kevent event_list[128]; + + ChangeEvents(change_list, server_fd_, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, + NULL); + + int new_events = 1; + struct kevent* curr_event; + + 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()"); + + change_list.clear(); + + for (int i = 0; i < new_events; ++i) { + curr_event = &event_list[i]; + + if (curr_event->flags & EV_ERROR) { + if (curr_event->ident == static_cast(server_fd_)) + throw std::runtime_error("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"); + + fcntl(client_fd, F_SETFL, O_NONBLOCK); + + std::cout << "Accepted connection from " << GetIp() << std::endl; + + 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()) { + char buf[BUFSIZE]; + 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; + std::cout << "request from " << GetIp() << ": " << buf << std::endl; + + std::map::iterator it = + clients.find(curr_event->ident); + if (it != clients.end()) { + if (clients[curr_event->ident] != "") { + Response response(buf); + response.ResponseHandler(); + std::string response_str = response.GetResponse(); + send(curr_event->ident, response_str.c_str(), + response_str.size(), 0); + } + // DisconnectClient(curr_event->ident, clients); + // clients[curr_event->ident].clear(); } + } } + } } - return 0; + } + 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"); - } +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; -} +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 19a6dd0..7e104ad 100644 --- a/jinhokim/src/client_main.cpp +++ b/jinhokim/src/client_main.cpp @@ -1,17 +1,16 @@ #include "../include/Client.hpp" int main(int ac, char** av) { - try { - CheckArgument(ac, av); + try { + CheckArgument(ac, av); - Client client(atoi(av[1])); + Client client(atoi(av[1])); - client.Set(); - client.Run(); - } - catch(const std::exception& e) { - std::cerr << e.what() << std::endl; - 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 13e475e..59958eb 100644 --- a/jinhokim/src/server_main.cpp +++ b/jinhokim/src/server_main.cpp @@ -1,17 +1,16 @@ #include "../include/Server.hpp" -int main(int ac, char **av) { - try { - CheckArgument(ac, av); +int main(int ac, char** av) { + try { + CheckArgument(ac, av); - Server server(atoi(av[1])); + Server server(atoi(av[1])); - server.Set(); - server.Run(); - } - catch(const std::exception& e) { - std::cerr << e.what() << std::endl; - 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 b7539f4..7531c37 100644 --- a/jinhokim/test.cpp +++ b/jinhokim/test.cpp @@ -1,176 +1,190 @@ #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" +#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 #include -#include +#include +#include +#include + +#include +#include +#include int bind_lsock(int lsock, int port) { - struct sockaddr_in sin; + struct sockaddr_in sin; - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_port = htons(port); + 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)); + 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()); + 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); + 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)); + 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); + 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)); + 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"); + 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, "/index2.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); + 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, "/index2.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); + 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); + } + + if (listen(lsock, 10) < 0) { + perror("[ERR] failed to listen lsock.\n"); + exit(1); + } + + // to handle zombie process + signal(SIGCHLD, SIG_IGN); + + 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; } - 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); + pid = fork(); + if (pid == 0) { + close(lsock); + http_handler(asock); + close(asock); + exit(0); } - if (listen(lsock, 10) < 0) { - perror("[ERR] failed to listen lsock.\n"); - exit(1); + if (pid != 0) { + close(asock); } - - // to handle zombie process - signal(SIGCHLD, SIG_IGN); - - 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; - } - - pid = fork(); - if (pid == 0) { - close(lsock); http_handler(asock); close(asock); - exit(0); - } - - if (pid != 0) { close(asock); } - if (pid < 0) { perror("[ERR] failed to fork.\n"); } + if (pid < 0) { + perror("[ERR] failed to fork.\n"); } + } } \ No newline at end of file From d1871448f169c7a33578ca172d8a5c308865eb13 Mon Sep 17 00:00:00 2001 From: kimjinho1 Date: Tue, 17 Jan 2023 21:34:32 +0900 Subject: [PATCH 10/16] FEAT: test code --- jinhokim/parse.cpp | 4 ++++ jinhokim/test.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/jinhokim/parse.cpp b/jinhokim/parse.cpp index f911905..b1f454c 100644 --- a/jinhokim/parse.cpp +++ b/jinhokim/parse.cpp @@ -1,6 +1,9 @@ #include +void a(void) { system("leaks a.out"); } + int main(void) { + atexit(a); std::string request_("GET / Moved Permanently"); std::size_t method_end_idx = request_.find_first_of(" "); @@ -13,4 +16,5 @@ int main(void) { std::cout << "[INFO] Request: method=" << method << ", URI=" << uri << std::endl; + const char* s = method.c_str(); } \ No newline at end of file diff --git a/jinhokim/test.cpp b/jinhokim/test.cpp index 7531c37..a8f0c39 100644 --- a/jinhokim/test.cpp +++ b/jinhokim/test.cpp @@ -103,7 +103,7 @@ void http_handler(int asock) { struct stat st; strcpy(safe_uri, uri); - if (!strcmp(safe_uri, "/")) strcpy(safe_uri, "/index2.html"); + if (!strcmp(safe_uri, "/")) strcpy(safe_uri, "/index.html"); local_uri = safe_uri + 1; if (stat(local_uri, &st) < 0) { From 704c3eb86347fb2fff3f7d446749518e3e9c51fc Mon Sep 17 00:00:00 2001 From: jinhokim Date: Wed, 18 Jan 2023 01:37:17 +0900 Subject: [PATCH 11/16] =?UTF-8?q?REFACT:=20http=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jinhokim/404.html | 11 -- jinhokim/500.html | 11 -- jinhokim/Makefile | 4 - jinhokim/html/200.png | Bin 0 -> 27012 bytes jinhokim/html/404.html | 15 +++ jinhokim/html/404.png | Bin 0 -> 79163 bytes jinhokim/html/500.html | 15 +++ jinhokim/html/500.png | Bin 0 -> 46496 bytes jinhokim/{ => html}/index.html | 3 +- jinhokim/include/Client.hpp | 3 - jinhokim/include/Response.hpp | 9 +- jinhokim/include/Server.hpp | 7 -- jinhokim/parse.cpp | 20 ---- jinhokim/src/Client.cpp | 2 +- jinhokim/src/Response.cpp | 96 ++++++++--------- jinhokim/src/Server.cpp | 11 +- jinhokim/src/client_main.cpp | 2 +- jinhokim/src/server_main.cpp | 2 +- jinhokim/test.cpp | 190 --------------------------------- 19 files changed, 92 insertions(+), 309 deletions(-) delete mode 100644 jinhokim/404.html delete mode 100644 jinhokim/500.html create mode 100644 jinhokim/html/200.png create mode 100644 jinhokim/html/404.html create mode 100644 jinhokim/html/404.png create mode 100644 jinhokim/html/500.html create mode 100644 jinhokim/html/500.png rename jinhokim/{ => html}/index.html (57%) delete mode 100644 jinhokim/parse.cpp delete mode 100644 jinhokim/test.cpp diff --git a/jinhokim/404.html b/jinhokim/404.html deleted file mode 100644 index f61c4a1..0000000 --- a/jinhokim/404.html +++ /dev/null @@ -1,11 +0,0 @@ - - - 404 Status Page - - -

-

404 Not Found

- 404 cat -

- - diff --git a/jinhokim/500.html b/jinhokim/500.html deleted file mode 100644 index 7d9b284..0000000 --- a/jinhokim/500.html +++ /dev/null @@ -1,11 +0,0 @@ - - - 500 Status Page - - -

-

500 Internal Server Error

- 500 cat -

- - diff --git a/jinhokim/Makefile b/jinhokim/Makefile index 3c12f99..32aa5f8 100644 --- a/jinhokim/Makefile +++ b/jinhokim/Makefile @@ -49,10 +49,6 @@ $(BUILD_DIR)/%.o : %.cpp | dir_guard @printf " \r" @printf "$(YELLOW)[WEBSERV] [%02d/%02d] ( %3d %%) Compiling $<\r$(DEF_COLOR)" $(COMPILED_FILES) $(TOTAL_FILES) $(PROGRESS) -# $(BUILD_DIR)/%.d : %.cpp | dir_guard -# @$(CXX) -M $< > $@ -#@$(CXX) $(CXXFLAGS) -I $(INC_DIR) -c $^ -o $@ - dir_guard: @mkdir -p $(addprefix $(BUILD_DIR)/, $(SRC_DIR)) diff --git a/jinhokim/html/200.png b/jinhokim/html/200.png new file mode 100644 index 0000000000000000000000000000000000000000..504bd4922ceef7540174315749284395faecdc25 GIT binary patch literal 27012 zcmeFXbyQqU*C*Pz1b5fQT{>7ufZ*OpBVIs4S9`faPKy?6DmrC*x>B2`5dMF0v40Dyx01N>SAJO$uj zVqs%p;$UNA~T0x~J3OA)`J+E@^d~mWZ-l^Eh_f9joP1R5FB^e3#>T@5O z-Z;IU$nuNbke`bJjwbHU zLCxf3{FmO&hYOwZv27P){X2Wd{Gq9(P;7*A5NbVbK5*`@v|relk4vXUX1zpX;f+Qq)kP!^!n_HJWK@pqctNhO4%6TO;9~!XZ!-BdTHHbrnc!g&a_I zRTpSrm+3HCDe0&o;&nOADxxvYCWKnYgApg2L7sY@&-(5U7TQ|&J|Z6ST(deknSY#B z$Gp^Y+j&8NOV|aZuA|G3LqS@Fvs{ROl;h6+kqWJj4(M$p3 z31?RmzhYYUq(%YJ9ut#`;0o9T0;s{qU+-je=w!CO?-y-bF%(M%MUNhd)el=RtF*Od zYN8TMsm?Y%^vhhcyV+|j9dJFF_}OZelflCx6GVam#T}vv$fy6uZX8ef*IXRlIoX=N za7w2)j#5co7+;bQ@;|M{ChJ=m$rP>SQ#+n|F&R)YtZ95Xqsdoqz(?221G17O=5?|j zJsHtiP&-m^u%h*{$@uty-p}ul-@l8p{=La7aYc1rFL=Y;QJi+VzL9c%#Y-ZnA zKQbB)ZRJa=hbD2tMmT78FV7KU{KB2i{Crh$WdT`v34VD|PNQ<>FXy>btu+olmO^KZ zZ6Km*N2h{@u8unClk?r5j#ejLxqk1=TVn+q z0!er_wc%UDqlb?{mVcnJS|xtCcP*w^Q!nZi*bU92kn^xX)5RoWi{Jw{9v_A5 zL^_YHIO@m0(0!@Nj$RU{`?6EkpmE1n<$RMa)ucQp!RI03x2#h)^Wbe-|D>nxw2^mN zlPm1FdsVV+GTG2i9)TbPpZ6#`NI%EdU*JBvJ&B>c5tt<*4IMo-loas zs=@{{Ls6Y>*>BzxKiADST0Y`Ac4*F)HFdjK+B!Z@1H)0@AS?i;7S&uR0Bo@d!!$c-|M8)>e zOY`nOOb~jw-(MY_Q>D+{#6+bp=;({LbMdRN_o@$b4x8oBo%g#RoBK4|x6B^2rm10s zCOHk+m&1A`^Cop?RTdXKYT=Rmneti79_h(myQMoekA7&JJTH3x!RUr&QhZ@#29)e4 z)jsC($;N-FbHE+UqG#n~n8YN5Kx{j{H2#MJ56P>8=hEDVBHerIt1~VRSvhGWpgns7 zN($hS%w@~R`0ZuN#paqfsh1mu)|J98*%

MTTU_pv=M-w#)UBIfxDQMFQh`AH?z6 zH7>qklrUKQ=*;=BR6vTq!_~oDe~ufSCRQd0wGN9?cWdFV@&l5SR`b>;D^{Q8`O=xi zr_aBbi!!oG;)vMIs^@^PMY))|I3Ahj1<(`MnqPLN8K^bW&e!U)W7AZrH>ep~_8=U{ zm0Tyn%ShC~MJam+8lJ-QoKb0+vDp+ZhYze$w!2FGboJ+<#-T`DbOQGBe#BqgFC2`` zkHhksjV{-+=r-Elx3A!ZaMfh)UNL7HE2`VssMB}0-XHf4s;AeVr!RP}w1L~wC@JQy zt2G~s4NYp=xmMPeoD}AU)Ob#{epI1I>~k)9*{8)Z$~2Nl%NXr~Z=4nIfFRc0# zabzJ<9(t?4n!b1}RL71zucp_a1~x*AsJt-wE4K7-&tZa1$AlJN@AI=CSk<@O(5NyU z`679knuugbl+fkaO7Kg9c8DfoE}mGLJ144G1Oz>qWsDjG`7pJ7*qr-zWGgi#Yusb6p{Nw`$>=}uyASi< z%+j4nOT4_9K928FUxG!YQIyWVY%U#kF;%Vey?Y!Wmg%OdS=yzzUvagu3!NLS>UdG* zS`4~8GkduZaO+v*);Ox98QPgWS?_lX60wC{*`}z+s`rEl@)*L@lj_mqY}HujpFfT$ zV{)*Y7|EK+nu|`u!kmm=`lADsGffNAZ*RAsrmODVn+bIy%=n_%9n&ZGjmC>3z&yOI zORY^4>FR##lfc$zwht4EJ?GbaTv})S_gif*ovQ0T*Vp7;-Z+nDZ7+M$+lf_sXD+W6 z+6r5n8C5FOsgDdrp<&8lm*MF1fz#MduJZ<8?w1bBmV!!ib@}wc-St~de=QLJ%Uizb z6!I`{RPTd1D^7S)dr2ILfF}xb6)IG-Viyf&KYX~^9yB#!wGvAxxSY?C>|0r#a#YnY z5{0N$rAH4I$FHe~%-Pt+IKMk~^>!}PQ1PN^ zhVT@1bR0!FJ-W%K_$P3QU%Jko=&7|uEH#m`C$mx8dSs)W`8q>|4v=7*ZJs+~Aw%X~)$Hd~XQaPR~ z!_cu)GTF6t|td&KT__wyg0fBf~-Yqy!=ZPZ)4vHGZNuLdoi z7w>X4fEfwI>46)U3c{`eU@3{knk%1$el!w?>Lz2CXN@IJ@q`zL+9iRS;s15*-nF*QLujWfaAFVXLaiPcFLu@`|Y%w(kNBOd84*CPnuF5E_OpR+6aXh z#IQTdWlNt4#gVmRB}N`+~KS^+%`edmvt)7+mfThMqy+aMZz<~Ua0~wGe|oYk&?^}kbG#M_f}udPyKjv zCh(7-0WfLzBY%5`hZ;pAN#b=XbHlJom>Nz_@pi7?>RV_^diCCO4xLP1XVlH%o;bArvl@O)-@tmACfp^jI|;%?Y4yH0Gb(iqkw`rCssM0o^ zv~aY%^Ww|Krv;MkAQf4a%521fN@<#f30O3n%cG9nibw4MUMfXm^l6liQwF(+-CnOh z4sF!@g7=M+Fd=srR>~gUNi`|-UR5q`%?W zlA1cr-t^Mk(u~gYL&*z|@1&yt!pkLCx|JFa9S-eym&qa@RWj$7hJ2%kIcoem6YvU`yafDNg5EZd)$=2EY#}(pNkE*4gX~DgG z9OX;it@5$yB#l7~2J@@0 zuu`$e^kfW&U{VOn`9t7gjS6nJwj38fCEZQ) zhc{+-?)*0PNJxD>@Kv z&0Q?by<9E)UHp%XfG=$3d9Nlqw$oH?Fog6W?R008Qf&SF4#&viLDbPUqoU0s{+^il zAtsl4Z8Gih<<0&6*zL{tA1^$s?OdC@Oj`i#*s1-bYJ7&n!NS%o?*tPCTQF5hl7aA% zW}~KZ5nIvj3Swc1x$`3bdzQbD0k9gS$6w6dOIYTTjNJ7bhrxJDqviU2PF&FwWuSD! z!-&`7$E=IuwftX_Yi1u?G&k;@7H1dc9^1`klVai!v&hsuB$H(kGL<0~q{LQFBbAln zaA_OwTOQ{&mXAbNiQYHp@BRZt=%IhGzkWDLZYvxW@Gx;k&_rELRvb|hrLId(z6xly zm(1L-N1U5nTZp-q6SNsFCIf)C`?g(**4ulgeyg- z&`3x|X1LOVvfy9x_(;=r=DVub;h8A9h8x>rh&FR&y_-WHiAtxA*2t=OSI3h!Ea|sc zx<6yMkOry()sXG$lP9JD@yKjZZIuOB6r6BiGPOWkUSZWy=XMo#19pD7QlmmNbAFKM z*SCK_00GIZ2fL?|9^-c5)y$7Z!{{7xoMmnXJ>n(t%DazAI1o z)mzhb2Z!;3O3TI)7B&f-d2672R3MZWrXuXX((9kLY?Ta3CO6g`j}z3BiDFgLiV6IK z!?1I9FA?xKr``ww;~i3_ijKA|D5kkuJMFaFD^11LSCf^)bt&UX?_`wQ_mMasAGeW= zcOg06t=4JegR<07%hf9@DpO&qQ=5=-39C3*NvrWDhQr8ImhD0?x!DQKUjE(40be-a z*PdUbCAty8R>C-n3d+MU;*p6~K8LaH1III3k7(-8qoWh0<6V{K_mf_eUY8#;_g~o6 z6w0^F%VQ=ojgY7u+xe?S!@p?*kg zn3Cd{8W2@b6<~PG$-?X5BEB?}QW|;w>v6P?;$7!oF_w8Ruhv+MmYVwPU-%l%lQ)*B zvMr7pyJ8?KV$5!t97M6Gz_BtloYKA{FWXENG^SMR;bqrkLH)8Ee95nye-wyvrgA%2 zyWw5^(0MuCzggF^(Gx5bL0`@8RSv3{7tx8K-Z__~7uo1v8>xKxkfuFXGr1A*&AD?? zdXQ#+>#oBoIh$r>u1CIMRL@Dxc##N*2jdn>Q>SK6=2e+*1SWZ93R;d~K--P#N)0RK z;{5j*>Hpv&^ZV|NR~Qqi{;CJZ4{80gIdsP1tNgpK-Q(9fygNG`@=|l0#BL^z1t(?M zyavWU?Ol21lIOl+^}Szj8EEkIqgtdC(`2??v2q$Om>&i0rFSMer`z>Q-@&R8+S{3-KCL`GiVruq59L{}nnQc`%!OAZ_%a3!g^T zY86hFF68poRdT~K@AXF0M|vM|wL|9{|4Ch){;{!>G~>IMdm8i~%hkAEc>3OT4Es%2 zk&!tx@omGB~fb$4(mp(Gm%dUv=x*_ki=xacKW#sB!`e@A2aZsw9+58vtiuzU@c z;@C)tWrRGD&VTmk;wtdJd+~oFd=kp@nV#q~CJpD;XvNW^ZAHv!$-Vp=)BR5`|MP&+ z>anKj>(VD|Y5CO&@!1>$T9x!ypSu5P{x7dsQt2NQ3>~|)`}>}U5p>t(QIBfB%x)_!C4{E_ml*$kct(xE!Nd?aEm6>LU7p4L@8|!| zZy1&NrMWIYOa}h%hCn;mEtk0}5BuM}_y3W0LgaxN4-o(r6%_>y9eIcbK>q(dF9RTt z(}+okNGWB=D6q*bnY7&k6Ol(~*vNx56jZ=3!0DMl;n(f$g!sqe|8HKnUeq64tBIFo z@$+}+EYel>ztL?Rbi7dUg}Vp!MUehbx*_c=-wME|nJyeG*{&+2^YcB}jv8=u+u59J zp&X#i%l=s=9=OFCG57(CfDR;X^a(9E4^Ld?(~}YcJYTVcUx4B~B452n44SyUDxb92TS=(YfL0+buFg~?UDXmgKp|1f;DL4r%vF38;&1n^VlG+NLd8V>#jFFzTy<$f zgx<=b*9X&;ZAEpMU>P-&P(~=37g!q9c zx0R(}IsX{rW}5QB(S`wCV6%%$NtS&gkz0HpQFlD~bsJl*BzE(v1rg<{dGaQ4%s|oD zd~ypf)k9_E@dL5mBOX%LMPE8Kp20}<+R;bwqb(!$tE;Q9<$e1%e)h4E`4pUE%{1Rv z(fn?O*X{Y3J=`jF-m@t)r-vgMx7P?F&8ke#f*FrQZA0bxZM|}jM%+K|1R}FYata|x zyMn1gV%`x-Qn5&?Py@nZXb2jw#W8>v*_j&Sr^2K5sD6BZuN;u*&8=6dQ>vO`yTzzh zWHf8~&Pvb7Ll$*Il+~>pr#FUUqA`77up592M~$SQ-Y|1j(Ca3$_XbFuFPlMzlN|WqP zGi=Yy)M0XyOwAZcmdgPu;>jOPFrU)h@GAMODCDv)Br`^S2vg>kMJF2Rju-lEGwFbU z2df|-81+P-GwVl{_Yp~AED=dhIHc}7!X%hJHXXPOj6bx2HqV?q^m+%0?Mf*!$a^Za zPEmn5G1Kt&j&waB@#IOp8d&WCFz8!mnpfTqt)pi}9`y}c?IZ~{Q!qC7QyWzA#TlfJ z-7G#dBco0JF`Ui8Y+M>17@2qls=&=KE94(&cHG|@@|Ur?=^zxaF` z%0g&!5sFIa*e7~~>Cm4Gy-*wAW>MFr(HOY@0-}45UklnQ#Fe*t*EBOT)GKqjDrG3v z_O{VsbeQ)mW1~aTmpOAPk*5OIQbe+Nof*h4?Cp<8cNu)eHw>mk4HZ&SyDx_Sto7;& zqGvVfQ}vf{*sYZ-w0`Yq&4E})^NzbrMZ?sG@CT+ zIcCoGeUf|JJsK7)myhs_v8Cvo#bvRuEoITgou^(Qot^$VQ9&O3-Abi$Mpn=>kA<_~ zJ6P@h{HSIw%_Zk2m_Pur*mu7F5jNe{&RMkxGtcWG>ix02RS4C$y4^o$v zNsTS;cK`4<&^Y5Cj5@D(mpF>gtN_;gRGDO<$Ctg4APPnr#MxPV z$y4dNjUy+3y9j^oV9ld^I{;OHkT&u@q*`?R&GW%#`jC|2z}__qCy#z!65e4;W!#Q) z$MJPU)4y?EPACMJ9AGYBhG>K)a!_y_ko0R0bj6&K;es8MtRI8pEa;5ZW!3H2%h~!t|8hh z;y$`~v;8PkLT>g}xq&Aw(thZ(kMZL9C80vNcaPrxkm3^67|LvN@K@uJ@JaJ7w##WK z^P{;xkzU9dvQLUctGBm0{MaH4wb!bvUSF|!s70UR=C+r7_(DxI1|mI7TlJ|(-Rwq*s8H=LngB4HUZs@Bt`MPFO z`?pJ;0gvI1YqP61q#{$%oHRHm59spp2GDqgH;fyd0QG3FX#*sB}~9 z#L*ri`neygKy1A&yZ6$Oyo(F7Gs*IS7HLuo_0j~6G)s<5^t>W0zUB>-@q-`xfbqE- zU!8b7w3^?ueHjdI78hwIsvV@CbFeFB5)MSzJ`m)}tYkD?6SE54C=tIszN7vq; zY9heUHR0KzORWAUtE|tOY=_FP1;U z>FDH53)g>fNF*Xf=gv-73wsLWe~SEHGZ}W0|H051DxDFS0!idI@}b^Oj;cnkZ*mrH2^;W2p$A?PlWD z(Sf%zGQtF5(4m#mXa_nEa;x7vk(Lh@_uCkjn0LWwKZW zaZfMj@>|zhbmG<7cl41bZufL(nBG(bBs-H#=s^c9nFL(GZL1MooqD><)DOQ0)Kz#L z6lPAJLl;kAHQ?Rz0Z+SvT;DTgS)ox3q1b&I7r5YxVJus(gbX1ndX8ST_RZC zIk{9F+1!t|>>H{Bd2B`rlj`_WtY;*YPV*k7nauudz8^m&OOZDfPL`)^aH0ZOO%xhE z{RKed5J$5ddD{!VJk!X%YPK%M!87S{?e_i!2v!sMR@a=Q(5;9Pl?1g^;!ff`N0a4~ zBKWh%t3PDv?d74-di#o8Pv>g}o<e+`T6-7 zY@-daC)1B2OM*iA@njh|)f1EW1!H(AyD9ELtw6(*h|b`9A3Tfpv=0#gEt%1n+;BxwB3OS&_=Ch@ z?WkwJBM_Qu2buvEsmk~TCpbbZ1)W*`mv&tV2Yxz)x)Lm5)OJ>gOSpM0n>1 zu@(e$a!V2MztaHq&2;s9vDtC86(feRXQU%4V4J8*KkGqfxt`>UiN?mbDu0H4is=oz z3}w2k~z9=W5a4Yvv3Bt@$~lwabgP zh(#?Ghk?2kH6*i9aX@83>R=)o<)|<&r^CK8b|bWHD=Zw2t^ht17D!M)&iR2z%QaS; zKb}lppzrM&R-kTN{1pE0_~}IzCLuTvv$m2=f<+J34u;jYMa~WG(|X6I-M)&@dqxmn znpK+5W}v7;fQ(VyFErIjA4LJ}twN@(H@l^{i(=1nC%oh2gzLz0Lw<*qxm*m=5d~;i zb7lTW7VxmDCg#n zXL`~iXdR~iAmnE<+LwWe{;%i18T4i~oCm2hEJof?Nn7wc*=~Ed4_$wDWRB*!T6egm z1U97}Ig0P%I=H(Qz_{{(gfN>=Sd6p;gbE)bW?On zj|zA5`tpi!KZF&W2mKl#TtoSLek%8 z{2Jg2YIIOAH+>bpB7}Ep>SpvQ>2lPl#(|DCN)5LAgDOptAvPcu=#QsON=cu#Exh%)bqliu6|Vv3b)b{_1;H$*%t?8<(+J%!#vX z!Qm-CS&mbZ8-6~;LKp4oZHA#*?bzF}9~Zo#+dE-ymWl6u&Obit2x0f*db+!W1vTKLL1n zPEl{(*DBG($J?vqfPN_1QBFp8z5~6w!ZTCu@7AmG!=(P$(i6B9iv51FanpSVIe3ZK z>qlR&w!7j70dz!eZ)V=8(FwW}&SDY$(7n;^QzCq%$j^geAtcaw9o}@Esv)dpyPU`Mb{ARN8*3OigZ^@wcCK4ZTP&HO()11DHSTY?mh8 z?^NIX0o+@XY?1q^T)(F+x4uc#>AHy>i3FyZ+#pdEUkkIYdwNtMdTEsPvv!cC_&mb zP-G>s_g|$%v?s_?;+6k=!3W|RS$TT*L8rFCK_%H!rQ4A@&!M zbu_JCz|Z}Tc6-weiz#WdFJY6sw-rJo?7UdC;d&06`V)J4%a68y0mwZrYW)^}a_=Xw z|D`=UQ(q-abX^!Q4s{$O+g}J>+a^yuxiAn}Hdy-p`-yu!GMd}B_*#{xv}cwYx-`62_H$C3$>96zZ z!zOiqu6B`oHLD5a_-s{A!hQiJ&il(5?li6e<8z^jDs`ZHQT5`Htno3-@l z_=j!1Mg61i@9UPSg<%I*m-nMKN_+q)yoeX~{l zB=HVOZ2u+@)pUgHJ^T5`JTW^AUO($vUd#R_B6b-3wTw)k)OTFx)wOil-5pBXy1kyb zn(-gC@3Qy1leVqTuYZYbTkcwZ|60q)`QypQCme{T=ePFx$qOc8-^3<|kNL8HWbg0X zq)ogYzq&+reDY_Q`no58_CLdlo0%iKnxdJsPpTaA<^9OrFS$vZd_DOGN~fA|X7VetDmld1P+8{S$*X7$HDmY0(^X(J-{$2iHONZ{BX3Hy6D3j;p~ude)$ ztv@Xs{JugnQ^X*I0s@F?fM42Q+vKzSw%h2x?DmoMH#K?rFq*4Fs zUXOxtX1AXVulobpMzn>W_e5()SAqZA4`Q+TZS#qSh6Y^H6xhARs)Ldri1Z$0>VznO zg`stY8>F^Y!&Ij-R>PJNb;Z4G1t^N~D4yZWaN_p}4j|nWj(Un^X5kn&^@?rnj&I}R z5p`YB22ur0^&sud$k4|*&!VEb(RxTWyU8ZpEFF{NhwiTV)m=2WeVGFjNi${?i)BjL zLQo3Firn%zkEZe#Q8DE9gZtDT48h$bVv|59*fd7=i=QOKgZIUE5(YIm1mD5nIBWW4 zE8mF+5T8ZvhWl>pL+&e#2|l%n(>N;V!4`5y2DqLsk%8q|+TGWlw4_g~o%1g*ilto> z>bKfmB!4l6Lnw%9hjrW0ZEZ5GY$>=nE2#_(A^h&^x#Wts5=#|}*f(45cdiw_%IrHYk74AZ=n`*Mi*nK?!#+|dgJk;#h*3E zGlvFimsMawT2tKSDYA-e&I?T4F)*HnU;=wS>k*8P&sB*^_gPnE!U`q!BC($*^3)iFs+dbA8k(SW-V~KR6C0h1>TO!0zbr3{Bl}ZCY!UzW`rdV+CWL5Ez zQ=x)Tbl3vP2+G&wiZ(UKp4$qM%PLApM?6UM)Bq_7dSRo=F`@eq9i>L)2=jm-(|{R@ zr(VQH$8FUQh(YV2XZZT#7!lm?wp7!5v`j-`EdAjD4ZnjR-AaXJSd@~lEPfBV7{+SW z=7rxYx{b@zTu;9@GB+mCZ)kkh9gA_1ijvW@9+ak_N;9dLvyd#%PtuX()hv3c^CdAr z=A-U%NTx;tbEb~?frn;N&L*-d7tLxmr$4P<`m8m@h|Ei>0$-oa5nfUDIf&D>w*sz= zEefezx*@|(+wtTM4bRcyT&Q8`Pz`2YxAqXeN~O)>Ji~sD&AjA&(DKOKLP_kO75|OQ zbZ6@Oy3{aF}&sYLa28p7l??LMBnIxEt)&1}XTfo(bQAc}CBqLFKA+ zQb~!NwJoexz#~<`5Cec-f)@&q!+b9+X^GN$#7(s z$>>(_DcBQ*9p+J#H!Ydmxh^0j`r=nPx)Eewk}?ge`T%f!>BVM^o~@aCiGRC)I{HuH z^S1eL-7sUez(L|({jGIA0)vpojH(faEnEMjr`#PGPEqEu2Fc+rkgxrC&?=K~_f#oN z_f#PlJWZ`_YT2xJ6<-_Wb|0jGAl~~?Mw1_K$1i$AwlS~woeI6lZUk)mJ|kIAD0^y!Km^xkYr; z=gR7bvgmZ$*vwO;zJVF9vBBT<8T)Z^aUX|HgawkI3abQX1_wQ36Lo*O&Bb=ckoTB0 znVMBT36?n&j9M`2&WV|V!(I{W8)}HHK>j_Da|$CA-f9KkEAl;K1K?{`1_>?C3-v~# zfLl|B5`)m2~ScE)RxcrNvD#ZUO$Vp6UBx;=`Y8LXda#0vs8^SgOv zP(z&|CiL>j4Gdv6QGGTL2~Ay2J{#;5a$R`94hqHc$_i_x0+@7zJ9Mv7KMPQ%WSmMQ z(ZR#>4DS=18oH-B$RSy>vymdscwq`ML!l|IP-qy(F;bufdVW^D^cPi10l5-Uyg;Ea z4slA3XumyKM#fC()8qdvUlwB)-#+{d@unK5z^1{LMRg1$lZh6e(n(VwMZB&rTH zSjZ7|*DCkc3;9$Dn195dhD!Ey69Y%0k4*lP)3fXu?XUeRtcP@A6f%QDINXI|f+4a6 z7Vy43!5o#VUjT{RuQ9_crHl&|!j2%cPbB%I#nckQ81qrjHrU<~?a8M=;g&VX6b6-bcizD(wYT*6iu&R1>%W+VNKIQ^XOY{b)t zX;w*a=+m55qhV}>l5h!ZijGFvGzH%Qz(fn9Mz|J{$~jQ!7_kegnDV*z`hq={gC?*u z9#{J)DKZ^?@QpssyTiQhjGDpRkBRoy#Zzx-|xk`cPCOq!XNc})6 zd34;|6>5jo(XoY)H1#zCW@Q_yL`zOsqyiWVFi(4=j5}iN7sz>bcr^&Z zF8!ap7%5}+9~rqZMps9_g0x++gy%>>%!0wB^og9htSv>j$aCKFGL`kcqBw}XtCOl< zQpVF>Ph%_w9-_10-gxe?kv?PX%xWr>=dUyw)K!;7`t{TB_FP$t-VN>778uAs^#QlT z*Xj)E0DZA>dm-?b(2nRp(sRe*!Wx{GW361e8=XN`NGEua6}PmuwS#aKSlPlK#t z;qr%CB&`pm@(3JfHn{zuc+8#(s^@Wi90b_coUE@Y#ZeJw)1TyC_-ZGuhcRM>&i11V z!YUXX>nrsQJxKuh_oIHpY1BJE1wyJW$ zZ+yA+SmaQ^qghc~@U04Wbhf}yMf3fv)Ajld9)aTg-wUCIzyb8{tP6s~(y8ts#_4yJ zKS*jD?h`CTzLftMt^1}Ud&c0)G}P5w8dcbML$^)Uclt~hpWVM9j-7jH)?D9&_VYa4 zL6C*^=Dav0s>mTX&PMh;u=25WL0O`(VLUe&nst?B2}H+?ToiuSoDIiwaF=bXIPfg8 z@#u$BMM>r)YbeP95>=gZx_1>feaEqvJO~iWz%!Cb}v!#aYD=!aY$&j#^DSDU+z8{zUj=-p}>b|I+dJ%{K-d&qqQuBj&wq%vIt|$KcgdxUc0Uc9YsGzFr2qX<8>x` z!`}H;<5Y7;#+F+;x@Sq$1pSnu@7$MEeX-d(L}b4RIFCqkb-}*Xnj@#ZY9>$HbWNz9u`)%pT|$g-2YGUQb9*Si z0J(T|A{wGo<0)ElhMqFGJ`Iw`6)zaHhTBYv+66kgk5#G@?7*J|5)g`;g4A%bqfgX_vKtOE|2t8@u(2XroFgBtxgdMsUT;h{xZ2B~#P~Fs&Wyt%))V zeQ4GA!re(x&`ot&l%c0RFI3DP+MS_l6E4FP$-%bq)aPFUAcNb#0>m>js&@tNd~%W_ z<-WaX+NAa}7#o;T;4?yl<3s)MhLTXEZc(N5vYCZp5O_|9hc1do&cb8lo$9w}ij z_KQ$7)gCC{2_gE4%TEJ*viNt3rjDF(FF18sbVm}i0Qs~y%-G=^Bq|EM4MhZTK45B%Q@Xq5$FZ?lcz)F~@dvs;#8fHmp4Wk2VT z&5tPY=!zy*nY^ZHnD{Or!9BFpj>oN>CzZwO(7wEZeVszXZ4P#?wtYh7Ibe$V@{IhM z6Dk#4xfnuMha$1ez^Os;a8HicojWl;kP>9wKoS!MA(xlYij8I!^_{lr6=&uUM>mdU zU6+7o*K0tFDkO#E;&d%4Wai!#GZedhTwY^r2$H%l2YRQkgx6!(ec?K&B!)^{pPCUNi&xknbm zI|mG2h{+F_eeYffqt$%Qais1VTeASpy$=s83`&_p;1%dahv?+&O^<^lIYfj%ht^65 zY7=nk6`^3s#FMlt&A~;rxn1|c^50_|_1v3r@Bvki%HD}%Ek+F+TUS&m<1AI&iZXoF z@b+bW0xoj+O2`O?+hge-V+VB?iIZEn?G%JH1Pd_zW1j97jTQ&^oDT(I#iq4R zBkGiC+R-2Y^hq{#!*uu@oj@)GpvfA`h;pE|iy7W(ELtd5P(KK!jb~#uCF_|REC0?N z+-4e*73N49K~nmh==HmdfR=~_VeKI)IK#bUvtZ=hbNX5^$<`#r^(eDBxUP5xY7uhNw6TK^^?$cVUE{^M`y;yt{HLo3m|bm)$9@VR3G4s5|K zN5;Wc8s3p;EqUA;D4H(r%;X#ZYAnxvei*Z9>R+-9^Tp zEYnm;Jvq63Y4)W^Bp|{U5Cd0{P3`v74W|<*IOnoR^61x5E!H5~qF{PAwny(6zeTIB zha#7XV(nsF>S_p=q~k93`>8`Dbuw(ULk2DF+~I8;kEJbPz&4dvJ4TTP41_@c6Ji{$P;*FfM!;hP!6HrU~%M5}>b$n)9*|NOMOPWUENxF$Mdl!H95*$DsM2BGY%)P`TVaI29qo8pEXIu?smN1g?nT_BWuZ6w zH96jjC;ik5h43)jiE&g4$LNrE{Dxo)3>tn7$}9}+)*&+43bXg#&-*OCaQu_w)*Lu= zSbg-Wyl26*WWsei!UmI5kzru%5#yY&N1bY6V1Y2w?nSF1Yt)^WEuc7l_f0aQc*w%% zr=Vw3K2bWVAMEQ0E+JTOLU1PuWC`xB zf#5+F4-(wnAy{x&G&sTC-3bB0x8&aY-~0Y^=FHiysh+8>nR&Xqs=8KsNLKnIHhY2k zs*s7WAb9J$NNRUssUUM`_P2tUaaxoTzjr1@MbgFughgs?B}t3jTep)vze^drl6}Pq zV$P7(O_TzyP2+Wyf%Bz7^ujVvXn(1a2}lRlNTcVa_#K>8e_&*p|1-RLm}eF{4y|Dv z*5U_@CHoeE^Un*Z6V?0^l^G%F-NheiV3=vId^z8o$4`zNGNqJ!U(ecM6Sj@&@C{v+GjK7o0ed9+@f{u{KUEkcp2-Uf&l_ z?CkpR{tQ`C)yDzyx*)$k!Q_BfW`18loy@P6RgJd6zSJDFi@$=DcNadw{bSb-ztq=6 zNy|zW6b$+#N`E{4DBTc1%gVRVc8J}yO{Nft`y{6%stE6e$hiFoRoUW=!0OYR*qc8f zbB8j%+_-@h4{W$8Rr;wKm1)1NLKl<-DpqnOQJp8)Z=gmUK4MWe_eSEhSJmZldz9iu zi}xz!i*!Z;#0-P>SUK&bOK=cUl;QVu6_uHgc66af4w!{v%gM$Y9H&sOa7lDvaw*w$ z*t)BLzZ)v${HCNM=4W%e8gNd|ONlnZ%zf-3O%l~X-w+&*4U}>cWF#~LSZMNgn8O4o z2jB0#IEQ4EzSfL<+1PI49&50O)3cCeZNkP75e>^GvI0KsiUFqN4&V3cZ@SP6>K)ZP zT!iE2ewf_KC^v}xu@mW!Jcl+ipZH(tWw|;b2l$C&LzEb@&I$?5?|}bt3oTX$2S)uovhAnQFjM zr#72B_m@Ii50(G5)3Q8R2ZNSRq^46pWxHk3u0+cw0a(lOjI(+JKtXl!(M!MR!5op- zVEqM=lNT6_fc8g(-3zC+c?En2VFA`i=(sjNe5HGG4%sdB_=K}@j+^to&-w*E6SeCz zaaFei3it;7G7bvRu;~ChT5t%iu?hv^QL-o_hjH7@^HCRg4v5q&#E`yl7NE)t579)) z9l`(JR1<* zBxXM_&(?&$I)Pd2)5Tg-C!uu@$ZF%_olIqI)x%%VQYj*OMyQ0Px1ls`5H^oo+%9gX zqltt7FUPK&e_#61Cab5xALE*CeN|F5KPv~7l#hvLe~@UGEdVfuKO{};F0MtvSf+1!kr6Z(fk+LFe#bQGWFG5EXwfL;cXF1sOml;XDVpxy2y6~gaI{K_D)p%jJ34)FRJGKt*i>}V_P&2k z7k(@A2J|rV^B;#7$v@ToLmR(7FijEEoGM%76!bCP|&lOdqX<8=UTc!Zk%;uVK zuqXt-VASPeW|-(dFO-%d2V8_Fiq=~8uu{ilD3(v=M5~$P4m~7Q5Bt-?1ij8WAH+8a z6`45{;P(jIFEqjc8@nh%Tu*1nAo8*NM7l$fNVc5aq}rk35d+MUrJZgLT~h@G6tbYr7_iyoVRiJW@1HKvgS<$W?qMn? z_kB~6w2FO(C%%#66%*ul9iNGhOSg-1@hqvI*Bn?ytRadS4@gd+9vhps@v-1D5Odg5 zG+%lM#qNr{#bkAq>oYpwBa^X~X;tqLI8amR98=x?SykEXh@+V%sfB>Cma`_<1{36J z*%+ZQN(GWeKG>*kVPW}e>i^I;HCUnyOXc`Py;|1zC?`lFi&7uR_ACjwR8Va7dMLC1 zWcgNMlA!Hpe?&=+Z=j*4V|=ZaJgZcxsd>tL-qjpg5d@(sd1 zB%}EDcI`?p81V5)Va`Y`*NUo;oFJ!UeR8&EgkGjQpg_D>PKFtanl0F)Xa;4sZ?W{O z?VVQ=shYe{tcJXl-Mj#87qLQ_f=k*j%YCIQYgtpZdJb8rrqblR0L{g`0A71(p%Qbx z-@!_C%ai|DK*Sefe*e6i%0v5}q0A(*>1RHP2T*EHrd&^y`Q)4RFjySqD^P=B9ZcrZ zg35j$p3-2_H}sy&$(!jl$QEg`mO{hk1#sM}!-Nk9Ya9Y*0RkS~} zscL>*UgP=2T)FA3-aiu|{gH3deC?wAKC7u$Iwe)NR3Pq%-I4F>ex>`~5Fow6vx@a8 zdD#{SJ#ve#%ZAOf>*~G`v z&gx>PR&(ZO{0i;6%GnFKqCVoc_Ix^i?NpConSvsdP8r{ESN1uWV$BbHYUvAdAk_QAYoOj;=B41|Fe>;F2snsBd!8d@lRbneb6{gI$(-os?SuvI?xNboL>vPa0RP(=h z>3}*Smr7jtNl0)##Q;kKmZ{F!2I~n8Bh-1__$>|U?MXkKDwy>NoNtdc(aS=ZB;i*h z4R3C8vAPN!_}3ql<>KN)?K=NXbhWcsGVoWejMHNl3));m7GG6SZD{=Kll2`eZ5>{1 zr$+fe$<@~+Z85ntefa8Mmq{6w6cxM`JgEbsl6g3hq#ikI;1o>UI955_y@ALVWn(j_ zeY|@4`OQH>y>C5ZJ;?-m*w;71ZY3=++Xo-ES8HZ8bS6w0)^y0@>S>SF8);wtN=aAJ zgT$mcC5KHBi-W#@9#4qW!fb1hQFW%Zq&5L8T2Zo*Ll)=~h6{6}XzK%-B28o-=ML#J zks1>bI6Y=FhEZG>UkD~o?W$UdlPD_CcRRet!0-2;-+n&MIaFW=KF=cehO+(tVqo%r zaPhgYxe0%f!z?&au$ZWBUVm-_F0#SS#Agm=wph-gruUv8&agRa_0{&1*3UMbknX0#C$AfGaXin$gso zMEirikyn~HW;`>t#WU;iknNWPv6zJ5%@ym|2jlWmwQY^ zV$J+%L#QM09?+@D9-ItsYFW+ec+EIREh}j}>X4EdU6@;m9w-AEpeQE_q5@V~37Ed+DC7V1r7+MXFGOmG7)6pmND%uNR*VO=vQ$ z=Ru16q^iMc{uGla>x zhJ)F22>$jRP`E)ce+xQZ{c;?Z;<+Jh)YgMu+wE{^52B1pH?HJnwq$U+(tWB_^9oLh zL)9*h9N`yX6D|LVVP)PxG<9isyz*=xM9=P)+V3UZK@UrO z>G(YNU!=5Yy)L;ir&ysSJNZCtk>K=>=3rz#Dz0{~*FP_TOlU5xzyFXcDmo#fPH|X%i zY}f`AHZg>~@tI(pV~{E-S2Oq5KFoEP@hWccSFIMQXO%Tdgm zv`)ie$K9wL;OV4b!}$?3M>+MO<9P1fui@!8)K-XaJe*GJhj=u_vo)y4__X@%_4H!( zC28^2RF88)MX8Tyoj}RAK&$Jk%GCG!Y_`Kg;d@Vw^nZV_veos(o;y2&3J*+co#ZWA znIg(3*Ef}^@19G$d)-ZcucXcTdV4p$SanI-w6)oDkx)_oD7}e#>P<(p(YVbe+rjwTVBM~>O_c$d-j>l>XXRi<1150jXD z(_c)%Vm_Pl$LgJ(h;K8BCD+jD6z zZxDh5K^XpraQ8Ri#~9fk4sUOIE7Lhdrpu!p9H{8xuqvYY2rVI+fA{Of^zhf})5*K@ z;L3Cx(~aVLzz#7~bbfe1|HXM2f)sf=HR{cXsGfaWTO;<5{;VI3arSsOiVbMr1ICzp zyc^O#mmlRQh2MCJaZp!VAI|;0p0kck{j72<#eWISgtje<{Y<^_=AoH$(zjN%^dE?4}lO1kAbYh^2pmCaE; z@v3Pc1fUUL>!{n{cIk1-^a>M2+smP1bkI|d<2fqbe*Bz==#0++&J0br^X9FG z!iCdx8mQ5RGQUbTidhxo*bL3!ixu7b5DJdWTJM+-99ouPXm?JM43)2LiQ79^4o#-? zmu#D*ZNAW@$F8z81?`o;FF z%HN6n1aGn^3}+RY-f`nU1wL}`mt*$guBeim*Sky!lk4@ri}q?_G@RLHgtViMDGS{L zhNBny?g8kB$r~;E0x6Fe^m&J(rSAdN`b%`QieuGw)5D|Q6c8n7(~@~?D<|^lr^m5s zs*RV8TXhi(weYar#~94|m+WsCf8Ep>-UF_Hb`9M3fVtwTK1d0!d)nK=q-^}}uU+to zXT$CR%hVyw@+>x%4-oo%Ik@qEg8P?5N^`VdP@*UZJ?9olP?*Zu^Ee>AX9?+Xp zeeNJu??A25e9JvXo~AfONnUd3!|U<-(Mmn2`}5^xEwRy|>znASo;8jva8S9O?Fm?- zLi=Wz8n@&g5Tc4yOP&IJ`|chf8>l$De$>vqdoXynhw}S|{P(i|)zMA*3>*63g1vRn zcxSk!%WJs4`cIK_2-}CTH{*-0anoA~KOuIOvH(JRq>LChmQKd(Xc%OykQ z-TR|&JmyE{w$%OifI9N+9?UO(%*U3RlQ#~tb#D#XwMuhRhHcZ@36nVu!jV=Kt!kAm zQa8?CUCCWJ)hEYf&J0){dX8Ccy=UqH82;Te+XkUk6a>Cbl*T({hVvll+H z9@G^JWJ^Enjn)I0bbX|#(XF^!p45Gw(+#Iub{{8qFH(GYs?0MO)4zK4ZMLCnBK9tN z)&;`#Mb@)2q8G_JVM<@~?DO@FQ%lusdCJ$}x!jBRJ2&08@9UNlQj0M5NT$Ai3*JLL zn$?fNmpp8-wv*^!T{ZmmQM>e7F!{WhrMB6;zd35Dz)YYIwKSQM23W#pIc>I?7Ns?P z3Jkt8s$0GXr2pp7sQZ-8d)lJ=#$u~EK?t6Mh&P$j$M3hon%y~^<5+Xo=kO_-PO_j_ z4Z(XW4V@bb_*Ne4JY7OQ+5dB32^xvQ@@eObviE)yLsiv)3-^|vzEvfJhptAYxh`}9 z+LYO3_^}7Gq_TnsV52*(?PGE{ls7G~AJKT`xBpew+NV?}zxF-xUNM%oO_vqlo_)KQ zPPyXhwI^y%*R8z-G07#^MHnaT#7~EvUydayO0guH{ZUq<>8#9K0lq4%s+5fPZA9KeW&l1f2dq5vNdvz zGrrCFeC8@Y91g_**MW!@mcq`MZpM4wkwL7yyYjZwS8wA#Io*u+Dbb}-d~h=gqF4)} zcsPd2Lqi(Q?dW)LA^jp^Gtx&umdyhI*eK!E*`8&1V5vD ziija6KD|}$QcF#$fEArq%SB^2!sut+f z|6Zk^a|uo5bp)1mHGmA5g$MJ8ebIO?inqS})S{M(O4C=1z`+k^WLt0>l?sx>mXxZQ z-0dEE4^W;;t}CKoM-QUu0tR*9fNOwGRwXhNRFJk&m>5^Bcn`lQGQI|gYlaP9ScIrc zJ&5AN+sIAy$xZ;>8YaI%oZB-T{!TLL#A4O%`I7~3J(%+Gd<|QqX zkwyJ>M023EKOr?zMgXOO^*K%}z5N6A*xmSoJC!(bQd*;IbXyp_LKOX;AV-od#~=OR zRG`wYV-sIN(A@Vmh@mV!m*tjl8Nrf;D@RTo*?sPRK$3an?q z>^2LxZH`twWM6=0qPJTvxsRL&9 zfAIS1M63K#F3&{F10B4YiQqtcE%!$mzan?&ro;+-#BWSYC%h%j#Fp{jCR9znMm!+Z zD=~|{bDFh;4cCs6GAnP1WiqO7aJm7hY-_(eq9FK$UDzWD47y>bIC4JhrOe>LX}buT zZn)S$-{WHBM=Xw;R)Vs=^;_4Txh`^CF%~(U_=OIUNCjKh$J{!JiJ*XSF?xw0+V*8W z;5pf17R5|4dum~%xa^3wY7m7Lp9tf$6XrBW6Q&Vr6ACK97XcGoQI-Ht{GdZd>ni#B ziWYY)7<(VO zr5+s4O-chxNjWnxhF0@yX=zD)ZlYR6i-?E_qjPq?SUO#sU2Cj8`2rs%RGzk-7?dS3 z@z@ZUgXUQZ25e;%ZpNjWhvF!b~w4v0;x z=+800bctS3x2E8$1!GkpaJHLp9=Kr>W z4A)X&hB54@RG@ELtlv_ecVknbA@9WSkYL6WDtsCo$?&3FkI~Bxnc$^Hp7W-kO_R5R zF{+H%;253} zO&y4fhD+Kl6NRc$bD|r8BDdE+8oX&(wuET4fH8!GOMsLT87qtW8NmoItfA)pU|(JL z2k5$}Pr~X4vB*`U7le6yKs*>@h6swx`ZDIP$cmfDuLn1g1?giNToJfLH5RD4u~FN2 zcr7J0WslY5$X|fx*8v$yFpNgb-Tu_i*b%H!9E4qC;_R5(x|{{O{Si3)bJhnDxIODl zFY;OCIiIj$yC{lhm@^q|;HOk8W<16jTBSB0^~${Ci_hoNC1r$N6^x&CJAVxX6^?5oRWOmJdn5Div2O&FX!C_`pE? zmE-(odj!Kux9v!xpuop7^Vdo~N z-Bn;9M3&T~ivL-PB3E%mq`mjmeYwOX?hvu|^YU>%qIOETL z572u1_aFut2^r&`kMIF19*_b=4-r?ji^}l*Gl)S!LLkM@qN>WhGYf*iB68;AO|I}H zZiB|%y~&}q$(Boc3ph}-%Oew0k{JGWh5UH>YvtM=l$O6n^5W5(=PQwFJ$jEnU9rq3 M(O*RF5#P`JA5&5oXaE2J literal 0 HcmV?d00001 diff --git a/jinhokim/html/404.html b/jinhokim/html/404.html new file mode 100644 index 0000000..672d557 --- /dev/null +++ b/jinhokim/html/404.html @@ -0,0 +1,15 @@ + + + + 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 0000000000000000000000000000000000000000..beab571edca32e140bb0741efc3533299e411388 GIT binary patch literal 79163 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 cmxex}22H-pmK*a zAEdbf*)4dv;aRm=YP0QrE-JS6fNB58$g*;O{G-M|d-`dPl!kf?$^%Knlh0CH}4()H53B;($RvXBf&Jl@PxUNk84O+GnV&2-L{LZzl=*I|+T z@Td|D{76Zu8C_wUm|!pL`>vfilvXZMFjvny@&h9XY(#wvgxZc5LzGHyrY6=M{eqlG zi9I}C-v5K3Asn*K>c8qvED`J?Iq-76-c&d89`6!hN|+?c6DdylRu~}&C}lTBX|7^3 zto^pA&>+0t?=kE@QEsO;(oXcX7f%n{(M1*M4?yqa$EBj$)3ghkkoiblT6wmWDVr#L z2k)(4K;zD|ZsWFIH#aF$OTgvwt(m=;Bde+gpQ-H5yr30Sxwj#z%n2^x1tL#Yj3v14*qhmC2{koHy{#YCPup>|uKQxepqMKV4*v@n>j!#XG{ zc6y`mp1~>-kjYKGN6?=eu$*BGP*qv3-G^{llgNw}lEop%XUiv#55sU5SjFTZGI}m5 zr+ii3wFFjhK+}o{errx(weg;%dRbgswdZwHFEd!+Qd#*)=j}5QV6b;QKBk`TxH3>} zM&&DRI{P8!>{3Cf^iodPRK|^%@!aQTcD8nr8zqRQjof{O>!~f_y9>rF17cZ-u`|fR zC>|%X)VbxUYXe$#ABY@vUpHEDkp8t8m0$bwdQ++kTT-<(-^&V;-Ncj*R8f@--+YWH z%%Ee{g(9zBCYSjR46~iPEfD|OL&W1ZR&>A{6|%E?kF0018IKatU4xJl+f$ipZFOaZ znpf+O^%tgXC)oD|6Cbn8`Y()Oyc9*1I+_o9UTdtXJkrC!0 z1N7e=#iBm=#%`wDya-&w8`39%;nyn zpStW@#Y{r3BItp4NuwNiSSjw>(G4m1*;d*ut4Q?cQ*t7*4rocyA(-!#NP4Tkb%3Dn z$}d81Iqf{O9}8GIcV#U`kfjN!CPE2*j@+GHQWs~ItuHKXsg7l~{o8Gpe_^C%4*bn} z>bd!7oo$V{=+NgV@#9Tyxx+_OsWhO>Dc7;I^N*aFAR#@%6l`4F?CNM&xfG_f>PU99 zwg`u2jY-qVMU~b~_9vUT9|np^u3^sXWxxQgr5bMBGixfS)uAvsy6#c_{EXEjlQpdT z?+6T$E((Fc6h_Mi3ub=xh!x*zf`*?6<(gU(J5|0bR#aAY0G?MR+^V>uv{7E#QQDsw z%!$S%>vF-`{*@FG{G&amoLoAg3At*X=OVaVm+z41V^Nk)%CsY`1V){MYJ#a6?g5Vdo|>1QDYUZ(%&VY4HswKgh^Q)2!tul+5fr^w|OUTM@& z01q{bw|8qU6CZ`B29|O($q;GgY4P;9xa&Ke-=FZ3B|Jvehbcq^r|4L})jcjmaGP8V zEG2hnBh<>Jg#o`TcS;RtTdo?;;5%nn$<#%pR33t0bBX1XPYA)Iz!Slf!d+xhFIN$#u?XaaH<-9r41z{<$;w@iuHnAyf^P`+YR zNGO%M{GK}BkalM@mfJaBCU(~lGxI6cc)VPzpTS{nrp2OM0|mc8K9rW8gAAGea_8$8 z2hxK$ciB;WVAFi4 zX@xhll`MdiKLGkhvC~fTqzPs*h+h}S~lEG-0R!gE?mn5f=u3NW(Ve@S}}%A z-kc*DtYQ4JIK6CC*yBY`^1dU@4Iv*ArZp^`!KP#sIsuirUvxr-+oA{ri3$J&R0vlD zbbM7(o{TCfM4DcDx5Bl&N`w&2OUU=_VC@(2c%0{c*)ObPyH?|}oigQ1p9Q-XvyKro zv-uMSN9W}(D}d~hsj3wyM0Gr@;^ipB3G^vS5o#ri&n*`}@_RN-V?&8ht7noe9Si5i z--wH8z9jL{@J!HPNxEd;S9bfav8;D5i4NMh#PEW8v_lS2ktHF8GvCh&m40z2i+0CA ze=#NO$>y%6y>c0Z;Z`IEp5i>v?6!xa5{>dQnOWV(s=;yznzY5&Z!>@`jK1kcClec$ z47JDMgr!lB5Dkw+3vWB#5|&c4}RoMyXShlWKsHXj?876N$Z zI^LRMGa*M-F>;dU8vG914?9}|mwafP-f*!X-Npc`n#vQpKpaLrPHc&26dV2#by~eU z2x(ZA?6e_Oob{(H?qJb@VTqsTE#FVOU!{xSkTfT?hBJoLi6Ls4^3A(Zg~E8048vCN z=oXn15?*$mz=Jt#dHT09ePWAv3}maG%PnAGy0=E6Q#Mz@id~ECA&#)`k;jh?bY(hk z+Zc%qq>S#V#NWvQSoVPeEY$>~>5RoL$Y)>>--PZx9xAo&r>ksh;31cE6MM~>EAtt) zEkb*#rc3z}Z#b`;0_v$*Oc8>q6TvPvS10t7WbhY(7U0MaND?8G(w_(SA1Y z^UbYVzE0a0jcf#mXlHRI(;hS`$5ZCWc*}f%;QR_g!xXoD9DS@hpsxASJ8j|OB!V4& z*!Mr}S{O>6?kb-zIx_6RRd_5l5?osm(@kd zf1rne5-WJRl8{xZ;YMm;6;j+eh1`j$ZT6n+mOuxamo@&gx>DTE7_CuV@=eBv(h#De zm_`%ySj1%z@ur#(x;!m3H?)#;ekpAwT>r+X%m97F1xOu2+XynyaZi|aV?wX7nHe{0 zr0Sz}B4uSg`7kDnov-yfuG5WG6g>pqRkl2_RW0-*R{#jJuZMjA1!Hs`pH-I$1tsm6CSMQk&M}OT6Z0$FdzRlyFV8B%& z<##2{9R6MzS326D-65O0BbpDPK+Cm`o9!6G;ocO$ks4#?1T;1)S!kt&f$;P$(4F9& zCm}W3fVU))(Vt*^>LVa+vJz2l?hm_E+D0ZmfVgP&~#Og5}^zVwVWI6ucf>D}3;ZwWD@5A3KWxskA=5W*==> zvL(0bv!Cw}mkdr^jLxB(%dre<<`gIG$m}$odIjt_ZBJ-gT;Ye`@1o@t51XIQ(W5Kq z-j5!e9P1v6UxyKPrxft(l%-L~>n*}jcZUBk*%vKgje}u_L{NKI61X~lWDH_0j1Ko% z`5i+unXF5XU}<9uIB-vt72F?FRmYFw3fHhCgA^QLp=pgT?Os%=W-#hJZ!E$Xmw0OS zekUQzhm&fuN>+&Zby{MKm>2#VLk2%nm{gZCCn-Zk^j64(7C<~yW+h%-oZMZOf!~{F z`NeAFi|=c=d`X4|q9zg!d-{h2ds;`eO8UPrKWb%qLjllXr;Cc4gMcbkbMb2lucrv{ zPM~@kR0Yf%BP<#hyVDz5hibaoW?ZgRPPQqxFC3s>6q$*^PLgt!Vl-00y+tsD;yaK0 z46ixa?Yz)}zAIDlMj7&+o5ve9bMyqKt^-et<%oE(diWc?z5c%OheLFlcXZ7b@Qqoe zHU>-0_6tEwikjno8}yC*D~}ru|G?nEbum*{hdq(lU*s<;9QYt6NA|L*773VC!Xv@_ zMaPVv1=JaZBhfEVODb+}x?78gjG&u*^5*5zjZ5e>mK;#R#26CG@ms|io{xj^P<7wm zOukp_fDf5uql(QFDKUWu%urFQ$ARiRjjr0Pdz>F9#8jsny5{aGI95mlnKNS)wMizc zi3AtKtb@p**#-V zB^CD^!{TsUQnd4eL74-hWwXB-7lzA@q)H-*UbTyep0e+%cl+4zF+QeZA(jyzfwN4N zSi#JhnMSPf_^ly9#wc!L z+}gS(wPrO2m?TzA)>c5Bj+FMB8!#q!{f4E^;y?$y_s+tW6fj4Vn;v;b9i;r(1We=c z#_uB~J@B4nS4USxBQe~AgFJh9Ust0-{zi+CgEvNm7+yDO)~bKu=drji&fh{(>j( ze^-3^%yw7*mRb;@SaH*+oUjN55@HGx@r&q&LSzp92i z{LH1BY-!BRs*bGl-V&6@D!h)4Q#;xuQ#r5bxta3wR4@JQ<$Tg`RO6z}WMiRYv~uGW0@ESz*cwnsxdBF$uGB4mt3A%9#(qUuagS#$bhxhw1WZsQMT6gJg4|eSIHr&Iw z1#!&3Fyn!hZod|mhB*JHGi5!AgTBy91{kkO<<=-6jdN7x`>I7a>KzUOF+c*As8ehT zXXZv%1VTbwnhOPoezw%oMo~7*s_qzqR+;-F`VU-rn&wo#F+M*JfV2_a>_??DyBY)t z9-2|%bm)m`yWDV+@~Lg93K2qpI7lxf!cjvBIG!~DuRaNMT&^ebV2#aG|5?Xe$wnn7 zG=Wh$?iw@0>YibaMI6ZzsWG}Bi{s{6tp82%{Me&Ge(b0-WhqOZ@c=!Mk(mm4vO%6D zC3^Woussd;=za%eJbG`8OUMuSJo4E2i3*860YkM>RnRZi&RdXoX0DQ@a`^$VUhhqo zJ(MqrpqHQ!3zy)@+AZ;Gt<(MGe$S_h;RUJLlxEFv8V%Ul5Ccdum$vVbe2R*4XDim3 zCr?%}Q0x|O<+N02s3Jk9^B41dCs^zOFwRa#gJ5(`Hai!o1uviaEaETtjRFIUo(En7 zFXk|YdmtZW!Cvq{gMkc+%5U=)>_{RicHHXhMWZ!4zVhp3ARNxmNWbae%+yFRpkyMn z9sA?BKKb!q6Q$rU%>0sfrJD-Sfd!Y5r!j+XEIZwmi?QOBi+aI3wNhKB=8zBpvP3;b z#pcDtC_^OA{!YVFf_e1yf|zasKAL{=;?8)B_njJ+F(rJ2rqoSP7nM0FQSR*xQ|c># zk$~zol-C@Ki5ZEL29JdkQ4O9YA@hS)TrWt+y5hj%T_UwPc}(Xu_I-{jt2tm@@2Oz^ex03rPa5dnWdgvFCV z?7;yG9fUQLSJl<7^cITD_apiqS%?>kr?9s=5eQhwGWQ)yGrN^%!6n9=SIDlo56~=@ z4Q(Ek*BCEv0R&(#32an_>%5E#pi?;)%xjk!~eKUmW&Zx zx|SNst$JZODePWRo|mOsZY>Ii=ZC*%pvC!SNL%8JlE}##z$x*j(A*lYa8S}#%YU09 z5%4-7c>jAJmQ_`gPR<>WRXW|-DL)3mWiMh7Dq)3>4d1B1AhTZr=C+PrG%@ni3({aZ zE7Inz#lNDUYwuV4^SBeVqmKWY;p!(YG7ZPWXY*Eri_e&O#Rr%T15s_&MtG+xaE#D+ zV~2=re|V}gkySS&7yDp>~xLH;$3XtANCs zl!{)}C*{4PN2@a4on6TPXgfE_SL}FU)WCPyGrcX(33|#HhY>g2wx>|&PT-c)&^)_P zrgL5x5D}o3r%j@_wml06n%06FyBgj{Z1GTd)<*qsmw4 z&&<0Ks$;8Pl&@b9#feQ!;0{$10^_lWTUg5w6rUBzjo@airVOb~Mx+$0#e8<~{lTqp z&9)MIi~D^sa-u{=(YU#UwICx66IX7K(=rOt=tpe|4Pk3--2Vb_eDh{6U$de-`b`iF~W^p;TyUFoE#4z7u`OYCwjQLr0jXr$+&``ZaD(SV9o zHki9QfimO$HnIzj*Xt9a&#Dc#qqpOA1F*&x+#y8m)qW8M@1(Xy`PFGZ(V5Wh6LT#E zG$~9{V}7@kO~1KScy=G=2<>nHZ|V(`bN?9@YIe0|9|A*U#rth^eHf<_DGh4SaimU~n#T)0tJpTmwjxw$A3P=v!JX>YpnkpbS}xcN(BRP@ERCR7egoRBQD38C5KyH{zM9p#wT$Zq56fMWerW{F zEkSlh;az}FBIV?0i$7px%vSDb-o7-=moF?)e+IFQ3;iI4`$ly&I4WI;*X7!u@3U_> z8hwH4)Ct;QX1+<~{Ecc?ob`yP16m`|;f$dcrV;X-vWaPI`Kb0c{gu28_6P!jBzwU# zGL$2(TS8&07_mLE*k?Gn&o@n~DW*EqC~15MbiO6t*53IC{5s7Q6kGhF9{<=3OU7^> zO?)-XbZI$wuw1zDv^D2s&KAi%M}DA{EqpJF5)c!ncFp%Nu5Y5f?)#$p&v?+}5Q``% zCwp|EM3%e3ijq7|W=NVM>zDm$2%1fJalkNik_a*BCU&^sSbw5QGd)@g)w;AfHssBoYPA0C#8qe&Sj?Lro5+++>ir+j?o}1T_e3>*l9~s&4H)KfbJ+Lwnj))uGrcs-mF}-q zefiGZZ&oFnPS>%Mpvvlbtha8Oim|1W`j!x=E^ThLSv`fEiRNShVmy#}xWt?qCPim5 zI@d^hq}@<++tEV#@Kta4$cKgqMV77r%BtbL6yy}}ZcgH#v=F{|cEU0*s@NDH6=DmX z9`sC}lvtCAl4@PNN0Y|WeF#wxHWuYZJ}ac_D>iepOq{WtY#Qo#*cnaMJ+15BK=zUa zYY|XWD+@zXMDAz{4(m z`%?Typ)gGdI+vP7B^q79@$lZvFjN2%V}RpHQyW!R28`K!Z5=M*J4kq(v~lV~x(w6< z80sxqjglr4<4vwC+{0e&dR7H)YOsLGpo(rvtZ`H#uBKuq6(bz?DLY6SX3pk~VRSH1Tt>S2or6kN5O6 zc4ej%Oqi&DVW!(YyW-(gvobR*#g_aC*jPF<&nk4SB5GwueoG`WS*g{0Ql9q%)8m52 z{Lcc>0xDOAF~J0a9~FJICNh}7B?EOTW`rer;kL1kFB%0B)XwZ2>aZ@1ao*KyQGuU*L&x?MieQ_#bhOH;&hYTZ z6-Z>Gs1e@#&?^2$Qt-Up+Y_|gvq}MK074EQ{7^@43V8~i7qT>z;CsTxk4-;|ZsmdG z!Rl2MT3q@Kh!Px8N4aXFhv|%TcFKW6>Uo3AuE(99c`c?hOb=-ixoB}>u+wF`>$usN z66_#nk4iJxy+y=p1|?C(WnZ{DCy1;B;4Q!2Zq9^IFzZ|uNh)%Q_m)Lp>Ry2Jv=1C| zaMmX*7KP>d@I#w7LsJ-0ElZ^@M>xQ#gocmt0Bf5}Q2|6o9QKKgh{WvrApD=tP{}C< zUaIsyo0ZcZ`YfV~R1?up3R&^bxj9Ll;URuP+xlOZdQ2bRzvxZ!E6yvMWyJIv13o4t z72t}#Z#_R;VvEcGu1DETV~|##xzH9G*h*9AelY-!gi<2JD_fOO7mq$hZyXqq;^%Ix z3R=2vGp~V#)Q8^A2z%wfFo}BNuzeeQQJeG|RjX$fOHu`=mvc^0QSxhfj4@X|O0P7Y zwHRGNl#elsEmvYPuhgEU0r?tHm|{hDb*8p1Cx2nEg?00R%ZWe#$en7LoGt@6U+15c z-#0x)t^JWbwSQnQZs}`wPOtYmGyFvB*%Oec5ft6SW^%c_aq?1p*XmMoCh#$_SsYrn zaF4tBx*)6>POeDFbxBxqeI+LQ78=seV=_5f{@C5O$)R{`c4l+`D}FID&RAXj!>{DU zVPxUVpnVC&wJtA?$JoWTy|bVj%2J1&&Wx$oh9^)$Mx~FlxjltR%6mBmi*QA`tHXb3m03icU)c>?rJgA45HY@%I<34%)vT|5zMsu zX8)6CjZ@IW>)R0pc*44GsPF&&URZl>#wu2RR~v9`7!>ouBCoevdwwZVT4{Su7aJX* za_We_V<8Q9$x&)Iv&ycX5ANXz3OF~C!0puS`BVEBX81L&KJeV-MxxZ=tMJyd>RS!7 zZ*&Q7w>zGuGBW-spBgQyeo=#N+A0Pwd4-iBZR#nS@fq8^oelxsVKe!?+P5F1xYp}c z|Csmo@qrGn44$md)G&Ct?$0?t!d5H=deKWC1KsjzB;5=M$cNchbFz} zx7Qf6I&AyLKL4kCRZAGv-+JDVeWQ=&xOe_ZAGh>O>->_kG(I5m4G8XW`MwlI1xdZU zyje93&y!r**z^zJ)UYh90=l#*ptRQR^8D7bl#13%n@9`yvtboJy{)IUZ+p_zUCjef{lXs|dHU<+^@b{Uu{{C$K$l-?+Hk z%c=D;4{rIZe@CZ8bQCu> z9uMMmU5@w8E5XD|M0yh)=Vj(|-X3uK_14fJlb+U~-Mr4$cDZuD1@P8VeF;~9*luPJ@_Gs6V6EmG zt|!G~)FW8(m)pV{l@*6`*;14EH#Dw^o|;dj=Y2bD#pb)h5{0IwZBANeopE=zdQgw0 zt`PCbC!fI0rSB&SN=zY z5Fhp^ORWu-C&A-!PmX6>-<*J^!(F|2ScUhejx(`efoq0Ko6mPU#4BNXXcV842 z_pe}6&sN-)m)Pr;myXx9ik`OH>ExI7y6Ug57oLZI#ySIbyxN-I_kzgjb1w4T-emsr zmYnx>t==jBq1^eEVybhMJWT(O_$fPSkkOrYm(|yke5De<);`-nkzeTbtAT2NVdU+e zma6E_dW!Ph*Q$!rj+m-G3ybvsj8k4vGyq>qu<6UYU;J&l>;t+Ptx4|vDXP`_jZ~=R2RIxqDGh3KH!NK4kYsooX-%)fhNgjqeLPM{3(ut=R6On;DUFNY z5NNf`tU?EGcxALLi=f5ouEAkRL%07U0LGAbBLF`4-@RY5S8yURP7>x|d}f)HK)p>I z>@NOM|NllIf1$DAYX5|y;A^4JSIzrbY;I4qyPa#(vbSAWX>rf@7oULDwtvb~;NANt z=ZD%kpB}R7uH8PB-TT^yfYpbt-If#bYs1gi{x1RntKA1Z=l2N@^-DfI;K2X>;XjuS zZt5&uorz%S0-8)yZgPt={ zpMbS<@@ttv_Mo0m4UgW%A#YWIkotC;d$0X7Py0-m%Kyj*%nwI(?N&AgZroQlEs5WU zzmH(IX1T3o4;6J=$xlZr-VmW(FVXYZDiL)GE`V}W=Vvd$D8WCv(!Y(= zdI&)IFV$*=KG$!uOFl*esL$AA87378vYG=1b5|A0Y86N15UgMasz-z>$p}hn7>Es6 zt>3jE!|!NJ*!f{rtok8-gnnkutmW1hki}|Ht-+7c3sBFMW&m)f%1J{Sh(9PE$Kwmq zurbu~4rm!@qMzj)*p(GT5J5wUQaYuaXSDbr!{h{|aFTes)g%=#A2Ow6H;i`btD2t( z1MfMEzTLqivS_;>3J&HjFWD%ChTFI>=S_PX;BRDyoNW1RozAlh7Ev|6mq&G zRY%4N^baDkx;qP28bTKM-}BUe)<{bFLBTZsGoFf67ksFx9*j9fin%EIF*BUl3237e z$Vqk6itH0WXc0c>Fa$qNFLLJ`jt&(8CK!8y_jlT9WsEWP^4S3L0uQVn!gs)aN42RQ z_>TSM%%o%KJMz`xfUhKe(HQ=*27Fq2G)fX!|?!$yt!9s;xpBh_uD~0je^@tK3)hPrueSlOP)D4sG zFtc8c>bM6%&Zb(M#x8ma>hBgNR2E(Q1q78YL%i+iGS&It=l_&wBrsfHjDv-g6(ulg z;4!^JH49}sHAiOkttdn}KtB>G`Q+mt=gz{DlAAmtoXI+wr#+D`jhQomlSga*PE(a< zfMAVD@|+P@U!@>-VMG}pzwW-Q0(EfOTp~D6lE%G<9PhMJCD%>$N5Ti(<*+Zv7t1zn zkV$;`j|c_*Y^op7TcnLNV!%WZSlfjJOVkANI)abm6GH6vODmv2v_AigV#mODvRI-C z`c(1i3l@!EC*45BRoh>jzx_NezdgWBp`qnsoC=XNf|G7xK!}5v>S5$B(+ellUOdZU z4j`z2H{>bBHH8n2L>a~$wztwQ)5a?0Cqsm0ZJ{U3%i%HoYQ+QI=L~AK zg4m0)bVKd6Ou!NNn=o86Dl5r|mCGj9RF`F|n2_(d9lEH=Ol7Cvs_+on({Kl*!iX|e z?Rpf#m1p*}@9HgrH%W@V)IpqaiOS{XalVX@bEaBXhGD=a6cvTZx^Jy)zWhfNbzZxX zw`Rcxg|gA+1443&{8eo3DGC@Z0n z*unapY3JJ&FtZv30tr(Ul~^nzWG=+o!Q3QesJd^2iv^;DpFZ$56AA*NE2Kk-wc!W2 z(C9N{H^9KCpVeGsKiFt-8CnPD-g#pWA zzZYCkU~`m|Yv`si$kkrH0t~A%QX+f4A;YXt1o2ZDYwe_GM*AQiqTTB8)__7qn}VVj zKYp@wdE~>x50DfZe(A_j4*GX89(*T*;)m_jOJDDnemBhDv7fPIT-6Q1;5nR)Mu^8# z*sU@vjw{PcED$a~4o>k@9diAh=SFh^+@RwH^Qnv)!eInpXVNY4%>c&n0JDcoa!VMB z9(I&eeEPJfHijn_c!lWMGktvQ7q5Q_OSBNHOrzH0bNG|q_fNC3y%2<0=L=Hh^qB_C6m#jc(C25! zU?g`)W-GUJqk`s%^;{$}x}g5c@CWJ#S{?6xT4H?7=rJGnI$V*P_N7yvH;c}~FS6?( zlA4e#dtO$Jw$zD>drHQu3s|a9YSi;*K$rpfcm1>S%;oQ(Vi_)%?KjIR2S<EQr-Ckul;L+7^Q*=_l}N=xsQ4ulrK|A+K(6 zDRmw$3go;}9X+ypJ@xEts&mLuw*gvz;}0^d^=Yv)^emM0;4#_@3<4TtK z>7Dh1+0GK~$`Ins?Yd9kHOxj`96Dd)A^_WjhDs%g9r6}#Os?-B&snRNw})lamL$jG z)!_}wEb>;uuE3aMj+p+xMP@&YkFw!z-GeSytx3yK^%g$O+L0};B8(esZbmzMmFXUW zYBoN(&=1nfF^*Vyb-5`TIiMI6BNKKzA3R6ZA6^?tz!cfi1tZbSY@Pq`YIvGAe z=wrDNnU*tFV}9#3hffZ*Du$RU9y`$o={fv&$&2rxu&F7OcBjX3jKGuJ(>Y7el+Mwn zsa2LOMba>={uuyS_!bBX-9gu9;JmgddssGOU20vz=ybs3k^Tf3_jHHL2TR+GL}$=0`HJBfbQaKftN*sBM;dp zNBVh}-xbEQ>|Q71qBN3yp!bD=av(~J9kczm2cC@8M&Dl?HMesLXs>( zscLqW=yC@#<_4fL+B9n+ofWIg*|VGWhgQ6!uCkHCigl-qKgaSkv1KK><=oG<7po@u z5K)1DF6l_n`a=x-6cbj9r+LL4p57kK)8WCNDpB3?MEL2=p1Dcr51z0AN`fJMUsOJj z$nYS~hlUi!X+@Z6pz_~(``Dl^M<04)S}pKLZcbSx)S%vAP;UGb%Fhyw2fK@?S&}yn zdIfceupikqzVS1Zo|uUB6gJ;gOcbz&yNgWcKv)_qy=Ql3DAM=@5b_Gn9o0biUeNEq zyaM{D%nG9c0>pcsfz2g4)m^cSo1G+Wfz5 zyyL?*(1<)LP~+|<0?`kk2^p}R)$*Mgghql`$HgF*$kJ4xgUzUjMSG#B00{=3pCaiG zYtxbDk@&@R!mC4YwiUZgE{Bu?G~w_FKgaz*VB&I=01DBAgqR&f-!P`#AR-QUcOc4O zLyA*L=mB6$oy-k}8ex4|N4QZ4DXL-tPNFChz6NxOJ?6gz1-Ktt+Ni_x&lwwOg~f}G z&l}Ik`QcB4UXSYMyB;e2Po};3&|&Q=Qkij8l~}gpf269PX$-q)BTrRc%iwnT3!R1H z6LwEYmVJlp=f$pD1de+e*lnHjXDF$`#>>wHEv+xV`IioJ-uTCc<-s$m>~k4|EMuQA zK!=TJY~S}aNLh)>M{xaVv_V1=Z7o>uG4y`@DpFJLXUZx2qCRf+%PoFcNo%0SKr_}|stt0n)V+_Bt~R?f5=c9+-fZL%%_7siQc?UI zzE+9SN%_SIQe?_ z3+!jdMNa#}HDcwd%)49m<;cG&+cQ137p(kap%Ou)$j;hq59TL>08(4A4m&NnVUHjn zTTfpd#xmPsHOKmQEC&3F*%ZBlI-m4 zy|c2i*11llBRQP7Sp*9DEGRYK4YB2dVta#luiFnV!`uK|wd?cAiu)cavE`SnBqqQXj+TDZ)MF+4Zb3+riAP!Y z7J=D!zD{)J?d+ujJ-mpylABH|I4obg;xj2=Q`!nKX|sxp`%E^&S_IVE(~m}`#UE3C zB4c_(Begq6OoQXFNuj7Z4|bf4BK$NZCpXS0pk4GSul zzt|;PB>56zT0tAp32)Da{1$8cUk&SLj*FdE5lWP@5?zUoIFsgEK5=c4&um{fX8kW> zL2B!+&#vLgMjz%#^0X)uhUVhLYvI&{7XEq}{?PCY+H^)vZ38klFaIDe2L+{irW4Nu zPKzQ;nW}}CRP^)nl7LRA`n;5wa+~D@YFTJAC0*#rloix-hKEf?{50+ps2|z8KU2-!6c*{-7MB`GiKQApBz0Pi$MfVLfQ6JfV&Pgm zPS_JQ77|Yoqp$nWA8MtD%4rkR%nrupU609DT8hPay|Xp!1cTJY2y08#Da*z#>8MjR zk1-E9j?et8Y9D9;K{d`@K@9gMUgaJAutmfh45-#WIXcmhrr!T&7wuoT@>-{fKaPbQ zKBrvG^v%jvet0oTQZlBpl6ixZL!!$WD6<--BqACxN2o*l2F;XCm~mxVz8~WRPix| zhZSk-eL?_g_FHl>ui-n)AyGBs@WoUz@*H%S_gQA|tzY1rqb4KX203*FDFRPaKwb-U zry2}`V3AnZ922DP^yDnc)(U>uQ@7k`MXS^5V$O`J#rM2`Je(q`-|7#QJzk0!<3|PX z3uTFUg2Vx9KQ>!*T$tOc?(G(f9YDGU7|g6#Q9e>*z6|i!`!D7u7JLEXg*PpD)NWSJ4IK4?9l^xR?jw>iDuai`~0g zh)<%Dv%u^;7Ho`G-5g)&{Cc z_SQ{wH6g^4VKV*-@?x!EQH0ehK`5sY=Ax0C^zWCIhrESvNMs;1>D^Y;pV)4wOP8h( zK1E!1-%=5xu}jgeh>uA)m|(#qCsLO0Lm(LM;@+yhes|Sy9IbHyxp^%j>Ndq>>qsf{ z*_Jtx@`2fe*>FesXeP};&E0u4jdzdYlI=RhK2DU!4|lrg6~1oG!y7>otZKOU`E!db zW3fY6M9*DGgd~Phuks(Lcx~gE{whc5=t0e_^#6)KkvVL>h+45FZJYvn98WlIMBhqm z#85ovUZTori~No0{ah+mb=P2Y%@YW==M+R89c3~E10y-bI+{jf()&``#DZjt6T;|Y zgcFrF##a&@hA?Z{K{Ms#k#MhX?R?A) zfpuS?iksaltu}-U+0Bvlf~{D?twj7oTq`T^0f3xawDHY4FE-~irp8j?V3at_nP?Q= zm0D>x*K+?FDNHs@(2*7a(P5M2rV}73SP^61tfgi*^NDFrF+7<&$(89tj7`qVG8I0a zx}m0o5h=at%tnE9bcYf5ndDR)HMdv-ph9yxhTlm^NU59JKkxtl1k2`L*p#3LEMYQo zTvVJgACe2JMG2xiLqi6uK>I-vOX9?gAe}=Av(|$p+FGLJU_48+ionR`% zY3`8S37WG$AfdV(Cd_zzPByzZbC@cMNTs4OQk0fse+Nu4O{Q`Myy60VaoD+H#hmUO z3A}<1;w|GG>NI3-TVGPr;W#%m!#|-O*@6JKjY+lDwm}N>{$c+W7=}Ay*j1ll8qF$} z_?eB3BO3L{g64N3=QOX`bftF(hup` zJYP^JCKf2@32?#JU==Ei{G}0s!j(G5;@@n7#zp_`@V~1X2$3|=a4H{X(sK7eB=ixH z^!k=9>P~qX=Yx}?V}9q23DD4LpvPxP8lnHd%>#bO`I(1`!E9Z&rMg&JGZc^nQ*rs-G z5f7ztGTxwKMn*YSc$n(V3ScoTQ!LCC_(HG-aoaqSOr!j8Zec&n0jARlwgv+rB^YKI z73pBobi&dB69nF6=~Cklw_jfWri4`lBNOgb4qx{)&{|TfXUX+IF|-474;EYEDVu3) z<42!3UTgZ}_uiz$hqi$U%b|IqK$aU6f3i|GqQ4f?>22g@=$D`n7_P|D87sH5eev(q zgm*W+5tM|T=893vgcg^f$f#=ygiW!ihhN}xVXB7Ablk#_B_o@0>&WViRGvEP z;zJs$EwxNgAL@7xt+TdsTeW4@A^!ca8~Ph#y4In4Gl<8#Jc_k&#MC<5t!Rv7@|*dB zIe9oA=JCM*+uE(xMa(JNoQFZ@9)@ac*WmRW8V%|YHn$v}pqvqa83(eQ4bqgl14ejx zyj?if1z})PXNAxpMxlc`d_^^dzzJ!LI9b)}p>tgFt} zVW2M1`C(tLgLElWfs(Ew!&wK^;z*2!(E)UjnspqJv%40>hFfs$z-Qw#7U!v$3>wKxR?@OZa*yZ|_I+?I}(jD=sK-w7y zGh(Q&@|P8aTdTj_|J{7v`;|dOAgdPX#2XoqMH)|$j@OxWh&zAiR6zo;A;UzL2qymY9%M_yRKu);qnb>!I|l$(bKYHe{jP zruz<Q2f>;FuxVdSVO<1Wi}hcWz!Wn-&F6HCI*n6 zFyEM`BXzZMiHcjAH`qm_)S0i$j)~7-U|EdpiNJ+(1_h=t0J=YP*V(Q2;2Lx9CU_my zmS+T=FkA$t)Cyj8a|0@KhyIk`EB(c|&NH%CsLlAo_PVKLABZn2FPz zFd}fusmBPIH8b)=!uwmM)T;0r?BKnMDH&$oYIec3L+S^6|L6%U4dTtBbO{=uo&bzh zv!ks06!-ewzeT ztH;+%yTYIJ(d$sdOpZcngVUJHWJ1mqS`BM{=E2s0J?o&^pO=p!@T8?^MMNpV52A-t zRkJ!+ZxJuF$gy4ssp?MY6-Hu+Dv_`Kg~RJbN;krE@1&wP9w5`FVjiFDlcFq|ZcXZ& zWN^5ZY)yKs;X}7KEG+4~9qRj~gCV-k$!b0SBk?-j%4(?WP!P_PNkQB66!>wdfp7$+ zQ)yM2KEZ?9jUepn@V^@wf-COHFEKQc@RHiF@hAdm2cE}@9G)Cfi%U5CmeJVz%ss5F zw4f*kRG(f(cxk~p57y!M&2V&gG&U_UOK>rNN+V|5&^4Mf(_HcGrj~Y-UNWdP{79xB z|4ha@HO?9`(pE@@HZ9yQy3S8^tf4~R>s<+|WxFCXD}rgBH$|PZJpg?(iQ+E_>MI_1 z;V-c!Qi!)j9f|SKR?-&W*Sv|I=IBHgk05wm#(Q;S(hhO-E$4pX=FFAt;DSEkH;Dgu z{qShc+0-bJvWxH;l8|qKF}!Cp!dwEykkl-5i%^sFG4-x2zXy8KI))<zf{+|J6nb5s&7{>mvcD-9bp@jMwA9VH+Vzz#s$I{2C?kF30& zMx{QW^eQe&LC26?2k$7k3uIGB^!%(TH=+y;4b%xn3I zZ93ibR%!xiX9_dK2ng3a!vcLtu}B(f{5+e;*f~3b0a2~z%t6YwC6-C*8+7Ix95~3u z`})HokJ&$H!jE2HzpiA%<-p;sY=uU%_4OQcBLBE^wasfEzo#`k{(M10D4Bwv`MZU& zhTB&wCXM>rk9k<+DwRRmbDP6XTi6`LJ_J(0P~Q}R1SDT zgVxrN-qQL%U>76tgA{`Si$Z#b`F&12Lr5?jw89!gJ4K>HUxxU9Wl1fqM>Degp^gQI zaF^EH!sXE?NC;{xa;RMTY3JB(IQ>_(zpaotZ{q2L&GG$-04wcXqlC?4%hpd2O8eoL z_v%zptFm<~z$|7igmn(($nm@T@BIMO;hCIWDzJDnmyUdhq_emQ)1mknb8Y>W=$ToJeXyC^M%9^20r()l9CE6w|e;T4-?Jo`OsXR@3 zIMgrtJ1s)LA~ze1X+Og5QY~YlYxH~EFLzE6!x0r;dk1kS-{jGVR4!L=RDZyvu5g&e z%8BY~6iZR(>ita5KRD)KsMs?}aJaz%M}EqTk#aEX%?XvWiblN~e~rF)kBgJ2ITzOM z=b78eoc}9pOSV2D8HK9Vr}>!1O?X(iHWTI; z6@i6D0?eKABlmZ^S$ii+Kq${v-JB|HwzDVd%-t9sN4DVaMjLMAxmois1b5&>j(ghj zF}&*BZZoSY!Sd@$erM%g#PdPUGsU&k$j|kwq*N{{ePpbA%re3Y)*sD$Q@1?jlF9H( zai5^U`IdrNjr3Xlgd`}KoYUv3XHl7_Yw%BW>of*ttb=>V>_{^Ju&CZK&Jq>Yu0k0H zIZle`j+e@~R0m@ZTU2RQxtGDX3ne{90ww8NcyNSt9wl1IVbdZg=^k@pMnmYiGu;{n zQU;{&6!fwjWbcV2%^hp zo?3VLf*_6hY(uEXKX7dvWO6oCJ|!I7X8icS`PmW1QFkzg3$xbwqf3gX96@v)j7Yf9%mNjrlKA_24QBm3o+hdTHJ6vFZxfJ?}b`{z`CMb*7BF0w=3ih3!5|Bjaa+y;CbP z!Qka8nt+6auot70wYXsI3)F8xOdtbd_IH!3SL3ee3DjlIm_=Vi7?`^B4uWEBtk;4o zY2Q*Yb}iP8P9Z@`_OR?RR=N$SOT6Fq$q+TCg%ch;OH^?{XT_Gz89x;^^LDp5w@DxI zzn1N``lQP?&-`Xj(=gsV@*jnVVTDs@Bb(PMK+KD3T5S>|x}iMZT{LV4m*oV-N(5-mroqPr`9wU5=5~Qy0 z3~9v9!&S#*v!vA3!`*_2uQ{wnK{XM;K3?q7Fml%cno?Omcl1pp&R#nevhl2!vYAywU z*`Mf(T`bkO(rzH&Ux~4k`=pCptHJmD0z~+$iVFEeTtSh$b&LY##i1rtP7Wcid#<&6=9fN{?NUPHbC&!p!rJeZ06A%Ex{0_rUa?GQ7W@Z5N5!*y zpG(59uTT`XA21e$L7Ce^0_ZMY$n~+*XFzAYKAk1H_EHl?7pz0lq@RJqY>dj_{LSD$ zTRRvJ3W+s=(OBMOxb=&5?93hst@~VetsMQc3*F3%VKK-Gx!TXJB|nO@K8v(6|9QO< zGhW>Mm$8zbq+>auXwZE zyKrBufG?ZhsIpR!##%<<`2#yd-X&;=lc8!TV$=CN+UTi&EjFWQk z490zqlY~V9;SEZ0qpf;8k%~fX1rbI`9EJ=&=S6lA@o~PRT+~InExW)*MHvxotIwqD zOm5U;<#BXA%l%}y`I)2Sw!?~Yt2$=HJZH|8)EuS_P_bE=FnrltZ|)0KABnoKkXlWa zr-8JFk`Q$gx>-Hv-PK(7N`NXXo5b`{{2-S=4T);*u;l(kz8!t+|+JGje@U~wc^VXHO#K=^24zNl0Ip! zPQC!LW-SKfX1hV@D5lj}!h&DdTabbGnFF(KYRaJ!)N3N;!V4chpy)(U2PJj3*rKZ3uMr^ z2g^&Z=N);XmD0vv&-uS5{1HOf)iUgdA;su2xFV|F@u-RJ@LkB6UA_COf$6NaKTABW zM;Q{&Zi4$)BXqCXd5BBeGHS|;t(U@oqWFzphgLw9AB8LCRV(8L^$4jyh zgHLEE)~QZ+YA}c1H_Wrj=>1zxScp&@IY!=vZGhTZI=aFV$u=--53!P^vp(>^6|?jS z{JR;Qc_xd~id#n__94tvh10rZL`-AcwGCTrCg#s{bc}7dq2Q$<MquS-=#ji4Plt zpGu+-Ol}>C!oZ$8n`g=1SyI9_2S?W`MrBIN&6|(bRHnpXNJQRT|I|w~jidbuS;e_y z*Y4>t?IDOr_xM7&ChI3!j3Z2^|Ef%D1<6XuYkHaBtW6TU(QYxfoAX1M6AW9IF>x^! z@n92q#Tx3}58dvin(i}SzKX$pJQc25A>AOG=tVOu^_^m6v7}cwh(e%%qA{=(W^rc$Suk%gqpu8&ftp{MUyd z(jDqFW0M5sgDY`26^|$9FVXv*KbD+%Cm}ivj5lPuS2QaPIxjPhY)=z>PGWdX7@h~2 zJTSmW=x_9#X6i9TkM*6Y@li@~126Vl5)v+3cM!S27a=ZjX%UTFV%2vl@JxIB{ddgV zKDDw@r(oPYdlNt~`Edxmb;9K665`Eh%zB4xJ^w^>H(uC6h;#HB)wc+GOm{DkQG)z%f2kzdjx14WCE zwUX+!P&cEJF9)gt(dp)+Azhw@L^zeGr2LgIs8a?M4D{YBGg z_?)Hc+23d^nCeUvwPSj=g4Mt|r=q^T51L{%E(Nv@=3ep4Z%p;$M8~0!BVf9RG9Jvl z@4}8d8Pk3_e^h_m+$Adc(`ho$PgHI-qQsafMaAWo%=|L;C zsQ&Q4Ct^y$Txf!67r=8`T~pswsHhqh-IvAOIIlFj9Q++mYfLkBC2d!7_KKe!(b`_Z^y*QePB}~uJmw>v$uSUmo+e8b#2{t3gZ>OPvP>L!_~T*pSvk%JQll>1<<%SFL-Nx0X{s zOUt$==J3ODlUL8S0)eE};T2$BH)UtjIwFg4+RBFtT!n4;9&(ubBHyC&-b%t^f$#Qkc3eZ7?KJ;mpnL9Ic|j@fHsROKoCX&Nix>MgNZonf`eF_ z5v$?$nRvic?P2mg27Ruk+~>?4S7a4D4ZluZSYe0szjiO>b_wK-MY-YEA?zUCpd=*A z2gNv&R1ZDioobJ~%jF??Py8%LipgEDCe?(Zax8tp$}{Fu5R1OijHzw14BGzbNXkIl z84S#(YB#0jw)Fk_S>ErMrrw$*Dj27i{P~Lr%Te7-(axmDbxaRHT_6~8`nfsO^LL(s ztu}`Zxu<8ve^(Lz(ycOIHf_d|A0@-c^aC<#DpkQ{GTA^vJ8_O;%${+Va3Ll3pa$i& zAuT*OMxbUI3P-QVCW5;mEn_+4d@N|@W#Nwy+gU=6#7 z!(aeOh?fjQNG|GZ^OWNxuUKY^>gu1!^W7iSioVsyo5FcICas<+fea3)2c5N)(4+2f zItJ_{hgx0KMKF`K#d@RpA57HsT@NFj@_E$2>#FSbDQ6ww=4~cNTcq6Z&X6<>N+I85 z89ZkRP=s-cFC(+kPrSW(D5t|reA&W!ZMy-po-d{p5Ua%%@!F>HbTfiYhJ0t-8dE6g z^;cFE$Q->zE0+_7#C*NUp-`ZPOB_q-`+gn}iTo(+T@DQ)iFxZ`C}TTg#oQI6XC4-a zK@05n7Tze%`N}O-2=*Z2Vea;${Yi1igj^=K91DLx zKD>&~Qh_aTHhX5ftp7VKZz9&$OL7!YGyXoJ?Zw{^z%x`Mv%t3P**ZChSi zKMlA1w?|u-XHCf8wuknB^{=0n*B-3{W1;oiS}s@CYu_&ds()V2{R1HQxvO##sMpf) zzW$aLLkc_7LPBKUB(SfFJ%w0lwC5fN}13L>%7gs zNF{E4Tv`1;nYDoVw}8z5bW+v)pK<60Kv{&B?Z01ey+Zqj4mDu?KmUJh)%~~P#}#Oc zm0!HZo~_TCnOk)NFCV+Wfs4>;FVHJ|{7?P=Wd7Um-wd?jr+sLLU%!uDX0D%)7EcE> z74#)OLGSU!?o%+d;2HVUPI>A0VR3EMV%Cr$um|r2 z-Y94rniJ7=RJ7mey^b?V+%p;<-zNZCdH=3In?by?xr_?j@j8v9{q`Mb>f zH&-v}kWcM_v4fhMjM-M>N|T4?Y8gp&tA-{oj(HrwBLpGGfyF2LcOBP9B>&6IZY1j= z$}e!Q2QL+GL;~9;%Wt~ zL{okKo3KVZ>%+DM=UIA**2zZV*S;F~vmJu^A-yPv0UGT6($h2<}*r+6w47~#iS^zqNg&^0@tF#g`+PfNVPR9lXo=8l1zww&A6)y|4(y+kG(JwCXyiAcCu_ zCkb&I_R{>}zD8t%)hc`r&~|P7zVcUe!bu$)^9#i(J3+Z+hRoj|xRMo*hFNY#bT};i zmv%x`{{ZGji;}a$-)KI1F}iJxyY=@K;NicanK{(y*ab%7y`Mi*@<6uO3pi=~a{8t= zVp~3GD;IaizfN9U^o!gL1I!>ZC%s#|ly}3wx481K9#uL%P|mD1?Ix==9}kE(T~<75 zwuPtm2jtz7oe-0x^DaLS5D+Xl*&c;9>SnNZekxV*A|>_H&s>{MZd%XYQe^j=;wg5a z7v0*+u)nMj9BQ_KH6wI;q%Uyu`jY6OZxE1d=|J3YVzmFv=eqGI*D;_+Q{mhEo^iw` z6<(V-ErcNr>mh#iC*P-M^-7c%_2*yBtu3?5)3#14`vK$=_I>jCNp-X5P@nJiW-gTP zA3s6CaqQSIP&6DY9Q5~Z3I~G&poSvjOkIMLi<-dw*mEX(*Pt6JDDW-me^75QpP-?T z-?mp6H|;gaM1NdQLh)U7c6!Tg|4IHg_uu0GBU$43o5{)y_09qNPN0d)GCC4`Wk+f~ z3VqWN==xxSxu%a(u}MNIDi8**d~4_b92KVj4f%P6tnL+BfC=wa^bbdbcHh7sr~?G3L>;NIUZRbl?}P8PNd70JAsr<>^Y8(km3z(T z{a)JXqrbUEC4cOM;FQZ?jr-Iv&@cM$nlL%4a3OYaL=0Jgrk&!Sgz(pP%Zb!fBb07V zqN`+i`lcGY{{biiv@!e*zI(}e9bxxj&aK^{aw*#c%24+Dg~~a$O`denW{xm51yy~T z!Q;h+lbCdNJ&ANDWXQ6?NofCigD%^fdkZpya4?sm*_|-x0+{qS)*_Pi%XSRv5!f@L zH=_Bd(g(Z;MxWt6v19BM%ZTp9liGI_y4PUF|2iZI8V+GN6*$lOP93TqXJ5 zC1;P7*u-H-NwPvBE!cv)WDH{RI*gd+(SqwNp`QA_+0GNRhJ@0JeX{Bt-}I7KJ64zhFy%)?`&|jd zwF5GFcZ6oiPJ?+iGbcA182N++0|Nvi)UOj>L6_8R(1^x5Ma<+zt{Ev828_T=9NT$n zplyN(E9#lT1aa$fZ{pkSUrhS6Tayk>L@MD1f0G2jM|{$`n{lKHrc-TU2rP} zM@VKfp}A^~fogC<>PnC2N6A8xJK_DzW7na70N+K;h3BZ@^eo;RdGYG-gvwur)-#H$ z9Mp&0V$mA*1Rrae>EJP!etX4IRC0g5JE~2NiK1a9&$FJskR_GG9NIB33!1gHyT3~g zN;ONmGUCu~Y74%f@4Js(gBNiMU ze|0GZ+?MmE;$>i6+3idq%iA*UDHh=F(yq0bXhM`b!asC%&~5yJCx85A)3<=%VtU3* zWo7o_JE-4&VD|AWt|}Py>C-Y{>kaBoJ$k|b9wA0uKpGtaH25XW&?mr zVV7R&T~tkkPfcq_Rls=uu7w2Z6vg=LS;xm$c>}|S31FX0OxVxSi|uIfk6?Zf8B77ZT!16FUj}Vw(CuPZl03Th~Vwop=zl^!7nd?91Nd-pym} zGP0qdh=@6@1v%sR5o~2Nnh@UZBunLn>VzH4pZJAO#>*PmBL>*Z?`Nb*kG#3kkBKVS z7PIz>NorsvKSBhpMuS9oJE41rYWC5hHvP2`oG4$L^A5~i#KN&|tH*d39U(Nl^VuD; zGctZUy-$Celc*ioy7HaJi_pJYPT#DM?}6yYz4U>Zp`;O|p>Q)d@G(@}<(vYtC00#W z1_!k)rnls;)5ter#>{%Z++himh2=njKc3v8zRFhV#*ltKai5%XPN_H|mHcB};N&|X zM(-7SfC_SM$$AWGrQUq!D0VR+wO_^(r^ju55bW$d?aM>N(xaBqHG8V&``cfcqcz$= zZst`!WZl@YOm*237-d-$!*tf?B1>!e0z&Odb3ygKVipq#_|74TQ93lv8Gk6&2zLpM!nV@W5My$7Znbv1mw>=`G@U@V~Wv%5qU_hF~{FkmDM2D{{PIZO#ZnyM-bJ z+*=Xn`HZ~1q7){_h1s7rCtk=nD#pjYD#CFTbBrmM8c>qHlk88a%^puLp920o4ok`Y<9M0iMz}#PF5SlT(RleIDh^?a7e@~Ln02OK$VFD zmcVVgvZ3=6DQ6R5K=@6`SA32qSwKVTP-tJZWhw0fpYjL`e3Qc23|E)Q7{XgejT)27 zn;C3zY#H@i2eo+J=Vm_S)X2Rvz%d@i^55G{AH;hxWx2&K{j2N8+Th0wUQX}D-``yp zY^hsCe9qWy&YV}&Z=NR$zxm z=Y%Q0g!V9?h<4iM-PFP$=1KGlcd*LhGJN22zq0;IQd=-BrAsFY*p5MeJQd+Ht6Itn`dHinjF$@`0fwBY^ac8IRh6 z@psaA#CPkTE{eY;))Z=lR zG(X2Hz4tjw{=2C#F3L`vjG10zo%T$eq5moOS~njDB)E$|SEfZL)I0q8*-)t_YQWic zF`rEF@oz~de024u6-8mVN$>i;N0q5oXD??eHbNPB@ovbX=Hbf8d@G?S3aZCRJ93Fkrf8`%y3#}XrlWlguBKQYD1lK3Ib0r`M zASiyi2@b~in$M*)+f^?;HD9daUF93B8Z@I~XgP}@OgflxGeVVE;C)3!g429kE;}lz zh_tsY^>c?ic+DV@$!lyUcyz+h6jMy1CK#MzhY(kDp;GVJrNXk`MH`p#gr2=yXN46VmQKIT zY{|;eK=)?h5y?wLtUBgQSuZh>rx+J8pJ;w47YiuNcwg>z#ui&2W=jp^=yK7zdE%*s zyQq{>SP5OeS%7?S@bTpU$#YMM@I6fwI?CESV8$UX8NiG1{?hOIyvFUmE&+iE?z-6VCY`|auK7i};<*J` z^YjEpm^TDhh+OfYCmW2RIyF_nM#`JK{NSsCPp;7LXgbgSCfS8ryg&Rn0fpI;28hG` z17Dpbw6cOywgUT-mqrorhdC7~I~*bbwFXS+w0J96SP)c)x|-nWsUY^rP{QBT5fRYP zKp_Q0MR_ z+Or*;bxKrU>`k?<6v83~gddYz4%$VV8iYZsus`DP1|0Y?_tGn<6EBos_s2|Y!d4$D z`NcjcUN`|WW6Jpt%`NG-Td`2G>V!OUt^NmP#m(_y^{9Jmg;aw0?wS)`ROIUOv2 zh@oLnqJ<5Z$&n?^Mju=v8S$(6d-qB$#D9}FK!m$$&jJT;ROPw;S|M^nTxXiaaKu`l zGjT{=F&E%9n^;3@60($;Cv{M%yeV#^t&PRmw>yVF57{5Oxnyur!d{5%&6N`Ra$}N; z6#F-x;h`%#UU+x*^oxE2{{!H!)`;wT3d9&u_!C&doMzF-&YjP_>rBL|;)Nm>%p9U= znzN@fn9B^n_s(|f#R^5}vQ!wAzLZbpAdA}NFm!pGu@j&Em?<2mSY*J{wR;Gdn=DC_ zR5H~m6?n|debN*nAEg_~98N>tp~5{>d?{K+Rv zI21mO82hMTS*ip!QhfPtq{(*(8glXm3KjQqC=eR1j&S8pJn-p)(hi(uk|2FFpsqw5 zQnVOjX>TYXM$+`^pR(X%!sCvvj(vr{ zM=!(qZLp*7jfr~z2g6T%(IJ%>15M$75>R^pyMHqEDKSDL%@K$&#+tIqs3N4PECl)c z?ORw_J5xOw>}SotqlY-WWDZzuE9&Gm93FFEZ&U?dl_*y&Yd*8GX7Q%4OyU4#bYi`m%Fqh>9+-{x|e-1{eLu}LmAt$3oPexL+ zGb3D6jR*S=r~)lQ9sH(gn`YlEe_sQT1tiy%yj|isLUANX0S#W#oKd~ef>eS0v#`7v z>-ik?gJ_bYvI=RpfrD|H9iM`;_iM!7?0EX)o3OI2Q`T?uzB>0k@VeH6!5YHfUrlM+ zsxOks{SGG-fs9t){tnA)bV_hfD>tEykjvu^gY`ZtPtmq-VT}^P-f3eHiPsu(&&bC8 z#F#3pA<~U<325PCfu3S#(U)<1#s(ej^ZS=g&`!FV1>ximNF!6X8; zr*0)`ZOdzsvI73N&PTTGH0^R2zPrD-xRQE^yu$&-2)Ad=jQD^X`z1}>f?7EZDSGXexq-yiB`g&y7< zykyKv&%Wg*XTefQZtVn%F&8rux%oT=gQKUo&%!m4n#yisZGh-wZ4#_)+MFtRw8Mqq zVku;X8O*JtUx85t*EcxoGLsw<_yL?^t6)!&DJdoaOhvN=^GZdMNhDdQ!3V_>VYAnS z8Wvx?8=Xprq>`QgWb%yDOMk+9;F9jRq-_AVH5sUTZs+4VL?rYxx;Hqus&=$W?nkF* z9OzIt4XRPp1BTdP>lG1-uHYK?9OF&E1M(OxJmi-kWsTa;?t{^NPRQ5%6p6=^Xrj%} zHyv5)%33~HL@KkX#?`yFxG!)=gdhLh?qVTn(}hv_i#+DWFhUGz7XUkG^QJ{Z&vvQI z2z^yR;-U(oyj8>Sau8KiyuV_70#>PM_3oQ~AmkTt25MCruK`dROpzH4rDv~1~`&SKbCQ3qj+h-}eC~oOt zM#gmt1lf_VLnYRj&w}$o2*?_K?p{$jw^kAZP`iqxj&u{BdhmeXs5wjw$>*NoVep4u z!XV7QBW!8i+}AzK&aZ(lP)?5qB2B1p3KW_8Oc*~tD zWmymwi9I=&f=}fGK-R*SiDO)Z@)uZdbD7cjBRZC3A!oCQ3NYqFWd9HQzPQP#UX$nMT$TE9fKAV^}54d_kOm zuzb_oip(}gr_xz81qDPNJc}S8=j)`CLjNP<@!-AOi6km7E0~TcwctA_jp8%0`}PdS zUrq^D>K?Fi)f!fhSv4M#j%0}@&l!5FR)}7)(Y_OWWvUO%?B6S*o{y4px-XDjQh&o$ zsb|++F(Hucj3HxPC(;5D%w2WB(ChXobvY^eM!WV5^hDk))o5};hlTo7gA#4qFM_!{ z;4VRx8(GV?9`!OsI?A$9y<{O^AFsXb^09}sM8n@XJ*>t1qe*D{8AcMvrOoRA)XMaDjYMt`WBYgQRlByoK9waWG z@0uZA0p@M$b8{P^-*?okj)=Jah({SC#22lPp(Q>ZCM&C)wrzOTc*IT0G*{q<@Y^uBYF6c!6T4``6P)h1 z?A7-R$|K~_oBlr^Uxt2d6-+1+dZ+iFA9je*d5Vgjn8>31A_P1NT}V zM}(jeKb|O>)bOw=tqQiKTO~a&j`Y8iZS*b{QY2iHXniKWz3}uI;qWO-9k0w5!VoR`%-DRe?|pd1 zQVvfKYvTb@B)cbJg7Z|_D}3>f$rPopv7q~lVH#mT>;PX^2Yp>WYe&5;{9@C* ziqTe~0FRl*NbWWy?P*^caFp1VfHXJI7qffLv>)M~EtPNGz=SyAG(|NS!_Kl~kc7Y| zj=0Sd#M*G+0D`MsFj14gM#mh#2B12U+QyI<3sxFno^f;l~o{{Yf;gU0^=>RvJ^yM>SSm!%2LY?!7> z4soad04KwVz=3|hImJWTavw9$?ZjrEeNXpum8HZ5U>G3G8rZ{fihkT(Fr6?T^tfin zFN>o!=Uq%uZkSCdU{q`{c9;q)=2uq5l~}UqG5zOuJ(yb=t|k1LW{rrSL|y#D-hMbe zUh`u}WKOZG4xsht))JOsy6GM=vIp}X@^C9@>npi{C9kycl5N{gPy5T#FJ9hXSke)= z;vujUzc%sf0M|-zecs18;sk>Gk5Bi5kX)-D$ZGEvPKk7Nml99}Z1?L8^lY+edoT?I zDL$xwCI+Q&c86iW)%$@|!(sfw1HN*lz)CYzha;>8AuB?I9t4YkVg)7w@d8q8!jy76 zM(n-+0K7oIe{cA9D4#=qEsG|>)VVsj1gEKEANtMl7itwe7)HASJR{lo$0n-0PAR=axF=3L6>^wN z4hy9xZWmmrC8lZ2$8z4>zm76NH^pDckcWJ;^mCP?5gL5R{Hk(?+$Njn%lD60jpCUc zbZeCD%MeBW4|p!NI8Dz}8IVzoRu|MZpP`dn(57p-EzRsqJgoqRSkw87c=&Gxi#vCg z$Jl0$&$#Q*ZaukuO4~PqX1G9nPxpfi_=)lx$-iLe{0CU2tvhr^!^`~~Z-ww&(F);v zFs#$Qihb<$hPcQbC(a2WcHiLo$SKh&>FM#4)C`^rh!PxH>@~Cf;sZOsns@IsKOhd# z82;bJ4cr=w3$kFziLYmd;3m5?>7C+6B1PpaFxFuDPVW2bSyZqKATUFthvqnJZ8)H{ zSI(2x5|$OqM|wUcNf|*1fRI(Qylv5fQar=GH`h45)BHApda}!*mB$lCuZfn;%|T)B z198m}*SOGSx}_EZmanW z)`Bn*=5J0!+*czeZIezCMzcE^k$|*&&=9E6gF}_G{f^vto|^Xl%%7lU0qy9cul9n2_9c~C-^>_H6@eAGcAwS&l?uuRk%qV5G z<%|ZW2|dpl1YurLaUAGsO-b_)~S^{FB2Mf?~C>{Xq zBu^L~mor)c4!FW*QFoPJ!4nx<0)U8b&n3&aF&po7FAlV0jiCeV=6ZXbis|dq{AgiLwxUp5s(ziv{T8B;uFZ}aMZmxS}R@PMQMv? z$uGQ1MR;cPl;{5P%ElUy3QoG`9pADlbya;^pZaX~75#HpT_$0Q!t-#Y173LImWQwB6CIe1FJir$ z`-l)!s3{{utrG^d4(i<~@y)*GI4%BAe2Z!8t}&4z?ZNCr^guNC6O98#9q6H?JGYvdU8EdEObN1<>Q#;zvbU$1ao3t|+snoDK8q zB)(8=dX7K@DHn+G3@=HPaGHL@Dw>|T_al3GO*Gx}a19dekVj$XAn+qejomQO1}4+X z%3&^ZZad_8Go+rWe=V4ef@u;=X3an2(Lmi{fka^O3yE4`9WdbrLCS0XI2=~RIOzF& zWcFtU*NIL|J?Qmei2Y=K+U&&N1%vxnm%Ef)AI2{Roe9<|f}+m@h+QZe#O)W{sfB4s zp7LQ!SF`Wraexd9_{rr0mjQ_!2Mc5HORBVCIg zpukR4)$%-lI3rhHV1K+$*Ty>?KC%zL;wK(4wnH`G`1hJBhn%bko%QDqT_Y$42s(B7X8Dg=d6Q3^7+KqtzBZo6IWv9=4b3b z!|)u4GRQ^;7qV!;^*5dr@$WeXRwYh_}U^Sf&D-67t?<~D|8wu3-gunCBj^&7<0J^Rgq_7E}D97w12728gH z`f&j|LHXc%k-^%dZAkiFux!{Uh~+!&!*$nJs!65LZVjcP6c28Y_kd5qAK5KpXinnk zx&iNLj~0OZGA#+Vx!`Bt{x=x&imi#~0g>-mjbl7xv+OzEx-(~)S@9i=Y)|tmw7ZzR zC%OLsSkAzlDgEV=mqmETnzr}@yL95(WvkLVPNq{J3YUK0Tfp=-t#*4oYZ-V5cjEPo z`YNBZ;P>VjH5;h%fa&q`yd<&}(6F!vfaACrz87DeIk4(i`bV74UmSGyTnvQ5LI;hy zTl&h)x(cVra{mAW4B9tuL!=$WiR$8liC7`pz%fhs;5?2zdFKTHI0H}KJc7WJS5faD zAbT&d@8&;9(aHY+=@_82QQAtXuSOliG0jtS5$C5-0v#G%(myR-oyyGeT zWFSIH8s1l;jUv7$j9HBV-);BfBV+@UBqto!AD*!iASk8J1N!eA?Z8NSD_=Gw!`K03 z@LPtR-*~PHjJwzqd`@CG`9z@7mLLYC#fT_24!Mex7~t5HP^U*$Me&mkKdJs6!|QEn zPA?}ROT2*M<&UdlY4ab2re>CboM+zwz=o`FoKJbDD)7aduyrST1}3G;pga~8eKhqysEv7`az zM8uy%y8d`&;%dYIeijM8QXuyK0DxZDng>%oziT8{^${LHBRrWEU40bFsJn<=MozoR z;s6Ex)&7f)S`@~esMW`P3X~78Ki3%d729XS4+cD$xAB|3Tfs;1mBm_w1Iz0=xOu_v z0-BchJ?HYl+*R76{{W}5V`VIob>sg4$2S9{@fBYWya|8`v<}GKpT<`U`3o6+yD);_ zk}czdqH~1_4p#mTrr6EK)V+A+^@*(rHN!+*2Lu3P5En;=a;C?;Z4;0rzPDd~Or}DC zjfrs>9C(}Q(`ei9=>EHK&_f`t}4jc@d5M2-heFjP_$b_WD9C+Z#5y|rMbQ-v9 z5UWUuoVFI)CY3*d#u?anX6g6TDVmx@4EyLmmNjtt2>2ag<+VhRoDFSsX5oGX(xlM)pQT0_D=6u9SPLm#jK<88?GZ9I;{t_}B%YBi3WL4DxDrfsV;mrs{g@mwCR9^RV|`jE?8k+E;&#KZ%HSzKpX? zeC>uJPr8k8r+6KKK@+;3F*a^%bHmW}f`$pr9wF?NaK+ABMbi$9MFoC>DX+9Y8DW(Y zj^to;_<27+lLIXsCN!Vg+4GTaMg#-|L9KW^`s*CQcTvE1I(q!#&k)l> zwjHt;tR39*&uitxXp}jJr>qvgThXXpd*IC<`<3T%U+)N7AY*g}roCd}XdOT<*l6>H z6-Ue&Kpw%?1z^i2yHE7%_2 z7u*{kWWgfMmGYNQjk&nCJzpl1#W!%WL{h-?jbp&{;wmhm0h`hT@4PB81QVwC^^u%J zv9rQ=g(&(2Pu$;8DkuFnG3(zc3+XhqCl5HS_^Tlj9!ich^)by~Rj z)BYV|K!7jWaM8|&UA%gkQemY%7v3P`)jrpZ8JLK7Etl~eA&d~n2DD|O7i%~#@yUb| z4~P=U+qsK-a8|3#@smR{BWu`r$*YT?YMTecK)@7HRxOlMkyaR45X7)&3SmTLstGio zG2P8d$y%Sq{NO1~a#OY*@Pha0X*hFAdkF;Lo~6m};ti#-^P^aX0+-8rTsJ8fC~||~ zV3Bg7(ntyC8s!p}C0X&oq5Ia8P{i0D7>XkIZe1EhZwE*H{#V z2xt$C7-)vFoBktboImP($-H==zA!c?ClYYIxXXut@H>tz)XX!T;8;N{%#k9p5d`J=rVi20u%Ztp9&n_$NUP=p zg8}_e!zO?%()rC0UXtxR_`pesBSrO+Qw&>^1Hou>j>pM~5l<@~*8uyRInM?m3UK43 z9ROS7AWk%uSnsVa15_3i&SL2@`Q9q$)~lJ-Qi=gtbCPv(6>%3g3r7Y|hP_ z?f(D{##_$4O7P~^5`wGZ4|w))Sck^&E(K9)F9^RJ6>88pwY3rr|t_`=o$w* zI{xy6_CvrauzTwi2WIdBHay~0ft!PB-eAw?0+Ve8P1HKUJW^1BBcOcbc+sZALiFzo znpr2uyRLlC7(n`k*j-d~bxaH?5!5V6bvS!56G`8ZolVk`<(Pm!D;K;%Ma@wh5a$T@ zUjxDetzxz!RFwwuI!V!nXI;cc3I_AIj(+H#V?AO>)`eQJAFML_N`{`^zF-|Ru zq*Rd}1C_*hu?(p>4u2it4yZwPPciQSO^?BHyq_?k-p61*PU?O#ya}Pca$%@acikGi z7&NqkyEL&_X?KHnSAX*d8mHx(zcT|`T1DEU9bwJrN0Lu_MDdqst1R;lW(NhL8P8Y@Yw(8g2>B355m%rGk2ogKx;f$bO5*4!6R><_n-j)B<#biz zIJt9BJ?<3o)-I3`;_xj$oYkpRQ1WSrVHYr4DaprGObmr7ua{@OUso&iV{8itVTN^S zq@Ll|Pq{ZJfL;-r{d0*nb!)BC3)J2+rK~EQ!?!od&Qj7CGL>yA>q*WJMb!FPq2!q5 z&yCzZW2a52;@=p#_8Mn+9|3zZ#r6fUz|dy~vK?qD?t?+?h6<^AWv;C;A$4)j#S&ua z2G*AJYXfeC2Ad$-b+;K_V`@lPaB*}00Clqe0Ec30g?zg6U`VQ_;|QYljBgd~1Kv~( z;JdwX-fyF&P_u!sIVZducEnH~x_#s!D|YDbw^}g}lOS{6#=5Q-D9CPn6Rlw5XcEo$ zd3B2<;$W7e(J6VEL-CNH>7|ygB!+7^J`iN}ka4}}vX*h9jx%EYPCz!kT^LlMuc{FP zy?nvJ;-n8`(9XXYt<@of#?AqMjGczj5kNiZCG%4ub)nl1O#f z@X)RlIjCgPay0}WE&Rqqd5(8kKg`?+qk)d{MeT3P@0za_uUr&!+ zVcH=!M6n(;>nsRWB@0!sA^XE-<04KH^Yg3?#wyV}98X+vrcrpS6Oyxg0jrN-&?r6$ zn#f@)tZmT$09dtps3ZU5FmD=&wUh&BQ3*LAQpX(><2Y5vG z?;oj>lDV&DoP#msCCE7lNB}X*KSem=>+Q%%Fi-+3^KM8j1XBC2XS_*bL9n32_t(pY z85tz2KZG!tdLgpBof!Id%@Va7elhUZwFQ(oUZ)E)Q#r=i@iZwJ=M{)bAxBSrTq{Py z(eNY5?;Q%+C;%V_$20^nMO6iKa2RdX6hS*VN^{;b15_rkwdGUUakE7Sh8CW5WeJGA zcZwi%MyJCmzl#e76UdBe6bfdtfc;eB>tr)uJTFA-A zJ3L?p(7Q2JI@*&c@a7MGoUQ~W*|mJCgevK$Y_76*8p(meor+^$J{l$RCjea(lgEg2yZY`?6vuD3VPL|Y4B}-7%DeRk#acK8o+Ln z`u3oyp)yty4Nncc=8TRYQ!h|H7~svuEq<}bS08EGxL_I*^X~{y(Z5G-2j}P={1@!x zTi=l`Y_f$E3D@TYW`G=W#(q~Iu-iK2(SUD?LmUUB0&9AlJ>k(6O^f#fE3heqidcYJ zonY(aSA|4giCm#bkWm%5Y$UW$9mBkQt8@#Js@^6XJ8>6y_yxc)j1qwB!^S!T4e-yl zV!#w3Ic0h#C3NiTC>;yl~T+*=lS^LQ~RMzjgeQPu{Le~R2Pw3|qbzSvU`?K(7 zx_Iu@P)ynWfy;=M37HL+&NjNZWq*f?=e7nh&LA)Zh zJ>gk8`xD0>oR4{g)B?-V#t4hTu-4Q^2V4OOqr$u$;#0L0iri7o5)?xe8kjiVq({&Z z99CFGQV&^u%dGW-$N)ekZdTf%cqy<#meCV*MyYCaiphxm=kGg%QkoT{Ej0gEvmF^d`WEBRa>9 zO-wdmky$t9;ikfXf{xG$u;4L=wsu523_h8Yh){ULv!GK<3S9Q4%zHn>V(H779pWm& z$7$V7&skfc08`8z-?-=(5SeF5+@G6>xT|6}pSeJ}BB5;}CMb>w5NDIe=L#Jpg$hwD zNb%<6_&FORhbLs?2)s%!RqA-gfkb&al{yJ|Nu5Sqx4sZcfC!a)~a3Lozw$~(IzGc+{a1nqg8V%}R}E3zwYj?5WhsVH=S z{o~F)P(h-e(qcr@XmbAmSp*j8E%RiA!6Ki`> zeN)18YhwMS?;xT_g>5*FLGHmaa8%i{;bf^&wuS?vmlp}y&42|$@y5E!@;-_H+dTM} zZV5P&<{F-Zu;9vvNnZe=+f9+Q+;5tHtftKZ-Qzcv_+H@!JSGE8MC$> zI|`-*xot%!W3l{`AqnP`maB>ryd*AnGI>A4V!deh=4oO#54XDGXe|UZk~f98u*XGc zoM6`pbWI%t=`cDgL)d9gIR)$juRoKM65=hS^?T@W{`I2*_#8e;wzR&Sd&>{Y#m-$5 z`EuwAV=xUnz}dE(QtM{VZy6;eWilV1`o#@2;4y^l(3ZaO7LB@J?I{%&6W?WPM;nq*jgujk=KPdK_dr&9eG< za*VCT#iZ(vXvwP1vag`fUoLsc56dCG?c3t<_(O*IFbYmS0Om*4xPCU1$bA#1c$7j2n)dWN8!X#A%Yib z^M`05@I0T|&fG^9{{Xi@;2Lkd0ZGVsagZMi*d63fS%U##4&p9xE1|<7`*y01$ zUixk#poY$({xI0A>(1-Zv)hy3A?f^Zo%+Jx8W2ZUtvItj?*~ktsF$MTEwb;E!`cj{ zD7Zk1ZcfN11cD@2t)X`W8Qdy_q!iKXOPa0%t3>eeadZ(w0r#dH=Xvk`h5jBPMR*=r z;9(%c2h$$5N(SChdBYn>19naL#b#O#@mf6xtghG@-{iS}kdHy@r;aeS$*C0hPA;+V z0jV)gEgT%-tm*LclTwwYgY3W&)tqSGSup`=^ZCVyi)XjLStpanOW`m88fhD@>(*Tz z*s_W19iEJ7vj7G4Jmq(8`k^Mt`Lo)xjaN2eFWnQ!mOl8W`N2V!uZ zoOA~df8)u36{a$w`FES6Z6`zHolP`F9`5kcM%1c*Jz|9crbq5|j@%vEOUa$MKyDzE zxwC=C+(0)D@F?6>s&_?&4QUKCqZD!csSRo5>xIQV^A~B*k_&%0{WS8}IldoQ1h-KE z-#q18I3qQ%{DpE|St3Njn5nFG;CGuEF17t82I<}qcp=G;;3e27lYM@&k13b2wmszQ zK0OAhSoXg#*RxgGg+2pV1I%#zcXY%8O?J5UEkzPu2YY{RF2h2t8gz7HqM4%2EbzP= zwCvt1fODe4e?Br0-dNQ18h<$xQ)2`7hCF2D`oai3Egv{I1Zr#GJTg3r**CMph77GH zyjN3!!7Gg-;#r|mJ*FJWpMv-&K0RjbwGLh={;8Y3 zrrZyt#q2820Txwa^0=R=JDGn)E{_##eQSXDhimus; zNkgFs9yfsnE5$>m&c95SFen}zEGP4e(wdN0XkNLq49A`_%!{C|khl<~n_IGhLt#Qp z3@}?M(*8^%lme|RO;J;&v<4kJ!)dlFEI_Ckmq)h|Po3(F@LeK<9 zp69%CRa0cz`NqiP2wM2TTTvn5dUJ!|RCns)>f(qXM*jfzah#t`L-U1+$_l?`4>g5d z96+InhwycXW9FkGE||BknNWrePkx@h;8O-|{{TmvT?;!){{Uuf0(nw?Nsvih>7(hl zB9!FleRD)er4#8ub8mD_DW9p83)cK*wOlr$a*2OD86vPWU+!Qq5yea8VXs01rRWbC zKp^m8OXG~&S7wfe*9P;TI0nDS8Lz$8trRzNX^}y6$WBb#x=>n7ucD~wyP##szfoT1 z&2s6=6>KQ_)%-YfY7&w;v4EquneP~xKAXqw90ZNsgOKYZVgOi|w~TA0Mw&uiikrioi?R>p6p(@J+t*7Lx;t#5f2+=G_#V6LbO5I$uv(WcJQ&I}Pb z>r?9z?&5;S=Zr4fHj}1)sn&scvuCAC0&49BcdQjO~=))6I>9G^_nZFLJHCwS=Wh_8>A4u075H6?ZZMTT`yNwW16bL zYCWADTz-f|fEC3-(0j*Lp=W}So|++u%7P=w)C*sEE=0sas|=rFb(%gVf6qbs|Rw zORr~$^0-72?g>v9ECidU{@iYX+)?zxq#QQJAI~_-H`*WT8y+qVOW#I0iH-A*g5(lh zYG2Nb`EUSp{a}e~R&k3r=C(g`1>Ojw4^8I<)}!F@9x)j-hxJZeS~v$7`Y>S`q7&2G zk2FSlGjV$%`^{$9k4m~QNf0#Pw?%=CDHh*{tWw7%>6?4u#m@$4SKVuA746 zeW5?Y@rhZww#i>uS`FJ#dOPtk95_ab8XMPmk7k>PR{GIB;_TT2XdL+Gyq)_8mT<2v z&Iq;8Pf;z`SbzXZCeVoNK|OB_PR-$x2WK}1QE0W?2P2W8h$#`I?OrE%Bt4PZXb|Cn zh^t*404YGa&K){M+V6g zfF{S3bQqOe%?2J`W=BzUWPN$Ug7#}?fYSQTQYzGcKzquN@oOjVS!R>q1?_}}6R9L~ zc&oi(50?`eng#qJULRR>)4c}1oMGmKkgd)m_F99q{{XKz)KELnI0pmX7vxZFbmGGf zZMxArm;u``V33;niA{b-<{~sGI)EGSijQVc;Tt=vhWF7~r$0;13k95(3 z$-zfXsBxYI;kGuC8t@p&{{UDwh2WvocfDKINq03*n7Xt#HH^+&;k2nOhlxGvJcV>= z9m~z&@qtj5$o)BRL0Qyz(gqdv5(wk-MG~} z=(jn@G!0}B&3nf{Wg}_BdTIS&)I5HjK)3c_U;=@|=!zz6M;>6Qy)oA#f;*=KJk`dZ zSlH84H6L7D8Uf3k3%-6BqI}#)EE8z+z2U3ndqs8}EKo_X{Hv*z>#D%5Mu^|GW2ziA zR`cM^v;6p-C~^C7F|k%b=>dDdYozPjun&>W`lt>qkt^Bf84ikD`36OC0Cus{!+}u> zC1bBc-z>_-A~oOoCI}!hs64U96BH?`C_WSi)+tL_r)`?=4DQw1KHqpXgey~-&*Kry z9iv|UdcbPHRUi%k+&|t2ppZA8o;9;sGY3S%0|TV3cJs(!D><@8?;Y8D`v#A250@ml z_9Exl>!KMlMSm2{d`y-90G`Ok87M6(z&!63X$_=x&~{}p!-13I(~4*+GDBXUNr4>} zKL>I1fXKgjrj5SeTxU{pcrL>@He2*bo%f0c0HvTcG7?S0N?;B#LvLMTcX+iipsN1> zK3k6iD#SgYOV1o)$QVPw>by9J^j6~dbbG_7Ce@`SYP;tZ(#6mYhN`UgV$%t0R_|8e zJA@(u>~+zzNro9jDC(jqIC8Td##$y0m{siW- zRUN8f)f|p*nP##h1v%Vtf-(g_N+3%_J1)AZZDjgSA-HMLG5eynK6byH3DO*Bm16AL zYUsqKphYb}P!h1X)TLZl4JiVs^<=?Rk%7}e5|rJx%n7qi1aGN;VmWRInBu6X1+!yh zlNDqL6C_R|Zp~v4ElxApK=2s1KD6A`U*XVwnjVf!J6gDg{{S23EJ7g7ueXdBFL0^B zI8LxoQ!Q4rM}tt!%YjCZ1aT3~&lEUUyuV1+uxQVzzZV{avv+{nbMMw(n_Q4|Vp?F3 zkIn;t#v(^=P6|@g*TLLLo0vLoQYMD_w;miKiX$hM-Q?WQA&MsW^KtlMU2`2LSOQji zY#@77-Vj>5Bmu8IkC}aa1h2@Pjpr`?GVmp9!4C09x;qeU9k6e{Fct+EWP}dtCPcyZ z0=cgS8?ZnWYSqi3$s4bh*cJ8*PBC^H0Zf*UM_7-0gh|xJuJHMfqr-uunOW)-?=F6| zJbKAy*v8&J096QuKk3F`HADl7{akO5VxBvhacQBwR}^);Z=`>UxR4tNdt6-^JbfYi z!%(3%h6(FW=OnEa(@RGA#6JMx{{Y*Xs@?1V0D272m%J9%VM~Bd3*!$xN9^Da7=%zY zt37jTfoU1whj=O(81SC%Shb2yteeR*7{NTP=T4qxSx6>n>;sIwhZ|HgQ1gwmmN8vY zyg1fXaNu}8^UHI6A0dE>_6d3|sK#8L)?{a|BjvQCimtTgV?BM3YwCzlJ91M&glp_are zsGNzb-0_Jd1*#Qx*wey}G=0}fVvDe;I^IwrEF3-&qenct!4qu-{;wl?#JKK&5lEUX z`Zmj51{R`}c^aha9*b!KuL$R(=K_1+9P z(i%O_SRpe$o<+pdfp)jSIw5ft(rQPlyuLcY3JQ&{f|ppU%v1;^44R_mq~KEu07cc9 z)(xhLwvjdY<0DR>VvFfCCTrwHvUXnES%Ot_tOlY)*V8(=(^2PVgMVef(O)3}Seq!p zddIL)Up$CDnG%fyK29d}`ok?Q1s=FZya_EZ1}Yu|QwX=>2nOq5$*dfMF~T&%ckTjv zF+c^}l^z$ofpq(naquyOtFAlU+)l6RDc zAq({+Z&+(aLC=CsXzRumTd(hJe-M~3fo{FzgWgdFW|$*F7!$MvawUSS*#c@Xo&pZeDYv8CcXkbo)y6ksTeR#mZP^^xTwV~05SByrVt>9QCRRN z70Ukrwow@)plK!3kkdvtR>GoGISjIOCL}qkFF|58-Um!6SV}{hueD6o3DW?tV9vwV zB4ztgT~MI=)yf$q1*=p_;B6zLt)BT~DY*pa>~oEpB3>9!3nw_`AM!cRT&X3wD;^^* ziXt=)+-hwMGsCSWt4-UCtOyVALvD%IRWlCu8Vy1Vz3U(Qz54$E;n17ZGSie_hKCZ< zMx;5F#M2~Mw_|e?*3}ua@>@1x?4TAz7N$NexKrd83V5Cz6HsIl4v{13V=hS9D+(3R zdbbDz$&Ug=8Pu3FP{aT`dk^OwD%j);VJwxs>kfEsH~~-|3*g}jE4E$YDYfokl2p+k zgf-jO%y^Z}vQZWQ1m;&7ZCHy(a7_@FcY}#70H?uW>lCWHFt{OEUS9A}?8I7&lw;Xk z;g`ye3$VERiznmXT!lXpIXeE$;7csoJ6Vees1$Oj%@S4yO~XN9brDqRc+E_W8-Qpw zj8;UM26|CK*b+sCrU^*mYUetDJ2z%P;2op~YKWfMkeeA}HZX`%UUqYm&H}Il3vs9wr=?=Aie04-kf~3~30r!#Ms&&K624BulQ^lh#`br-WiWgExqC}$31mQf* z+DB~8OvGVAhxnf9fE;Y4kJe}>6^Q^sXtyP;E3C*JmW6Q6?f=>2gI2eLi zUSX)lCL{A&cv2$)iwd_5l8CHb2-^*uJH(#AUFE6pEk*<{!C@K>sD-HO5R*b;_JE3{ zs1EU4L$YgOR9#45V^z_R)dfIo?Z#bpNR3pjE~|vqO_M=xn%8V0-dUn-hoCg-bA{pp z{f;&(X%g)-q`}w$ZDB=E#t#}=If$cN9RMA2<*bcH8|1)oo|(V!=K!L0{H8VP#rim_ zd>Du@MF#t(u_!>G+bV!EJ9py{)S3c`AoOZsz#@zQ@G&pkBbA_J`7UopRUiVa>_p11 zAPbjNFS$6qz2})M=s5JOuCv;Pg9jm_4)CHDl?MxApzo$-x(du7VjCXtf$Ecbna2#sy2SL- zDA=JLWP?u9dN|2!GAS&ln?PhZZb%c;-;kxL+HN_e(`gS(BF!YMCMcQ12|KI5JHib^ za#|;$;dynGM_@EYuaVYFErlq!&j=9i%q+e^sT~~Yfrh|ftpQO4xAN;LqeR+KBw|Or z-SjMFmVhyZ)C@OM6b6FHxX9XKO``Vi>8-@PrX=QKHU31!D7dkC=e{{d(`n2h&8vmt zxXu-#dxLnu;)peWFBmpOSVL#tU+*T-5&;x=Ia@K9q$R&|l(m|~+4CVEIA3_C;AmHa zz;t8+Q3-6PWB3gGeMM*n*JSGzu3tKMm{w}K!SZ??WgYl-@O!V`utNzioWSfE##*9d zQJ7G)>k`64+CMXg)22Ku{6~&0z-?s^_g$;R3wnkMT0>x3p}fBMq>6c+J5lIjri>IG ziP7R^rP^qYNjnbaha_wRm(l1QtT`)<+-$VrsABKupK;P5{!G)G?Mv++D>f5Rx>$X~T}-~<*?qC8dW3rnJ#wfD0Ow^0bqg!`l3 zY2PAtzC(A6wsqYn2T?eco%q~qjBak1LSe*UGCO{^n0^xRg;tvbMm+S&)KrHf` z@!*CUps#Xyidoe-NX3JEeDo7uj2TtBnsmJ;Pk<_A&<9_#+&=#RaycZ~-p(+Tc;_k! zxLuedFWv+==WaYwWl$tjmxl?9kzc4`NCTbK?>Jp-D#h`5#s!@B;{j63k@%j*v8$47J6`-lLOoY;dy2)<*9?!vFe#+yst0l*tn27uEoawAJzC%Sae zJm#2xXdEgWsRNXlXBfbeI;d?u;bM@XqqRI9PO!(y1y{Ip$Zv*W?!wnh6J@HrIJSdH zyn0V;a7RoUE|{Uwk#TX&&>pM$#Xy!8&b33JRWF}?f|q58^tul zsXuV+p(-Sen1duG>TZijEu*6z2~1qy6k2K1zI#AI2wd`)?9Xi5zL453qD z%HwpkSz^j1b`yUvlu3|mG!zaAD_Vf=omor9BJ0A;0*t?|5kNf1d@dK)*A3g+-Vf)wwe$V6OsOK>9Ti%Z@+ zvZrD!O|}atfWSx^kVjzvTi19-9)Ls+5dQ$!!_Df32N6YB`GEJfBbbdYy8<`Z#9=5e z9aJutUa>c~wFHTVk#!~-KT%hhgG~htaRrtz7TE_cTOjt}#XZ>RN96F!r$`G}13+Ev~W|fJGtV4$x!S(pUqM)33 zxG}SMpy>}Ucwk*PEbf#s(&(K~IV%D{i0bg|zzZ4xORyEVi%JJO3bMErT5BF?<#}}* zbd-BJcDK}%=X}3+a=_m+h+uQ)rV7AvvAe_7DP7)AZ{L&5aj0NOx&jgIZep?jC%XgYA9}`hC@3aDk52%YF7J#2 zdTvl?BTmjvEyA3t1KaMY->gtKscb$*xTIVV%fo2`!Re3Z2~kK5uskcP%+3ukQu9LW za$HE@ry0nybI9)$z3speF{LC#xiM-f<5?CU?y_91ip#2Pnje_bKn@UEK25eHFjW|` z9mydBre*U9%9{ZrdcETkFp&!#sBbASZ9*!%f<@D+$JQ&(ge_Chki=!8lBuxf{{RL? zp$`XEz}Xt35ziInZ3Uu)w%X)$zFi~asQgQdQGEtLLZQlA5;#W5qY_0n0MHuom9I{M z@G3sm5@j9ZF?^m2zj%O=oPacVZif)LRjX0RQ6%C^KyupJSU}_k{5CEabwcprmS=Fr zE)-E72?mOC-U=CM&+kwD=K87k7V%RRYprBVIH|WcU0LUhQe|`K_IHib74bi@$H7kF zqv6B%j2U8~zE534#x(6#sp&dTytpl@tiVG`w(P`fwNuDlm|q%zH?XdUML9E!XxLbb z(eH@m7qt^wDm`iwtUEaZTQ^5xZurPF`eV6^d?_QnR!$MfHnq0A0MF!l#vQrkatiFT zZl-Idg08|P9a0L{Dn~vAaLTWJnnP0zFWGBo3)9Dmyl8+)1z4w~fx5+R2gKtKI9gOI zmhjt7fJnNGG?;X>Y$<2nF$$gnz(rbG62@rFw2I5B>yT(|=K*W5Ml~Y`##e4aPWwsI zPk7yb@cE-k-7gagmW>7kuODVbj4HsruAE!UOAYs&x!u4E~76XXs zhqW+pO1a^q_rZn-8bG?xF4gSAu}bjm3rK~Zytk_;2o3eh?D>H+@hj+SaJEn3!cdd?YgtuG3Ck90}+cng7V=bEJM|Z z_dcVE5u2))rgD17-5_WH&>k^E3uY)291h}VacPj2L0C-_|;*FNR{xVIe6kSV6xHN;O18dbI#PVFblbBG_ zu*w+EML>X0Nb$;Kb>@3{1mZxA69gbHPsqLe#Sop$qwOTblh?Lnscz&Arz;)_jJ}+Y zB`sZCwReT)&ndT#8FOID=b(!CeM-_u^9Aid!jz7I$|o zg;JyxI#AplDmWCUxJ!ZVZU1lIzTI!n@Z}~aCzq3(+&M`mIlu9WtF%oVF*jsqtZMV9 zM1Qp}MIGFZj^i_^25x5$WYS!zDu)cTSF%1>gU`rO2VE1Nb5cL=hUirVOocxkUY_v? z?DpMHGI+JwJ1_q(QR{LbG(KsnORPKOMIg5JzMr3Sh~0R!hRl!S7i}zJp9gaSkXN#} zSyU}DNcLp-J}_z-cMl)IYsx~=yXPlZXoNcl7-yY^gsgsryGwlo%`S@-aId4G2>k|a zi*FWqy*dXU_peZNd7TrggPZCVn!(gp#n{-io7&erVj{3ib){~VJtp8Z+9G>*G*`Yw z84i74Y|i&^in8>E-L8V5?rL zsL-Qj7OO<;7Ic)isOE`R@7+}34>S5jrD$SuQjNGBl)P+o~j=qeOw2j zgo4PWp7)&yrbucvh*L>&Pu!*yPc{#r$iQ*ed?-f6pL(E(z{59tQ(J^e5|9->j}jZ= zHH5xZ8yi*3WNnF!v)N_Kvla*t$Cv9yal`xEUU%LSXNN>PK2w2XdMRfv^Em0T(u9WA0Dru*bU$SF zAZFE&Bh+Swne^(V@~b@hQ01fsiqD8%m_vv%Ri4ORUITHWP`-P}8ZMQ%*AQ<5ZCgw+ zxd)bzaaL8i?!)^Pp%&eeOSzm*rMYN$7A2RSD$h9W(1wBM%CZ)DC%AmlPk{Pqrzo-y z+Cf+{0IZ1L)yjwblhUF8aJNYA5ml4d49b_tv=>79g&6oW8*B(t`}|c+;@6BbrTNzN zTuA_Ga%8DP?3z~1v(uEzoX?%!-!Z_d+b4v#f*xvlj~EpbF+{ugMc3H9Ks6evfWwSd znh2Z)F2VW@a^_Z7sS+e|4qb!jX_RLKM4I}gEwk}ImYLW%pZ<6t&ZFFoS09gG8d>Ju zu_iwaON)&D;Y;7sxu7W1yJi6;?|}$RE_4{mcC`h3p&*X(=*_6cL`~K%CXVIFqke#U zqlx|)!`4-FE1`1{fANZKo-!3VRjOe6FuS}8GAo-fq3-%jAA{6KV9}=(;Yflvpp)($ z|8`kq=uN9uKvt+=Qf2+HPSzS@*dsKQBK^Kp{F-F~LsSwv_4xG*Wv`Fgyv7^bTyn#F z9~3xvSX(fcT43+`;>s&3No(~=Y#}%)5@IMt-XRqvOG;}OLS&YM2FNeSdDjU@kT@5s zF%{5^dEa2naXW$uu79UQ8uVBB8nFlWZSqjz#-uIao;$3+=M7cq}@Rp5lXf*}{ya z8yV|{;Csg!txe|_+hF@^T3q&K_v4(n>NEq;CFSXQ9_|zW^^SzkmIG9Q=IqUbr^DJU ze23Vmng@w67rw7#gW_2fpD%B zqcP-gaHk`#9do&sY!){*0l^WPby>hBy*EkB5k6nhE)I-i*H$&*_Nu$(Ek>%=4Ad^m zh$GcS8Ad-&SF{PyF{bI^cht=OC}V;$rK*46R==0Rmk75*83E8`8$oLc7#+>tGlNtn z)_adjj6JXsamgR3^USxrkBAaFdNk2^Brv(HGT1iMj!#~x;1GXV4iV|gk$q3VL65Cu zhhoq@O*cq!G8K`guY3M8e{C_2faT0YKuBgWjs_m_;IWAzMbE)Gz5;H7%^&zva@9oH ziNFTM?HeJ}$mkPEvw1lKyn&NXf@2KOh-Sdchf56o1Q*ceFuTy(ENxS~a%CmkeM=!j zZPfF_UlTs*MeLi(SZixmWl#k2E1a8zE=@Dm*4=f#N0D*fgS${a&81Q)G5YqCxMKNN zxu=saK8fo^?Dgs8H!{@&G1BA&1#fTQA_X3t2WV26I$a>bN(XYsjiy*M9f)H z-us7awIEwO%Px|9eLJ{H%%H#AAOOK3mmwS12n?WcOt@8te7@Rb28G=4HfpUr;uZQnidaXD_0sq`=E{r;5YKZ70^t zvBXdK4BZM9w1A9qe00BwZKZ(f>{ftY*qgY>@v~s5bW5_0y(Y0X?KEq0I`kwt$enT|67DwWPW7U*g9)$h&yhnN7DDHU8)P);k{ooX zP~cu0Z)(x6M_v<#hqF+sn~&>$fH{pgchE!q@GWIO)zok;#BgaLO7A4#iw94hUMHoq zJCPGtxE;z#lR6O7bM~v!v`elfC%7`=81Qu#N~RGD%}q=4#zY5VKzP*qsBo&n?20 zJ%>!KrW7Si5RR@s$0k0Gqwoz%PhenaMm=WRD+pte6K)b* z$K@bkm&Pl1djaZx#gs^X3=9ZWQLF5~3iu^f7`%BtacvD7K&U!%p0qEP8{5M#TPBAn zm8{jY%ZEtkY5N4I;u-gc{lYRGq6~k}Y*l?isi+^!{uS8W8nbyG@LmvDJl1&H zvgwEBYPc}n^K~@WW>nH98@i8emBil(OVB<;1u}|EG;_erJs7eH^v-$Ro3xS*OHrVe zR^vJQY@MRJe4Cz3cGRNzPA93bUw(S6N8-+LBXEX^>=g}PWr&F4E@0-^^9e( zrv2SLtmnzUm>KG2^Wu36*IKZin(S;eEkct|9*zglsmy~f($u_#NMGEb>puEe^9j1Y zd$mWD&PIvBeQ?B~r`Y<4v$ajVVVz-}ht8=eI=1YE5B&Mi3yl@?o*pTt<{?Q=U zIJ%IAS#oUZemigZ8zkjIA;GtH?IDm3j-1qv+EypP!#(4kiM3rPxz&vvX7#s4om?Yg z>M7UtJ)CszTyutfl& zz*&&PE1cfcX7T*`*}S+sd*tG@(X8Gx+xywNQ4`7iUi`DT(eLiJG^Jv^w+TIXSsWr+ zx(}x~08EwMX-fOeDp82sapK9=Jy`64wIy-Ftc}&r2!3yaj^&$pGn*mQab0u2xw7Af z$eUe2Ubz(d4xN92?c(Qu0{BQM@83>&EJUBNeDyqaY+#{laaJQzzCI&0Z@u8E`_-ZS zTc%Vq&n^kEocgGa^?nnwO7PWZdBXv_Ez6ODv1SU60`uX*WxM5rb@m6Lm#v{4_0O8N z#u_P25}`ClqeKgYOJh3qSvHA@D1}UnIitE#?vJg1ZyPzv3lsmj#Z16^Q3dH^E;zbK7$=C;?CAHNcPJ zofaSL^U>ul-|+*R6j?}1F0Rj>QwV1trr;O;29f2MI}dCL{swj1oxOYj1g=VQCur2n zUT(rAq7s+Qr4p#bVfVW7-e6yWsdLA(hE1y9pcUpq$GV&FhM&K7kJ=KRhtOOX(gm7~ z{jj{fle~Ga`jTbkz%Pd6wB=#sevW3iGq=yT_?O0=noS4Ub~kpbtylr91LHS{*ns)i zVE#Dlo!;&8OOKP^Ai8U+?^~S%X6Aid*KeZiFHMm`tDz7h|_2dP}F^Uc^%@cv6($bvnky3X|MEqQ+AbHYN8RT zFfV;u+R{ef%aV!E)_ z#vt)X=MtV@8=%kd8x*_hK{0UFdgu$kivJBFdbu_f1e!Y-M>`%8S8~dAkuQ$L44TU- z2<~F*dl~HDJDvU#c*p2_euGH$zwTCSkI|FmHoC05H;(uXN@0|Z`tb%H{IQ@81K7K8 zEYhp#X#Jp-r$&lG^BdI9wS#-^KhMlrJ3P>ydH%+$Zs+*eJ2wX3h+tdJOhf^r(v50> z8TqAlVcG4D5WJ`wsq+pqfp;FY!BsUYzsd9Iv)4v@G@ljxmYP-AYgX=5C7(FP z$D7Wey=%X@|2UQ|?ze{)#eq7%vK`@0xP2pE3>v#LqbnlrpI^CpEN(oidLPqN+w}yG z8lQA-aZk1D-7j1|fn%5NZ((4@CC`V?@|i&UogM>dObmn|Bvd3cq`w6ELrBOdsDvN_ zB4S!@DKrw&q@uqVOvp$`$ROZgb6xvA&D6yIB7kvEGjne~b1w-*CK)hpeuGMHg>U{% zt?@)^H3+-9uH^9WaNnQ*WN5e!Qv59`u9{_5Q>&q= z`iwj=$q!&(kW)Ry+BI=)gDbx&r3@2C-*rBx7xYP5~b70 zclxagqT^BhR7Zk_$EpVeYoB|E+oG$u=M{q=enpj+(|YQ+x9@vO?H6kG9Q11Q6C+SRG$}$>r#=PmiidNXIf`qpwU);EH5Z1$QBg9douK`^VpdtP#*_5B?(_T zUC&r^#&xl}dPlQN?*_=-Nu9NTdERwSZi4vKql%6IZ~3X~HYCX#69ddeyS|DjMJLox zTBzYtY@d+v402Kp5C}=id&Kbk%civWoLiX179b;(TP zDx~lEaPRr1>+R)mVB zH3d#@`dMJJoSSy*xxMNsHuthRyJL!4%FOKb@G=R?YhXVRP{V!@#Af+;)ogEU#2A@i z>!}!}3bT~UBZpoq^i(~n>tV86+6XJMD5^_39_=uBPL1%=jjOyG%akCCuc#7UT<*%5wTojqg&8= zu@iMUE7F}}=n3B4bPOXL^h9w1)lFluW({xjtwrGo+_7>PsoJGw*a)qI-5+xYX$KvS z!$IfZaz5SWllM?%W6eS0GJ!oLd1Y!WRPJ;|P3yCcvCteCBjmh!M9VCx!fMoInnBhN zW|?(b!bYK5ido?f+@y1^(MA8mtYu%-8dR+*jW8jj8`gN96qoRWtK~G@TLvxTD`8n% zm1F0J!YQZIRCZ!gt%BFRy~>%;bOr6lDtq;Bfj5R^O_O5Qm<2IuajgBizGR*x<9Pk8 z8+JiDot3H)*Qm^Bx1H!gTZMUmHM{@d1WDv!z`C+zNoLaXPtRl{o<^sQmbb@{1Ksk( z8Nz_#q(Sp|ctKAW&AvvT5juyZ4_Zr!Xxm?V78U*EW;RKQI7NS6_NubqJKHrR1sL&U zSn%IWQ^;Nw#1meUxk6cFrLVeZEw_6%; zzH46b4&7}d(Vr*fz20kV%?^*Qu~N;@BA`Tk`FoFED{hP7FFQ)w2BVay92~ijQC0^f zO*UmObv3oqqbN?A*VjX|Yx7PZe2h2zT5#e$Z;O4Fpf`?v?U z>%-;|ocAU&KZn2gJYP>;C3(`$ba?)OI0MV#C4`epQaZyV2CZsl z!9C=~yFs78+2Qby4ANnvvXN{pfYE`D1}gXD)zZo}T}DlZ;jrKD%SrfC`?&)S*voNrM-n|5`iK1%v)91Te! zfR^>cFw>qU_)0&r%*2g${dlWVqth;YNir)-LM>jX4P$T4F zPfHY>)peqL!+CgY@rB6iy|zk5S(<{3m8T%2Oe${1uZi?AL73Mk786;d{eJw{X86`{ zbX@#;Z6%=>H;>6Iwm;9Q?Z(JZW-2p~Z2O-i7n2NY(x43w*&hKOGMow-NGc#B1NqBTjnw%kf)n`< zhnPth!a=%{nKmwzzruzjeIoO?;*mH|7y|7?04va+}I*(34N;N!

DFvDBU7D=MG20Wf71V+2%iRvSGd{IJPgYFWOZdkjZNbY0_SzgR z=h@kn>e|62*$*>TW%BHv9WlbIZXC&;FFL_O(;w4O{X+oso34k)EUS8g?Uw*I0lf$i-bCt)` zwvC%X-r}c<4NRJ5RZI&>uDqdFK44^S)hXfapR3T7e>cXGHk(~E07g7k^2sLRVN#Y! zH0_}N4MJGWnf;PUCDDwq4x3HgEhl{Slj&ImenUr$*v<=|!P*?wXPSklv_c!Rc~D#g zBfq>-iQ(+(NX*Y*9;_>Z>X=meCJtDRpnzylK8Yjv)`Xw1UrvN1JuRV&I+ctgJkR-j z6J{=xO1EZjJNOlafw^92#KX?C*GyuH8Kyh}OFH3E!cAo;UxY!$73x*$+>#DNBR{c{ zej6ddlJ(5`B4P%uV2HKRraNgglNZKj@$hnXH5O+@Q2RTJakt|3mnezL`D7ST#%dOw z=PYknL~odutqx!M+z1R`nQ+YTtg=&S^V={=mNH<~#&{?yHHDg~(m;c;uSXg}yjMbIEOP8K= zTK0({;-}^!hg}+Mu-k^bOjuN|zjs|uJVr}kH??3VgZX{R5q}^e2>0O;nPNk7z4Q50 z%UOziguLtlZbM~D;p9{`dN#CWnxVXEI$pLkWu#xpSvAA5p88bo zpa$V%zq))FzDqxtj<=GN{R)v@fanUc15GfYzzCTv(*~STE7RlX^V>GKWNR~&DMydf z#Zns8lCrVMgo{QbJIl$>eyOG}{&jh(`^iArlyZtgo8eM+TYP{pK&F~Jd0yvf#_NI& zGoA(h*K~>%Yy{E015kB6!eRYB-I$kg`x&OO2FC{*tfUbBZ*wXoNhuA`f(SY_>;__)-8)tn787LUamtS`uLG{k)jqkZrR)a#aH8nU z=BMV>0yv3T*;X)ym@RM$i$kv0m&mMS8Lol1tPB`uSBR4+uIpzll&RaLDCPm)$m{E+oIQN_s#>3LWquqq zs$8)PXH}aL)0!Pi-5EV~Wtg~_)8_9w_h5!fHW0pA+{v3`V+=D^)A~BiVnmQi!eDX} z81(w;Fd$krju4l@ny%~_ZdB37)-Hzqa@mch1TGy~IZ}2xg%(q;W1k1{JGtZ-vL|#U z4lvE)EwA|6WKHK85y;ctW?3UbvrW#ai@L&(qu-SzOBuD901W9Fz7n;R5hz3sf5xU= z-ZpVc7?@v6hYa6|cf?x1d=zZ`8>DJ}9`Q`Je@M`#UbQ}xoY;r)lK(FF<254a3$?%$ ze{z{^()YP1bRx(Qc3?2j9jxoA*VsHb{U%^!UNU?m8q^OA{M3T-KWpDQa9zx^-UWYF9$~R>=rFdg zOZ<47vDsUB@~4Vuevhtyy(G_^ON7&)OW*{7uc61*s{>vCPm&Yo*?;E#N74-WVetQ! zGWZSBT>)$k{a>5&XZ{UB`D=0h%)kFmzqzGoQ8h{aWo!U@^k@35P+$4Sq&@i){0;xc z{;_j^dH)WcV1>){{3rGOXQHRCucv!&4bJ%!QRnlkgK!u{8KwF!y;Vkbqok_D=;CnZ zP7-pz^1bvTr_Kc?k{u+Ug+$fy!_+g=LnD`Id{(dCOxg!H0)^Ui{8mR0?O5oCM_P%Q zry?kCgYc*`q9#w-~ViXk}#HF^hw0zKm{MCXqIMwgR9yu?une7Om zP&?=0IS!4_!O!oe0Drf7Bq=d?MXGaN6Y|l5N(bPy2`SOv=#{Td5-b79odx}YYu~_+ z!KQbS`n$OU`0`s8N)M!ZE(;4PnNxxZ)M4uQdtqSiUqIG^ZQ=nA#!oj&NiPEzLNiat z(CT-uYaKrUkQ3kBD3PnHtKvTq>X~UGe4@j}C^?W2i0LEc_9Y)I_L^lO5p|@GE?U&s z-O{oeqe)dYSwrDVv%>!VcKXTK7vFFml`RVeD1LkEU|hZ5;P9A1)7>^NO)-L9I@6-s znPsSzjN)CA6Yz}ci*HM?e~_LSFC+D9ayXDTBJ&I_utjg8X!?ZXP++^Wu~7LHHb0&o$bO)kj*Lq`O)Sqia2$W64Eg_OjLe0m6C1P4FW;1Gg%< zcaJT<91C?G zmBbck8@y~RCt53sO{eyScU|bmq+Pk?xydqC?AGi|=yeDCS} z7JCP=TYYT@XV}-=C~15>$w52Z0tdftgi&xguOj8q;vzh97&CE7!L|_p3|S764PjA+ z9&|J;Bfb<&jP3qzG7pc^Fw)^0;IYj0M9ILliaQYkRT$oz^9_w+@qtA&zy9$djW9Y%7?wnsVxmSU*=z2kvLS8~VZ_!!G3OQ5d5@f2P30PmL`ap4WX{4MK{6W2W#_ z?T-|pJ?2iKlP!@VkhjUazzAR9sIFGiu>kzS5{7c01MSWEfESLWcjCx1RK3toKlg## zQoDiO78gm2zH$erQfL^7NiiAI3wtC!FE1W0Yh$X>r>DCtBrymIr9e+F>KmQ}{W;4D za%1Q&!$oHb$y1F6L5*dcBcbFZqF0SihK0CE^!9;jeJ}<|gSe3!-dl{#Bjc?JY(}D= zyl0oEhe+XrW6;Gii4`#`E$FSbY)5La=?pt*p1jn#UcC{)#lxwl#YG5AzovCEoX1)6 zQBIk4ug|;-_cNgaOEuVg432E!5|=m|cB8Tt-BE%iDo-AQswa2&XmLtF)6K6GK+~PJ z#o-RR?u79aMbS2ZLsk~gajHyp2?pJ+2oQN;7>MS{3%}nWNx-wBu`sZ>sfy6TZg8H5 zLJbjvHVhWuU8h2^2)J9r(;7euM@ANMt`^5w zbEDZlNhIirGu(MLTxM$^H$PY#_z4g9`N_je@bpoO<$$~?!pHq2;y?iv4-Pj1UWlce z>T_r7^{Pc%Z=kEcdmmsrH=woxOO4h-FaxEa*9}j!;8>`Z{*1Wr5zP-56YF;m*3;aU zQyV;*&!}yB#s6wwW^sc-4ci8Fodc(jv z+h>w8GF#!1LKs8SfY`1O_eDXad42I|9;}8tF|}-&KQkAg!oPoiVn76-+$^=RKkLb3 zV`HNxTZcy~9dFw?&k>VCf29GTtdIBFzd?YlWCCm@5(?^{xf#ko(=!kfAqoKmnTS@C zTT07<4(k4IbNP2}7S*7`nuCkRldW2z!gXEJG;l8GnmFC^j_qGzi&I{z)enG~!;@WV3b^Gq zU<7R(J_%&!Cr59H^pojud)?tL9;Yc$laV)4z7{I?>b-g1kcfjlqBuJ+&LwEAemeM! zR$G~%u3!Y+zXW}V-%%nKzFH@!$=U|_8AKatVGVV@;*Kx z+s#LgMYR!Tn8J|Y>2gS$Yw^GZ7tCj1AIi0WQBSF@E=Co{m%6bMqMl5&wI&u@t^AgLv6pNOIjo!O++bvW=oh^%NTPr?45H zY|LCY+IU$ + + + 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 0000000000000000000000000000000000000000..266681f1860fe7ad71136df9f6b2e7bc79f270a5 GIT binary patch literal 46496 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 literal 0 HcmV?d00001 diff --git a/jinhokim/index.html b/jinhokim/html/index.html similarity index 57% rename from jinhokim/index.html rename to jinhokim/html/index.html index 2c49f00..152b289 100644 --- a/jinhokim/index.html +++ b/jinhokim/html/index.html @@ -5,7 +5,8 @@

Webserv...


- 200 cat + 200 cat
+

diff --git a/jinhokim/include/Client.hpp b/jinhokim/include/Client.hpp index 555242d..e9df367 100644 --- a/jinhokim/include/Client.hpp +++ b/jinhokim/include/Client.hpp @@ -2,12 +2,9 @@ #define CLIENT_HPP #include -#include #include #include -#include -#include #include class Client { diff --git a/jinhokim/include/Response.hpp b/jinhokim/include/Response.hpp index d198c1f..c05b9cb 100644 --- a/jinhokim/include/Response.hpp +++ b/jinhokim/include/Response.hpp @@ -2,15 +2,12 @@ #define RESPONSE_HPP #include -#include #include #include #include #define BUFSIZE 10240 -#define HEADER_FORMAT \ - "HTTP/1.1 %d %s\r\nContent-Length: %ld\nContent-Type: %s\r\n\r\n" class Response { public: @@ -18,12 +15,14 @@ class Response { ~Response(void); void ResponseHandler(void); - void FindMime(char *ct_type, char *uri); + std::string FindMime(std::string uri); void FillHeader(int status, long len, std::string type); - void Handle200(int ct_len, char *local_uri); + 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: diff --git a/jinhokim/include/Server.hpp b/jinhokim/include/Server.hpp index dbea027..9cf82f5 100644 --- a/jinhokim/include/Server.hpp +++ b/jinhokim/include/Server.hpp @@ -2,13 +2,8 @@ #define SERVER_HPP #include -#include -#include #include #include -#include -#include -#include #include #include @@ -16,8 +11,6 @@ #include "Response.hpp" -#define BACKLOG 1024 - class Server { public: Server(int port); diff --git a/jinhokim/parse.cpp b/jinhokim/parse.cpp deleted file mode 100644 index b1f454c..0000000 --- a/jinhokim/parse.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include - -void a(void) { system("leaks a.out"); } - -int main(void) { - atexit(a); - std::string request_("GET / Moved Permanently"); - - std::size_t method_end_idx = request_.find_first_of(" "); - std::cout << "method_end_idx: " << method_end_idx << std::endl; - std::string method = request_.substr(0, method_end_idx); - std::size_t uri_end_idx = request_.find_first_of(" ", method_end_idx + 1); - std::cout << "uri_end_idx: " << uri_end_idx << std::endl; - std::string uri = - request_.substr(method_end_idx + 1, uri_end_idx - method_end_idx - 1); - - std::cout << "[INFO] Request: method=" << method << ", URI=" << uri - << std::endl; - const char* s = method.c_str(); -} \ No newline at end of file diff --git a/jinhokim/src/Client.cpp b/jinhokim/src/Client.cpp index 9c6b043..35ef3fe 100644 --- a/jinhokim/src/Client.cpp +++ b/jinhokim/src/Client.cpp @@ -21,7 +21,7 @@ EX) 7. 커낵션을 닫는다(close) */ -#include "../include/Client.hpp" +#include "Client.hpp" Client::Client(int port) : port_(port) {} diff --git a/jinhokim/src/Response.cpp b/jinhokim/src/Response.cpp index d607110..5df7a5f 100644 --- a/jinhokim/src/Response.cpp +++ b/jinhokim/src/Response.cpp @@ -12,15 +12,11 @@ void Response::ResponseHandler(void) { } std::size_t method_end_idx = request_.find_first_of(" "); - std::cout << "method_end_idx: " << method_end_idx << std::endl; std::string method = request_.substr(0, method_end_idx); std::size_t uri_end_idx = request_.find_first_of(" ", method_end_idx + 1); - std::cout << "uri_end_idx: " << uri_end_idx << std::endl; std::string uri = request_.substr(method_end_idx + 1, uri_end_idx - method_end_idx - 1); - // char* method = strtok(const_cast(request_.c_str()), " "); - // char* uri = strtok(NULL, " "); if (method.size() == 0 || uri.size() == 0) { std::cerr << "[ERROR] Failed to identify method, URI" << std::endl; Handle500(); @@ -30,103 +26,103 @@ void Response::ResponseHandler(void) { std::cout << "[INFO] Request: method=" << method << ", URI=" << uri << std::endl; - char safe_uri[BUFSIZE]; - char* local_uri; - struct stat st; + std::string local_uri; + if (!uri.compare("/")) + local_uri = "html/index.html"; + else + local_uri = uri.substr(1, uri.size()); - strcpy(safe_uri, uri.c_str()); - if (!strcmp(safe_uri, "/")) strcpy(safe_uri, "/index.html"); + struct stat st; - local_uri = safe_uri + 1; - if (stat(local_uri, &st) < 0) { + 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); + Handle200(st.st_size, local_uri.c_str()); return; } -void Response::Handle200(int ct_len, char* local_uri) { - char ct_type[40]; +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); - char buf[BUFSIZE]; - FindMime(ct_type, local_uri); + ct_type = FindMime(local_uri); FillHeader(200, ct_len, ct_type); while ((r = read(fd, buf, BUFSIZE)) > 0) response_.append(buf); } void Response::Handle404(void) { - char uri[9] = "404.html"; struct stat st; - char ct_type[40]; - int r; + std::string ct_type; char buf[BUFSIZE]; + char uri[14] = "html/404.html"; + int r; int fd = open(uri, O_RDONLY); stat(uri, &st); - FindMime(ct_type, uri); + 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) { - char uri[9] = "500.html"; struct stat st; - char ct_type[40]; - int r; + std::string ct_type; char buf[BUFSIZE]; + char uri[14] = "html/500.html"; + int r; int fd = open(uri, O_RDONLY); stat(uri, &st); - FindMime(ct_type, uri); + ct_type = FindMime(uri); FillHeader(500, st.st_size, ct_type); while ((r = read(fd, buf, BUFSIZE)) > 0) response_.append(buf); } -void Response::FindMime(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"); +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(".css")) + return "text/css"; + else if (!mime_type.compare(".js")) + return "text/javascript"; else - strcpy(ct_type, "text/plain"); + return "text/plain"; } void Response::FillHeader(int status, long len, std::string type) { - char header[BUFSIZE]; - char status_text[42]; - switch (status) { case 200: - strcpy(status_text, "OK"); + ComposeResponse(200, "OK", len, type); break; case 404: - strcpy(status_text, "Not Found"); + ComposeResponse(404, "Not Found", len, type); break; case 500: - strcpy(status_text, "Internal Server Error"); + ComposeResponse(500, "Internal Server Error", len, type); break; default: break; } - sprintf(header, HEADER_FORMAT, status, status_text, len, type.c_str()); - // stream >> status_str; - // response_ = "HTTP/1.1" + status_str + " " + status_text + - // "\r\nContent-Length: " \ - //+ len_str + "\nContent-Type: " + type + "\r\n"; - response_ = std::string(header); + 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 9318dd0..dfedb3e 100644 --- a/jinhokim/src/Server.cpp +++ b/jinhokim/src/Server.cpp @@ -67,7 +67,7 @@ int Server::Set(void) { if (bind_result < 0) throw std::runtime_error("Failed to bind socket to address"); - int listen_result = listen(server_fd_, BACKLOG); + int listen_result = listen(server_fd_, 1024); if (listen_result < 0) throw std::runtime_error("Failed to listen on socket"); return 0; @@ -115,8 +115,9 @@ int Server::Run(void) { 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); + // 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()) { char buf[BUFSIZE]; @@ -128,7 +129,9 @@ int Server::Run(void) { } else { buf[bytes_received] = '\0'; clients[curr_event->ident] += buf; - std::cout << "request from " << GetIp() << ": " << buf << std::endl; + + // std::cout << "request from " << GetIp() << ": " << buf << + // std::endl; std::map::iterator it = clients.find(curr_event->ident); diff --git a/jinhokim/src/client_main.cpp b/jinhokim/src/client_main.cpp index 7e104ad..44113fa 100644 --- a/jinhokim/src/client_main.cpp +++ b/jinhokim/src/client_main.cpp @@ -1,4 +1,4 @@ -#include "../include/Client.hpp" +#include "Client.hpp" int main(int ac, char** av) { try { diff --git a/jinhokim/src/server_main.cpp b/jinhokim/src/server_main.cpp index 59958eb..48adec8 100644 --- a/jinhokim/src/server_main.cpp +++ b/jinhokim/src/server_main.cpp @@ -1,4 +1,4 @@ -#include "../include/Server.hpp" +#include "Server.hpp" int main(int ac, char** av) { try { diff --git a/jinhokim/test.cpp b/jinhokim/test.cpp deleted file mode 100644 index a8f0c39..0000000 --- a/jinhokim/test.cpp +++ /dev/null @@ -1,190 +0,0 @@ -#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); - } - - if (listen(lsock, 10) < 0) { - perror("[ERR] failed to listen lsock.\n"); - exit(1); - } - - // to handle zombie process - signal(SIGCHLD, SIG_IGN); - - 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; - } - - pid = fork(); - if (pid == 0) { - close(lsock); - http_handler(asock); - close(asock); - exit(0); - } - - if (pid != 0) { - close(asock); - } - if (pid < 0) { - perror("[ERR] failed to fork.\n"); - } - } -} \ No newline at end of file From 6c36096fa42c80135d879c54d39ab35549877167 Mon Sep 17 00:00:00 2001 From: jinhokim Date: Wed, 18 Jan 2023 05:14:09 +0900 Subject: [PATCH 12/16] ADD: favicon.ico --- jinhokim/favicon.ico | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 jinhokim/favicon.ico diff --git a/jinhokim/favicon.ico b/jinhokim/favicon.ico new file mode 100644 index 0000000..e69de29 From 26ae64c9dfbec6a46061f002f7ba2a556e1a097a Mon Sep 17 00:00:00 2001 From: jinhokim Date: Wed, 18 Jan 2023 05:14:45 +0900 Subject: [PATCH 13/16] =?UTF-8?q?FEAT:=20=EB=A9=80=ED=8B=B0=20=ED=8F=AC?= =?UTF-8?q?=ED=8A=B8=20http=20=EC=84=9C=EB=B2=84=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jinhokim/html/index.html | 25 +++--- jinhokim/include/Server.hpp | 18 ++-- jinhokim/src/Response.cpp | 2 + jinhokim/src/Server.cpp | 172 +++++++++++++++++++----------------- 4 files changed, 121 insertions(+), 96 deletions(-) diff --git a/jinhokim/html/index.html b/jinhokim/html/index.html index 152b289..e046e02 100644 --- a/jinhokim/html/index.html +++ b/jinhokim/html/index.html @@ -1,12 +1,15 @@ - - 200 Status Page - - -

-

Webserv...


- 200 cat
- -

- - + + + 200 Status Page + + + +

+

Webserv...


+ 200 cat
+ +

+ + + \ No newline at end of file diff --git a/jinhokim/include/Server.hpp b/jinhokim/include/Server.hpp index 9cf82f5..fffbab3 100644 --- a/jinhokim/include/Server.hpp +++ b/jinhokim/include/Server.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -16,19 +17,24 @@ class Server { 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, + void ChangeEvents(int socket, int16_t filter, uint16_t flags, uint32_t fflags, intptr_t data, void* udata); - void DisconnectClient(int client_fd, std::map& clients); + 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_; - int server_fd_; - sockaddr_in address_; + std::vector server_fds_; + std::vector address_; + std::map clients_; + std::vector change_list_; }; void CheckArgument(int ac, char** av); diff --git a/jinhokim/src/Response.cpp b/jinhokim/src/Response.cpp index 5df7a5f..ca8b490 100644 --- a/jinhokim/src/Response.cpp +++ b/jinhokim/src/Response.cpp @@ -93,6 +93,8 @@ std::string Response::FindMime(std::string uri) { 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")) diff --git a/jinhokim/src/Server.cpp b/jinhokim/src/Server.cpp index dfedb3e..55563ef 100644 --- a/jinhokim/src/Server.cpp +++ b/jinhokim/src/Server.cpp @@ -30,46 +30,56 @@ 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; +int Server::GetServerIdx(int sock) { + std::vector::iterator it = + std::find(server_fds_.begin(), server_fds_.end(), sock); + int i = it - server_fds_.begin(); + + return i; } -void Server::ChangeEvents(std::vector& change_list, int socket, - int16_t filter, uint16_t flags, uint32_t fflags, - intptr_t data, void* udata) { +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); + change_list_.push_back(temp_event); } -void Server::DisconnectClient(int client_fd, - std::map& clients) { +void Server::DisconnectClient(int client_fd) { std::cerr << "Client disconnected" << std::endl; close(client_fd); - clients.erase(client_fd); + clients_.erase(client_fd); } int Server::Set(void) { - server_fd_ = socket(AF_INET, SOCK_STREAM, 0); - if (server_fd_ < 0) throw std::runtime_error("Failed to create socket"); + int optval = 1; + int sock; + sockaddr_in address; - address_.sin_family = AF_INET; - address_.sin_port = htons(port_); - address_.sin_addr.s_addr = inet_addr("127.0.0.1"); + for (int i = 0; i < 5; ++i) { + sock = socket(AF_INET, SOCK_STREAM, 0); - int optval = 1; - setsockopt(server_fd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + if (sock < 0) throw std::runtime_error("Failed to create socket"); + + address.sin_family = AF_INET; + address.sin_port = htons(port_ + i); + address.sin_addr.s_addr = inet_addr("127.0.0.1"); + + server_fds_.push_back(sock); + address_.push_back(address); - int bind_result = bind(server_fd_, (sockaddr*)&address_, sizeof(address_)); - if (bind_result < 0) - throw std::runtime_error("Failed to bind socket to address"); + fcntl(sock, F_SETFL, O_NONBLOCK); + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); - int listen_result = listen(server_fd_, 1024); - if (listen_result < 0) throw std::runtime_error("Failed to listen on socket"); + int bind_result = bind(sock, (sockaddr*)&address, sizeof(address)); + if (bind_result < 0) + throw std::runtime_error("Failed to bind socket to address"); + int listen_result = listen(sock, 1024); + if (listen_result < 0) + throw std::runtime_error("Failed to listen on socket"); + } return 0; } @@ -77,76 +87,80 @@ int Server::Run(void) { int kq = kqueue(); if (kq == -1) throw std::runtime_error("Failed to init kqueue"); - std::map clients; - std::vector change_list; struct kevent event_list[128]; - ChangeEvents(change_list, server_fd_, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, - NULL); + for (std::size_t i = 0; i < server_fds_.size(); ++i) + ChangeEvents(server_fds_[i], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL); int new_events = 1; struct kevent* curr_event; while (42) { new_events = - kevent(kq, &change_list[0], change_list.size(), event_list, 8, NULL); + kevent(kq, &change_list_[0], change_list_.size(), event_list, 8, NULL); if (new_events == -1) throw std::runtime_error("Failed kevent()"); - change_list.clear(); + change_list_.clear(); for (int i = 0; i < new_events; ++i) { curr_event = &event_list[i]; - if (curr_event->flags & EV_ERROR) { - if (curr_event->ident == static_cast(server_fd_)) - throw std::runtime_error("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"); - - fcntl(client_fd, F_SETFL, O_NONBLOCK); - - std::cout << "Accepted connection from " << GetIp() << std::endl; - - 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()) { - char buf[BUFSIZE]; - 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; - - // std::cout << "request from " << GetIp() << ": " << buf << - // std::endl; - - std::map::iterator it = - clients.find(curr_event->ident); - if (it != clients.end()) { - if (clients[curr_event->ident] != "") { - Response response(buf); - response.ResponseHandler(); - std::string response_str = response.GetResponse(); - send(curr_event->ident, response_str.c_str(), - response_str.size(), 0); - } - // DisconnectClient(curr_event->ident, clients); - // clients[curr_event->ident].clear(); - } - } + if (curr_event->flags & EV_ERROR) + HandleErrorEvent(curr_event->ident); + else if (curr_event->filter == EVFILT_READ) + HandleReadEvent(curr_event->ident); + } + } + return 0; +} + +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; +} + +int Server::HandleErrorEvent(uintptr_t ident) { + if (IsServer(ident)) + throw std::runtime_error("Server socket error"); + else + DisconnectClient(ident); + return 0; +} + +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")); + + fcntl(client_fd, F_SETFL, O_NONBLOCK); + + 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; } } } From bb6eb9c7fffa2ee954090935781ce9a24e3a8dea Mon Sep 17 00:00:00 2001 From: kimjinho1 Date: Wed, 18 Jan 2023 17:26:00 +0900 Subject: [PATCH 14/16] FEAT: default html --- jinhokim/html/404.html | 1 - jinhokim/html/500.html | 1 - jinhokim/html/index.html | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/jinhokim/html/404.html b/jinhokim/html/404.html index 672d557..5651299 100644 --- a/jinhokim/html/404.html +++ b/jinhokim/html/404.html @@ -8,7 +8,6 @@

404 Not Found


404 cat
-

diff --git a/jinhokim/html/500.html b/jinhokim/html/500.html index 0f0b784..a24f305 100644 --- a/jinhokim/html/500.html +++ b/jinhokim/html/500.html @@ -8,7 +8,6 @@

500 Internal Server Error


500 cat
-

diff --git a/jinhokim/html/index.html b/jinhokim/html/index.html index e046e02..1fb1d41 100644 --- a/jinhokim/html/index.html +++ b/jinhokim/html/index.html @@ -8,7 +8,7 @@

Webserv...


200 cat
- + 200 Cat

From 5fa774eaa8bbea3c4a99682f1dc8b4ce0b5fc75c Mon Sep 17 00:00:00 2001 From: kimjinho1 Date: Wed, 18 Jan 2023 17:26:14 +0900 Subject: [PATCH 15/16] file size check test --- jinhokim/test.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 jinhokim/test.cpp diff --git a/jinhokim/test.cpp b/jinhokim/test.cpp new file mode 100644 index 0000000..92c293e --- /dev/null +++ b/jinhokim/test.cpp @@ -0,0 +1,21 @@ +#include +#include +#include +#include + +#include + +int main(void) { + int fd = open("html/200.png", O_RDONLY); + if (fd < 0) return write(2, "File Error", 10); + + off_t file_size = lseek(fd, 0, SEEK_END); + if (file_size < 0) return write(2, "Lseek Error", 11); + + std::cout << "lseek file size: " << file_size << std::endl; + + 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 From 3d4777c4eb50d4bb49cf742684e0d0aee2780d4e Mon Sep 17 00:00:00 2001 From: kimjinho1 Date: Wed, 18 Jan 2023 17:26:33 +0900 Subject: [PATCH 16/16] =?UTF-8?q?WIP:=20=EB=A9=80=ED=8B=B0=20=ED=8F=AC?= =?UTF-8?q?=ED=8A=B8=20http=20=EC=84=9C=EB=B2=84=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jinhokim/src/Response.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jinhokim/src/Response.cpp b/jinhokim/src/Response.cpp index ca8b490..1545410 100644 --- a/jinhokim/src/Response.cpp +++ b/jinhokim/src/Response.cpp @@ -52,6 +52,8 @@ void Response::Handle200(int ct_len, const char* local_uri) { 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); }