Skip to content

Commit 5daaab7

Browse files
committed
Add Cloudflare Workers examples
1 parent 1ec98d6 commit 5daaab7

File tree

16 files changed

+3964
-0
lines changed

16 files changed

+3964
-0
lines changed

examples/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ each example.
2020
* [`deno/`](./deno) - Usage with a Deno application as the origin.
2121
* [`http-stream/`](./deno/http-stream) - HTTP streaming using GRIP.
2222
* [`websocket/`](./deno/websocket) - WebSocket-over-HTTP using GRIP.
23+
24+
* [`cloudflare-workers/`](./cloudflare-workers) - Usage with a Cloudflare Workers application as the origin.
25+
* [`http-stream/`](./cloudflare-workers/http-stream) - HTTP streaming using GRIP.
26+
* [`websocket/`](./cloudflare-workers/websocket) - WebSocket-over-HTTP using GRIP.

examples/cloudflare-workers/README.md

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
# Examples for Cloudflare Workers
2+
3+
The examples in this directory illustrate the use of GRIP using
4+
a [Cloudflare Workers](https://workers.cloudflare.com) application as the backend.
5+
6+
* [`http-stream/`](./http-stream) - HTTP streaming using GRIP.
7+
* [`websocket/`](./websocket) - WebSocket-over-HTTP using GRIP.
8+
9+
For details on each example, view the `README` file in its
10+
respective directory.
11+
12+
## Running the examples locally
13+
14+
Each example can be run locally by running it alongside an instance of
15+
[Pushpin](https://pushpin.org/).
16+
17+
To run the examples locally, you'll need:
18+
19+
* Wrangler (Cloudflare Workers CLI) - [installation instructions](https://developers.cloudflare.com/workers/wrangler/install-and-update/)
20+
* Pushpin - [installation instructions](https://pushpin.org/docs/install/)
21+
22+
> NOTE: Instead of local Pushpin, you can also run the examples using Fastly Fanout for the GRIP proxy.
23+
See [Running the examples on Fastly Fanout](#running-the-examples-with-fastly-fanout-as-the-grip-proxy) below.
24+
25+
1. Set up Pushpin by modifying the `routes` file with the following content
26+
(See [this page](https://pushpin.org/docs/configuration/) for details on
27+
Pushpin configuration):
28+
29+
```
30+
* 127.0.0.1:3000
31+
```
32+
33+
2. Start Pushpin.
34+
35+
```
36+
pushpin
37+
```
38+
39+
By default, it will listen on port 7999, with a publishing
40+
endpoint open on port 5561. Leave Pushpin running in that terminal window.
41+
42+
3. In a new terminal window, switch to the example's directory.
43+
44+
4. Start the example:
45+
46+
```
47+
npm run start
48+
```
49+
50+
This will invoke Wrangler (Cloudflare Workers CLI) to start the local
51+
server to run the example application.
52+
53+
5. Go on to follow the steps under each example's `README` file.
54+
55+
## Description of common code between the examples
56+
57+
Each example has the same general structure:
58+
* Setting up the request handler
59+
* Configuring GRIP and instantiating the `Publisher`
60+
* Checking GRIP status
61+
* Handling (specific to the example)
62+
63+
### The request handler
64+
65+
Following the format of [Cloudflare Workers](https://developers.cloudflare.com/workers/)
66+
applications, these examples declare a default export of the `addEventHandler` function to declare the `fetch` event handler.
67+
68+
```typescript
69+
addEventListener('fetch', (event) => event.respondWith(handleRequest(event)));
70+
71+
async function handleRequest({request}) {
72+
const requestUrl = new URL(request.url);
73+
74+
// handler code ...
75+
};
76+
```
77+
78+
### Configuration of GRIP
79+
80+
Each example interfaces with GRIP using the `Publisher` class.
81+
82+
To configure `Publisher`, a GRIP configuration object `gripConfig` is used.
83+
The example applications give it a default value of `http://127.0.0.1:5561/` to point to
84+
local Pushpin.
85+
86+
```typescript
87+
let gripConfig: string | IGripConfig = 'http://127.0.0.1:5561/';
88+
```
89+
90+
It may be overridden using a `GRIP_URL`, which in the Cloudflare Workers backend application is set as
91+
an [Secret](https://developers.cloudflare.com/workers/configuration/secrets/). Secrets are read as
92+
[environment variables](https://developers.cloudflare.com/workers/configuration/environment-variables/).
93+
Additionally, in the example, the utility function `parseGripUri` is used to merge in the `GRIP_VERIFY_KEY`
94+
if it's required by the proxy.
95+
96+
```typescript
97+
let gripConfig: string | IGripConfig = 'http://127.0.0.1:5561/';
98+
const gripUrl = env.GRIP_URL;
99+
if (gripUrl) {
100+
gripConfig = parseGripUri(gripUrl, { 'verify-key': env.GRIP_VERIFY_KEY });
101+
}
102+
```
103+
104+
Alternatively, the values for `FANOUT_SERVICE_ID` and `FANOUT_API_TOKEN` are checked, and if present,
105+
they are used with the `buildFanoutGripConfig()` function to build the `gripConfig`.
106+
107+
```typescript
108+
const fanoutServiceId = env.FANOUT_SERVICE_ID;
109+
const fanoutApiToken = env.FANOUT_API_TOKEN;
110+
if (fanoutServiceId != null && fanoutApiToken != null) {
111+
gripConfig = buildFanoutGripConfig({
112+
serviceId: fanoutServiceId,
113+
apiToken: fanoutApiToken,
114+
});
115+
}
116+
```
117+
118+
These environment variables are typed earlier in the file:
119+
120+
```typescript
121+
export interface Env {
122+
GRIP_URL?: string,
123+
GRIP_VERIFY_KEY?: string,
124+
FANOUT_SERVICE_ID?: string,
125+
FANOUT_API_TOKEN?: string,
126+
}
127+
```
128+
129+
Finally, this `gripConfig` is used to instantiate `Publisher`.
130+
131+
```typescript
132+
const publisher = new Publisher(gripConfig);
133+
```
134+
135+
In the Cloudflare Workers example, this initialization happens inside the request handler,
136+
because environment variables can only be accessed during the handling of requests.
137+
138+
### GRIP status
139+
140+
The backend application is intended to be called via a GRIP proxy. When the handler runs,
141+
a GRIP proxy will have inserted a `Grip-Sig` header into the request, which it has
142+
signed with a secret or key.
143+
144+
The request handler calls `publisher.validateGripSig` to validate this header,
145+
storing the result in the `gripStatus` variable.
146+
```typescript
147+
const gripStatus = await publisher.validateGripSig(request.headers.get('grip-sig'));
148+
```
149+
150+
This result can be checked for three fields:
151+
`gripStatus.isProxied` - When `true`, indicates that the current request is behind
152+
a GRIP proxy. If `needsSigned` is `true`, then this will only be `true` if the
153+
signature validation has also succeeded.
154+
`gripStatus.needsSigned` - When `true`, indicates that the GRIP proxy specified in the
155+
configuration signs incoming requests.
156+
`gripStatus.isSigned` - When `true`, indicates that the signature validation was successful.
157+
158+
### Handling the request
159+
160+
Following this, the request handler in each example handles the request in its
161+
respective way. Refer to the README in each project for details.
162+
163+
A catch-all at the end of the handler handles unhandled requests with a 404 Not
164+
Found error.
165+
166+
Refer to the README in each project for details on how to work with the example.
167+
168+
## Running the examples with Fastly Fanout as the GRIP proxy
169+
170+
By publishing these examples publicly, they can also be run behind
171+
[Fastly Fanout](https://docs.fastly.com/products/fanout) to benefit from a global
172+
network and holding client connections at the edge.
173+
174+
Aside from your backend application running publicly on the internet,
175+
you will need a separate Fastly Compute service with Fanout enabled.
176+
This Fastly service runs a small program at the edge that examines
177+
each request and performs a "handoff" to Fanout for relevant requests,
178+
allowing Fanout to hold client connections and interpret GRIP messages.
179+
180+
The [Fastly Fanout Forwarding Starter Kit (JavaScript)](https://github.com/fastly/compute-starter-kit-javascript-fanout-forward#readme)
181+
can be used for this purpose. In many cases it can be used as is,
182+
or as a starting point for further customization.
183+
184+
One simple way to do this is to host the example backend in a free
185+
[Cloudflare](https://dash.cloudflare.com/sign-up/workers-and-pages) account,
186+
and then set up a Fastly service with a
187+
[free trial of Fanout](https://www.fastly.com/documentation/guides/concepts/real-time-messaging/fanout/#enable-fanout).
188+
189+
### Setting up Fastly and the Fanout Forwarding starter kit
190+
191+
The following steps describe the process of setting up the
192+
[Fastly Fanout Forwarding Starter Kit (JavaScript)](https://github.com/fastly/compute-starter-kit-javascript-fanout-forward#readme)
193+
on your Fastly account.
194+
195+
1. If you don't already have a Fastly account, sign up for [a free developer account](https://www.fastly.com/signup).
196+
197+
2. Create a new API token (personal access token) that has `global` scope for your
198+
account.
199+
200+
3. If you haven't already installed the Fastly CLI, [install it](https://www.fastly.com/documentation/reference/tools/cli/).
201+
202+
4. Set up the Fastly CLI with a [user profile](https://www.fastly.com/documentation/reference/tools/cli/#configuring),
203+
using your API token from above.
204+
205+
5. Create a new directory where you will set up Fastly Fanout Forwarding, and switch to the
206+
directory.
207+
208+
```
209+
mkdir fastly-fanout-forward
210+
cd fastly-fanout-forward
211+
```
212+
213+
6. Initialize the directory as a Fastly Compute application. Provide a name for the application, a description, and
214+
author info.
215+
216+
```
217+
fastly compute init --from=https://github.com/fastly/compute-starter-kit-javascript-fanout-forward
218+
```
219+
220+
7. Deploy the application to your Fastly account.
221+
222+
```
223+
fastly compute publish --status-check-off
224+
```
225+
226+
* You will be asked whether you want to create a new service. Reply `y`. Provide the following values:
227+
* **Service name**: CUse the default value, or provide a name that you like.
228+
* **Domain**: Use the default value, or choose a subdomain of **edgecompute.app** that you like.
229+
* **Backend**: For now, do not specify any backends.
230+
* Your service will be packaged and deployed to a new service.
231+
* Make a note of the new service's ID (You'll need it to configure the publisher in the next section).
232+
233+
8. You'll come back to Fastly to set up Fanout and origin host later.
234+
235+
### Setting up the example (backend) code
236+
237+
Follow the steps provided by [Cloudflare Workers's startup guide](https://developers.cloudflare.com/workers/get-started/guide/)
238+
to set up your code to be deployed to Cloudflare's platform.
239+
240+
You'll want to deploy and keep in mind the following:
241+
242+
* You need to set up the [Secrets](https://developers.cloudflare.com/workers/configuration/secrets/) needed by your
243+
Cloudflare Workers application to configure the `Publisher`.
244+
245+
You may either provide `FANOUT_SERVICE_ID` and `FANOUT_API_TOKEN`, or `GRIP_URL` and `GRIP_VERIFY_KEY`.
246+
247+
1. Using `FANOUT_SERVICE_ID` and `FANOUT_API_TOKEN`:
248+
* `FANOUT_SERVICE_ID` - Set this to your Fastly service ID.
249+
* `FANOUT_API_TOKEN` - Set this to your Fastly API token.
250+
2. Using `GRIP_URL`:
251+
* `GRIP_URL` - Set this to `'https://api.fastly.com/service/<SERVICE_ID>?key=<FASTLY_API_TOKEN>&verify-iss=fastly:<SERVICE_ID>'`.
252+
* Replace both instances of `<SERVICE_ID>` in the URL with your Fastly service ID.
253+
* Replace `<FASTLY_API_TOKEN>` in the URL with your Fastly API token.
254+
* Don't forget to put single quotes around the whole thing, so that Glitch can treat the colon and ampersand literally.
255+
* `GRIP_VERIFY_KEY` - Set this to the value `{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"CKo5A1ebyFcnmVV8SE5On-8G81JyBjSvcrx4VLetWCg\",\"y\":\"7gwJqaU6N8TP88--twjkwoB36f-pT3QsmI46nPhjO7M\"}`
256+
257+
* You'll need to note the Public domain name of your Cloudflare Workers application. Public domain names given by Cloudflare may
258+
look something like this: `<name>.<zone>.workers.dev`.
259+
260+
### Enable Fanout on your Fastly service, and point it at your backend
261+
262+
1. Switch back to the terminal window where you deployed your Fastly Fanout Forwarding service.
263+
264+
2. Type the following command to add the example application to your Fastly service as a backend with the name `origin`.
265+
Insert the public hostname of your example backend in the command below.
266+
267+
```
268+
fastly backend create --autoclone --version=active --name=origin --address=<example public hostname>
269+
```
270+
271+
3. Activate the newly created version.
272+
273+
```
274+
fastly service-version activate --version=latest
275+
```
276+
277+
4. Enable Fanout on your service.
278+
279+
```
280+
fastly products --enable=fanout
281+
```
282+
5. Wait a moment for the updates to deploy across Fastly's network.
283+
284+
6. Go on to follow the steps under each example's `README` file.
285+
286+
When you do this, access the application at your Fastly service's domain name (e.g., `https://<something>.edgecompute.app/`)
287+
instead of your local Pushpin address.
288+
289+
Back to [examples](../)

0 commit comments

Comments
 (0)