summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/tools/third_party/websockets/docs/howto/heroku.rst
blob: a97d2e7ce039375eaf346cfd196a80f530dd8aa9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
Deploy to Heroku
================

This guide describes how to deploy a websockets server to Heroku_. The same
principles should apply to other Platform as a Service providers.

.. _Heroku: https://www.heroku.com/

.. admonition:: Heroku no longer offers a free tier.
    :class: attention

    When this tutorial was written, in September 2021, Heroku offered a free
    tier where a websockets app could run at no cost. In November 2022, Heroku
    removed the free tier, making it impossible to maintain this document. As a
    consequence, it isn't updated anymore and may be removed in the future.

We're going to deploy a very simple app. The process would be identical for a
more realistic app.

Create repository
-----------------

Deploying to Heroku requires a git repository. Let's initialize one:

.. code-block:: console

    $ mkdir websockets-echo
    $ cd websockets-echo
    $ git init -b main
    Initialized empty Git repository in websockets-echo/.git/
    $ git commit --allow-empty -m "Initial commit."
    [main (root-commit) 1e7947d] Initial commit.

Create application
------------------

Here's the implementation of the app, an echo server. Save it in a file called
``app.py``:

.. literalinclude:: ../../example/deployment/heroku/app.py
    :language: python

Heroku expects the server to `listen on a specific port`_, which is provided
in the ``$PORT`` environment variable. The app reads it and passes it to
:func:`~websockets.server.serve`.

.. _listen on a specific port: https://devcenter.heroku.com/articles/preparing-a-codebase-for-heroku-deployment#4-listen-on-the-correct-port

Heroku sends a ``SIGTERM`` signal to all processes when `shutting down a
dyno`_. When the app receives this signal, it closes connections and exits
cleanly.

.. _shutting down a dyno: https://devcenter.heroku.com/articles/dynos#shutdown

Create a ``requirements.txt`` file containing this line to declare a dependency
on websockets:

.. literalinclude:: ../../example/deployment/heroku/requirements.txt
    :language: text

Create a ``Procfile``.

.. literalinclude:: ../../example/deployment/heroku/Procfile

This tells Heroku how to run the app.

Confirm that you created the correct files and commit them to git:

.. code-block:: console

    $ ls
    Procfile         app.py           requirements.txt
    $ git add .
    $ git commit -m "Initial implementation."
    [main 8418c62] Initial implementation.
     3 files changed, 32 insertions(+)
     create mode 100644 Procfile
     create mode 100644 app.py
     create mode 100644 requirements.txt

The app is ready. Let's deploy it!

Deploy application
------------------

Follow the instructions_ to install the Heroku CLI, if you haven't done that
yet.

.. _instructions: https://devcenter.heroku.com/articles/getting-started-with-python#set-up

Sign up or log in to Heroku.

Create a Heroku app — you'll have to pick a different name because I'm already
using ``websockets-echo``:

.. code-block:: console

    $ heroku create websockets-echo
    Creating ⬢ websockets-echo... done
    https://websockets-echo.herokuapp.com/ | https://git.heroku.com/websockets-echo.git

.. code-block:: console

    $ git push heroku

    ... lots of output...

    remote: -----> Launching...
    remote:        Released v1
    remote:        https://websockets-echo.herokuapp.com/ deployed to Heroku
    remote:
    remote: Verifying deploy... done.
    To https://git.heroku.com/websockets-echo.git
     * [new branch]      main -> main

Validate deployment
-------------------

Let's confirm that your application is running as expected.

Since it's a WebSocket server, you need a WebSocket client, such as the
interactive client that comes with websockets.

If you're currently building a websockets server, perhaps you're already in a
virtualenv where websockets is installed. If not, you can install it in a new
virtualenv as follows:

.. code-block:: console

    $ python -m venv websockets-client
    $ . websockets-client/bin/activate
    $ pip install websockets

Connect the interactive client — you must replace ``websockets-echo`` with the
name of your Heroku app in this command:

.. code-block:: console

    $ python -m websockets wss://websockets-echo.herokuapp.com/
    Connected to wss://websockets-echo.herokuapp.com/.
    >

Great! Your app is running!

Once you're connected, you can send any message and the server will echo it,
or press Ctrl-D to terminate the connection:

.. code-block:: console

    > Hello!
    < Hello!
    Connection closed: 1000 (OK).

You can also confirm that your application shuts down gracefully.

Connect an interactive client again — remember to replace ``websockets-echo``
with your app:

.. code-block:: console

    $ python -m websockets wss://websockets-echo.herokuapp.com/
    Connected to wss://websockets-echo.herokuapp.com/.
    >

In another shell, restart the app — again, replace ``websockets-echo`` with your
app:

.. code-block:: console

    $ heroku dyno:restart -a websockets-echo
    Restarting dynos on ⬢ websockets-echo... done

Go back to the first shell. The connection is closed with code 1001 (going
away).

.. code-block:: console

    $ python -m websockets wss://websockets-echo.herokuapp.com/
    Connected to wss://websockets-echo.herokuapp.com/.
    Connection closed: 1001 (going away).

If graceful shutdown wasn't working, the server wouldn't perform a closing
handshake and the connection would be closed with code 1006 (abnormal closure).