Skip to content

dyatlovk/ws_server

Repository files navigation

WS Server

A modern, high-performance HTTP web server library written in C++20, designed for building scalable web applications and APIs.

Table of Contents

Features

  • HTTP/1.1 Support: Full HTTP/1.1 protocol implementation with keep-alive connections
  • Modern C++20: Leverages the latest C++ features for performance and safety
  • Epoll-based I/O: Efficient event-driven I/O using Linux epoll for high concurrency
  • Routing System: Flexible URL routing with parameter extraction and regex support
  • Middleware Support: Modular middleware architecture for request/response processing
  • Static File Serving: Built-in static file server with MIME type detection
  • JSON Support: Native JSON request/response handling
  • Thread Pool: Asynchronous request processing with configurable thread pool
  • Socket Abstraction: TCP and Unix domain socket support
  • MVC Architecture: Model-View-Controller pattern support for structured applications
  • Comprehensive Logging: Colored, timestamped logging system with multiple levels and fmt-style formatting
  • Comprehensive Testing: Full test suite with unit tests and benchmarks

Quick Start

Basic HTTP Server

#include <http/server.h++>
#include <http/options.h++>

int main() {
    auto options = http::options({3044, "127.0.0.1", "My Server", "/public"});
    http::server app(&options);

    http::router router;
    router.add("/", http::request::methods::Get,
        [](const http::request *req, http::response *res) {
            res->with_body("Hello, World!");
        });

    app.with_routers(&router);
    return app.listen();
}

Examples

The project includes several comprehensive examples:

  • Basic Server - Minimal HTTP server setup
  • HTTP Server - Full-featured HTTP server with routing, JSON API, and static files
  • MVC Application - Complete MVC web application with frontend/backend separation
  • Logging Demo - Comprehensive demonstration of the logging system features

Installation

Requirements

  • Git - Version control
  • CMake 3.26+ - Build system (as required by CMakeLists.txt)
  • C++20 Compiler - GCC 11+ or Clang 12+
  • Linux - Currently supports Linux with epoll

Build Instructions

  1. Clone the repository:
git clone git@github.com:dyatlovk/ws_server.git
cd ws_server
  1. Configure and build (Debug):
cmake --preset=makefile-x86_64-linux-debug
cmake --build --preset=debug-build-linux -j$(nproc)
  1. Configure and build (Release):
cmake --preset=makefile-x86_64-linux-release
cmake --build --preset=release-build-linux -j$(nproc)

Build Options

Option Debug Release Description
CMAKE_BUILD_TYPE Debug MinSizeRel Build optimization level
SRV_BUILD_EXAMPLES On On Build example applications
SRV_BUILD_BENCHMARKS Off On Build performance benchmarks
SRV_BUILD_TESTS On Off Build unit test suite

Running Tests

# Build with tests enabled (debug mode)
cmake --preset=makefile-x86_64-linux-debug
cmake --build --preset=debug-build-linux -j$(nproc)

# Run the test suite
./build/makefile-x86_64-linux-debug/tests/server_test

Running Benchmarks

# Build with benchmarks enabled (release mode)
cmake --preset=makefile-x86_64-linux-release
cmake --build --preset=release-build-linux -j$(nproc)

# Run benchmarks
./build/makefile-x86_64-linux-release/benchmarks/response/benchmark_response
./build/makefile-x86_64-linux-release/benchmarks/router/benchmark_router

Running Examples

# Examples are built with both debug and release configurations
# Run basic example
./build/makefile-x86_64-linux-debug/examples/basic/basic_example

# Run HTTP server example
./build/makefile-x86_64-linux-debug/examples/http_server/http_server

# Run MVC example
./build/makefile-x86_64-linux-debug/examples/mvc/mvc

# Run logging system demonstration
./build/makefile-x86_64-linux-debug/examples/logging_demo/logging_demo

Quick Verification

After building, verify everything works:

