{"id":23289791,"url":"https://github.com/cmatosbc/charon","last_synced_at":"2025-10-27T22:30:49.480Z","repository":{"id":265108009,"uuid":"894993609","full_name":"cmatosbc/charon","owner":"cmatosbc","description":"Throttling and rate limiting in a cohesive and simple way.","archived":false,"fork":false,"pushed_at":"2024-11-28T19:05:58.000Z","size":26,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-20T04:18:13.946Z","etag":null,"topics":["laravel","php-library","php-rest-api","php81","symfony","throttling","wordpress","wordpress-rest-api"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cmatosbc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-27T11:20:37.000Z","updated_at":"2024-12-03T20:58:24.000Z","dependencies_parsed_at":"2024-11-27T18:20:33.998Z","dependency_job_id":"8e2b3e24-ee6c-4b5a-9cd4-30555444418b","html_url":"https://github.com/cmatosbc/charon","commit_stats":null,"previous_names":["cmatosbc/charon"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmatosbc%2Fcharon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmatosbc%2Fcharon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmatosbc%2Fcharon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cmatosbc%2Fcharon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cmatosbc","download_url":"https://codeload.github.com/cmatosbc/charon/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238563730,"owners_count":19492975,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["laravel","php-library","php-rest-api","php81","symfony","throttling","wordpress","wordpress-rest-api"],"created_at":"2024-12-20T04:18:08.648Z","updated_at":"2025-10-27T22:30:49.172Z","avatar_url":"https://github.com/cmatosbc.png","language":"PHP","readme":"# Charon\n\nA simple yet powerful PSR-15 compliant rate limiting middleware for PHP applications. Charon provides an effective way to protect your applications from abuse through configurable request throttling.\n\n[![Software License](https://img.shields.io/badge/license-GPL--3.0-brightgreen.svg?style=flat-square)](LICENSE)\n\n## Features\n\n- 🚀 PSR-15 Middleware compliant\n- 💾 PSR-16 Simple Cache support for storage\n- 📝 Optional PSR-3 Logger integration\n- ⚡ Efficient rate limiting using sliding window\n- 🔒 IP and User-Agent based throttling\n- 🎯 Configurable rate limits and time windows\n- 📊 Standard rate limit headers (X-RateLimit-*)\n- 🚫 Automatic blacklisting for repeat offenders\n\n## Installation\n\nYou can install the package via composer:\n\n```bash\ncomposer require cmatosbc/charon\n```\n\n## Usage\n\n### Basic Usage\n\n```php\nuse Charon\\ThrottleMiddleware;\n\n// Create the middleware with basic configuration\n$middleware = new ThrottleMiddleware(\n    limit: 100,           // Maximum requests allowed\n    windowPeriod: 3600,   // Time window in seconds (1 hour)\n    cache: $cacheImpl     // PSR-16 cache implementation\n);\n\n// Add it to your middleware stack\n$app-\u003eadd($middleware);\n```\n\n### With Logging\n\n```php\nuse Charon\\ThrottleMiddleware;\nuse Monolog\\Logger;\nuse Monolog\\Handler\\StreamHandler;\n\n// Create a PSR-3 logger\n$logger = new Logger('rate-limits');\n$logger-\u003epushHandler(new StreamHandler('path/to/rate-limit.log', Logger::WARNING));\n\n// Create middleware with logging\n$middleware = new ThrottleMiddleware(\n    limit: 100,\n    windowPeriod: 3600,\n    cache: $cacheImpl,\n    logger: $logger,          // PSR-3 logger\n    logAllRequests: false     // Set to true to log all requests\n);\n```\n\n### With Automatic Blacklisting\n\n```php\nuse Charon\\ThrottleMiddleware;\n\n$middleware = new ThrottleMiddleware(\n    limit: 100,\n    windowPeriod: 3600,\n    cache: $cacheImpl,\n    logger: $logger\n);\n\n// Blacklist clients after 5 rate limit violations\n$middleware-\u003emaybeBlacklist(5);\n```\n\nWhen blacklisting is enabled:\n- Clients exceeding rate limits multiple times will be tracked\n- After reaching the specified number of violations, the client will be blacklisted\n- Blacklisted clients receive a 403 Forbidden response\n- Violations are tracked across multiple time windows\n- Blacklist status is stored in cache with client signature\n\n### Framework Integration Examples\n\n#### Slim 4\n\n```php\nuse Slim\\Factory\\AppFactory;\nuse Charon\\ThrottleMiddleware;\n\n$app = AppFactory::create();\n\n// Add the middleware with blacklisting\n$app-\u003eadd((new ThrottleMiddleware(\n    limit: 100,\n    windowPeriod: 3600,\n    cache: $cache\n))-\u003emaybeBlacklist(5));\n```\n\n#### Laravel\n\n```php\nuse Charon\\ThrottleMiddleware;\n\n// In a service provider\npublic function boot()\n{\n    $this-\u003eapp-\u003emiddleware([\n        (new ThrottleMiddleware(\n            limit: 100,\n            windowPeriod: 3600,\n            cache: app()-\u003emake('cache.store')\n        ))-\u003emaybeBlacklist(5)\n    ]);\n}\n```\n\n#### Symfony\n\n```php\nuse Charon\\ThrottleMiddleware;\nuse Symfony\\Component\\Cache\\Adapter\\RedisAdapter;\nuse Symfony\\Component\\Cache\\Psr16Cache;\n\n// In services.yaml\nservices:\n    Charon\\ThrottleMiddleware:\n        arguments:\n            $limit: 100\n            $windowPeriod: 3600\n            $cache: '@cache.app'\n        calls:\n            - maybeBlacklist: [5]\n        tags:\n            - { name: 'kernel.event_listener', event: 'kernel.request', priority: 300 }\n\n// Or in a Controller/EventSubscriber\nuse Symfony\\Component\\EventDispatcher\\EventSubscriberInterface;\nuse Symfony\\Component\\HttpKernel\\Event\\RequestEvent;\nuse Symfony\\Component\\HttpKernel\\KernelEvents;\n\nclass RateLimitSubscriber implements EventSubscriberInterface\n{\n    private ThrottleMiddleware $throttle;\n\n    public function __construct(\n        private Psr16Cache $cache\n    ) {\n        $this-\u003ethrottle = (new ThrottleMiddleware(\n            limit: 100,\n            windowPeriod: 3600,\n            cache: $this-\u003ecache\n        ))-\u003emaybeBlacklist(5);\n    }\n\n    public static function getSubscribedEvents(): array\n    {\n        return [\n            KernelEvents::REQUEST =\u003e ['onKernelRequest', 300]\n        ];\n    }\n\n    public function onKernelRequest(RequestEvent $event): void\n    {\n        if (!$event-\u003eisMainRequest()) {\n            return;\n        }\n\n        $request = $event-\u003egetRequest();\n        $handler = new class implements RequestHandlerInterface {\n            public function handle(ServerRequestInterface $request): ResponseInterface\n            {\n                return new Response();\n            }\n        };\n\n        $response = $this-\u003ethrottle-\u003eprocess($request, $handler);\n        if ($response-\u003egetStatusCode() !== 200) {\n            $event-\u003esetResponse($response);\n        }\n    }\n}\n```\n\n#### WordPress REST API\n\n```php\nuse Charon\\ThrottleMiddleware;\nuse Symfony\\Component\\Cache\\Adapter\\FilesystemAdapter;\nuse Symfony\\Component\\Cache\\Psr16Cache;\nuse Nyholm\\Psr7\\Factory\\Psr17Factory;\nuse Nyholm\\Psr7\\ServerRequest;\n\n// In your plugin file or functions.php\nadd_action('rest_api_init', function () {\n    $cache = new Psr16Cache(new FilesystemAdapter());\n    $throttle = (new ThrottleMiddleware(\n        limit: 100,\n        windowPeriod: 3600,\n        cache: $cache\n    ))-\u003emaybeBlacklist(5);\n\n    // Apply to all REST API endpoints\n    add_filter('rest_pre_dispatch', function ($result, $server, $request) use ($throttle) {\n        if (null !== $result) {\n            return $result;\n        }\n\n        // Convert WordPress request to PSR-7\n        $psr17Factory = new Psr17Factory();\n        $psrRequest = new ServerRequest(\n            $request-\u003eget_method(),\n            $request-\u003eget_route(),\n            getallheaders(),\n            null,\n            '1.1',\n            array_merge($_SERVER, ['REMOTE_ADDR' =\u003e $_SERVER['REMOTE_ADDR']])\n        );\n\n        // Handle rate limiting\n        $handler = new class implements RequestHandlerInterface {\n            public function handle(ServerRequestInterface $request): ResponseInterface\n            {\n                return (new Psr17Factory())-\u003ecreateResponse(200);\n            }\n        };\n\n        $response = $throttle-\u003eprocess($psrRequest, $handler);\n        \n        // Check if request should be blocked\n        if ($response-\u003egetStatusCode() !== 200) {\n            return new WP_Error(\n                'rest_throttled',\n                $response-\u003egetReasonPhrase(),\n                ['status' =\u003e $response-\u003egetStatusCode()]\n            );\n        }\n\n        // Add rate limit headers to WordPress response\n        add_filter('rest_post_dispatch', function ($response) use ($throttle) {\n            if ($response instanceof WP_REST_Response) {\n                foreach ($response-\u003eget_headers() as $key =\u003e $value) {\n                    if (strpos($key, 'X-RateLimit') === 0) {\n                        $response-\u003eheader($key, $value);\n                    }\n                }\n            }\n            return $response;\n        });\n\n        return $result;\n    }, 10, 3);\n});\n```\n\n### Response Headers\n\nThe middleware adds standard rate limit headers to responses:\n\n```http\nX-RateLimit-Limit: 100\nX-RateLimit-Remaining: 99\nX-RateLimit-Reset: 1635789600\n```\n\nWhen the rate limit is exceeded, a 429 (Too Many Requests) response is returned with:\n\n```http\nStatus: 429 Too Many Requests\nRetry-After: 3600\nX-RateLimit-Limit: 100\nX-RateLimit-Remaining: 0\nX-RateLimit-Reset: 1635789600\n```\n\nWhen a blacklisted client attempts to access the resource:\n\n```http\nStatus: 403 Forbidden\nContent-Type: application/json\n\n{\n    \"error\": \"Access denied due to repeated rate limit violations\"\n}\n```\n\n## Logging\n\nWhen logging is enabled, the middleware logs the following information:\n\n### Rate Limit Exceeded (Warning Level)\n```json\n{\n    \"message\": \"Rate limit exceeded\",\n    \"context\": {\n        \"client\": {\n            \"ip\": \"192.168.1.1\",\n            \"user_agent\": \"Mozilla/5.0...\",\n            \"method\": \"GET\",\n            \"path\": \"/api/resource\"\n        },\n        \"requests\": 101,\n        \"limit\": 100,\n        \"reset_time\": 1635789600\n    }\n}\n```\n\n### Client Blacklisted (Alert Level)\n```json\n{\n    \"message\": \"Client blacklisted due to recurring rate limit violations\",\n    \"context\": {\n        \"client\": {\n            \"ip\": \"192.168.1.1\",\n            \"user_agent\": \"Mozilla/5.0...\",\n            \"method\": \"GET\",\n            \"path\": \"/api/resource\"\n        },\n        \"violations\": 5,\n        \"threshold\": 5\n    }\n}\n```\n\n### Request Processed (Info Level, when logAllRequests is true)\n```json\n{\n    \"message\": \"Request processed\",\n    \"context\": {\n        \"client\": {\n            \"ip\": \"192.168.1.1\",\n            \"user_agent\": \"Mozilla/5.0...\",\n            \"method\": \"GET\",\n            \"path\": \"/api/resource\"\n        },\n        \"requests\": 50,\n        \"limit\": 100,\n        \"remaining\": 50\n    }\n}\n```\n\n## Use Cases\n\n- **API Rate Limiting**: Protect your API from abuse by limiting requests per client\n- **Login Throttling**: Prevent brute force attacks by limiting login attempts\n- **Resource Protection**: Protect expensive operations from overuse\n- **DDoS Mitigation**: Basic protection against distributed denial of service attacks\n- **Fair Usage**: Ensure fair resource distribution among clients\n- **Abuse Prevention**: Automatically block repeat offenders with blacklisting\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nThe GNU General Public License v3.0. Please see [License File](LICENSE) for more information.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcmatosbc%2Fcharon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcmatosbc%2Fcharon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcmatosbc%2Fcharon/lists"}