#include "httpd.h" #include #include #include #include #include #include #include #include #include #include #define MAX_CONNECTIONS 1000 #define BUF_SIZE 65535 #define QUEUE_SIZE 1000000 static int listenfd; int *clients; static void start_server(const char *); static void respond(int); static char *buf; // Client request char *method, // "GET" or "POST" *uri, // "/index.html" things before '?' *qs, // "a=1&b=2" things after '?' *prot, // "HTTP/1.1" *payload; // for POST int payload_size; void serve_forever(const char *PORT) { struct sockaddr_in clientaddr; socklen_t addrlen; int slot = 0; printf("Server started %shttp://127.0.0.1:%s%s\n", "\033[92m", PORT, "\033[0m"); // create shared memory for client slot array clients = mmap(NULL, sizeof(*clients) * MAX_CONNECTIONS, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); // Setting all elements to -1: signifies there is no client connected int i; for (i = 0; i < MAX_CONNECTIONS; i++) clients[i] = -1; start_server(PORT); // Ignore SIGCHLD to avoid zombie threads signal(SIGCHLD, SIG_IGN); // ACCEPT connections while (1) { addrlen = sizeof(clientaddr); clients[slot] = accept(listenfd, (struct sockaddr *)&clientaddr, &addrlen); if (clients[slot] < 0) { perror("accept() error"); exit(1); } else { if (fork() == 0) { close(listenfd); respond(slot); close(clients[slot]); clients[slot] = -1; exit(0); } else { close(clients[slot]); } } while (clients[slot] != -1) slot = (slot + 1) % MAX_CONNECTIONS; } } // start server void start_server(const char *port) { struct addrinfo hints, *res, *p; // getaddrinfo for host memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if (getaddrinfo(NULL, port, &hints, &res) != 0) { perror("getaddrinfo() error"); exit(1); } // socket and bind for (p = res; p != NULL; p = p->ai_next) { int option = 1; listenfd = socket(p->ai_family, p->ai_socktype, 0); setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); if (listenfd == -1) continue; if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0) break; } if (p == NULL) { perror("socket() or bind()"); exit(1); } freeaddrinfo(res); // listen for incoming connections if (listen(listenfd, QUEUE_SIZE) != 0) { perror("listen() error"); exit(1); } } // get request header by name char *request_header(const char *name) { header_t *h = reqhdr; while (h->name) { if (strcmp(h->name, name) == 0) return h->value; h++; } return NULL; } // get all request headers header_t *request_headers(void) { return reqhdr; } // Handle escape characters (%xx) static void uri_unescape(char *uri) { char chr = 0; char *src = uri; char *dst = uri; // Skip inital non encoded character while (*src && !isspace((int)(*src)) && (*src != '%')) src++; // Replace encoded characters with corresponding code. dst = src; while (*src && !isspace((int)(*src))) { if (*src == '+') chr = ' '; else if ((*src == '%') && src[1] && src[2]) { src++; chr = ((*src & 0x0F) + 9 * (*src > '9')) * 16; src++; chr += ((*src & 0x0F) + 9 * (*src > '9')); } else chr = *src; *dst++ = chr; src++; } *dst = '\0'; } // client connection void respond(int slot) { int rcvd; buf = malloc(BUF_SIZE); rcvd = recv(clients[slot], buf, BUF_SIZE, 0); if (rcvd < 0) // receive error fprintf(stderr, ("recv() error\n")); else if (rcvd == 0) // receive socket closed fprintf(stderr, "Client disconnected upexpectedly.\n"); else // message received { buf[rcvd] = '\0'; method = strtok(buf, " \t\r\n"); uri = strtok(NULL, " \t"); prot = strtok(NULL, " \t\r\n"); uri_unescape(uri); fprintf(stderr, "\x1b[32m + [%s] %s\x1b[0m\n", method, uri); qs = strchr(uri, '?'); if (qs) *qs++ = '\0'; // split URI else qs = uri - 1; // use an empty string header_t *h = reqhdr; char *t, *t2; while (h < reqhdr + 16) { char *key, *val; key = strtok(NULL, "\r\n: \t"); if (!key) break; val = strtok(NULL, "\r\n"); while (*val && *val == ' ') val++; h->name = key; h->value = val; h++; fprintf(stderr, "[H] %s: %s\n", key, val); t = val + 1 + strlen(val); if (t[1] == '\r' && t[2] == '\n') break; } t = strtok(NULL, "\r\n"); t2 = request_header("Content-Length"); // and the related header if there is payload = t; payload_size = t2 ? atol(t2) : (rcvd - (t - buf)); // bind clientfd to stdout, making it easier to write int clientfd = clients[slot]; dup2(clientfd, STDOUT_FILENO); close(clientfd); // call router route(); // tidy up fflush(stdout); shutdown(STDOUT_FILENO, SHUT_WR); close(STDOUT_FILENO); } free(buf); }