Expanded and more compliant handling of responses.

This commit is contained in:
Solderpunk 2019-08-13 18:09:29 +03:00
parent 13ddad1759
commit 1f1ab73fcf
1 changed files with 33 additions and 23 deletions

56
av98.py
View File

@ -214,16 +214,12 @@ class GeminiClient(cmd.Cmd):
address, f = None, open(gi.path, "rb") address, f = None, open(gi.path, "rb")
else: else:
address, f = self._send_request(gi) address, f = self._send_request(gi)
# Attempt to decode something that is supposed to be text # Read response header
# (which involves reading the entire file over the network
# first)
header = f.readline() header = f.readline()
header = header.decode("UTF-8").strip() header = header.decode("UTF-8").strip()
self._debug("Response header: %s." % header) self._debug("Response header: %s." % header)
body = f.read()
# Catch network errors which may be recoverable if a redundant # Catch network errors which may happen on initial connection
# mirror is specified
except (socket.gaierror, ConnectionRefusedError, except (socket.gaierror, ConnectionRefusedError,
ConnectionResetError, TimeoutError, socket.timeout, ConnectionResetError, TimeoutError, socket.timeout,
) as network_error: ) as network_error:
@ -248,32 +244,29 @@ Slow internet connection? Use 'set timeout' to be more patient.""")
self._go_to_gi(new_gi) self._go_to_gi(new_gi)
return return
# Catch non-recoverable errors # Catch other errors
except Exception as err: except Exception as err:
print("ERROR: " + str(err)) print("ERROR: " + str(err))
return return
# Look at what we got # Validate header
status, mime = header.split(maxsplit=1) status, mime = header.split(maxsplit=1)
# Handle different statuses. if len(header) > 1024 or len(status) > 2 or not status.isnumeric():
# Everything other than success print("ERROR: Received invalid header from server!")
if status.startswith("2"): f.close()
if mime == "": return
mime = "text/gemini; charset=utf-8"
mime, mime_options = cgi.parse_header(mime) # Handle non-SUCCESS headers, which don't have a response body
if "charset" in mime_options: # Inputs
try: if status.startswith("1"):
codecs.lookup(mime_options["charset"]) print("User input not supported.")
except LookupError: # Redirects
print("Header declared unknown encoding %s" % value)
return
# Handle redirects
elif status.startswith("3"): elif status.startswith("3"):
self._debug("Following redirect to %s." % mime) self._debug("Following redirect to %s." % mime)
new_gi = GeminiItem(gi.host, gi.port, mime, None) new_gi = GeminiItem(gi.host, gi.port, mime, None)
self._go_to_gi(new_gi) self._go_to_gi(new_gi)
return return
# Error # Errors
elif status.startswith("4") or status.startswith("5"): elif status.startswith("4") or status.startswith("5"):
print("Error: %s" % mime) print("Error: %s" % mime)
return return
@ -281,8 +274,25 @@ Slow internet connection? Use 'set timeout' to be more patient.""")
elif status.startswith("6"): elif status.startswith("6"):
print("Client certificates not supported.") print("Client certificates not supported.")
return return
# Invalid status
elif not status.startswith("2"):
print("ERROR: Server returned undefined status code %s!" % status)
return
# If we're still here, this is a success and there's a response body # If we're here, this must be a success and there's a response body
assert status.startswith("2")
if mime == "":
mime = "text/gemini; charset=utf-8"
mime, mime_options = cgi.parse_header(mime)
if "charset" in mime_options:
try:
codecs.lookup(mime_options["charset"])
except LookupError:
print("Header declared unknown encoding %s" % value)
return
# Read the response body over the network
body = f.read()
# Save the result in a temporary file # Save the result in a temporary file
## Delete old file ## Delete old file