Last active
February 24, 2022 15:47
-
-
Save petabyt/12c7d12dfa12c2b19cf41f3e712a2be8 to your computer and use it in GitHub Desktop.
tiny c99 web server
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Inspired by https://gist.github.com/laobubu/d6d0e9beb934b60b2e552c2d03e1409e | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <netdb.h> | |
#include <signal.h> | |
#define MAX_CONNECTION 100 | |
#define RESP_BUF_MAX 1000 | |
int recieve(char url[]); | |
int serv_init(int port, int *listenfd) { | |
char port_s[16]; | |
snprintf(port_s, 16, "%u", port); | |
printf("Starting on http://127.0.0.1:%s\n", port_s); | |
struct addrinfo hints, *res; | |
// Pre set to empty connections | |
memset(&hints, 0, sizeof(struct addrinfo)); | |
hints.ai_family = AF_INET; | |
hints.ai_socktype = SOCK_STREAM; | |
hints.ai_flags = AI_PASSIVE; | |
if (getaddrinfo(NULL, port_s, &hints, &res)) { | |
perror("getaddrinfo() failed\n"); | |
return 1; | |
} | |
for (struct addrinfo *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)) { | |
break; | |
} | |
} | |
freeaddrinfo(res); | |
if (listen(*listenfd, MAX_CONNECTION)) { | |
perror("listen() error"); | |
return 1; | |
} | |
} | |
//client connection | |
int respond(int n, int clients[MAX_CONNECTION]) { | |
char *buf = malloc(RESP_BUF_MAX); | |
int rcvd = recv(clients[n], buf, RESP_BUF_MAX, 0); | |
if (rcvd < 0) { | |
perror("recv() error\n"); | |
return 1; | |
} else if (rcvd == 0) { | |
perror("Client disconnected upexpectedly\n"); | |
return 1; | |
} | |
// Quickly filter parameters from response dump | |
char *url = strtok(buf, "\n"); | |
while (url != NULL) { | |
if (!strncmp(url, "GET ", 4)) { | |
url = strtok(url + 4, " "); | |
break; | |
} | |
url = strtok(NULL, "\n"); | |
} | |
// Route file descriptor into stdout | |
dup2(clients[n], STDOUT_FILENO); | |
close(clients[n]); | |
recieve(url); | |
fflush(stdout); | |
shutdown(STDOUT_FILENO, SHUT_WR); | |
close(STDOUT_FILENO); | |
clients[n] = -1; | |
return 0; | |
} | |
int serv_start(int port) { | |
int clients[MAX_CONNECTION]; | |
memset(clients, -1, sizeof(int) * MAX_CONNECTION); | |
struct sockaddr_in clientaddr; | |
int listenfd; | |
serv_init(port, &listenfd); | |
// Ignore SIGCHLD to avoid zombie threads | |
signal(SIGCHLD, SIG_IGN); | |
while (1) { | |
int slot = 0; | |
socklen_t addrlen = sizeof(clientaddr); | |
clients[slot] = accept(listenfd, (struct sockaddr *) &clientaddr, &addrlen); | |
if (clients[slot] < 0) { | |
perror("accept() error"); | |
} else { | |
if (fork() == 0) { | |
respond(slot, clients); | |
break; | |
} | |
} | |
while (clients[slot]!=-1) { | |
slot = (slot + 1) % MAX_CONNECTION; | |
} | |
} | |
return 0; | |
} | |
int recieve(char url[]) { | |
printf("HTTP/1.1 200 OK\rContent-type: text/html\n\r\n"); | |
printf("<title>Basic test</title>You are on URL, [%s]\n", url); | |
} | |
int main() { | |
serv_start(1234); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment