A simple HTTP server for micropython. It uses asyncio to manage concurrency.
mpremote mip install github:haum/micropython-aiowebserver
import mip
mip.install('github:haum/micropython-aiowebserver')
import aiowebserver as web
If you do not use asyncio
in your project, just use the run_forever
helping
function:
import aiowebserver as web
# Add route handlers
# ...
web.run_forever()
With your own asyncio loop, just add an asyncio
task:
loop = asyncio.get_event_loop()
loop.create_task(web.start())
Defining routes can be done using decorators, that can be chained:
@web.route('GET', '/')
@web.route('GET', '/index.htm')
async def root_handler(rq):
await rq.w('Hello, world!')
This defines routes /
and /index.htm
using GET
requests to be served by
root_handler
. This function should be a coroutine (async
), receives the
request object as parameter. Here, it prints Hello, world!
.
To read the data part of the request, use rq.r(n)
with n
the number of bytes
to read.
To write an answer, use rq.w(str, drain)
where str
is the string to send and
drain
is an optional parameter set to False
to not wait for the message to
be sent, for example to buffer messages cut in several small pieces.
It is possible to send static files with
rq.sendfile(path, directory, mimetypes)
, with path
the only
mandatory parameter.
path
and directory
give respectively the path and the directory in which to
search for this path. Without directory
the current working directory is used.
mimetypes
is a dictionary, with a mimetype (value) associated to extensions
(key). The special extension *
serves as fallback mimetype. A default
dictionary is used in addition to this optional parameter. Using a string
instead of a directory forces the mimetype without reading the default
dictionary.
The function can send gzipped files. The path should be without the .gz
extension, which would automatically be added by the function if the file
without this extension does not exist. If the path is a directory, it will serve
index.htm
inside.
The function uses os.stat
to compute an ETAG to avoid sending files that are
already in the cache of the browser.
The function uses a lock to avoid sending multiple files in parallel that could saturate the low RAM of the target.
If not specified, the returned status of the page is 200 OK
.
To change it, you have to call rq.return_status(code, code_text)
before
calling header or write functions. code
is the status code, code_text
is the
associated text meaning that can be omoitted for a few common statuses.
Redirecting can be done with rq.redirect(url, permanent)
where url
is the
URL to redirect to and permanent
is an optional parameter set to True
to
use a 301 Moved Permanently
status redirection. This function use header
redirection and therefore should be called alone.
Custom headers can be added with rq.header(name, value)
. Those headers have to
be added before sending content and after the optional return_status
Special methods help to send Content-Type
header: rq.header_html()
,
rq.header_text()
, rq.header_json()
.
Some methods help to deal with forms, in particular rq.is_postform()
,
rq.decode_postform_data()
, rq.is_postjson()
, rq.decode_postjson_data()
and
rq.decode_query_data()
.
See forms_and_404.py
for usage example.
Note that code to connect to a network or start an access point is not is not present in the examples. You should have setup network before starting them.
See examples
folder for examples.
simple.py
Simple example with an index pagesimple_port.py
Same example, but defining the listening portsimple_ext_loop.py
Same example, but without usingrun_forever
helper function and with another counting taskforms_and_404.py
Example with different form handling (GET, POST, JSON) and a custom 404 error pagestatic.py
Example of static file servingeventsourse.py
Example of SSE/EventSource event communicationwebsocket.py
Example of websocket communication
Initially tested with micropython 1.24 on unix and ESP32-C3 ports.