webserv is a fully functional HTTP/1.1 server written in C++98, built from scratch without any external libraries.
- Multiple virtual servers — host multiple servers on different IP/port pairs from a single config file
- HTTP methods — GET, POST, DELETE
- CGI support — execute
.py,.shscripts via configurable CGI path - File uploads — configurable upload directory per location
- Autoindex — optional directory listing when no index file is found
- Multiple index support — serves the first existing file
- Custom error pages — per-server error page overrides (400, 403, 404, 405, 500, 501, 502, 504)
- Redirections — per-location HTTP redirects with configurable status codes (301, 307, etc.)
- Cookies — basic cookie handling in responses
- I/O multiplexing — non-blocking I/O using
poll() - Signal handling — graceful shutdown on
SIGINT
Clone the repository and make
git clone git@github.com:opheliamarboeuf/webserv.git webserv
cd webserv
makeRequires a C++98-compatible compiler (e.g. g++ or clang++).
To clean:
make clean # remove object files
make fclean # remove object files and binary
make re # full rebuild./webserv <config_file>Example:
./webserv bigbrowser.configThe configuration file uses an NGINX-inspired syntax. Here is the bigbrowser.config provided with the project:
client_max_body_size 10M;
server {
listen 127.0.0.1:8080;
server_name bigbrowser;
index index.html;
root /www/html;
error_page 404 www/html/errors/404.html;
error_page 403 www/html/errors/403.html;
error_page 405 www/html/errors/405.html;
error_page 500 www/html/errors/500.html;
error_page 502 www/html/errors/502.html;
error_page 504 www/html/errors/504.html;
location /cgi-bin/ {
root /www;
cgi_path /usr/bin/env;
cgi_extension .sh .py;
}
location /data {
root /www/html/data;
index fallback.html;
autoindex on;
}
location /uploads {
upload_enabled on;
upload_path /www/uploads/entry.txt;
autoindex on;
root /www/uploads;
allowed_methods GET POST DELETE;
}
location /test_allowed {
allowed_methods POST;
autoindex on;
root /www/test;
}
location /test_redirect {
return 307 /redirected.html;
root /www/test_redirect;
}
location /save {
root /www/save/;
allowed_methods DELETE;
}
}| Directive | Scope | Description |
|---|---|---|
listen |
server | [ip:]port to listen on |
server_name |
server | One or more hostnames |
root |
server / location | Filesystem root for requests |
index |
server / location | Default file(s) for directory requests |
client_max_body_size |
global / server | Max body size (e.g. 10M, 512K) |
error_page |
global / server | Custom error pages for HTTP codes |
allowed_methods |
location | GET, POST, DELETE |
autoindex |
location | on / off — directory listing |
upload_enabled |
location | on / off |
upload_path |
location | Path where uploaded content is stored |
cgi_extension |
location | Extensions triggering CGI (e.g. .sh .py) |
cgi_path |
location | Interpreter used to run CGI scripts |
return |
location | HTTP redirection (301, 302, 307, 308) |
| Feature | How to test |
|---|---|
| Static file serving | GET http://127.0.0.1:8080/ |
| Directory listing | GET http://127.0.0.1:8080/data/ |
| File upload | POST form at /post.html → stored in www/uploads/entry.txt |
| File deletion | DELETE http://127.0.0.1:8080/uploads/entry.txt |
| HTTP redirection (307) | GET http://127.0.0.1:8080/test_redirect/ |
| Method restriction | GET http://127.0.0.1:8080/test_allowed/ → 405 |
| Shell CGI | GET http://127.0.0.1:8080/cgi-bin/start.sh |
| Python CGI | GET http://127.0.0.1:8080/cgi-bin/home_redirect.py |
| Cookie session | Fill form at /get_info.html → cookies set via set_cookies.sh |
| CGI timeout (504) | GET http://127.0.0.1:8080/cgi-bin/timeout.sh |
| Custom error pages | Request a non-existent path → custom 404 page |
- Event loop — single-threaded server using
poll()for I/O multiplexing (server + clients) - Connections — one request per connection (
Connection: close), clients handled via a state-basedClientclass - Request parsing — incremental parsing:
- headers read until
\r\n\r\n - support for
Content-Lengthandchunkedbodies - request validation before processing
- headers read until
- Routing — longest-prefix match on
locationblocks - CGI execution —
fork()+execve()with pipes:- POST body sent via stdin
- output read via stdout
- timeout: 5s →
504 Gateway Timeout
- Error handling — centralized via
makeError():- custom error pages if configured
- fallback to default HTML responses
- Security checks — path normalization, access control (
stat,access), body size limits
poll() is used to implement a single-threaded, event-driven server.
It allows handling multiple client sockets without blocking on I/O, while remaining portable and simpler than platform-specific alternatives such as epoll.
webserv/
├── inc/ # Header files
├── src/
│ ├── cgi/ # CGI execution
│ ├── config/ # Configuration parsing
│ ├── request/ # HTTP request parsing
│ ├── response/ # HTTP response building
│ ├── server/ # Server loop, client handling
│ └── webserv/ # Entry point
└── www/ # Web root (HTML, CGI scripts, uploads)