Better error handling for closed connections
This commit is contained in:
parent
b49cf9d2b6
commit
c288c72ad2
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,5 +1,17 @@
|
||||||
# Jetforce Changelog
|
# Jetforce Changelog
|
||||||
|
|
||||||
|
### Unreleased
|
||||||
|
|
||||||
|
#### Static Fileserver
|
||||||
|
|
||||||
|
- Error stack traces are no longer shown when the client prematurely closes
|
||||||
|
the connection.
|
||||||
|
|
||||||
|
#### Internal Framework
|
||||||
|
|
||||||
|
- If a gemini response returns a twisted.Deferred object, the errback will
|
||||||
|
now be invoked when the TCP connection is closed.
|
||||||
|
|
||||||
### v0.4.0 (2020-06-09)
|
### v0.4.0 (2020-06-09)
|
||||||
|
|
||||||
#### Features
|
#### Features
|
||||||
|
|
|
@ -7,6 +7,8 @@ import urllib.parse
|
||||||
|
|
||||||
from twisted.internet.address import IPv4Address, IPv6Address
|
from twisted.internet.address import IPv4Address, IPv6Address
|
||||||
from twisted.internet.defer import Deferred, ensureDeferred
|
from twisted.internet.defer import Deferred, ensureDeferred
|
||||||
|
from twisted.internet.error import ConnectionClosed
|
||||||
|
from twisted.internet.protocol import connectionDone
|
||||||
from twisted.internet.task import deferLater
|
from twisted.internet.task import deferLater
|
||||||
from twisted.protocols.basic import LineOnlyReceiver
|
from twisted.protocols.basic import LineOnlyReceiver
|
||||||
|
|
||||||
|
@ -50,6 +52,7 @@ class GeminiProtocol(LineOnlyReceiver):
|
||||||
def __init__(self, server: GeminiServer, app: JetforceApplication):
|
def __init__(self, server: GeminiServer, app: JetforceApplication):
|
||||||
self.server = server
|
self.server = server
|
||||||
self.app = app
|
self.app = app
|
||||||
|
self._currently_deferred: typing.Optional[Deferred] = None
|
||||||
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
"""
|
"""
|
||||||
|
@ -60,6 +63,14 @@ class GeminiProtocol(LineOnlyReceiver):
|
||||||
self.response_buffer = ""
|
self.response_buffer = ""
|
||||||
self.client_addr = self.transport.getPeer()
|
self.client_addr = self.transport.getPeer()
|
||||||
|
|
||||||
|
def connectionLost(self, reason=connectionDone):
|
||||||
|
"""
|
||||||
|
This is invoked by twisted after the connection has been closed.
|
||||||
|
"""
|
||||||
|
if self._currently_deferred:
|
||||||
|
self._currently_deferred.errback(reason)
|
||||||
|
self._currently_deferred = None
|
||||||
|
|
||||||
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.
|
||||||
|
@ -102,20 +113,22 @@ class GeminiProtocol(LineOnlyReceiver):
|
||||||
environ = self.build_environ()
|
environ = self.build_environ()
|
||||||
response_generator = self.app(environ, self.write_status)
|
response_generator = self.app(environ, self.write_status)
|
||||||
if isinstance(response_generator, Deferred):
|
if isinstance(response_generator, Deferred):
|
||||||
response_generator = await response_generator
|
response_generator = await self.track_deferred(response_generator)
|
||||||
else:
|
else:
|
||||||
# Yield control of the event loop
|
# Yield control of the event loop
|
||||||
await deferLater(self.server.reactor, 0)
|
await deferLater(self.server.reactor, 0)
|
||||||
|
|
||||||
for data in response_generator:
|
for data in response_generator:
|
||||||
if isinstance(data, Deferred):
|
if isinstance(data, Deferred):
|
||||||
data = await data
|
data = await self.track_deferred(data)
|
||||||
self.write_body(data)
|
self.write_body(data)
|
||||||
else:
|
else:
|
||||||
self.write_body(data)
|
self.write_body(data)
|
||||||
# Yield control of the event loop
|
# Yield control of the event loop
|
||||||
await deferLater(self.server.reactor, 0)
|
await deferLater(self.server.reactor, 0)
|
||||||
|
|
||||||
|
except ConnectionClosed:
|
||||||
|
pass
|
||||||
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")
|
||||||
|
@ -124,6 +137,13 @@ class GeminiProtocol(LineOnlyReceiver):
|
||||||
self.log_request()
|
self.log_request()
|
||||||
self.transport.loseConnection()
|
self.transport.loseConnection()
|
||||||
|
|
||||||
|
async def track_deferred(self, deferred: Deferred):
|
||||||
|
self._currently_deferred = deferred
|
||||||
|
try:
|
||||||
|
return await deferred
|
||||||
|
finally:
|
||||||
|
self._currently_deferred = None
|
||||||
|
|
||||||
def build_environ(self) -> typing.Dict[str, typing.Any]:
|
def build_environ(self) -> typing.Dict[str, typing.Any]:
|
||||||
"""
|
"""
|
||||||
Construct a dictionary that will be passed to the application handler.
|
Construct a dictionary that will be passed to the application handler.
|
||||||
|
|
Loading…
Reference in New Issue