# 1. Verify tests pass
./build/makefile-x86_64-linux-debug/tests/server_test --gtest_filter="*headers*"

# 2. Run a simple server test (in background, then test with curl)
./build/makefile-x86_64-linux-debug/examples/basic/basic_example &
SERVER_PID=$!
sleep 1
curl http://127.0.0.1:3044  # Should return "index"
kill $SERVER_PID

# 3. Check benchmark runs without errors
./build/makefile-x86_64-linux-release/benchmarks/response/benchmark_response

# 4. Test logging system
./build/makefile-x86_64-linux-debug/examples/logging_demo/logging_demo

API Reference

Server Configuration

// Server options
http::options options({
    3044,               // Port
    "127.0.0.1",       // Host
    "My Server",       // Server name
    "/public"          // Static files directory
});

http::server app(&options);

Routing

http::router router;

// Simple route
router.add("/", http::request::methods::Get, handler);

// Multiple HTTP methods
router.add("/api/data", {http::request::methods::Get, http::request::methods::Post}, handler);

// Route parameters with regex
router.add("/user/\\d+", http::request::methods::Get, user_handler);
router.add("/blog/\\D+", http::request::methods::Get, blog_handler);

Request Handling

auto handler = [](http::request *req, http::response *res) {
    // Access request method
    auto method = req->req.method;

    // Access URI and parameters
    auto uri = req->req.uri;
    auto params = req->req.params; // Extracted from regex routes

    // HTTP version
    auto version = req->req.http_ver;
};

Response Methods

// Text response
res->with_body("Hello, World!");

// JSON response
miniJson::Json json = miniJson::Json::_object{{"key", "value"}};
res->with_json(&json);

// File response
res->with_view("/index.html");

// Redirect
res->with_redirect("/new-location");

// Headers
res->with_header("Content-Type", "application/json");
res->with_added_header("X-Custom", "value");

// Status codes
res->with_status(404, "Not Found");

Logging System

The server includes a comprehensive logging system with colored output, timestamps, and multiple log levels:

#include <utils/logger.h++>

// Basic logging with different levels
LOG_DEBUG("Detailed diagnostic information: {}", debug_info);
LOG_INFO("General application flow: {}", status);
LOG_WARN("Warning condition: {}", warning_msg);
LOG_ERROR("Error occurred: {}", error_msg);

// Configure log level filtering
utils::Logger::set_level(utils::LogLevel::INFO);  // Hide DEBUG messages
utils::Logger::set_level(utils::LogLevel::WARN);  // Hide DEBUG and INFO
utils::Logger::set_level(utils::LogLevel::ERROR); // Only show ERROR

// Advanced formatting with fmt-style syntax
LOG_INFO("User {} has {} unread messages", username, count);
LOG_DEBUG("Server stats: CPU={:.1f}%, Memory={:.2f}GB", cpu_usage, memory_gb);
LOG_ERROR("Database connection failed: host={}, port={}, timeout={}ms",
          db_host, db_port, timeout);

Log Levels and Colors

Level Color Description Output Stream
DEBUG Cyan Detailed diagnostic information stdout
INFO Green General information about program execution stdout
WARN Yellow Warning conditions that should be noted stderr
ERROR Red Error conditions that indicate problems stderr

Integration with HTTP Server

The logging system is already integrated throughout the HTTP server components:

// Server startup and shutdown
LOG_INFO("Server starting on {}:{}", host, port);
LOG_ERROR("Failed to initialize epoll: {}", e.what());
LOG_INFO("Server is shutting down");

// Socket operations (automatically logged)
LOG_DEBUG("Creating inet_socket for {}:{}", host, port);
LOG_INFO("Opening socket for {}:{} (domain={}, type={})", host, port, domain, type);
LOG_ERROR("Failed to create socket: {}", std::strerror(errno));

