Updating changelog and fixing examples

This commit is contained in:
Michael Lazar 2020-05-26 00:25:48 -04:00
parent 8d7a3372c0
commit 9c5eb99266
3 changed files with 21 additions and 29 deletions

View File

@ -65,16 +65,18 @@ variable.
#### Other Changes #### Other Changes
- A client certificate can now have an empty ``commonName`` field. - A client certificate can now have an empty ``commonName`` field.
- For the ``JetforceApplication``, named capture groups in a route's regex - ``JetforceApplication.route()`` - named capture groups in regex patterns will
pattern will now be passed as keyword arguments to the wrapped function. See now be passed as keyword arguments to the wrapped function. See
examples/pagination.py for an example of how to use this feature. examples/pagination.py for an example of how to use this feature.
- A ``CompositeApplication`` class is now included to support virtual hosting - ``CompositeApplication`` - A class is now included to support composing
by composing multiple applications behind the same jetforce server. See composing multiple applications behind the same jetforce server. See
examples/vhost.py for an example of how to use this feature. examples/vhost.py for an example of how to use this feature.
- CGI variables - ``SCRIPT_NAME`` and ``PATH_INfO`` have been changed to match - CGI variables - ``SCRIPT_NAME`` and ``PATH_INFO`` have been changed to match
their intended usage as defined in RFC 3875. their intended usage as defined in RFC 3875.
- CGI variables - ``TLS_CIPHER`` and ``TLS_VERSION`` have been added and - CGI variables - ``TLS_CIPHER`` and ``TLS_VERSION`` have been added and
contain information about the established TLS connection. contain information about the established TLS connection.
- Applications can now optionally return ``Deferred`` objects instead of bytes,
in order to support full-blown asynchronous coroutines.
### v0.2.3 (2020-05-24) ### v0.2.3 (2020-05-24)

View File

@ -5,18 +5,13 @@ This is an example of how to return a 10 INPUT request to the client and
retrieve their response by parsing the URL query string. retrieve their response by parsing the URL query string.
This example stores the guestbook inside of a persistent sqlite database. This example stores the guestbook inside of a persistent sqlite database.
Because each request will run inside of a separate thread, we must create a new
connection object inside of the request handler instead of re-using a global
database connection. This thread-safety can be disabled in sqlite3 by using the
check_same_thread=False argument, but then it's up to you to ensure that only
connection request is writing to the database at any given time.
""" """
import sqlite3 import sqlite3
from datetime import datetime from datetime import datetime
from jetforce import GeminiServer, JetforceApplication, Response, Status from jetforce import GeminiServer, JetforceApplication, Response, Status
DB = "/tmp/guestbook.sqlite" db = sqlite3.connect("/tmp/guestbook.sqlite", detect_types=sqlite3.PARSE_DECLTYPES)
SCHEMA = """ SCHEMA = """
CREATE TABLE IF NOT EXISTS guestbook ( CREATE TABLE IF NOT EXISTS guestbook (
@ -25,8 +20,7 @@ CREATE TABLE IF NOT EXISTS guestbook (
message TEXT message TEXT
) )
""" """
with sqlite3.connect(DB) as c: db.execute(SCHEMA)
c.execute(SCHEMA)
app = JetforceApplication() app = JetforceApplication()
@ -36,16 +30,14 @@ app = JetforceApplication()
def index(request): def index(request):
lines = ["Guestbook", "=>/submit Sign the Guestbook"] lines = ["Guestbook", "=>/submit Sign the Guestbook"]
with sqlite3.connect(DB, detect_types=sqlite3.PARSE_DECLTYPES) as c: for row in db.execute("SELECT * FROM guestbook ORDER BY created_at"):
for row in c.execute("SELECT * FROM guestbook ORDER BY created_at"): ip_address, created_at, message = row
ip_address, created_at, message = row line = f"{created_at:%Y-%m-%d} - [{ip_address}] {message}"
line = f"{created_at:%Y-%m-%d} - [{ip_address}] {message}" lines.append("")
lines.append("") lines.append(line)
lines.append(line)
lines.extend(["", "...", ""]) lines.extend(["", "...", ""])
body = "\n".join(lines) body = "\n".join(lines)
return Response(Status.SUCCESS, "text/gemini", body) return Response(Status.SUCCESS, "text/gemini", body)
@ -55,9 +47,8 @@ def submit(request):
message = request.query[:256] message = request.query[:256]
created = datetime.now() created = datetime.now()
ip_address = request.environ["REMOTE_HOST"] ip_address = request.environ["REMOTE_HOST"]
with sqlite3.connect(DB) as c: values = (ip_address, created, message)
values = (ip_address, created, message) db.execute("INSERT INTO guestbook VALUES (?, ?, ?)", values)
c.execute("INSERT INTO guestbook VALUES (?, ?, ?)", values)
return Response(Status.REDIRECT_TEMPORARY, "") return Response(Status.REDIRECT_TEMPORARY, "")
else: else:
return Response(Status.INPUT, "Enter your message (max 256 characters)") return Response(Status.INPUT, "Enter your message (max 256 characters)")

View File

@ -106,13 +106,12 @@ class GeminiProtocol(LineOnlyReceiver):
# Yield control of the event loop # Yield control of the event loop
await deferLater(self.server.reactor, 0) await deferLater(self.server.reactor, 0)
while True: while True:
try: data = await maybeDeferred(response_generator.__next__)
data = await maybeDeferred(response_generator.__next__) if data is None:
self.write_body(data)
# Yield control of the event loop
await deferLater(self.server.reactor, 0)
except StopIteration:
break break
self.write_body(data)
# Yield control of the event loop
await deferLater(self.server.reactor, 0)
except Exception: except Exception:
self.server.log_message(traceback.format_exc()) self.server.log_message(traceback.format_exc())
self.write_status(Status.CGI_ERROR, "An unexpected error occurred") self.write_status(Status.CGI_ERROR, "An unexpected error occurred")