freddie / mercure-x
A Mercure Hub PHP implementation.
Installs: 323
Dependents: 0
Suggesters: 0
Security: 0
Stars: 95
Watchers: 8
Forks: 14
Open Issues: 9
Type:project
Requires
- php: >=8.1
- ext-ctype: *
- ext-iconv: *
- bentools/querystring: ^1.1
- clue/framework-x: dev-main#cfe017426d8acc7a92b5893a501728e034a873cc
- clue/redis-react: ^2.5
- doctrine/annotations: ^1.0
- lcobucci/jwt: ^4.1
- nyholm/dsn: ^2.0
- phpdocumentor/reflection-docblock: ^5.3
- react/async: ^4.0.0
- react/promise-timer: ^1.10
- rize/uri-template: ^0.3.4
- symfony/console: ^5.4.0|^6.0.0
- symfony/dotenv: ^5.4.0|^6.0.0
- symfony/flex: ^1.17|^2
- symfony/framework-bundle: ^5.4.0|^6.0.0
- symfony/options-resolver: ^5.4.0|^6.0.0
- symfony/property-access: ^5.4.0|^6.0.0
- symfony/property-info: ^5.4.0|^6.0.0
- symfony/runtime: ^5.4.0|^6.0.0
- symfony/serializer: ^5.4.0|^6.0.0
- symfony/uid: ^5.4.0|^6.0.0
- symfony/yaml: ^5.4.0|^6.0.0
Requires (Dev)
- clue/reactphp-eventsource: ^1.0.0
- pestphp/pest: ^1.21
- phpstan/phpstan: ^1.2
- react/child-process: ^0.6.4
- squizlabs/php_codesniffer: ^3.6
- symfony/http-client: ^5.4.0|^6.0.0
- symfony/process: ^5.4.0|^6.0.0
Conflicts
This package is auto-updated.
Last update: 2024-12-05 14:55:29 UTC
README
Freddie
Freddie is a PHP implementation of the Mercure Hub Specification.
It is blazing fast, built on the shoulders of giants:
- PHP 8.1
- Framework X and ReactPHP
- Symfony 6
- Redis (optionally).
See what features are covered and what aren't (yet) here.
Installation
PHP 8.1+ is required to run the hub.
As a standalone Mercure hub
composer create-project freddie/mercure-x freddie && cd freddie bin/freddie
This will start a Freddie instance on 127.0.0.1:8080
, with anonymous subscriptions enabled.
You can publish updates to the hub by generating a valid JWT signed with the !ChangeMe!
key with HMAC SHA256
algorithm.
To change these values, see Security.
As a bundle of your existing Symfony application
composer req freddie/mercure-x
You can then start the hub by doing:
bin/console freddie:serve
You can override relevant env vars in your .env.local
and services in your config/services.yaml
as usual.
Then, you can inject Freddie\Hub\HubInterface
in your services so that you can call $hub->publish($update)
,
or listening to dispatched updates in a CLI context 👍
Keep in mind this only works when using the Redis transport.
⚠️ Freddie uses its own routing/authentication system (because of async / event loop).
The controllers it exposes cannot be imported in your routes.yaml
, and get out of your security.yaml
scope.
Usage
./bin/freddie
It will start a new Mercure hub on 127.0.0.1:8080
.
To change this address, use the X_LISTEN
environment variable:
X_LISTEN="0.0.0.0:8000" ./bin/freddie
Security
The default JWT key is !ChangeMe!
with a HS256
signature.
You can set different values by changing the environment variables (in .env.local
or at the OS level):
X_LISTEN
, JWT_SECRET_KEY
, JWT_ALGORITHM
, JWT_PUBLIC_KEY
and JWT_PASSPHRASE
(when using RS512 or ECDSA)
Please refer to the authorization section of the Mercure specification to authenticate as a publisher and/or a subscriber.
PHP Transport (default)
By default, the hub will run as a simple event-dispatcher, in a single PHP process.
It can fit common needs for a basic usage, but using this transport prevents scalability, as opening another process won't share the same event emitter.
It's still prefectly usable as soon as :
- You don't expect more than a few hundreds updates per second
- Your application is served from a single server.
Redis transport
On the other hand, you can launch the hub on multiple ports and/or multiple servers with a Redis transport (as soon as they share the same Redis instance), and optionally use a load-balancer to distribute the traffic.
The official open-source version of the hub doesn't allow scaling because of concurrency restrictions on the bolt transport.
To launch the hub with the Redis transport, change the TRANSPORT_DSN
environment variable:
TRANSPORT_DSN="redis://127.0.0.1:6379" ./bin/freddie
Optional parameters you can pass in the DSN's query string:
pingInterval
- regularly ping Redis connection, which will help detect outages (default2.0
)readTimeout
- max duration in seconds of a ping or publish request (default0.0
: considered disabled)
Alternatively, you can set this variable into .env.local
.
Advantages and limitations
This implementation does not provide SSL nor HTTP2 termination, so you'd better put a reverse proxy in front of it.
Example Nginx configuration
upstream freddie { # Example with a single node server 127.0.0.1:8080; # Example with several nodes (they must share the same Redis instance) # 2 instances on 10.1.2.3 server 10.1.2.3:8080; server 10.1.2.3:8081; # 2 instances on 10.1.2.4 server 10.1.2.4:8080; server 10.1.2.4:8081; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com; ssl_certificate /etc/ssl/certs/example.com/example.com.cert; ssl_certificate_key /etc/ssl/certs/example.com/example.com.key; ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; location /.well-known/mercure { proxy_pass http://freddie; proxy_read_timeout 24h; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Proto $scheme; } }
Example Caddy configuration
Single node
example.com reverse_proxy 127.0.0.1:8080
With multiple nodes
example.com reverse_proxy 10.1.2.3:8080 10.1.2.3:8081 10.1.2.4:8080 10.1.2.4:8081
Payload limitations
⚠ There's a known limit in Framework-X which prevents request bodies to weigh more than 64 KB. At the time of writing, this limit cannot be raised due to Framework-X encapsulating HTTP Server instantiation.
Publishing bigger updates to Freddie (through HTTP, at least) could result in 400 errors.
Feature coverage
Tests
This project is 100% covered with Pest tests.
composer tests:run
Contribute
If you want to improve this project, feel free to submit PRs:
- CI will yell if you don't follow PSR-12 coding standards
- In the case of a new feature, it must come along with tests
- PHPStan analysis must pass at level 8
You can run the following command before committing to ensure all CI requirements are successfully met:
composer ci:check
License
GNU General Public License v3.0.