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):
|
def lineReceived(self, line):
|
||||||
"""
|
"""
|
||||||
This method is invoked by LineOnlyReceiver for every incoming 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
|
self.request = line
|
||||||
return ensureDeferred(self._handle_request_noblock())
|
return ensureDeferred(self._handle_request_noblock())
|
||||||
|
|
||||||
async def _handle_request_noblock(self):
|
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:
|
try:
|
||||||
self.parse_header()
|
self.parse_header()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
|
@ -124,7 +124,8 @@ class GeminiCertificateOptions(CertificateOptions):
|
||||||
keyfile: typing.Optional[str] = None,
|
keyfile: typing.Optional[str] = None,
|
||||||
cafile: typing.Optional[str] = None,
|
cafile: typing.Optional[str] = None,
|
||||||
capath: typing.Optional[str] = None,
|
capath: typing.Optional[str] = None,
|
||||||
):
|
) -> None:
|
||||||
|
|
||||||
self.certfile = certfile
|
self.certfile = certfile
|
||||||
self.keyfile = keyfile
|
self.keyfile = keyfile
|
||||||
self.cafile = cafile
|
self.cafile = cafile
|
||||||
|
@ -136,7 +137,7 @@ class GeminiCertificateOptions(CertificateOptions):
|
||||||
fixBrokenPeers=True,
|
fixBrokenPeers=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _makeContext(self):
|
def _makeContext(self) -> OpenSSL.SSL.Context:
|
||||||
"""
|
"""
|
||||||
Most of this code is copied directly from the parent class method.
|
Most of this code is copied directly from the parent class method.
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue