A lightweight HTTP web-server library for creating simple web servers in C/C++.
The library provides a clear and minimal API for starting an HTTP server, defining routes, handling different HTTP methods, and working with request data such as headers, query parameters, and cookies.
It also includes JSON serialization/deserialization support (optionally via a third-party library).
- Static and parameterized routing (e.g.
/user/<int:id>/profile/) - Support for common HTTP methods:
GET,POST,PUT,DELETE - Simple interface for registering handlers for routes
- Server startup and graceful shutdown
Acceptance criteria
- Ability to define routes with fixed paths and path parameters
- Correct dispatching of requests based on HTTP method and route
- Server can be started with a single function call and listens on a given port
- Access to HTTP request headers
- Parsing and reading cookies from incoming requests
- Ability to set cookies in responses
Acceptance criteria
- Handlers can read arbitrary headers by name
- Cookies are parsed into a convenient structure
- Handlers can add cookies to the HTTP response
- Parsing of query parameters from the URL
- Convenient access to query values inside handlers
- JSON serialization and deserialization for request/response bodies
- Optional usage of a third-party JSON library
Acceptance criteria
- Query parameters are correctly parsed and accessible by key
- JSON request bodies can be deserialized into structures
- Responses can be serialized to JSON with correct headers
- Asynchronous I/O with epoll: Event-driven I/O model using Linux epoll
- Non-blocking accept and read operations
- Edge-triggered mode for maximum efficiency
- Configurable maximum connections
- Backward compatible with blocking I/O mode
- Worker Pool for Parallel Processing: Thread pool for concurrent request handling
- Configurable number of worker threads
- Thread-safe task queue
- Automatic load distribution across workers
- Combined Benefits: Epoll handles I/O events efficiently while worker pool processes requests in parallel
Acceptance criteria
- Server can operate in epoll mode or blocking mode
- Epoll mode handles multiple concurrent connections efficiently
- No blocking on I/O operations when epoll is enabled
- Multiple requests can be processed concurrently via worker pool
- Worker pool size is configurable via API
- Thread-safe task submission and processing
- Proper cleanup on server shutdown
- Graceful degradation if epoll is not available
- Zero-copy file transmission using sendfile()
- Direct transfer from kernel page cache to network buffer
- Automatic Content-Type detection
- Efficient handling of large files
Acceptance criteria
- Files are served without loading entire content into RAM
- sendfile() is used for file transmission
- Content-Type headers are set correctly based on file extension
- Large files (>100MB) can be served efficiently
This project follows the Google C Style Guide.
- GCC (C11 or later)
- Make
- cJSON library (installed via
third_party_installation.sh)
- Clone the repository:
git clone <repository-url>
cd examproject1-chessnok- Install cJSON library:
./third_party_installation.sh- Build the library:
make all- Install system-wide (requires sudo):
sudo make installOr install to a custom prefix:
make install PREFIX=/path/to/prefixThis installs:
- Headers to
$(PREFIX)/include/httpserver/ - Library to
$(PREFIX)/lib/ - pkg-config file to
$(PREFIX)/lib/pkgconfig/
To remove the installed library:
sudo make uninstallBuild the static library without installing:
make allThis creates build/libhttpserver.a that can be linked with your applications.
Each example is in its own directory with a dedicated Makefile. Navigate to the example directory and use make run:
# Basic server example (GET, POST, PUT, DELETE)
cd examples/basic_server
make run
# Cookie demo
cd examples/cookie_demo
make run
# JSON API
cd examples/json_api
make run
# Query parameters demo
cd examples/query_params
make runTo build an example without running it:
cd examples/basic_server
makeIndividual examples are available in the examples/ directory:
basic_server/- Server with GET, POST, PUT, DELETE routes and parametersjson_api/- JSON request/response handlingcookie_demo/- Cookie management examplesquery_params/- Query parameter parsing
make cleanAfter installation, include the main header:
#include <httpserver/httpserver.h>Or include individual headers:
#include <httpserver/server.h>
#include <httpserver/request.h>
#include <httpserver/response.h>gcc -o myapp myapp.c $(pkg-config --cflags --libs httpserver)gcc -o myapp myapp.c \
-I/usr/local/include/httpserver \
-L/usr/local/lib \
-lhttpserver \
-lcjsonCFLAGS += $(shell pkg-config --cflags httpserver)
LDFLAGS += $(shell pkg-config --libs httpserver)
myapp: myapp.c
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)#include <httpserver/httpserver.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void hello_handler(http_request_t* req, http_response_t* res) {
(void)req;
http_response_set_status(res, 200);
http_response_set_body(res, "Hello, World!\n");
}
void user_handler(http_request_t* req, http_response_t* res) {
const char* id = http_request_get_route_param(req, "id");
char body[256];
snprintf(body, sizeof(body), "User ID: %s\n", id ? id : "unknown");
http_response_set_status(res, 200);
http_response_set_body(res, body);
}
void post_user_handler(http_request_t* req, http_response_t* res) {
char body[512];
if (req->body && strlen(req->body) > 0) {
snprintf(body, sizeof(body), "POST: Created user with data: %s\n", req->body);
} else {
snprintf(body, sizeof(body), "POST: Created user (no body provided)\n");
}
http_response_set_status(res, 201);
http_response_set_body(res, body);
}
int main(void) {
http_server_t* server = http_server_create(8080);
if (server == NULL) {
fprintf(stderr, "Failed to create server\n");
return EXIT_FAILURE;
}
// Register GET routes
http_server_register_route(server, "GET", "/", hello_handler);
http_server_register_route(server, "GET", "/user/<int:id>", user_handler);
// Register POST route
http_server_register_route(server, "POST", "/api/users", post_user_handler);
// Start server (blocks until stopped)
http_server_start(server);
// Cleanup
http_server_destroy(server);
return EXIT_SUCCESS;
}#include <httpserver/httpserver.h>
#include <stdio.h>
#include <stdlib.h>
void hello_handler(http_request_t* req, http_response_t* res) {
(void)req;
http_response_set_status(res, 200);
http_response_set_body(res, "Hello, World!\n");
}
int main(void) {
http_server_t* server = http_server_create(8080);
if (server == NULL) {
fprintf(stderr, "Failed to create server\n");
return EXIT_FAILURE;
}
// Enable epoll mode for async I/O (event-driven, non-blocking)
http_server_set_epoll_mode(server, 1);
http_server_set_max_connections(server, 1000);
// Configure worker pool for parallel request processing
http_server_set_worker_pool_size(server, 8);
// Register routes
http_server_register_route(server, "GET", "/", hello_handler);
// Start server (blocks until stopped)
// Epoll handles I/O events, worker pool processes requests in parallel
http_server_start(server);
// Cleanup
http_server_destroy(server);
return EXIT_SUCCESS;
}#include <httpserver/httpserver.h>
#include <stdio.h>
#include <stdlib.h>
void file_handler(http_request_t* req, http_response_t* res) {
// Extract file path from request (e.g., from route parameter or query)
// For this example, we use a fixed path
const char* filepath = "/path/to/file.html";
http_response_set_status(res, 200);
// Send file using zero-copy sendfile()
// client_fd is available in req->client_fd
if (http_response_send_file(res, req->client_fd, filepath) != 0) {
// Error sending file, send error response
http_response_set_status(res, 500);
http_response_set_body(res, "Error serving file");
}
// Note: http_response_send_file() automatically marks the response as sent,
// so no additional response sending is needed
}
int main(void) {
http_server_t* server = http_server_create(8080);
if (server == NULL) {
fprintf(stderr, "Failed to create server\n");
return EXIT_FAILURE;
}
// Enable async features
http_server_set_epoll_mode(server, 1);
http_server_set_worker_pool_size(server, 4);
http_server_register_route(server, "GET", "/file", file_handler);
http_server_start(server);
http_server_destroy(server);
return EXIT_SUCCESS;
}Note: The http_response_send_file() function uses sendfile() for zero-copy file transmission. The client file descriptor is automatically available in req->client_fd for all handlers. When http_response_send_file() is called, it sends the HTTP headers and file content directly, and marks the response as sent, so no additional response handling is needed.
For more examples, see the examples/ directory and the Getting Started Guide.
.
├── src/ # Source files
│ ├── include/ # Public headers (for installation)
│ │ └── httpserver/
│ │ ├── httpserver.h # Main convenience header
│ │ ├── server.h # Server API
│ │ ├── router.h # Router API
│ │ ├── request.h # Request API
│ │ ├── response.h # Response API
│ │ ├── headers.h # Headers API
│ │ ├── cookie.h # Cookie API
│ │ ├── query.h # Query API
│ │ ├── json_util.h # JSON API
│ │ ├── http_parser.h # Parser API
│ │ └── worker_pool.h # Worker pool API
│ ├── server.c # Main server implementation
│ ├── router.c # Route matching and dispatch
│ ├── request.c # HTTP request parsing
│ ├── response.c # HTTP response building
│ ├── headers.c # HTTP headers management
│ ├── cookie.c # Cookie parsing and management
│ ├── query.c # Query parameter parsing
│ ├── json_util.c # JSON utilities (cJSON wrapper)
│ ├── http_parser.c # Low-level HTTP parsing
│ ├── worker_pool.c # Worker thread pool implementation
│ └── utils.c # Utility functions
├── examples/ # Example programs
│ ├── basic_server/ # Basic server with GET, POST, PUT, DELETE
│ ├── json_api/ # JSON API example
│ ├── cookie_demo/ # Cookie demo
│ └── query_params/ # Query params example
├── tests/ # Test files
│ ├── test_server.c
│ ├── test_router.c
│ ├── test_request.c
│ ├── test_cookie.c
│ ├── test_query.c
│ ├── test_json.c
│ ├── test_integration.c
│ └── test_memory_leaks.c # Memory leak tests
├── Makefile # Build system
├── httpserver.pc # pkg-config file
├── third_party_installation.sh # cJSON installation script
├── GETTING_STARTED.md # Getting started guide
└── Readme.md # This file
The project includes comprehensive tests for all features:
- Unit tests for each component
- Integration tests for full request/response cycles
- Tests verify all acceptance criteria are met
Run unit tests and memory leak tests:
make test