Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
549b407
Adds original websockets implementation
eduardovra Jun 8, 2021
89542cf
Adds websockets backends
eduardovra Jun 8, 2021
ff16bf6
Adds original test files for websockets
eduardovra Jun 9, 2021
e2ee064
WIP for the connect event
eduardovra Jun 9, 2021
213c39b
WIP websockets
eduardovra Jun 10, 2021
eadd3dc
Renamed file for websocket protocol
eduardovra Jun 10, 2021
38f914b
Adds websockets docs
eduardovra Jun 10, 2021
d1f7333
Fixed websockets tests
eduardovra Jun 10, 2021
8d27e66
WIP - websockets
eduardovra Jun 10, 2021
814d255
Fixed tests for sqlite and redis backends
eduardovra Jun 15, 2021
0e2c067
Fixed S3 backend tests
eduardovra Jun 15, 2021
3e3b781
S3 tests using mock server
eduardovra Jun 15, 2021
909167b
Basic tests for dynamodb passing
eduardovra Jun 15, 2021
f74f0bf
Fix redis backend
eduardovra Jun 15, 2021
d0a9d56
Fix postgresql tests
eduardovra Jun 15, 2021
3a7b52b
Fixed all tests
eduardovra Jun 15, 2021
505da43
Adds tests dependencies for websockets
eduardovra Jun 15, 2021
2003acc
Adds OS package dependencies
eduardovra Jun 16, 2021
88c3ce3
Fixed most of linter problems
eduardovra Jun 17, 2021
e37cdbe
Fixed remaining linter problems
eduardovra Jun 17, 2021
e8e741c
Implemented backend connect function using a context manager
eduardovra Jun 18, 2021
d61629b
Fixed dependencies for tests on 3.8
eduardovra Jun 18, 2021
a1bd662
Tests and linters passing on 3.6
eduardovra Jun 18, 2021
54f71b6
100% coverage
eduardovra Jun 18, 2021
242f73b
Remove aws.context from data storage
eduardovra Jun 22, 2021
713bd59
Use relative import paths
eduardovra Jun 22, 2021
9efc356
Undo changes in base type for Scope
eduardovra Jun 22, 2021
c2fde58
Standardize s3 and dynamodb query parameters
eduardovra Jun 22, 2021
7cf7c8b
Replace print statements with logging calls on mock server
eduardovra Jun 22, 2021
a4017a8
Use respx to mock httpx on tests
eduardovra Jun 22, 2021
64c788b
Add test for scope layout
eduardovra Jun 22, 2021
03adecf
Instancing the backend in the main adapter class
eduardovra Jun 22, 2021
8436958
Implement context manager for the backends
eduardovra Jun 22, 2021
0d555cf
Fix connection url on the backend
eduardovra Jun 23, 2021
261e44e
Fix broken dependency
eduardovra Jun 23, 2021
8cf22c3
Move integration tests into the tests/integrations folder and use doc…
eduardovra Jun 29, 2021
8902526
Updated the documentation on WebSocket support
eduardovra Jul 8, 2021
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
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ Mangum is an adapter for using [ASGI](https://asgi.readthedocs.io/en/latest/) ap

## Features

- API Gateway support for [HTTP](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html) and [REST](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-rest-api.html) APIs.
- API Gateway support for [HTTP](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html), [REST](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-rest-api.html), and [WebSocket](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api.html) APIs.

- Multiple storage backend interfaces for managing WebSocket connections.

- Compatibility with ASGI application frameworks, such as [Starlette](https://www.starlette.io/), [FastAPI](https://fastapi.tiangolo.com/), and [Quart](https://pgjones.gitlab.io/quart/).

Expand Down
7 changes: 6 additions & 1 deletion docs/adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ handler = Mangum(
lifespan="auto",
log_level="info",
api_gateway_base_path=None,
text_mime_types=None
text_mime_types=None,
dsn=None,
api_gateway_endpoint_url=None,
api_gateway_region_name=None
)
```

All arguments are optional, but some may be necessary for specific use-cases (e.g. dsn is only required for WebSocket support).

## Configuring an adapter instance

::: mangum.adapter.Mangum
Expand Down
4 changes: 3 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ Mangum is an adapter for using [ASGI](https://asgi.readthedocs.io/en/latest/) ap

## Features

- API Gateway support for [HTTP](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html) and [REST](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-rest-api.html) APIs.
- API Gateway support for [HTTP](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html), [REST](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-rest-api.html), and [WebSocket](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api.html) APIs.

- Multiple storage backend interfaces for managing WebSocket connections.

- Compatibility with ASGI application frameworks, such as [Starlette](https://www.starlette.io/), [FastAPI](https://fastapi.tiangolo.com/), and [Quart](https://pgjones.gitlab.io/quart/).

Expand Down
300 changes: 300 additions & 0 deletions docs/websockets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
# WebSockets

Mangum provides support for [WebSocket API](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api.html) events in API Gateway. The adapter class handles parsing the incoming requests and managing the ASGI cycle using a configured storage backend.

```python
Comment thread
jordaneremieff marked this conversation as resolved.
import os

from fastapi import FastAPI, WebSocket
from fastapi.responses import HTMLResponse
from mangum import Mangum

DSN_URL = os.environ["DSN_URL"]
WEBSOCKET_URL = os.environ["WEBSOCKET_URL"]
HTML = """
<!DOCTYPE html>
<html>
<head>
<title>Chat</title>
</head>

<body>
<h1>WebSocket Chat</h1>
<form action="" onsubmit="sendMessage(event)">
<input type="text" id="messageText" autocomplete="off"/>
<button>Send</button>
</form>

<ul id='messages'></ul>

<script>
var ws = new WebSocket('%s');
ws.onmessage = function(event) {
var messages = document.getElementById('messages')
var message = document.createElement('li')
var content = document.createTextNode(event.data)
message.appendChild(content)
messages.appendChild(message)
};
function sendMessage(event) {
var input = document.getElementById('messageText')
ws.send(input.value)
input.value = ''
event.preventDefault()
}
</script>
</body>
</html>
""" % WEBSOCKET_URL

app = FastAPI()

@app.get("/")
async def get():
return HTMLResponse(HTML)

@app.websocket("/")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Message text was: {data}")

handler = Mangum(app, dsn=DSN_URL)
```

## Dependencies

The WebSocket implementation requires the following extra packages:

```
pip install httpx boto3
```

## Configuring a storage backend

A data source is required in order to persist the WebSocket client connections stored in API Gateway*. Any data source can be used as long as it is accessible remotely to the AWS Lambda function. All supported backends require a `dsn` connection string argument to configure the connection between the adapter and the data source.

```python
handler = Mangum(app, dsn="[postgresql|redis|dynamodb|s3|sqlite]://[...]")
```

<small>*Read the section on ([handling events in API Gateway](https://mangum.io/websockets/#handling-api-gateway-events) for more information.)</small>

### Supported backends

The following backends are currently supported:

- `dynamodb`
- `s3`
- `postgresql`
- `redis`
- `sqlite` (for local debugging)

#### DynamoDB

The `DynamoDBBackend` uses a [DynamoDB](https://aws.amazon.com/dynamodb/) table to store the connection details.

##### Usage

```python
handler = Mangum(
app,
dsn="dynamodb://mytable"
)
```

###### Parameters

The DynamoDB backend `dsn` uses the following connection string syntax:

```
dynamodb://<table_name>[?region=<region-name>&endpoint_url=<url>]
```

- `table_name` (Required)

The name of the table in DynamoDB.

- `region_name`

The region name of the DynamoDB table.

- `endpoint_url`

The endpoint url to use in DynamoDB calls. This is useful if you are debugging locally with a package such as [serverless-dynamodb-local](https://github.com/99xt/serverless-dynamodb-local).

###### Dependencies

This backend requires the following extra package:

```
pip install aioboto3
```

#### S3

The `S3Backend` uses an [S3](https://aws.amazon.com/s3/) bucket as a key-value store to store the connection details.

##### Usage

```python
handler = Mangum(
app,
dsn="s3://my-bucket-12345"
)
```

###### Parameters

The S3 backend `dsn` uses the following connection string syntax:

```
s3://<bucket>[/key/...][?region=<region-name>&endpoint_url=<url>]
```

- `bucket` (Required)

The name of the bucket in S3.

- `region_name`

The region name of the S3 bucket.

- `endpoint_url`

The endpoint url to use in S3 calls. This is useful if you are debugging locally with a package such as [serverless-s3-local](https://github.com/ar90n/serverless-s3-local).

###### Dependencies

This backend requires the following extra package:

```
pip install aioboto3
```

#### PostgreSQL

The `PostgreSQLBackend` requires [psycopg2](https://github.com/psycopg/psycopg2) and access to a remote PostgreSQL database.

##### Usage

```python
handler = Mangum(
app,
dsn="postgresql://myuser:mysecret@my.host:5432/mydb"
)
```

###### Parameters

The PostgreSQL backend `dsn` uses the following connection string syntax:

```
postgresql://[user[:password]@][host][:port][,...][/dbname][?param1=value1&...]
```

- `host` (Required)

The network location of the PostgreSQL database

Read more about the supported uri schemes and additional parameters [here](https://www.postgresql.org/docs/10/libpq-connect.html#LIBPQ-CONNSTRING).

###### Dependencies

This backend requires the following extra package:

```
pip install aiopg
```

#### Redis

The `RedisBackend` requires [redis-py](https://github.com/andymccurdy/redis-py) and access to a Redis server.

##### Usage

```python
handler = Mangum(
app,
dsn="redis://:mysecret@my.host:6379/0"
)
```

##### Parameters

The Redis backend `dsn` uses the following connection string syntax:

```
redis://[[user:]password@]host[:port][/database]
```

- `host` (Required)

The network location of the Redis server.

Read more about the supported uri schemes and additional parameters [here](https://www.iana.org/assignments/uri-schemes/prov/redis).

###### Dependencies

This backend requires the following extra package:

```
pip install aioredis
```

#### SQLite

The `sqlite` backend uses a local [sqlite3](https://docs.python.org/3/library/sqlite3.html) database to store connection. It is intended for local debugging.

##### Usage

```python
handler = Mangum(
app,
dsn="sqlite://mydbfile.sqlite3"
)
```

##### Parameters

The SQLite backend uses the following connection string syntax:

```
sqlite://[file_path].db
```

- `file_path` (Required)

The file name or path to an sqlite3 database file. If one does not exist, then it will be created automatically.

## State machine

The `WebSocketCycle` is used by the adapter to communicate message events between the application and WebSocket client connections in API Gateway using a storage backend to persist the connection `scope`. It is a state machine that handles the ASGI request and response cycle for each individual message sent by a client.

### WebSocketCycle

::: mangum.protocols.websocket.WebSocketCycle
:docstring:
:members: run receive send

#### API Gateway events

There are three WebSocket events sent by API Gateway for a WebSocket API connection. Each event requires returning a response immediately, and the information required to create the connection scope is only available in the initial `CONNECT` event. Messages are only sent in `MESSAGE` events that occur after the initial connection is established, and they do not include the details of the initial connect event. Due to the stateless nature of AWS Lambda, a storage backend is required to persist the WebSocket connection details for the duration of a client connection.

##### CONNECT

A persistent connection between the client and a WebSocket API is being initiated. The adapter uses a supported WebSocket backend to store the connection id and initial request information.

##### MESSAGE

A connected client has sent a message. The adapter will retrieve the initial request information from the backend using the connection id to form the ASGI connection `scope` and run the ASGI application cycle.

##### DISCONNECT

The client or the server disconnects from the API. The adapter will remove the connection from the backend.

### WebSocketCycleState

::: mangum.protocols.websockets.WebSocketCycleState
:docstring:
Loading