90 lines
2.6 KiB
Python
90 lines
2.6 KiB
Python
"""
|
|
An endpoint that streams numbers counting to 10.
|
|
|
|
This is an example of how a jetforce application can respond with a generator
|
|
function instead of plain text/bytes. The server will iterate over the
|
|
generator and write the data to the socket in-between each iteration. This can
|
|
be useful if you want to serve a large response, like a binary file, without
|
|
loading the entire response into memory at once.
|
|
"""
|
|
import time
|
|
|
|
from twisted.internet import reactor
|
|
from twisted.internet.task import deferLater
|
|
from twisted.internet.threads import deferToThread
|
|
|
|
from jetforce import GeminiServer, JetforceApplication, Response, Status
|
|
|
|
|
|
def blocking_counter():
|
|
"""
|
|
This is the simplest implementation of a blocking, synchronous generator.
|
|
|
|
The calls to time.sleep(1) will run in the main twisted event loop and
|
|
block all other requests from processing.
|
|
"""
|
|
for x in range(10):
|
|
time.sleep(1)
|
|
yield f"{x}\r\n"
|
|
|
|
|
|
def threaded_counter():
|
|
"""
|
|
This counter uses the twisted ThreadPool to invoke sleep() inside of a
|
|
separate thread.
|
|
|
|
This avoids blocking the twisted event loop during the sleep() call.
|
|
It adds an overhead of setting up a thread for each iteration. It also
|
|
requires that your code be thread-safe, because more than one thread may
|
|
be running simultaneously in order to process separate requests.
|
|
"""
|
|
|
|
def delayed_callback(x):
|
|
time.sleep(1)
|
|
return f"{x}\r\n"
|
|
|
|
for x in range(10):
|
|
yield deferToThread(delayed_callback, x)
|
|
|
|
|
|
def deferred_counter():
|
|
"""
|
|
This counter uses twisted's deferLater() to schedule calling the function
|
|
after a delay of one second.
|
|
|
|
This is equivalent to using asyncio.sleep(1). It tells the twisted event
|
|
loop to "go do something else, and come back to run this callback after at
|
|
least one second has elapsed". The advantage is that it's non-blocking and
|
|
you don't need to worry about thread-safety because your callback will
|
|
eventually run in the main event loop.
|
|
"""
|
|
|
|
def delayed_callback(x):
|
|
return f"{x}\r\n"
|
|
|
|
for x in range(10):
|
|
yield deferLater(reactor, 1, delayed_callback, x)
|
|
|
|
|
|
app = JetforceApplication()
|
|
|
|
|
|
@app.route("/blocking")
|
|
def blocking(request):
|
|
return Response(Status.SUCCESS, "text/plain", blocking_counter())
|
|
|
|
|
|
@app.route("/threaded")
|
|
def threaded(request):
|
|
return Response(Status.SUCCESS, "text/plain", threaded_counter())
|
|
|
|
|
|
@app.route("/deferred")
|
|
def deferred(request):
|
|
return Response(Status.SUCCESS, "text/plain", deferred_counter())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
server = GeminiServer(app)
|
|
server.run()
|