Skip to content

Commit

Permalink
Implementation of a "Append Token Router"
Browse files Browse the repository at this point in the history
Filter that appends the connection_id to each packet on the client side,
and uses it to compare against endpoint stored connection_ids to route
traffic to the appropriate game server on the server side.

Work on #8

Integration tests and Metrics are next.
  • Loading branch information
markmandel committed Sep 10, 2020
1 parent c23ceac commit 7fb012e
Show file tree
Hide file tree
Showing 7 changed files with 509 additions and 0 deletions.
78 changes: 78 additions & 0 deletions docs/extensions/filters/append_token_router.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Append Token Router

Append Token Router is a simple Client/Server filter pair to provide routing from a Game Client to a Game Server
through a given set of Proxies via a fixed length `connection_id` value that is sent along as part of the UDP packet.

To implement this, the Client proxy appends the `client.connection_id` data value to the end of each packet that is sent
from the Game Client to a Server Proxy.

On the Server proxy side, on receiving the packet, strips the packet off the fixed length `connection_id` off the end
of the packet. Then that `connection_id` value is compared to the `server.endpoints.connection_ids`. Any values that
is matches, the UDP packet is sent on to the that Endpoint's destination.

#### Filter name
```text
quilkin.extensions.filters.append_token_router.v1alpha1.AppendTokenRouter
```

### Configuration Examples: Client

```rust
# let yaml = "
local:
port: 7000
filters:
- name: quilkin.extensions.filters.append_token_router.v1alpha1.AppendTokenRouter
client:
addresses:
- 127.0.0.1:7001
connection_id: MXg3aWp5Ng== # (string value: nkuy70x)
# ";

# let config = quilkin::config::Config::from_reader(yaml.as_bytes()).unwrap();
# assert_eq!(config.validate().unwrap(), ());
# assert_eq!(config.filters.len(), 1);
# // TODO: make it possible to easily validate filter's config from here.
```

### Configuration Examples: Server

```rust
# let yaml = "
local:
port: 7001
filters:
- name: quilkin.extensions.filters.append_token_router.v1alpha1.AppendTokenRouter
config:
connection_id_bytes: 7
server:
endpoints: # array of potential endpoints to send on traffic to
- name: Game Server No. 1
address: 127.0.0.1:26000
connection_ids:
- MXg3aWp5Ng== # the connection byte array to route to, encoded as base64 (string value: 1x7ijy6)
- OGdqM3YyaQ== # (string value: 8gj3v2i)
- name: Game Server No. 2
address: 127.0.0.1:26001
connection_ids:
- bmt1eTcweA== # (string value: nkuy70x)
# ";
# let config = quilkin::config::Config::from_reader(yaml.as_bytes()).unwrap();
# assert_eq!(config.validate().unwrap(), ());
# assert_eq!(config.filters.len(), 1);
# // TODO: make it possible to easily validate filter's config from here.
```

### Configuration Options: Server

```yaml
properties:
connection_id_bytes:
type: integer
description: |
The number of bytes the `connection_id` takes up at the end of the packets.
```
### Metrics
Implemented soon!
12 changes: 12 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@ impl From<&str> for ConnectionId {
}
}

impl From<Vec<u8>> for ConnectionId {
fn from(contents: Vec<u8>) -> Self {
ConnectionId(contents)
}
}

impl AsRef<Vec<u8>> for ConnectionId {
fn as_ref(&self) -> &Vec<u8> {
&self.0
}
}

/// ConnectionConfig is the configuration for either a Client or Server proxy
#[derive(Debug, Deserialize, Serialize)]
pub enum ConnectionConfig {
Expand Down
28 changes: 28 additions & 0 deletions src/extensions/filter_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use prometheus::{Error as MetricsError, Registry};
use serde::export::Formatter;

use crate::config::{ConnectionConfig, EndPoint};
use std::ops::Deref;

/// Filter is a trait for routing and manipulating packets.
pub trait Filter: Send + Sync {
Expand Down Expand Up @@ -50,12 +51,36 @@ pub trait Filter: Send + Sync {
) -> Option<Vec<u8>>;
}

impl Filter for Box<dyn Filter> {
fn on_downstream_receive(
&self,
endpoints: &[EndPoint],
from: SocketAddr,
contents: Vec<u8>,
) -> Option<(Vec<EndPoint>, Vec<u8>)> {
self.deref()
.on_downstream_receive(endpoints, from, contents)
}

fn on_upstream_receive(
&self,
endpoint: &EndPoint,
from: SocketAddr,
to: SocketAddr,
contents: Vec<u8>,
) -> Option<Vec<u8>> {
self.deref()
.on_upstream_receive(endpoint, from, to, contents)
}
}

#[derive(Debug, PartialEq)]
/// Error is an error when attempting to create a Filter from_config() from a FilterFactory
pub enum Error {
NotFound(String),
FieldInvalid { field: String, reason: String },
DeserializeFailed(String),
FieldNotFound(String),
InitializeMetricsFailed(String),
}

Expand All @@ -67,6 +92,9 @@ impl fmt::Display for Error {
write!(f, "field {} is invalid: {}", field, reason)
}
Error::DeserializeFailed(reason) => write!(f, "Deserialization failed: {}", reason),
Error::FieldNotFound(field) => {
write!(f, "field {} is required, but wasn't found", field)
}
Error::InitializeMetricsFailed(reason) => {
write!(f, "failed to initialize metrics: {}", reason)
}
Expand Down
Loading

0 comments on commit 7fb012e

Please sign in to comment.