@@ -17,12 +17,12 @@ requiring "push" capabilities.
17
17
Symfony provides a straightforward component, built on top of
18
18
`the Mercure protocol `_, specifically designed for this class of use cases.
19
19
20
- Mercure is an open protocol designed from the ground to publish updates from
20
+ Mercure is an open protocol designed from the ground up to publish updates from
21
21
server to clients. It is a modern and efficient alternative to timer-based
22
22
polling and to WebSocket.
23
23
24
24
Because it is built on top `Server-Sent Events (SSE) `_, Mercure is supported
25
- out of the box in most modern browsers (old versions of Edge and IE require
25
+ out of the box in modern browsers (old versions of Edge and IE require
26
26
`a polyfill `_) and has `high-level implementations `_ in many programming
27
27
languages.
28
28
@@ -42,49 +42,44 @@ generated using the API Platform client generator.
42
42
Installation
43
43
------------
44
44
45
- Running a Mercure Hub
46
- ~~~~~~~~~~~~~~~~~~~~~
45
+ Installing the Symfony Bundle
46
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
47
+
48
+ Run this command to install the Mercure support:
49
+
50
+ .. code-block :: terminal
51
+
52
+ $ composer require mercure
47
53
48
54
To manage persistent connections, Mercure relies on a Hub: a dedicated server
49
55
that handles persistent SSE connections with the clients.
50
56
The Symfony app publishes the updates to the hub, that will broadcast them to
51
57
clients.
52
58
53
- .. image :: /_images/mercure/schema.png
54
-
55
- An official and open source (AGPL) Hub based on the Caddy web server
56
- can be downloaded as a static binary from `Mercure.rocks `_.
57
- A Docker image, a Helm chart for Kubernetes
58
- and a managed, High Availability Hub are also provided.
59
-
60
- If you use `Symfony Docker `_ or the `API Platform distribution `_, a Mercure Hub
61
- is automatically installed and your Symfony application is automatically
62
- configured to use it. You can jump directly to the next section.
59
+ Thanks to :ref: `the Docker integration of Symfony </setup/docker >`,
60
+ :ref: `Flex <symfony-flex >` proposes to install a Mercure hub.
61
+ Run ``docker-compose up `` to start the hub if you have chosen this option.
63
62
64
63
If you use the :doc: `Symfony Local Web Server </setup/symfony_server >`,
65
- a Mercure hub will be automatically available as a Docker service thanks to its
66
- :ref: `Docker integration <symfony-server-docker>.
67
-
68
- Be sure that recent versions of Docker and Docker Compose are properly installed
69
- on your computer and to start the Symfony Local Web Server with the ``--no-tls``
70
- option:
64
+ you must start it with the ``--no-tls `` option.
71
65
72
66
.. code-block :: terminal
73
67
74
68
$ symfony server:start --no-tls -d
75
69
76
- Installing the Symfony Bundle
77
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
78
-
79
- Run this command to install the Mercure support before using it:
70
+ Running a Mercure Hub
71
+ ~~~~~~~~~~~~~~~~~~~~~
80
72
81
- .. code-block :: terminal
73
+ .. image :: /_images/mercure/schema.png
82
74
83
- $ composer require mercure
75
+ If you use the Docker integration, a hub is already up and running,
76
+ and you can go straight to the next section.
84
77
85
- :ref: `Symfony Flex <symfony-flex >` has automatically installed and configured
86
- MercureBundle. It also created (if needed) and configured a Docker Compose
87
- definition that provides a Mercure service. Run ``docker-compose up `` to start it.
78
+ Otherwise, and in production, you have to install a hub by yourself.
79
+ An official and open source (AGPL) Hub based on the Caddy web server
80
+ can be downloaded as a static binary from `Mercure.rocks `_.
81
+ A Docker image, a Helm chart for Kubernetes
82
+ and a managed, High Availability Hub are also provided.
88
83
89
84
Configuration
90
85
-------------
@@ -95,57 +90,37 @@ The preferred way to configure MercureBundle is using
95
90
When MercureBundle has been installed, the ``.env `` file of your project
96
91
has been updated by the Flex recipe to include the available env vars.
97
92
98
- If you use the Symfony Local Web Server, Symfony Docker or the API Platform
99
- distribution, the Symfony app is automatically configured and you can skip
100
- straight to the next section.
93
+ Also, if you are using the Docker integration with the Symfony Local Web Server,
94
+ `Symfony Docker `_ or the `API Platform distribution `_,
95
+ the proper environment variables have been automatically set.
96
+ Skip straight to the next section.
101
97
102
98
Otherwise, set the URL of your hub as the value of the ``MERCURE_URL ``
103
99
and ``MERCURE_PUBLIC_URL `` env vars.
104
100
Sometimes a different URL must be called by the Symfony app (usually to publish),
105
101
and the JavaScript client (usually to subscribe). It's especially common when
106
102
the Symfony app must use a local URL and the client-side JavaScript code a public one.
107
- In this case, ``MERCURE_URL `` must contain the local URL that will be used by the
103
+ In this case, ``MERCURE_URL `` must contain the local URL used by the
108
104
Symfony app (e.g. ``https://mercure/.well-known/mercure ``), and ``MERCURE_PUBLIC_URL ``
109
105
the publicly available URL (e.g. ``https://example.com/.well-known/mercure ``).
110
106
111
107
The clients must also bear a `JSON Web Token `_ (JWT)
112
108
to the Mercure Hub to be authorized to publish updates and, sometimes, to subscribe.
113
109
114
- This JWT should be stored in the ``MERCURE_JWT_SECRET `` environment variable.
110
+ This token must be signed with the same secret key as the one used by the Hub to verify the JWT (``!ChangeMe! `` in you use the Docker integration).
111
+ This secret key must be stored in the ``MERCURE_JWT_SECRET `` environment variable.
112
+ MercureBundle will use it to automatically generate and sign the needed JWTs.
115
113
116
- The JWT must be signed with the same secret key as the one used by
117
- the Hub to verify the JWT (``!ChangeMe! `` in you use the Local Web Server or
118
- Symfony Docker).
119
- Its payload must contain at least the following structure to be allowed to
120
- publish:
114
+ In addition to these environment variables,
115
+ MercureBundle provides a more advanced configuration configuration:
121
116
122
- .. code-block :: json
123
-
124
- {
125
- "mercure" : {
126
- "publish" : []
127
- }
128
- }
129
-
130
- Because the array is empty, the Symfony app will only be authorized to publish
131
- public updates (see the authorization _ section for further information).
132
-
133
- .. tip ::
134
-
135
- The jwt.io website is a convenient way to create and sign JWTs.
136
- Checkout this `example JWT `_, that grants publishing rights for all *topics *
137
- (notice the star in the array).
138
- Don't forget to set your secret key properly in the bottom of the right panel of the form!
139
-
140
- .. caution ::
141
-
142
- Don't put the secret key in ``MERCURE_JWT_SECRET ``, it will not work!
143
- This environment variable must contain a JWT, signed with the secret key.
144
-
145
- Also, be sure to keep both the secret key and the JWTs... secrets!
146
-
147
- If you don't want to use the provided environment variables,
148
- use the following configuration:
117
+ * ``secret ``: the key to use to sign the JWT (all other options, beside `algorithm `, `subscribe `, and `publish ` will be ignored)
118
+ * ``publish ``: a list of topics to allow publishing to when generating the JWT (only usable when `secret `, or `factory ` are provided)
119
+ * ``subscribe ``: a list of topics to allow subscribing to when generating the JWT (only usable when `secret `, or `factory ` are provided)
120
+ * ``algorithm ``: The algorithm to use to sign the JWT (only usable when `secret ` is provided)
121
+ * ``provider ``: The ID of a service to call to provide the JWT (all other options will be ignored)
122
+ * ``factory ``: The ID of a service to call to create the JWT (all other options, beside `subscribe `, and `publish ` will be ignored)
123
+ * ``value ``: the raw JWT to use (all other options will be ignored)
149
124
150
125
.. configuration-block ::
151
126
@@ -158,6 +133,12 @@ use the following configuration:
158
133
url : https://mercure-hub.example.com/.well-known/mercure
159
134
jwt :
160
135
secret : ' !ChangeMe!'
136
+ publish : ['foo', 'https://example.com/foo']
137
+ subscribe : ['bar', 'https://example.com/bar']
138
+ algorithm : ' hmac.sha256'
139
+ provider : ' My\Provider'
140
+ factory : ' My\Factory'
141
+ value : ' my.jwt'
161
142
162
143
.. code-block :: xml
163
144
@@ -168,7 +149,18 @@ use the following configuration:
168
149
name =" default"
169
150
url =" https://mercure-hub.example.com/.well-known/mercure"
170
151
>
171
- <jwt secret =" !ChangeMe!" />
152
+ <jwt
153
+ secret =" !ChangeMe!"
154
+ algorithm =" hmac.sha256"
155
+ provider =" My\Provider"
156
+ factory =" My\Factory"
157
+ value =" my.jwt"
158
+ >
159
+ <publish >foo</publish >
160
+ <publish >https://example.com/foo</publish >
161
+ <subscribe >bar</subscribe >
162
+ <subscribe >https://example.com/bar</subscribe >
163
+ </jwt >
172
164
</hub >
173
165
</config >
174
166
@@ -181,11 +173,37 @@ use the following configuration:
181
173
'url' => 'https://mercure-hub.example.com/.well-known/mercure',
182
174
'jwt' => [
183
175
'secret' => '!ChangeMe!',
176
+ 'publish' => ['foo', 'https://example.com/foo'],
177
+ 'subscribe' => ['bar', 'https://example.com/bar'],
178
+ 'algorithm' => 'hmac.sha256',
179
+ 'provider' => 'My\Provider',
180
+ 'factory' => 'My\Factory',
181
+ 'value' => 'my.jwt',
184
182
],
185
183
],
186
184
],
187
185
]);
188
186
187
+ .. tip ::
188
+
189
+ The JWT payload must contain at least the following structure to be allowed to
190
+ publish:
191
+
192
+ .. code-block :: json
193
+
194
+ {
195
+ "mercure" : {
196
+ "publish" : []
197
+ }
198
+ }
199
+
200
+ Because the array is empty, the Symfony app will only be authorized to publish
201
+ public updates (see the authorization _ section for further information).
202
+
203
+ The jwt.io website is a convenient way to create and sign JWTs.
204
+ Checkout this `example JWT `_, that grants publishing rights for all *topics *
205
+ (notice the star in the array).
206
+ Don't forget to set your secret key properly in the bottom of the right panel of the form!
189
207
190
208
Basic Usage
191
209
-----------
@@ -253,8 +271,8 @@ Subscribing to updates in JavaScript from a Twig template is straightforward:
253
271
}
254
272
</script>
255
273
256
- The ``mercure() `` Twig function will generate the URL of the Mercure hub
257
- according to the configuration. The URL will include the ``topic `` query
274
+ The ``mercure() `` Twig function generates the URL of the Mercure hub
275
+ according to the configuration. The URL includes the ``topic `` query
258
276
parameters corresponding to the topics passed as first argument.
259
277
260
278
If you want to access to this URL from an external JavaScript file, generate the
@@ -333,9 +351,9 @@ by using the ``AbstractController::addLink`` helper method::
333
351
334
352
class DiscoverController extends AbstractController
335
353
{
336
- public function __invoke (Request $request, Discovery $discovery): JsonResponse
354
+ public function discover (Request $request, Discovery $discovery): JsonResponse
337
355
{
338
- // Link: <http ://localhost:3000 /.well-known/mercure>; rel="mercure"
356
+ // Link: <https ://hub.example.com /.well-known/mercure>; rel="mercure"
339
357
$discovery->addLink($request);
340
358
341
359
return $this->json([
@@ -351,7 +369,7 @@ and to subscribe to it:
351
369
.. code-block :: javascript
352
370
353
371
// Fetch the original resource served by the Symfony web API
354
- fetch (' /books/1' ) // Has Link: <http ://localhost:3000 /.well-known/mercure>; rel="mercure"
372
+ fetch (' /books/1' ) // Has Link: <https ://hub.example.com /.well-known/mercure>; rel="mercure"
355
373
.then (response => {
356
374
// Extract the hub URL from the Link header
357
375
const hubUrl = response .headers .get (' Link' ).match (/ <([^ >] + )>;\s + rel=(?:mercure| "[^ "] * mercure[^ "] * ")/ )[1 ];
@@ -404,9 +422,9 @@ To provide this JWT, the subscriber can use a cookie,
404
422
or a ``Authorization `` HTTP header.
405
423
406
424
Cookies can be set automatically by Symfony by passing the appropriate options
407
- to the ``mercure() `` Twig function. Cookies set by Symfony will be automatically
425
+ to the ``mercure() `` Twig function. Cookies set by Symfony are automatically
408
426
passed by the browsers to the Mercure hub if the ``withCredentials `` attribute
409
- of the ``EventSource `` class is set to ``true ``. Then, the Hub will verify the
427
+ of the ``EventSource `` class is set to ``true ``. Then, the Hub verifies the
410
428
validity of the provided JWT, and extract the topic selectors from it.
411
429
412
430
.. code-block :: twig
@@ -482,6 +500,14 @@ And here is the controller::
482
500
}
483
501
}
484
502
503
+
504
+ .. tip ::
505
+
506
+ You cannot use the ``mercure() `` helper and the ``setCookie() ``
507
+ method at the same time (it would set the cookie twice on a single request). Choose
508
+ either one method or the other.
509
+
510
+
485
511
Programmatically Generating The JWT Used to Publish
486
512
---------------------------------------------------
487
513
@@ -595,9 +621,9 @@ its Mercure support.
595
621
Testing
596
622
--------
597
623
598
- During unit testing there is no need to send updates to Mercure.
624
+ During unit testing it's usually not needed to send updates to Mercure.
599
625
600
- You can instead make use of the `MockHub `::
626
+ You can instead make use of the `MockHub ` class ::
601
627
602
628
// tests/FunctionalTest.php
603
629
namespace App\Tests\Unit\Controller;
@@ -624,10 +650,10 @@ You can instead make use of the `MockHub`::
624
650
}
625
651
}
626
652
627
- During functional testing you can instead decorate the Hub::
653
+ For functional testing, you can instead create a stub of the Hub::
628
654
629
- // tests/Functional/Fixtures /HubStub.php
630
- namespace App\Tests\Functional\Fixtures ;
655
+ // tests/Functional/Stub /HubStub.php
656
+ namespace App\Tests\Functional\Stub ;
631
657
632
658
use Symfony\Component\Mercure\HubInterface;
633
659
use Symfony\Component\Mercure\Update;
@@ -642,14 +668,17 @@ During functional testing you can instead decorate the Hub::
642
668
// implement rest of HubInterface methods here
643
669
}
644
670
645
- HubStub decorates the default hub service so no updates are actually
646
- sent. Here is the HubStub implementation :
671
+ Use `` HubStub `` to replace the default hub service so no updates are actually
672
+ sent:
647
673
648
674
.. code-block :: yaml
649
675
650
676
# config/services_test.yaml
651
- App\Tests\Functional\Fixtures\HubStub :
652
- decorates : mercure.hub.default
677
+ mercure.hub.default :
678
+ class : App\Tests\Functional\Stub\HubStub
679
+
680
+ As MercureBundle support multiple hubs, you may have to replace
681
+ the other service definitions accordingly.
653
682
654
683
.. tip ::
655
684
0 commit comments