// Connection handling
LOG_INFO("Accepted connection from client (fd={})", peer);
LOG_DEBUG("Reading from connection fd={}, bufSize={}", conn, bufSize);

Advanced Usage

MVC Architecture

The server supports Model-View-Controller patterns for larger applications:

// Controller class
class BlogController {
public:
    auto list(http::request *req, http::response *res) -> http::response {
        res->with_json(&blog_list);
        return *res;
    }

    auto entry(http::request *req, http::response *res) -> http::response {
        auto params = req->req.params;
        if (!params.empty()) {
            int id = std::atoi(params[0].c_str());
            // Load blog entry by ID
        }
        return *res;
    }
};

// Usage
BlogController blog;
router.add("/blog", method::Get,
    std::bind(&BlogController::list, &blog, std::placeholders::_1, std::placeholders::_2));

Static File Serving

The server automatically serves static files from the configured public directory:

  • Automatic MIME type detection
  • Efficient file streaming
  • Support for common web assets (HTML, CSS, JS, images)
  • Configurable document root

Performance Features

  • Epoll I/O Multiplexing: Handle thousands of concurrent connections
  • Thread Pool: Configurable worker threads for request processing
  • Zero-Copy Operations: Efficient memory management
  • Connection Keep-Alive: HTTP/1.1 persistent connections

Logging and Debugging

The server provides comprehensive logging capabilities for development and production:

// Development logging - show all messages
#ifdef DEBUG
    utils::Logger::set_level(utils::LogLevel::DEBUG);
#else
    utils::Logger::set_level(utils::LogLevel::INFO);
#endif

// Production logging with structured messages
LOG_INFO("Request processed: method={}, path='{}', status={}, duration={}ms",
         method, path, status_code, duration);

// Error handling with context
try {
    // ... server operations
} catch (const std::exception& e) {
    LOG_ERROR("Server error: {}", e.what());
}

Best Practices for Logging

  1. Use appropriate log levels:

    • DEBUG: Detailed diagnostic info for development
    • INFO: General application flow and important events
    • WARN: Recoverable issues that should be noted
    • ERROR: Serious problems that need attention
  2. Include context in messages:

    LOG_ERROR("Failed to process request: client={}, path='{}', error='{}'",
              client_id, request_path, error_msg);
  3. Be mindful of sensitive data - never log passwords or tokens

  4. Use structured logging for metrics:

    LOG_INFO("Performance metrics: requests={}, avg_time={}ms, errors={}",
             request_count, avg_response_time, error_count);

Architecture

Core Components

  • HTTP Parser: RFC-compliant HTTP message parsing
  • Socket Layer: Abstraction over TCP and Unix domain sockets
  • Epoll Engine: Event-driven I/O for scalability
  • Router: URL pattern matching and parameter extraction
  • Middleware Stack: Pluggable request/response processing
  • Thread Pool: Asynchronous task execution
  • Logging System: Colored, timestamped logging with multiple levels and fmt-style formatting

Dependencies

  • fmt: Modern C++ formatting library
  • MiniJSON: Lightweight JSON parsing and generation
  • Reflex: Regular expression engine for routing

Development

Project Structure

├── src/                    # Core library source code
│   ├── http/              # HTTP protocol implementation
│   ├── io/                # I/O abstractions (sockets, epoll)
│   ├── stl/               # STL extensions
│   └── utils/             # Utility classes (logging, thread pool)
├── examples/              # Example applications
├── tests/                 # Unit test suite
├── benchmarks/            # Performance benchmarks
└── vendor/                # Third-party dependencies

Contributing

  1. Code Style: Follow modern C++20 conventions
  2. Testing: Add tests for new features
  3. Documentation: Update README and code comments
  4. Performance: Consider performance implications

Complete Development Workflow

# 1. Setup development environment
git clone git@github.com:dyatlovk/ws_server.git
cd ws_server

# 2. Configure for development (debug + tests)
cmake --preset=makefile-x86_64-linux-debug

