Source code for demo_app
#!/usr/bin/env python3
import os
# import sys
from flask import (
Flask,
Response,
request,
send_file,
)
import time
import datetime
# ------------------------
# NOTE:
#
# - It's not necessary to wrap yimmo in a try/except. This is just a hack
# to allow the documentation to be built whether the yimmo module is or
# isn't.
# - You don't need to import yimmo to use the WSGI server. This is here
# pretty much exclusively for the WebSockets demo.
#
try:
import yimmo
except:
yimmo = None
# Create a simple flask app.
# (See https://flask.palletsprojects.com/en/2.0.x/quickstart/)
app = Flask(__name__)
[docs]@app.route('/status')
def status_ok():
"""
Status endpoint. We'll just use this to serve "OK" and as a check that
the service is up and running.
"""
return 'OK'
# ---------------------------------------------------
# NOTE: The following endpoints are mostly used for
# testing purposes!
# ---------------------------------------------------
[docs]@app.route('/echo/headers')
def headers_handler():
"""
Send the received HTTP request headers back as a JSON dictionary.
"""
return dict(request.headers)
[docs]@app.route('/echo/body', methods=['POST'])
def body_echo():
"""
Echo back the HTTP POST body, verbatim.
"""
return Response(request.get_data(), headers={
'Content-Type': request.headers['Content-Type']
})
[docs]@app.route('/source')
def get_source():
"""
Serve the source code for this app from disk.
"""
file_path = os.path.realpath(__file__)
return send_file(file_path)
[docs]@app.route('/chunked')
def get_chunks():
"""
Return a series of timestamps with some delay_ms, as HTTP chunks.
"""
# Number of chunks:
no_chunks_default = 10
no_chunks = request.args.get('no_chunks', no_chunks_default)
try:
no_chunks = int(no_chunks)
except:
no_chunks = no_chunks_default
# Delay between chunks:
delay_ms_default = 0.0
delay_ms = request.args.get('delay_ms', delay_ms_default)
try:
delay_ms = (float(delay_ms) / 1000.0)
except:
delay_ms = delay_ms_default
# ----------------------
# Create the response:
def create_chunked_response():
t_last = time.time()
for i in range(1,no_chunks+1):
if delay_ms:
time.sleep(delay_ms)
time_n = time.time()
time_d = time_n - t_last
t_last = time_n
y_str = (
f'Chunk#: {i: 4d} ({time_d:3.3f}s)\n'
+ ('#' * ((i%80)+1)) + '\n'
)
yield y_str
return app.response_class(
create_chunked_response(),
mimetype='text/plain',
)
# ---------------------------------------------------
# HACK HACK: toss together some endpoints with
# payload data.
#
# NOTE: This is no-good for benchmarking!
# (All in mem / no disk IO, etc).
# ---------------------------------------------------
PAYLOAD_10K = 'A' * 1024 * 10
@app.route('/payload/10k')
def payload_10k():
return Response(PAYLOAD_10K, headers={
'Content-Type': 'text/plain'
})
# ---------------------------------------------------
# WebSockets endpoints/code:
# ---------------------------------------------------
[docs]@app.route('/websocket')
def get_ws_index():
"""
Serve back a very basic HTML page with some embedded
JavaScript that uses the WebSockets API.
"""
file_path = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
"index.html")
return send_file(file_path)
# -----------------------
# WebSockets Callbacks:
# -----------------------
[docs]def on_open(ws):
"""
Invoked when a new websocket connection is established.
:param ws: the yimmo.WebSocket connection
:return: on success, :py:obj:`None` or ``0``
:return: on failure, a non-zero integer to set ``errno``
:return: any other return value is interpretted as "failure"
If an exception is raised or anything other than :py:obj:`None` or
``0`` is returned, the connection is closed.
"""
return
[docs]def on_message(ws, msg, flags):
"""
Invoked when a message is received.
:param ws: the yimmo.WebSocket connection
:param flags: WebSocket message frame flags
:return: on success, :py:obj:`None` or ``0``
:return: on failure, a non-zero integer to set ``errno``
:return: any other return value is interpretted as "failure"
If an exception is raised or anything other than :py:obj:`None` or
``0`` is returned, the connection is closed.
"""
ws.send(msg, flags)
return
[docs]def on_close(ws):
"""
Invoked after a websocket disconnect.
:param ws: the yimmo.WebSocket connection
Return values are ignored. Any exceptions raised are swallowed and
ignored.
"""
return
# If WebSocket support is enabled, let's go ahead and
# set up some WebSocket handlers:
if hasattr(yimmo, 'WebSocket'):
yimmo.init_websockets(on_open, on_message, on_close)