A HTTP/1.1 server written from scratch in C++98, built as a team Ed, Edd, Eddy.
No external HTTP libraries, sockets, request parsing, routing, CGI, and the event loop are all hand-rolled and driven by a single non-blocking epoll()-style loop.
(Screenshot of our internal test suite.)
- HTTP/1.1 parsing (headers, chunked transfer-encoding, keep-alive)
GET,POST,DELETEmethods- File upload / download and static file serving
- Per-route allowed methods
- Internal (301) and external (302) redirections
- Autoindex (directory listing) on/off
- Custom and default index pages
- Custom and default error pages
- Multiple servers / multiple ports via a single config file
- Non-blocking I/O, single event loop, never hangs on a slow client
- CGI support (Python, Bash, PHP)
- Cookies & session management (login, visit counter, private notes)
- Multiple CGI interpreters configurable per location
Server behavior is driven by an NGINX-style config file (see conf/*.conf), parsed by a hand-written tokenizer/parser (src/config).
server {
listen 8080;
host 127.0.0.1;
server_name example.local;
root www/webServPages;
index index.html;
location / {
allowed_methods GET POST;
autoindex off;
}
location /upload {
allowed_methods POST DELETE;
client_max_body_size 10M;
}
location /cgi-bin {
cgi_pass /usr/bin/python3;
allowed_methods GET POST;
}
error_page 404 www/ErrorPages/Non-Found.html;
}Full config grammar (EBNF)
(* Top level structure *)
Config = [WhiteSpace], [GlobalDirectives], {ServerBlock}+, [WhiteSpace];
(* Global directives *)
GlobalDirectives = {Directive};
(* Server block *)
ServerBlock = [WhiteSpace], "server", WhiteSpace, "{", [WhiteSpace],
{Directive | LocationBlock},
[WhiteSpace], "}", [WhiteSpace];
(* Location block *)
LocationBlock = [WhiteSpace], "location", WhiteSpace,
(LocationPath | LocationRegexPath), WhiteSpace,
"{", [WhiteSpace],
{Directive},
[WhiteSpace], "}", [WhiteSpace];
(* Location paths *)
LocationPath = Value;
LocationRegexPath = "~", {WhiteSpace}+, Value;
(* Directive structure *)
Directive = [WhiteSpace], DirectiveName, {(WhiteSpace, Value)}+, [WhiteSpace], ";", [WhiteSpace];
(* Directive names *)
DirectiveName = "listen" | "host" | "server_name" | "error_page" | "client_max_body_size" |
"root" | "index" | "allowed_methods" | "autoindex" | "return" | "cgi_pass" |
"cgi_timeout" | "connection_timeout" | "error_log" | "access_log";
(* Value types *)
Value = Number | Size | String;
(* Numeric value *)
Number = Digit, {Digit};
Digit = "0" | "1" | ... | "9";
(* Size value with suffix *)
Size = Number, Suffix;
Suffix = "K" | "k" | "M" | "m" | "G" | "g";
(* String value *)
String = (Letter | Digit | SpecialChar), {Letter | Digit | SpecialChar};
Letter = "a" | "b" | ... | "z" | "A" | "B" | ... | "Z";
SpecialChar = "_" | "-" | "." | "/";
(* Comment *)
Comment = "#", {AnyChar - EOL}, EOL;
AnyChar = ? any character ?;
EOL = {? end of line character ? | "\n"};
(* Whitespace handling *)
WhiteSpace = {" " | "\t" | "\n" | "\r" | Comment};make # build (debug mode by default)
./webserv [config_file] # defaults to conf/default.conf if omittedOther build modes:
make release # optimized build
make sanitize # build with ASan/UBSan
make valgrind # build for valgrind debugging
make tests # build & run unit tests
make run # build + run, ARGS_MAKE="conf/bonus.conf" to pass a config
make help # full list of targetsWe built a themed "test suite" site (www/glitch) that doubles as a stress-testing playground:
siege -c 255 -t 30S http://127.0.0.1:8080/empty.html| Role | Member |
|---|---|
| Config Randomizer | Ed |
| CGI Master | Mohamed Idbellaali |
| Multiplex Architect | Abdelkarim Laktari |