# 3. Build everything
cmake --build --preset=debug-build-linux -j$(nproc)

# 4. Run tests to ensure everything works
./build/makefile-x86_64-linux-debug/tests/server_test

# 5. Make your changes...

# 6. Rebuild and test
cmake --build --preset=debug-build-linux -j$(nproc)
./build/makefile-x86_64-linux-debug/tests/server_test

# 7. Test performance impact (optional)
cmake --preset=makefile-x86_64-linux-release
cmake --build --preset=release-build-linux -j$(nproc)
./build/makefile-x86_64-linux-release/benchmarks/response/benchmark_response

# 8. Test examples still work
./build/makefile-x86_64-linux-debug/examples/basic/basic_example

Testing

The project includes comprehensive tests covering:

  • HTTP parsing and generation
  • Socket operations
  • Routing functionality
  • Request/response handling
  • Error conditions

Benchmarking

Performance benchmarks are available for:

  • Response generation speed
  • Router matching performance
  • Memory usage patterns
  • Concurrency handling

Troubleshooting

Build Issues

CMake version mismatch:

# Ensure you have CMake 3.26+
cmake --version

# On Ubuntu/Debian, you might need to install from Kitware's repository
# if your distribution's CMake is too old

Missing dependencies:

# Ensure you have a C++20 compatible compiler
g++ --version  # Should be GCC 11+
clang++ --version  # Should be Clang 12+

# On Ubuntu/Debian:
sudo apt update
sudo apt install build-essential cmake git

Build preset not found:

# List available presets
cmake --list-presets

# If presets are not working, try manual configuration
mkdir -p build/manual
cd build/manual
cmake ../.. -DCMAKE_BUILD_TYPE=Debug -DSRV_BUILD_TESTS=ON
make -j$(nproc)

Runtime Issues

Permission denied when running executables:

# Make sure executables have execute permissions
chmod +x ./build/makefile-x86_64-linux-debug/tests/server_test

Port already in use:

# Check if port is already in use
sudo netstat -tulpn | grep :3044

# Kill process using the port
sudo lsof -ti:3044 | xargs sudo kill -9

Library linking errors:

# Ensure all dependencies are built
make clean
cmake --build --preset=debug-build-linux -j$(nproc)

Testing Issues

Tests failing:

# Run specific test categories
./build/makefile-x86_64-linux-debug/tests/server_test --gtest_filter="*HttpParser*"

# Run in verbose mode for debugging
./build/makefile-x86_64-linux-debug/tests/server_test --gtest_verbose

Performance issues:

# Run in release mode for better performance
cmake --preset=makefile-x86_64-linux-release
cmake --build --preset=release-build-linux -j$(nproc)
./build/makefile-x86_64-linux-release/examples/basic/basic_example

Logging Issues

Too verbose logging in production:

# Set appropriate log level to reduce output
utils::Logger::set_level(utils::LogLevel::WARN);  # Only warnings and errors
utils::Logger::set_level(utils::LogLevel::ERROR); # Only errors

Missing log output:

# Ensure log level allows your messages
utils::Logger::set_level(utils::LogLevel::DEBUG);  # Show all messages

# Check if you're using the correct macros
LOG_INFO("This message will appear");  # Correct
fmt::println("This bypasses logging system");  # Bypasses logging

Log colors not showing:

# Ensure terminal supports ANSI colors
echo $TERM  # Should show color-capable terminal like xterm-256color

# Test color support
./build/makefile-x86_64-linux-debug/examples/logging_demo/logging_demo

License

This project is open source. Please check the repository for license details.

Support

  • Issues: Report bugs and feature requests on GitHub
  • Documentation: Additional examples in the examples/ directory
  • Community: Contributions and feedback welcome

Note: This server is designed for Linux environments and requires epoll support. Windows and macOS support may be added in future versions.

About

Web server for C++

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Contributors 2

  •  
  •