Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <iostream>
- #include <thread>
- #include <semaphore>
- #include <vector>
- #include <sys/epoll.h>
- #include <sys/socket.h>
- #include <cstring>
- #include <unistd.h>
- //#include <fcntl.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <time.h>
- //extern std::string getResponse(std::string request);
- std::string getResponse(const std::string& request) {
- time_t timenow;
- timenow = time(NULL);
- struct tm *tt;
- tt = gmtime(&timenow);
- std::string response = "HTTP/1.1 200 OK\r\nServer: nginx/1.22.1\r\n";
- char buf[50];
- strftime(buf, sizeof(buf), "%a, %d %b %Y %T %Z", tt);
- response.append("Date: " + std::string(buf) + "\r\n");
- response.append("Content-Type: text/html\r\nContent-Length: 615\r\nLast-Modified: Sat, 09 Nov 2024 22:21:58 GMT\r\nConnection: close\r\nETag: \"672fe086-267\"\r\nAccept-Ranges: bytes\r\n\r\n<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\nhtml { color-scheme: light dark; }\nbody { width: 35em; margin: 0 auto;\nfont-family: Tahoma, Verdana, Arial, sans-serif; }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n");
- //printf("%s\n", response.c_str());
- return response;
- }
- // Rewriting to give each child its own epollfd to epoll_wait(...) on!
- // This is supposed to be what Nginx does OwO
- // Bugs:
- // * Child process sometimes (kind of rarely) somehow iterates the outer loop one time too many, so must be something wrong with the lock counting????? ...
- // as in we're not locking enough times or some kind of bad logic here?
- // *
- const uint_fast8_t verbosity = 0;
- const uint_fast16_t nthreads = 2;
- #define MAX_EVENTS 10000
- void fatalError(std::string message) {
- printf("Fatal error: %s\n", message.c_str());
- exit(EXIT_FAILURE);
- }
- std::vector<std::thread> threads;
- struct epoll_event events[nthreads][MAX_EVENTS];
- int listen_sock, epollfd[nthreads];
- uint_fast16_t lastpid = 0;
- //struct sockaddr_in addr;
- struct sockaddr_in6 addr6;
- int addrlen = sizeof(addr6);
- /*
- double microtime() {
- return (double(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count()) / double(1000000));
- }
- // Unused because we need string formatting for this
- void log(std::string message) {
- double timenow = microtime();
- printf("[%f] %s", timenow, message.c_str());
- }
- */
- void acceptNewConnections() {
- // Accept and register new sockets while child processes are processing established sockets
- struct epoll_event ev;
- ev.events = EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLERR;
- int conn_sock;
- while (true) {
- conn_sock = 0;
- while (conn_sock != -1) {
- if (verbosity >= 3)
- printf("main: accept4()\n");
- // Handle (accept) new connection sock on listen_sock
- conn_sock = accept4(listen_sock, (struct sockaddr*)&addr6, (socklen_t*)&addrlen, SOCK_NONBLOCK); // SOCK_NONBLOCK last param
- if (conn_sock >= 0) {
- if (verbosity >= 2)
- printf("Main: New connection: %d\n", conn_sock);
- ev.data.fd = conn_sock;
- // Calculate epollfd index based on last pid used
- if (++lastpid == nthreads)
- lastpid = 1;
- // Add new connection to epoll
- if (epoll_ctl(epollfd[lastpid], EPOLL_CTL_ADD, conn_sock, &ev) == -1) {
- perror("[perror] main: epoll_ctl: conn_sock");
- close(conn_sock);
- }
- }
- }
- }
- }
- /* DEF ALL THE MAIN OPERATIONS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
- // new mainLoop just listens on listen_sock and delegates new connections to the different threads, each with their own
- // epoll device. listen_sock is now blockable
- void mainLoop() {
- int nfds;
- printf("main: epollfd: %d, listen_sock %d\n", epollfd[0], listen_sock);
- while (true) {
- if (verbosity >= 1)
- printf("main: epoll_wait\n");
- nfds = epoll_wait(epollfd[0], events[0], MAX_EVENTS, -1); // currently NO_BLOCK set on listen_sock
- if (nfds == -1) {
- perror("[perror] main: epoll_wait");
- close(listen_sock);
- close(epollfd[0]);
- exit(EXIT_FAILURE);
- }
- //else if (nfds == 0 || (nfds == 1 && events[0][0].data.fd == listen_sock)) {
- // acceptNewConnections();
- //printf("Main: we're about to continue; nfds: %d\n", nfds);
- // continue;
- //}
- //unlockChildren();
- acceptNewConnections();
- // Wait for child processes to finish
- //for(uint_fast16_t pid = 1; pid < nthreads; pid++)
- // //lockUp.lock("main", 0);
- // //locks[pid].lock();
- // //lockUp.lock("main", 0);
- // locks[0].lock();
- }
- }
- void doTask(unsigned int pid) {
- struct epoll_event ev;
- int conn_sock, nfds;
- while (true) {
- //printf("Child: new for loop for pid %d\n", pid);
- nfds = epoll_wait(epollfd[pid], events[pid], MAX_EVENTS, -1);
- for (uint_fast64_t n = 0; n < nfds; n++) {
- struct epoll_event* event = &events[pid][n];
- if (verbosity >= 2)
- printf("child process: Pid %d: fd %d. nfds %d, events %d\n", pid, event->data.fd, nfds, event->events);
- if (event->events & EPOLLHUP || event->events & EPOLLERR) {
- if (verbosity >= 1)
- printf("child process (%d): event[%d] (nfds %d), fd %d: EHOLLUP/EPOLLERR %d (epollfd %d, listen_sock %d)\n", pid, n, nfds, event->data.fd, event->events, epollfd, listen_sock);
- //ev.events = 0;
- //ev.data.fd = event->data.fd;
- if (!(event->events & EPOLLERR) && epoll_ctl(epollfd[pid], EPOLL_CTL_DEL, event->data.fd, nullptr)) {
- perror("[perror] child process: epoll_ctl EPOLL_CTL_DEL");
- printf("Perror caused by pid %d uwu\n", pid);
- }
- //shutdown(event->data.fd, SHUT_WR);
- close(event->data.fd);
- if (verbosity >= 3)
- printf("child process: EPOLL_CTL_DEL -> shutdown -> close on fd %d\n", event->data.fd);
- }
- else {
- if (event->events & EPOLLIN) { // EPOLLIN == 1
- recv(event->data.fd, nullptr, 1024, 0);
- //printf("%d: EPOLLIN\n", event->data.fd);
- }
- if (event->events & EPOLLOUT) { // EPOLLOUT == 4
- std::string response = getResponse("");
- send(event->data.fd, response.c_str(), response.size(), MSG_NOSIGNAL);
- //printf("%d: EPOLLOUT\n", event->data.fd);
- //close(event->data.fd);
- //shutdown(event->data.fd, SHUT_WR);
- ev.events = EPOLLHUP | EPOLLERR;
- ev.data.fd = event->data.fd;
- epoll_ctl(epollfd[pid], EPOLL_CTL_MOD, event->data.fd, &ev);
- }
- if (event->events & EPOLLET) {
- printf("%d: EPOLLET\n", event->data.fd);
- }
- }
- }
- }
- }
- void initThreads(unsigned int nthreadsValue) {
- //nthreads = nthreadsValue;
- threads.resize(nthreads);
- std::thread t;
- for (unsigned int pid=0; pid<nthreads; pid++) {
- if (pid == 0)
- threads[pid] = std::thread(acceptNewConnections);
- else
- threads[pid] = std::thread(doTask, pid);
- printf("Spawned thread %d\n", pid);
- }
- // Main thread remains here atm, all other threads goes to mainLoop and doTask
- for (std::thread& t : threads) {
- //t.detach();
- t.join();
- }
- }
- void setupEpoll(std::string host, unsigned int port) {
- struct epoll_event ev;
- // Create listening socket for IPv4 and IPv6
- listen_sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
- if (listen_sock == -1) {
- perror("socket");
- exit(EXIT_FAILURE);
- }
- const int enable = 1;
- setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
- setsockopt(listen_sock, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(int));
- // Enable dual-stack (IPv4 and IPv6) operation
- int disable_v6only = 0;
- if (setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &disable_v6only, sizeof(disable_v6only)) == -1) {
- perror("setsockopt IPV6_V6ONLY");
- close(listen_sock);
- exit(EXIT_FAILURE);
- }
- memset(&addr6, 0, sizeof(addr6));
- addr6.sin6_family = AF_INET6;
- addr6.sin6_port = htons(port);
- addr6.sin6_addr = in6addr_any; // Allow connections to any IPv6 address
- // Bind the socket
- if (bind(listen_sock, (struct sockaddr*)&addr6, sizeof(addr6)) == -1) {
- perror("bind");
- close(listen_sock);
- exit(EXIT_FAILURE);
- }
- // Start listening
- if (listen(listen_sock, SOMAXCONN) == -1) {
- perror("listen");
- close(listen_sock);
- exit(EXIT_FAILURE);
- }
- for(uint16_t pid = 0; pid<nthreads; pid++) {
- // Create epoll instance
- epollfd[pid] = epoll_create1(0);
- if (epollfd[pid] == -1) {
- perror("epoll_create1");
- close(listen_sock);
- exit(EXIT_FAILURE);
- }
- }
- // Add listening socket to epollfd[0]
- ev.events = EPOLLIN | EPOLLHUP | EPOLLERR;
- ev.data.fd = listen_sock;
- if (epoll_ctl(epollfd[0], EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
- perror("epoll_ctl: listen_sock");
- close(listen_sock);
- close(epollfd[0]);
- exit(EXIT_FAILURE);
- }
- }
- // Will later be used by Cython instead of main
- void setup(std::string host, unsigned int port) {
- if (nthreads < 2) {
- printf("nthreads must be set to >= 2\n");
- exit(1);
- }
- printf("Running program with %d threads\n", nthreads);
- setupEpoll(host, port);
- initThreads(nthreads);
- }
- int main() {
- setup("::", 8080); // Use "::" to bind to all IPv6 addresses
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement