// Copyright 2026 Amber Hu // Warning: This was hacked together quickly as a proof of // concept for 26wi, since there was previously no event-driven // programming example code. This code should not be taken as // an example of good style, only as a demonstration of epoll /* Warning! Do not use this in any kind of production environment This allows the client to read arbitrary file contents. Very bad! */ #include #include #include #include #include #include #include #include #include #include "SocketUtils.h" #include "EventManager.h" #define FILE_MAX_LEN 1234 void Usage(); void AcceptClient(int listen_fd); void HandleRequest(int connection_fd); void ShutdownOnClose(int input_fd); // Global variables // This is the global event manager. It abstracts over epoll // so you can focus on writing the event handlers instead of // managing the event loop EventManager kEventManager; bool kShutdownFlag = false; int main(int argc, char** argv) { if (argc != 2) { Usage(); } int sock_family; int listen_fd = Listen(argv[1], &sock_family); // Whenever a client tries to connect to this listener socket, accept them kEventManager.RegisterCallback(listen_fd, &AcceptClient); // When the server admin types on their console, check if it's Ctrl+D. // If so, start the server shutdown process kEventManager.RegisterCallback(STDIN_FILENO, &ShutdownOnClose); while(!kShutdownFlag) { kEventManager.WaitAndDispatch(); } return 0; } void AcceptClient(int listener) { int conn_fd = accept(listener, NULL, NULL); std::cout << "Accepting client with fd " << conn_fd << std::endl; kEventManager.RegisterCallback(conn_fd, HandleRequest); } void HandleRequest(int connection) { char buf[100]; int res = read(connection, buf, sizeof(buf)); if (res == 0) { // Client closed the TCP connection std::cout << "Client with fd " << connection << " has closed their TCP connection" << std::endl; kEventManager.RemoveCallback(connection); return; } assert(res > 0); // Replace \n with \0 buf[res-1] = '\0'; std::cout << "User with fd " << connection << " requested file: " << buf << std::endl; FILE* requested_file = fopen(buf, "r"); if (requested_file == NULL) { std::string msg("File "); msg = msg + buf + " cannot be found\n"; std::cout << msg << std::flush; write(connection, msg.c_str(), strlen(msg.c_str())); return; } // Copy the first FILE_MAX_LEN bytes of the file and send them to the client // This is not the best way to do this, but I had to write this quickly. Future TAs pls fix char file_body[FILE_MAX_LEN]; res = fread(file_body, sizeof(char), sizeof(file_body), requested_file); assert(res > 0); res = write(connection, file_body, res); assert(res > 0); fclose(requested_file); } void ShutdownOnClose(int input_fd) { // Try performing a small read on our input stream (probably stdin) // If the stream has been closed (admin typed ^D), start the shutdown process char dev_null[100]; int res = read(input_fd, dev_null, sizeof(dev_null)); // Only start the shutdown process if the stream is closed. // All other typing is ignored and does nothing if (res == 0) { std::cout << "Shutting down..." << std::endl; kEventManager.RemoveCallback(input_fd); kShutdownFlag = true; } } void Usage() { std::cerr << "Usage: ./event_server port" << std::endl << "Warning! Do not use this in any kind of production environment" << std::endl << "This allows the client to read arbitrary file contents. Very bad!" << std::endl; exit(1); }