PHPackages                             duyler/http-server - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. duyler/http-server

ActiveLibrary[HTTP &amp; Networking](/categories/http)

duyler/http-server
==================

Non-blocking HTTP server for Duyler Framework worker mode with PSR-7 support

022PHPCI passing

Since May 21Pushed 1mo agoCompare

[ Source](https://github.com/duyler/http-server)[ Packagist](https://packagist.org/packages/duyler/http-server)[ RSS](/packages/duyler-http-server/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (4)Used By (0)

[![Quality Gate Status](https://camo.githubusercontent.com/58e185d217776a95a64b83efd02358d910578b6f47cdac020a2508c3138940ec/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d6475796c65725f6469266d65747269633d616c6572745f737461747573)](https://sonarcloud.io/summary/new_code?id=duyler_http-server)[![Coverage](https://camo.githubusercontent.com/d98a0fc8bde43b95d4ab8eebc0b788033176fbcfe2626610755daef31671c139/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d6475796c65725f6469266d65747269633d636f766572616765)](https://sonarcloud.io/summary/new_code?id=duyler_http-server)[![type-coverage](https://camo.githubusercontent.com/17a9d673cf92713c5d354b3c0bc1486066cb4f04a4312f2799c264f397ab9a5a/68747470733a2f2f73686570686572642e6465762f6769746875622f6475796c65722f687474702d7365727665722f636f7665726167652e737667)](https://shepherd.dev/github/duyler/http-server)[![psalm-level](https://camo.githubusercontent.com/a29b8de04e6e240788f565c71aad1d04c0d018c863c78699d202359e1a3cda1e/68747470733a2f2f73686570686572642e6465762f6769746875622f6475796c65722f687474702d7365727665722f6c6576656c2e737667)](https://shepherd.dev/github/duyler/http-server)

Duyler HTTP Server
==================

[](#duyler-http-server)

Non-blocking HTTP server for Duyler Framework worker mode with full PSR-7 support.

Features
--------

[](#features)

### Core Features

[](#core-features)

- **Non-blocking I/O** - Works seamlessly with Duyler Event Bus MainCyclic state
- **PSR-7 Compatible** - Full support for PSR-7 HTTP messages
- **Request ID Mechanism** - Unique request identifiers for parallel processing and request tracking
- **Parallel Processing** - Concurrent request handling with Fibers and out-of-order responses
- **HTTP &amp; HTTPS** - Support for both HTTP and HTTPS protocols
- **WebSocket Support** - RFC 6455 compliant WebSocket implementation with zero-cost abstraction
- **File Upload/Download** - Complete multipart form-data and file streaming support
- **Static Files** - Built-in static file serving with LRU caching
- **Keep-Alive** - HTTP persistent connections support
- **Range Requests** - Partial content support for large file downloads
- **Rate Limiting** - Sliding window rate limiter with configurable limits
- **Graceful Shutdown** - Clean server termination with timeout
- **Server Metrics** - Built-in performance and health monitoring
- **High Performance** - Optimized for long-running worker processes

Requirements
------------

[](#requirements)

- PHP 8.4 or higher
- ext-sockets (usually pre-installed)

Installation
------------

[](#installation)

```
composer require duyler/http-server
```

Quick Start
-----------

[](#quick-start)

### Basic HTTP Server (Standalone)

[](#basic-http-server-standalone)

```
use Duyler\HttpServer\Server;
use Duyler\HttpServer\Config\ServerConfig;
use Nyholm\Psr7\Response;

$config = new ServerConfig(
    host: '0.0.0.0',
    port: 8080,
);

$server = new Server($config);

// Check if server started successfully
if (!$server->start()) {
    die('Failed to start HTTP server');
}

// In your event loop
while (true) {
    if ($server->hasRequest()) {
        $requestData = $server->getRequest();

        // Check for null (race condition or error)
        if ($requestData === null) {
            continue;
        }

        // Process request
        $response = new Response(200, [], 'Hello World!');

        // Send response with request binding
        $server->respond($requestData->respond($response));
    }

    // Do other work...
}
```

Configuration
-------------

[](#configuration)

### ServerConfig Options

[](#serverconfig-options)

```
use Duyler\HttpServer\Config\ServerConfig;

$config = new ServerConfig(
    // Network
    host: '0.0.0.0',              // Bind address (IP or hostname)
    port: 8080,                    // Bind port (1-65535)
    socketBacklog: 511,            // TCP backlog queue size
    maxAcceptsPerCycle: 10,        // Max new connections per event cycle

    // SSL/TLS
    ssl: false,                    // Enable HTTPS
    sslCert: null,                 // Path to SSL certificate file
    sslKey: null,                  // Path to SSL private key file

    // Static Files
    publicPath: null,              // Path to public directory for static serving
    enableStaticCache: true,       // Enable in-memory static file cache
    staticCacheSize: 52428800,     // Max cache size (50MB)

    // Timeouts
    requestTimeout: 30,            // Request timeout in seconds
    connectionTimeout: 60,         // Connection timeout in seconds

    // Limits
    maxConnections: 1000,          // Maximum concurrent connections
    maxRequestSize: 10485760,      // Max request body size (10MB)
    bufferSize: 8192,              // Read buffer size in bytes
    headerCacheLimit: 100,         // Max cached header strings
    memoryLimit: 134217728,        // Memory limit in bytes (128MB)

    // Keep-Alive
    enableKeepAlive: true,         // Enable HTTP persistent connections
    keepAliveTimeout: 30,          // Keep-alive timeout in seconds
    keepAliveMaxRequests: 100,     // Max requests per keep-alive connection

    // Rate Limiting
    enableRateLimit: false,        // Enable rate limiting per IP
    rateLimitRequests: 100,        // Max requests per window
    rateLimitWindow: 60,           // Rate limit window in seconds

    // Security (see Security Configuration section for details)
    enableCors: false,
    contentSecurityPolicy: null,
    contentSecurityPolicyReportOnly: null,
    enableCspNonce: false,
    enableHsts: false,
    hstsMaxAge: 31536000,
    hstsIncludeSubDomains: false,
    hstsPreload: false,
    enableSecurityHeaders: true,
    frameOptions: 'DENY',
    referrerPolicy: 'strict-origin-when-cross-origin',
    permissionsPolicy: 'geolocation=(), microphone=(), camera=()',

    // Debug
    debugMode: false,              // Enable debug logging mode
);
```

Security Configuration
----------------------

[](#security-configuration)

### CORS (Cross-Origin Resource Sharing)

[](#cors-cross-origin-resource-sharing)

```
$config = new ServerConfig(
    enableCors: true,
    corsAllowedOrigins: ['https://example.com', 'https://app.example.com'],
    corsAllowedMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    corsAllowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
    corsAllowCredentials: false,
    corsMaxAge: 86400,
    corsExposeHeaders: ['X-Custom-Header'],
);
```

ParameterTypeDefaultDescription`enableCors``bool``false`Enable CORS handling`corsAllowedOrigins``list``[]`Allowed origin URLs (required when enabled)`corsAllowedMethods``list``['GET','POST','PUT','DELETE','OPTIONS']`Allowed HTTP methods`corsAllowedHeaders``list``['Content-Type','Authorization']`Allowed request headers`corsAllowCredentials``bool``false`Allow cookies/auth headers (incompatible with wildcard origin)`corsMaxAge``int``86400`Preflight cache duration in seconds`corsExposeHeaders``list``[]`Headers exposed to JavaScript### Content Security Policy (CSP)

[](#content-security-policy-csp)

```
$config = new ServerConfig(
    contentSecurityPolicy: [
        'default-src' => ["'self'"],
        'script-src' => ["'self'", "'nonce-{{NONCE}}'"],
        'style-src' => ["'self'", "'unsafe-inline'"],
        'img-src' => ["'self'", 'data:', 'https:'],
        'connect-src' => ["'self'", 'wss://example.com'],
    ],
    enableCspNonce: true,
);
```

ParameterTypeDefaultDescription`contentSecurityPolicy``?array``null`CSP directives as key-value map`contentSecurityPolicyReportOnly``?array``null`Report-only CSP directives`enableCspNonce``bool``false`Generate per-request CSP nonce (placeholder: `{{NONCE}}`)### HTTP Strict Transport Security (HSTS)

[](#http-strict-transport-security-hsts)

```
$config = new ServerConfig(
    ssl: true,
    enableHsts: true,
    hstsMaxAge: 31536000,
    hstsIncludeSubDomains: true,
    hstsPreload: false,
);
```

ParameterTypeDefaultDescription`enableHsts``bool``false`Enable HSTS header`hstsMaxAge``int``31536000`Max-age in seconds (1 year default)`hstsIncludeSubDomains``bool``false`Include all subdomains`hstsPreload``bool``false`Opt-in to browser HSTS preload lists### Permissions Policy

[](#permissions-policy)

```
$config = new ServerConfig(
    permissionsPolicy: 'geolocation=(), microphone=(), camera=(), payment=(self)',
);
```

### Other Security Headers

[](#other-security-headers)

The server automatically adds these headers when `enableSecurityHeaders` is true (default):

HeaderDefault Value`X-Content-Type-Options``nosniff``X-Frame-Options``DENY` (configurable: `DENY` or `SAMEORIGIN`)`X-XSS-Protection``1; mode=block``Referrer-Policy``strict-origin-when-cross-origin````
$config = new ServerConfig(
    enableSecurityHeaders: true,
    frameOptions: 'SAMEORIGIN',
    referrerPolicy: 'strict-origin-when-cross-origin',
);
```

Architecture Overview
---------------------

[](#architecture-overview)

### Request Processing Pipeline

[](#request-processing-pipeline)

```
Client Request
      |
      v
+-------------+     +------------------+     +----------------+
|   Server    |---->|  ConnectionPool  |---->|  HttpParser    |
|  (accept)   |     |  (manage conns)  |     |  (parse HTTP)  |
+-------------+     +------------------+     +----------------+
                                                      |
                                                      v
                         +------------------+     +----------------+
                         |  CorsService     |---->|  RateLimiter   |
                         |  (CORS check)    |     |  (throttle)    |
                         +------------------+     +----------------+
                                                      |
                                                      v
                         +------------------+     +----------------+
                         | SecurityHeaders  |---->|  AuditLogger   |
                         |  (add headers)   |     |  (log request) |
                         +------------------+     +----------------+
                                                      |
                                                      v
                         +------------------+     +----------------+
                         |  RequestQueue    |---->| ResponseSender |
                         |  (enqueue)       |     |  (send)        |
                         +------------------+     +----------------+
                                                      |
                                                      v
                                              Client Response

```

### Component Responsibilities

[](#component-responsibilities)

ComponentResponsibility`Server`Entry point. Accepts connections, delegates to processor`HttpRequestProcessor`Orchestrates request lifecycle: read, parse, security check, enqueue`ConnectionPool`Manages connection lifecycle, enforces max connections`RequestQueue`Thread-safe request queue with ID-based response mapping`ResponseSender`Writes HTTP responses back to client connections`ClientIpResolver`Resolves real client IP from X-Forwarded-For chain`CorsService`Validates CORS requests and adds response headers`SecurityHeadersService`Adds CSP, HSTS, X-Frame-Options, Permissions-Policy`RateLimiter`Sliding window rate limiting per client IP`AuditLogger`PSR-3 audit logging for security events### ServerInterface Decomposition

[](#serverinterface-decomposition)

```
                    ServerInterface
                         |
         +---------------+---------------+
         |               |               |
RequestLifecycle   ServerLifecycle   Metrics
    Interface          Interface      Interface
         |
WorkerPoolIntegration
      Interface

```

- **RequestLifecycleInterface** -- `hasRequest()`, `getRequest()`, `respond()`, `hasPendingResponse()`
- **ServerLifecycleInterface** -- `start()`, `stop()`, `reset()`, `restart()`, `shutdown()`
- **WorkerPoolIntegrationInterface** -- `setWorkerId()`, `addExternalConnection()`, `enableNotification()`, `getSocketResource()`
- **MetricsInterface** -- `getMetrics()`

### WebSocket Pipeline

[](#websocket-pipeline)

```
HTTP Upgrade Request
         |
         v
+------------------+
|    Handshake     | (validate Origin, compute accept key)
+------------------+
         |
         v
+------------------+
| WebSocketHandler | (frame parsing, message dispatch)
+------------------+
         |
         v
+------------------+
| WebSocketServer  | (connection management, broadcast)
+------------------+
         |
    +----+----+
    |         |
    v         v
  onMessage  onClose
    |         |
    v         v
 Connection  Cleanup

```

### Worker Pool Integration

[](#worker-pool-integration)

```
+-------------------+
|  Worker Pool      |
|  Master Process   |
+-------------------+
         |
    +----+----+----+
    |         |    |
    v         v    v
+--------+ +--------+ +--------+
|Worker 1| |Worker 2| |Worker N|
| Server | | Server | | Server |
+--------+ +--------+ +--------+
    |         |         |
    +----Shared Socket Pool----+
              |
              v
        Client Requests

```

Advanced Usage
--------------

[](#advanced-usage)

### HTTPS Server

[](#https-server)

```
$config = new ServerConfig(
    host: '0.0.0.0',
    port: 443,
    ssl: true,
    sslCert: '/path/to/certificate.pem',
    sslKey: '/path/to/private-key.pem',
);

$server = new Server($config);
$server->start();
```

### WebSocket Server

[](#websocket-server)

```
use Duyler\HttpServer\WebSocket\WebSocketServer;
use Duyler\HttpServer\WebSocket\WebSocketConfig;
use Duyler\HttpServer\WebSocket\Connection;
use Duyler\HttpServer\WebSocket\Message;

// Secure by default - origin validation is enabled
$wsConfig = new WebSocketConfig(
    maxMessageSize: 1048576,
    pingInterval: 30,
    // validateOrigin defaults to true for security
    // allowedOrigins defaults to ['*'] - customize for your domains
);

$ws = new WebSocketServer($wsConfig);

$ws->on('connect', function (Connection $conn) {
    echo "New connection: {$conn->getId()}\n";
});

$ws->on('message', function (Connection $conn, Message $message) {
    $data = $message->getJson();

    $conn->send([
        'type' => 'echo',
        'data' => $data,
    ]);
});

$ws->on('close', function (Connection $conn, int $code, string $reason) {
    echo "Connection closed: $code\n";
});

$server->attachWebSocket('/ws', $ws);
$server->start();

while (true) {
    if ($server->hasRequest()) {
        $requestData = $server->getRequest();

        if ($requestData !== null) {
            $response = new Response(200, [], 'Hello');
            $server->respond($requestData->respond($response));
        }
    }

    usleep(1000);
}
```

#### Public WebSocket Endpoints

[](#public-websocket-endpoints)

For public WebSocket endpoints that accept connections from any origin:

```
// WARNING: This configuration is insecure and exposes your WebSocket
// to CSRF attacks. Only use for truly public endpoints.
$wsConfig = new WebSocketConfig(
    validateOrigin: false, // Explicit opt-out of origin validation
    allowedOrigins: ['*'], // Explicit wildcard
);
```

See `examples/websocket-chat.php` for a complete chat application example.

### Static File Serving

[](#static-file-serving)

```
use Duyler\HttpServer\Handler\StaticFileHandler;

$staticHandler = new StaticFileHandler(
    publicPath: '/path/to/public',
    enableCache: true,
    maxCacheSize: 52428800, // 50MB
);

while (true) {
    if ($server->hasRequest()) {
        $request = $server->getRequest();

        // Try to serve static file first
        $response = $staticHandler->handle($request->request);

        if ($response === null) {
            // Not a static file, handle dynamically
            $response = handleDynamicRequest($request->request);
        }

        $server->respond($request->respond($response));
    }
}
```

### File Download

[](#file-download)

```
use Duyler\HttpServer\Handler\FileDownloadHandler;

$fileHandler = new FileDownloadHandler();

$response = $fileHandler->download(
    filePath: '/path/to/file.pdf',
    filename: 'document.pdf',
    mimeType: 'application/pdf'
);

$server->respond($requestData->respond($response));
```

### File Upload

[](#file-upload)

```
// Uploads are automatically parsed from multipart/form-data
$request = $server->getRequest();

$uploadedFiles = $request->getUploadedFiles();

foreach ($uploadedFiles as $field => $file) {
    /** @var \Psr\Http\Message\UploadedFileInterface $file */

    if ($file->getError() === UPLOAD_ERR_OK) {
        $file->moveTo('/path/to/uploads/' . $file->getClientFilename());
    }
}
```

### Graceful Shutdown

[](#graceful-shutdown)

```
$server = new Server(new ServerConfig());
$server->start();

// Register shutdown handler
pcntl_signal(SIGTERM, function() use ($server) {
    $success = $server->shutdown(30); // 30 second timeout
    exit($success ? 0 : 1);
});

while (true) {
    if ($server->hasRequest()) {
        $requestData = $server->getRequest();
        $response = new Response(200, [], 'OK');
        $server->respond($requestData->respond($response));
    }
}
```

### Server Metrics

[](#server-metrics)

```
$server = new Server(new ServerConfig());
$server->start();

// Get metrics periodically
$metrics = $server->getMetrics();
// [
//     'uptime_seconds' => 3600,
//     'total_requests' => 10000,
//     'successful_requests' => 9850,
//     'failed_requests' => 150,
//     'active_connections' => 5,
//     'total_connections' => 10050,
//     'closed_connections' => 10045,
//     'timed_out_connections' => 10,
//     'cache_hits' => 8500,
//     'cache_misses' => 1500,
//     'cache_hit_rate' => 85.0,
//     'avg_request_duration_ms' => 12.3,
//     'min_request_duration_ms' => 1.2,
//     'max_request_duration_ms' => 450.5,
//     'requests_per_second' => 2.78,
// ]
```

API Reference
-------------

[](#api-reference)

### Server

[](#server)

#### Methods

[](#methods)

- `start(): bool` - Start the server (returns false on failure)
- `stop(): void` - Stop the server
- `shutdown(int $timeout): bool` - Graceful shutdown with timeout
- `reset(): void` - Reset the server state
- `restart(): void` - Restart the server
- `hasRequest(): bool` - Check if there's a pending request (non-blocking)
- `getRequest(): ?RequestData` - Get the next request with unique ID (null if unavailable)
- `hasPendingResponse(): bool` - Check needs respond
- `respond(ResponseData): void` - Send response bound to request via ID
- `getMetrics(): array` - Get server performance metrics
- `setLogger(LoggerInterface)` - Set external Logger
- `attachWebSocket(string $path, WebSocketServer $ws): void` - Attach WebSocketServer
- `addExternalConnection(Socket $clientSocket, array $metadata): void` - Add external connection from Worker Pool
- `getSocketResource(): mixed` - Get socket resource for Event Loop integration
- `setExternalSocketResource(mixed $resource): void` - Set external socket resource for Worker Pool mode
- `enableNotification(): void` - Enable notification socket pair for reactive Event Loop
- `disableNotification(): void` - Disable notification mechanism
- `setEventLoopActive(bool $active): void` - Set Event Loop active flag
- `isEventLoopActive(): bool` - Get Event Loop active flag

### RequestData

[](#requestdata)

```
final readonly class RequestData
{
    public string $id;                      // Unique request identifier (e.g., "req_1")
    public ServerRequestInterface $request; // PSR-7 server request
    public int $connectionId;               // Internal connection ID

    public function respond(ResponseInterface $response): ResponseData;
}
```

### ResponseData

[](#responsedata)

```
final readonly class ResponseData
{
    public string $requestId;             // ID of the request this response belongs to
    public ResponseInterface $response;   // PSR-7 response object
}
```

### StaticFileHandler

[](#staticfilehandler)

#### Methods

[](#methods-1)

- `handle(ServerRequestInterface): ?ResponseInterface` - Handle static file request
- `getCacheStats(): array` - Get cache statistics
- `clearCache(): void` - Clear the cache

### FileDownloadHandler

[](#filedownloadhandler)

#### Methods

[](#methods-2)

- `download(string $filePath, ?string $filename, ?string $mimeType): ResponseInterface`
- `downloadRange(string $filePath, int $start, int $end, ...): ResponseInterface`
- `parseRangeHeader(string $rangeHeader, int $fileSize): ?array`

Request ID Mechanism
--------------------

[](#request-id-mechanism)

The HTTP Server uses a Request ID mechanism that enables parallel request processing and out-of-order responses.

### Overview

[](#overview)

Each HTTP request receives a unique identifier (e.g., `req_1`, `req_2`). This ID binds the request to its response, enabling:

- **Parallel processing** - Multiple requests can be processed concurrently using Fibers
- **Out-of-order responses** - Fast requests don't have to wait for slow ones
- **Request tracking** - Each request can be logged and traced via its unique ID

### How It Works

[](#how-it-works)

```
┌─────────────┐
│HTTP Request │
└──────┬──────┘
       │
       ▼
┌─────────────────────────┐
│ getRequest()            │
│                         │
│ Returns: RequestData {  │
│   id: "req_1"           │
│   request: ServerRequest│
│   connectionId: 42      │
│ }                       │
└──────┬──────────────────┘
       │
       ▼
┌─────────────────────────┐
│ Your Application Logic  │
│                         │
│ - Process request       │
│ - Create response       │
└──────┬──────────────────┘
       │
       ▼
┌─────────────────────────┐
│ respond()               │
│                         │
│ $requestData->          │
│   respond($response)    │
│                         │
│ Binds response to       │
│ request via ID          │
└──────┬──────────────────┘
       │
       ▼
┌──────────────┐
│HTTP Response │
└──────────────┘

```

### Basic Usage

[](#basic-usage)

```
while ($server->hasRequest()) {
    $requestData = $server->getRequest();

    if ($requestData === null) {
        continue;
    }

    // Access request data
    $requestId = $requestData->id;              // "req_1"
    $request = $requestData->request;           // PSR-7 request
    $connectionId = $requestData->connectionId; // Internal ID

    // Process request
    $response = new Response(200, [], 'OK');

    // Send response (bound to request via ID)
    $server->respond($requestData->respond($response));
}
```

### Parallel Processing

[](#parallel-processing)

Process multiple requests concurrently using Fibers:

```
$actors = [];

while (true) {
    if (!$server->hasRequest()) {
        // Resume suspended actors
        foreach ($actors as $key => $fiber) {
            if ($fiber->isTerminated()) {
                unset($actors[$key]);
                continue;
            }

            if ($fiber->isSuspended()) {
                $fiber->resume();
            }
        }

        usleep(1000);
        continue;
    }

    $requestData = $server->getRequest();

    if ($requestData === null) {
        continue;
    }

    // Create actor (Fiber) for parallel processing
    $fiber = new Fiber(function() use ($server, $requestData): void {
        // Simulate slow operation
        usleep(random_int(100000, 1000000));

        $response = new Response(200, [], 'Processed');

        // Response can be sent in ANY order
        $server->respond($requestData->respond($response));
    });

    $fiber->start();
    $actors[] = $fiber;
}
```

**Benefits:**

- Slow requests don't block fast requests
- Better resource utilization
- Improved throughput for mixed workloads

### Examples

[](#examples)

See the following examples for different use cases:

- **Basic usage**: `examples/request-id-basic.php` - Simple sequential processing
- **Parallel processing**: `examples/parallel-processing.php` - Concurrent request handling with Fibers

### Performance

[](#performance)

MetricValueRequest ID generation~0.01μs (sequential integer)RequestData creation~0.1μsMemory per request~164 bytesOverhead for 10K requests~1.6ms time, ~1.6MB memory**Conclusion:** Negligible overhead for production use.

Reactive Event Loop
-------------------

[](#reactive-event-loop)

HTTP Server supports reactive Event Loop through Notification Socket Pair. Event Loop wakes up only when an HTTP request has been accepted and parsed, without polling overhead.

### Overview

[](#overview-1)

Traditional polling approach wastes CPU cycles checking for requests that may not exist. The Notification Socket Pair provides true reactive behavior:

```
Traditional (Polling):
  Event Loop → hasRequest() → false → sleep → repeat (wastes CPU)

Reactive (Notification):
  Event Loop sleeps → Request arrives → Server notifies → Event Loop wakes up

```

### How It Works

[](#how-it-works-1)

```
┌─────────────────────────────────────────────────────────────────────────┐
│                          Event Loop Process                              │
│                                                                          │
│  EvIo watcher monitors notification socket (sleeps until notified)       │
│                                                                          │
│  notifyRead ◄─────────────────────────────────────────────────────────   │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
                                    ▲
                                    │ write notification (~1μs)
                                    │
┌─────────────────────────────────────────────────────────────────────────┐
│                              Server                                      │
│                                                                          │
│  1. Accept connection                                                   │
│  2. Read HTTP data                                                      │
│  3. Parse request                                                       │
│  4. Enqueue request → NOTIFY EVENT LOOP                                 │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

```

### Standalone Mode

[](#standalone-mode)

```
use Duyler\HttpServer\Config\ServerConfig;
use Duyler\HttpServer\Server;
use Nyholm\Psr7\Response;
use Socket;

$config = new ServerConfig(host: '0.0.0.0', port: 8080);
$server = new Server($config);
$server->start();

// Enable notification mechanism
$server->enableNotification();

// Get notification socket for EvIo
$notifySocket = $server->getSocketResource();

$ioWatcher = new EvIo($notifySocket, Ev::READ, function() use ($server): void {
    // Clear notification buffer
    // Non-blocking socket may have no data, suppress expected errors
    $socket = $server->getSocketResource();
    if ($socket instanceof Socket) {
        $previousErrorReporting = error_reporting(0);
        socket_read($socket, 4096);
        error_reporting($previousErrorReporting);
    }

    // Set active flag (prevents redundant notifications)
    $server->setEventLoopActive(true);

    try {
        // Process all ready requests
        while ($server->hasRequest()) {
            $requestData = $server->getRequest();
            if ($requestData === null) {
                break;
            }

            $response = new Response(200, [], 'Hello World!');
            $server->respond($requestData->respond($response));
        }
    } finally {
        // Clear active flag
        $server->setEventLoopActive(false);
    }
});

Ev::run();
```

### Best Practices

[](#best-practices)

1. **Always set active flag** - prevents redundant notifications while processing
2. **Clear notification buffer** - read all data from socket when waking up
3. **Process all requests** - use while loop until `hasRequest()` returns false
4. **Use try/finally** - guarantees active flag is cleared even on errors

### Performance

[](#performance-1)

MetricTraditional PollingReactive NotificationCPU in idlePeriodic wakeupsZero overheadWakeup latencyUp to polling interval~1μs notificationScalabilityConstant overheadOne watcher for any connections- **Zero overhead in idle** - Event Loop sleeps until request arrives
- **Minimal overhead** - single socket write (~1μs)
- **Scalability** - one watcher regardless of connection count

Examples
--------

[](#examples-1)

The `examples/` directory contains various usage examples:

### Basic Examples

[](#basic-examples)

- **request-id-basic.php** - Simple HTTP server with Request ID mechanism (beginner-friendly)

### Advanced Examples

[](#advanced-examples)

- **parallel-processing.php** - Parallel request processing with Fibers and out-of-order responses
- **reactive-event-loop.php** - Reactive Event Loop with Notification Socket Pair
- **evio-integration.php** - EvIo integration with reactive Event Loop

### Feature Examples

[](#feature-examples)

- **websocket-chat.php** - WebSocket chat application

Testing
-------

[](#testing)

```
# Run all tests
composer test

# Run with coverage (requires Xdebug or pcov)
composer test:coverage

# Run PHPStan
composer phpstan
```

Performance Tips
----------------

[](#performance-tips)

1. **Enable Keep-Alive** - Reduces connection overhead for multiple requests
2. **Use Static Cache** - Cache frequently accessed static files in memory
3. **Adjust Buffer Size** - Increase for high-throughput scenarios
4. **Set Appropriate Timeouts** - Balance between responsiveness and resource usage
5. **Limit Max Connections** - Prevent resource exhaustion

Worker Pool
-----------

[](#worker-pool)

For production multi-process deployment with process management, load balancing, and IPC, use the separate [duyler/worker-pool](https://github.com/duyler/worker-pool) package. It integrates seamlessly with this HTTP Server.

Roadmap
-------

[](#roadmap)

### Version 1.0 (In Progress)

[](#version-10-in-progress)

- WebSocket - RFC 6455 compliant implementation
- PSR-3 Logger integration
- Notification Socket Pair for reactive Event Loop
- Enhanced documentation

### Future Versions

[](#future-versions)

- HTTP/2 support
- gRPC support

Contributing
------------

[](#contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

Support
-------

[](#support)

- [Issue Tracker](https://github.com/duyler/http-server/issues)
- [Discussions](https://github.com/duyler/http-server/discussions)
- [Duyler Framework](https://github.com/duyler)

###  Health Score

22

—

LowBetter than 21% of packages

Maintenance60

Regular maintenance activity

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity15

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/69f18edde71f0f80540eda4e097854eddf8eb3390f38ff2ad241b9daaf622281?d=identicon)[milinsky](/maintainers/milinsky)

---

Top Contributors

[![milinsky](https://avatars.githubusercontent.com/u/17288321?v=4)](https://github.com/milinsky "milinsky (93 commits)")

### Embed Badge

![Health badge](/badges/duyler-http-server/health.svg)

```
[![Health](https://phpackages.com/badges/duyler-http-server/health.svg)](https://phpackages.com/packages/duyler-http-server)
```

###  Alternatives

[php-http/cache-plugin

PSR-6 Cache plugin for HTTPlug

25126.1M82](/packages/php-http-cache-plugin)[illuminate/http

The Illuminate Http package.

11937.9M6.9k](/packages/illuminate-http)[rdkafka/rdkafka

A PHP extension for Kafka

2.2k24.3k1](/packages/rdkafka-rdkafka)[httpsoft/http-message

Strict and fast implementation of PSR-7 and PSR-17

87965.9k114](/packages/httpsoft-http-message)[mezzio/mezzio-router

Router subcomponent for Mezzio

265.4M91](/packages/mezzio-mezzio-router)[serpapi/google-search-results-php

Get Google, Bing, Baidu, Ebay, Yahoo, Yandex, Home depot, Naver, Apple, Duckduckgo, Youtube search results via SerpApi.com

69127.2k](/packages/serpapi-google-search-results-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
