Update echo example
This commit is contained in:
parent
d4ceb4d085
commit
2d7856f27c
|
@ -0,0 +1,32 @@
|
|||
"""
|
||||
A bare-bones server that with echo back the request to the client.
|
||||
|
||||
This example demonstrates the simplest proof-of-concept of how you can write
|
||||
your own application from scratch instead of sub-classing from the provided
|
||||
JetforceApplication. The server/application interface is almost identical to
|
||||
WSGI defined in PEP-3333 [1].
|
||||
|
||||
Unless you're feeling adventurous, you probably want to stick to the
|
||||
JetforceApplication instead of going this low-level.
|
||||
|
||||
[1] https://www.python.org/dev/peps/pep-3333/#id20
|
||||
"""
|
||||
import jetforce
|
||||
|
||||
|
||||
def app(environ, send_status):
|
||||
"""
|
||||
Arguments:
|
||||
environ: A dictionary containing information about the request
|
||||
send_status: A callback function that takes two parameters: The
|
||||
response status (int) and the response meta text (str).
|
||||
|
||||
Returns: A generator containing the response body.
|
||||
"""
|
||||
send_status(10, "text/gemini")
|
||||
yield f"Received path: {environ['GEMINI_URL']}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
server = jetforce.GeminiServer(app)
|
||||
server.run()
|
|
@ -1,27 +0,0 @@
|
|||
"""
|
||||
A simple Gemini server that echos back the request to the client.
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
import jetforce
|
||||
|
||||
|
||||
def echo(environ, send_status):
|
||||
url = environ["GEMINI_URL"]
|
||||
send_status(jetforce.Status.SUCCESS, "text/gemini")
|
||||
yield f"Received path: {url}".encode()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = jetforce.command_line_parser().parse_args()
|
||||
ssl_context = jetforce.make_ssl_context(
|
||||
args.hostname, args.certfile, args.keyfile, args.cafile, args.capath
|
||||
)
|
||||
server = jetforce.GeminiServer(
|
||||
host=args.host,
|
||||
port=args.port,
|
||||
ssl_context=ssl_context,
|
||||
hostname=args.hostname,
|
||||
app=echo,
|
||||
)
|
||||
asyncio.run(server.run())
|
|
@ -59,16 +59,32 @@ class GeminiProtocol(LineOnlyReceiver):
|
|||
def lineReceived(self, line):
|
||||
"""
|
||||
This method is invoked by LineOnlyReceiver for every incoming line.
|
||||
|
||||
Because Gemini requests are only ever a single line long, this will
|
||||
only be called once and we can use it to handle the lifetime of the
|
||||
connection without managing any state.
|
||||
"""
|
||||
self.request = line
|
||||
return ensureDeferred(self._handle_request_noblock())
|
||||
|
||||
async def _handle_request_noblock(self):
|
||||
"""
|
||||
Handle the gemini request and write the raw response to the socket.
|
||||
|
||||
This method is implemented using an async coroutine, which has been
|
||||
supported by twisted since python 3.5 by wrapping the method in
|
||||
ensureDeferred(). Twisted + coroutines is a bitch to figure out, but
|
||||
once it clicks it really does turn out to be an elegant solution.
|
||||
|
||||
Any time that we call into the application code, we wrap the call with
|
||||
deferToThread() which will execute the code in a separate thread using
|
||||
twisted's thread pool. deferToThread() will return a future object
|
||||
that we can then `await` to get the result when the thread finishes.
|
||||
This is important because we don't want application code to block the
|
||||
twisted event loop from serving other requests at the same time.
|
||||
|
||||
In the future, I would like to add the capability for applications to
|
||||
implement proper coroutines that can call `await` on directly without
|
||||
needing to wrap them in threads. Conceptually, this shouldn't be too
|
||||
difficult, but it will require implementing an alternate version of
|
||||
the JetforceApplication that's async-compatible.
|
||||
"""
|
||||
try:
|
||||
self.parse_header()
|
||||
except Exception:
|
||||
|
|
|
@ -124,7 +124,8 @@ class GeminiCertificateOptions(CertificateOptions):
|
|||
keyfile: typing.Optional[str] = None,
|
||||
cafile: typing.Optional[str] = None,
|
||||
capath: typing.Optional[str] = None,
|
||||
):
|
||||
) -> None:
|
||||
|
||||
self.certfile = certfile
|
||||
self.keyfile = keyfile
|
||||
self.cafile = cafile
|
||||
|
@ -136,7 +137,7 @@ class GeminiCertificateOptions(CertificateOptions):
|
|||
fixBrokenPeers=True,
|
||||
)
|
||||
|
||||
def _makeContext(self):
|
||||
def _makeContext(self) -> OpenSSL.SSL.Context:
|
||||
"""
|
||||
Most of this code is copied directly from the parent class method.
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue