Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ publish = false
debug = 1

[dependencies]
fastly = "0.10.0"
fastly = "0.11.0"
39 changes: 35 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,50 @@

[![Deploy to Fastly](https://deploy.edgecompute.app/button)](https://deploy.edgecompute.app/deploy)

Learn about Fastly Compute with Fanout using a basic starter that sends connections through the Fanout GRIP proxy to a backend.
Learn about [Fastly Compute with Fanout](https://www.fastly.com/documentation/guides/concepts/real-time-messaging/fanout/) using a basic starter that sends connections through the Fanout GRIP proxy to a backend.

**For more details about this and other starter kits for Compute, see the [Fastly Documentation Hub](https://www.fastly.com/documentation/solutions/starters/)**.

## Setup

The app expects a configured backend named "origin" that points to an origin server. For example, if the server is available at domain `example.com`, then you'll need to create a backend on your Compute service named "origin" with the destination host set to `example.com` and port `443`. Also set `Override Host` to the same host value.

After deploying the app and setting up the backend configuration, all connections received by the service will be passed through the Fanout proxy to the origin. If WebSocket-over-HTTP mode is enabled on your service, then client WebSocket activity will be converted into HTTP when sending to the origin.
You'll also need to [enable Fanout](https://www.fastly.com/documentation/guides/concepts/real-time-messaging/fanout/#enable-fanout) on your Fastly service to run this application. To enable Fanout on your service, type:

## Note
```shell
fastly products --enable=fanout
```

This app is not currently supported in Fastly's [local development server](https://www.fastly.com/documentation/guides/compute/testing/#running-a-local-testing-server), as the development server does not support Fanout features. To experiment with Fanout, you will need to publish this project to your Fastly Compute service. using the `fastly compute publish` command.
> [!NOTE]
> This app is not currently supported in Fastly's [local development server](https://www.fastly.com/documentation/guides/compute/testing/#running-a-local-testing-server), as the development server does not support Fanout features. To experiment with Fanout, you will need to publish this project to your Fastly Compute service. using the `fastly compute publish` command.

## Running the application

After deploying the app and setting up the backend configuration, incoming HTTP and WebSocket requests that arrive at the service will be processed by the fetch handler:

1. WebSocket connections will be handed off to Fanout to reach the backend server. Fanout maintains a long-lived connection with the client, and uses the [WebSocket-over-HTTP protocol](https://pushpin.org/docs/protocols/websocket-over-http/) to transform the messages to and from the backend server.

2. HTTP GET and HEAD requests will be handed off to Fanout to reach the backend server. The backend can include [GRIP control messages](https://pushpin.org/docs/protocols/grip/) in its response, instructing Fanout to maintain a long-lived connection with the client.

## Next Steps

The starter kit is written to send all WebSocket and HTTP GET (and HEAD) traffic to Fanout. In an actual app we would be selective about which requests are handed off to Fanout, because requests that are handed off to Fanout do not pass through the Fastly cache.

For details, see [What to hand off to Fanout](https://www.fastly.com/documentation/guides/concepts/real-time-messaging/fanout/#what-to-hand-off-to-fanout) in the Developer Documentation.

The starter kit code contains a TODO section where you may insert additional conditions to check before setting the `use_fanout` variable to `true`.

For example, to check the request for the existence of a certain header:

```rust
if let Some(_) = req.get_header("fanout") {
use_fanout = true;
}
```

## Notes

The code in this starter kit cannot be used with the [`fastly::main` attribute](https://docs.rs/fastly/0.11.2/fastly/attr.main.html) on the `main()` entry point. This is because a function decorated with `fastly::main` is expected to return a response, but handing off to Fanout is an action that does not create a response. Use an undecorated `main()` function instead, and use `Request::from_client()` and `Response::send_to_client()` as needed.

## Security issues

Expand Down
7 changes: 5 additions & 2 deletions fastly.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# This file describes a Fastly Compute package. To learn more visit:
# https://www.fastly.com/documentation/reference/compute/fastly-toml

authors = ["<[email protected]>"]
description = "Enables Fanout on a service, forwarding to a backend."
language = "rust"
manifest_version = 2
name = "Fanout forwarding"
manifest_version = 3
name = "Fanout forwarding starter kit for Rust"

[scripts]
build = "cargo build --bin fastly-compute-project --release --target wasm32-wasi --color always"
37 changes: 36 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use fastly::{Error, Request};
use fastly::http::{HeaderValue, Method};

fn main() -> Result<(), Error> {
// Log service version.
Expand All @@ -9,5 +10,39 @@ fn main() -> Result<(), Error> {

let req = Request::from_client();

Ok(req.handoff_fanout("origin")?)
let mut use_fanout = false;

if req.get_method() == &Method::GET &&
req.get_header_all("upgrade")
.collect()
.contains(&&HeaderValue::from_static("websocket"))
{
// If a GET request contains "Upgrade: websocket" in its headers, then hand off to Fanout
// to handle as WebSocket-over-HTTP.
// For details on WebSocket-over-HTTP, see https://pushpin.org/docs/protocols/websocket-over-http/
use_fanout = true;
} else if req.get_method() == &Method::GET || req.get_method() == &Method::HEAD {
// If it's a GET or HEAD request, then hand off to Fanout.
// The backend response can include GRIP control messages to specify connection behavior.
// For details on GRIP, see https://pushpin.org/docs/protocols/grip/.

// NOTE: In an actual app we would be selective about which requests are handed off to Fanout,
// because requests that are handed off to Fanout do not pass through the Fastly cache.
// For example, we may examine the request path or the existence of certain headers.
// See https://www.fastly.com/documentation/guides/concepts/real-time-messaging/fanout/#what-to-hand-off-to-fanout

// TODO: add any additional conditions before setting use_fanout to true

use_fanout = true;
}

if use_fanout {
// Hand off the request through Fanout to the specified backend.
req.handoff_fanout("origin")?
} else {
// Send the request to the specified backend normally.
req.send("origin")?.send_to_client()
}

Ok(())
}
Loading