Echo Server Example¶
An echo server is a simple server that echos any message it receives back to the client that sent it.
First install a copy of Chalice in a fresh environment, create a new project and cd into the directory:
$ pip install -U chalice
$ chalice new-project echo-server
$ cd echo-server
Our Chalice application will need boto3 as a dependency for both API Gateway
to send websocket messages. Let’s add a boto3 to the requirements.txt
file:
$ echo "boto3>=1.9.91" > requirements.txt
Now that the requirement has been added. Let’s install it locally since our next script will need it as well:
$ pip install -r requirements.txt
Next replace the contents of the app.py
file with the code below.
1from boto3.session import Session
2
3from chalice import Chalice
4from chalice import WebsocketDisconnectedError
5
6app = Chalice(app_name="echo-server")
7app.websocket_api.session = Session()
8app.experimental_feature_flags.update([
9 'WEBSOCKETS'
10])
11
12
13@app.on_ws_message()
14def message(event):
15 try:
16 app.websocket_api.send(
17 connection_id=event.connection_id,
18 message=event.body,
19 )
20 except WebsocketDisconnectedError as e:
21 pass # Disconnected so we can't send the message back.
Stepping through this app line by line, the first thing to note is that we
need to import and instantiate a boto3 session. This session is manually
assigned to app.websocket_api.session
.
This is needed because in order to send websocket responses to API Gateway we
need to construct a boto3 client. Chalice does not take a direct dependency
on boto3 or botocore, so we need to provide the Session ourselves.
from boto3.session import Session
app.websocket_api.session = Session()
Next we enable the experimental feature WEBSOCKETS
. Websockets are an
experimental feature and are subject to API changes. This includes all aspects
of the Websocket API exposted in Chalice. Including any public members of
app.websocket_api
, and the three decorators on_ws_connect
,
on_ws_message
, and on_ws_disconnect
.
app.experimental_feature_flags.update([
'WEBSOCKETS'
])
To register a websocket handler, and cause Chalice to deploy an
API Gateway Websocket API we use the app.on_ws_message()
decorator.
The event parameter here is a wrapper object with some convenience
parameters attached. The most useful are event.connection_id
and
event.body
. The connection_id
is an API Gateway specific identifier
that allows you to refer to the connection that sent the message. The body
is the content of the message.
@app.on_ws_message()
def message(event):
Since this is an echo server, the message handler simply reads the content it
received on the socket, and rewrites it back to the same socket. To send a
message to a socket we call app.websocket_api.send(connection_id, message)
.
In this case, we just use the same connection_id
we got the message from,
and use the body
we got from the event as the message
to send.
app.websocket_api.send(
connection_id=event.connection_id,
message=event.body,
)
Finally, we catch the exception WebsocketDisconnectedError
which is raised
by app.websocket_api.send
if the provided connection_id
is not
connected anymore. In our case this doesn’t really matter since we don’t have
anything tracking our connections. The error has a connection_id
property
that contains the offending connection id.
except WebsocketDisconnectedError as e:
pass # Disconnected so we can't send the message back.
Now that we understand the code, lets deploy it with chalice deploy
:
$ chalice deploy
Creating deployment package.
Creating IAM role: echo-server-dev
Creating lambda function: echo-server-dev-websocket_message
Creating websocket api: echo-server-dev-websocket-api
Resources deployed:
- Lambda ARN: arn:aws:lambda:region:0123456789:function:echo-server-dev-websocket_message
- Websocket API URL: wss://{websocket_api_id}.execute-api.region.amazonaws.com/api/
To test out the echo server we will use the websocket-client
package. You
install it from PyPI:
$ pip install websocket-client
After deploying the Chalice app the output will contain a URL for connecting
to the websocket API labeled: - Websocket API URL:
. The
websocket-client
package installs a command line tool called wsdump.py
which can be used to test websocket echo server:
$ wsdump wss://{websocket_api_id}.execute-api.region.amazonaws.com/api/
Press Ctrl+C to quit
> foo
< foo
> bar
< bar
> foo bar baz
< foo bar baz
>
Every message sent to the server (lines that start with >
) result in a
message sent to us (lines that start with <
) with the same content.
If something goes wrong, you can check the chalice error logs using the following command:
$ chalice logs -n websocket_message
Note
If you encounter an Internal Server Error here it is likely that you forgot
to include boto3>=1.9.91
in the requirements.txt
file.
To tear down the example. Just run:
$ chalice delete
Deleting Websocket API: {websocket_api_id}
Deleting function: arn:aws:lambda:us-west-2:0123456789:function:echo-server-dev-websocket_message
Deleting IAM role: echo-server-dev
Next Steps¶
In this tutorial, we created an echo server with websockets. If you’d like to try something more ambitious, you can follow our tutorial for creating a sample Chat application with websocket.