Skip to content

Minimal C++98 event-driven HTTP server for Linux using poll(), with static file serving, Nginx-style config, and experimental Python CGI.

Notifications You must be signed in to change notification settings

nands93/webserver

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WebServer in C++

Minimal C++98 web server with non-blocking I/O (poll), a simple configuration parser, static file serving, basic error responses, and experimental CGI (Python) support. Developed by @nands93 and @amenesca for a university project. Educational and learning purpose only.

Features

  • Single-process, event-driven server using poll()
  • Multiple clients, non-blocking sockets
  • Nginx-like configuration syntax
  • Static files from configured roots
  • Basic GET and POST handling
  • Basic error statuses for 400/404/405 (static HTML error pages exist but are not yet wired to responses)
  • Experimental CGI (Python) invocation on .py URIs

Requirements

  • Linux (uses poll, POSIX sockets, fcntl)
  • g++ with C++98 support
  • Python 3 at /usr/bin/python3 (used by CGI)
  • Terminal (for build/run logs)

Build

make

Outputs binary: ./webserver

Targets are defined in Makefile.

Run

Run from the repository root (paths are relative).

  • Default config:
./webserver
  • Custom config:
./webserver ./conf/default.conf

The default config (conf/default.conf) listens on port 8080 and defines two servers (server_name localhost and server_name 127.0.0.1) on the same port. The active VirtualServer is selected by the Host header (without the port).

Examples:

  • http://localhost:8080/ → serves data/www/index.html
  • Host matching matters; e.g., curl with a specific Host:
    • curl -H "Host: localhost" http://127.0.0.1:8080/
    • curl -H "Host: 127.0.0.1" http://127.0.0.1:8080/

Note:

  • Tilde paths are not expanded. The root ~/data/www/ in the second location of the default config will not resolve unless changed to an absolute path.
  • Stop with Ctrl+C (handled in srcs/main.cpp).

Configuration

Syntax is parsed by ConfigParser.

Example: conf/default.conf

server {
  listen 8080
  server_name localhost
  body_size 1K

  location / {
    root data/www/
    index index.html
  }

  location /data {
    # Tilde is not expanded by the server; use an absolute path instead
    root /absolute/path/to/data/www/
    index index.html
  }
}

Recognized directives in server:

  • listen, server_name, body_size, error_page, location

Inside location, see struct Location in VirtualServer:

  • _locationPath, _root, _cgi_extension, _upload, _autoindex, _methods, _return, _index

Notes:

  • Location matching is exact against the request URI (no prefix/longest-match logic).
  • The selected VirtualServer is chosen by exact Host header match to server_name (port stripped).

Request Handling

  • Requests are parsed by RequestParser:

    • Method, URI, version, headers, and body (for POST)
    • Host header parsing also extracts the port and stores Host without the port
  • Responses are built by Response:

    • GET: Response::handleGET
      • If uri matches a location exactly, serves root + index[1]
      • Else, for single-location configs, serves root + uri
      • On success returns 200 text/html with file contents
      • On miss returns 404 (body currently not using custom error page)
    • POST: Response::handlePOST
      • If URI ends with .py, invokes CGI via cgiHandler::postCgi
      • Otherwise echoes a simple text/plain response if a body is present; else 400
  • Static files are read in Response::readData

CGI

Current state:

  • Experimental: child process executes Python, but the HTTP response body does not capture the script’s stdout back to the client. You will see a 200 OK with an empty body.
  • The standalone test harness cgiHandler::configCgi (used by srcs/cgi/maincgi.cpp) demonstrates execution/redirection but is not wired into the main server path.

Ensure the script is executable. The form in data/www/index.html posts multipart/form-data to ../../cgi-bin/index.py.

Static Content and Error Pages

Quick Test

  • GET home:
curl -i http://localhost:8080/
  • POST to CGI (multipart):
curl -i -F "nome=Alice" -F "[email protected]" -F "imagem=@/path/to/image.png" http://localhost:8080/cgi-bin/index.py
  • POST simple form data (echo path):
curl -i -d "hello=world" http://localhost:8080/
  • Select a specific VirtualServer by Host:
curl -i -H "Host: localhost" http://127.0.0.1:8080/

Project Structure

Known Limitations

  • Single listen socket; binds to the first configured VirtualServer port
  • VirtualServer selection by exact Host header match only (no SNI, no IP-based)
  • Location matching is exact against the request URI (no prefix/longest-match)
  • Tilde (~) in paths is not expanded
  • Some error responses currently miss body content
  • No directory listing/autoindex
  • No DELETE implementation yet
  • CGI response body not returned to client (experimental plumbing)
  • No TLS, no HTTP/1.1 persistent connections, no chunked encoding
  • Minimal error handling and validation

Development Notes

About

Minimal C++98 event-driven HTTP server for Linux using poll(), with static file serving, Nginx-style config, and experimental Python CGI.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C++ 68.2%
  • HTML 29.1%
  • Python 1.5%
  • Makefile 1.2%