Updated status codes for v0.9.1 spec.

Added command line argument for server hostname.
This commit is contained in:
Michael Lazar 2019-08-12 10:04:37 -04:00
parent a283fc1d14
commit 224c98966e
1 changed files with 39 additions and 13 deletions

52
jetforce.py Normal file → Executable file
View File

@ -38,13 +38,31 @@ If the TLS cert/keyfile is not provided, a self-signed certificate will
automatically be generated and saved to your temporary file directory. automatically be generated and saved to your temporary file directory.
""" """
# Gemini response status codes STATUS_INPUT = 10
STATUS_INPUT_REQUIRED = 10
STATUS_SUCCESS = 20 STATUS_SUCCESS = 20
STATUS_REDIRECT = 30 STATUS_SUCCESS_END_OF_SESSION = 21
STATUS_NOT_FOUND = 40
STATUS_SERVER_ERROR = 50 STATUS_REDIRECT_TEMPORARY = 30
STATUS_CERTIFICATE_ERROR = 60 STATUS_REDIRECT_PERMANENT = 31
STATUS_TEMPORARY_FAILURE = 40
STATUS_SERVER_UNAVAILABLE = 41
STATUS_CGI_ERROR = 42
STATUS_PROXY_ERROR = 43
STATUS_SLOW_DOWN = 44
STATUS_PERMANENT_FAILURE = 50
STATUS_NOT_FOUND = 51
STATUS_GONE = 52
STATUS_PROXY_REQUEST_REFUSED = 53
STATUS_BAD_REQUEST = 59
STATUS_CLIENT_CERTIFICATE_REQUIRED = 60
STATUS_TRANSIENT_CERTIFICATE_REQUESTED = 61
STATUS_AUTHORISED_CERTIFICATE_REQUIRED = 62
STATUS_CERTIFICATE_NOT_ACCEPTED = 63
STATUS_FUTURE_CERTIFICATE_REJECTED = 64
STATUS_EXPIRED_CERTIFICATE_REJECTED = 65
class EchoApp: class EchoApp:
@ -199,7 +217,7 @@ class GeminiRequestHandler:
for data in app: for data in app:
await self.write_body(data) await self.write_body(data)
except Exception as e: except Exception as e:
self.write_status(STATUS_SERVER_ERROR, str(e)) self.write_status(STATUS_CGI_ERROR, str(e))
raise raise
finally: finally:
await self.flush_status() await self.flush_status()
@ -288,11 +306,17 @@ class GeminiServer:
request_handler_class = GeminiRequestHandler request_handler_class = GeminiRequestHandler
def __init__( def __init__(
self, host: str, port: int, ssl_context: ssl.SSLContext, app: typing.Callable self,
host: str,
port: int,
ssl_context: ssl.SSLContext,
hostname: str,
app: typing.Callable,
) -> None: ) -> None:
self.host = host self.host = host
self.port = port self.port = port
self.ssl_context = ssl_context self.ssl_context = ssl_context
self.hostname = hostname
self.app = app self.app = app
async def run(self) -> None: async def run(self) -> None:
@ -305,6 +329,7 @@ class GeminiServer:
) )
socket_info = server.sockets[0].getsockname() socket_info = server.sockets[0].getsockname()
self.log_message(f"Server hostname is {self.hostname}")
self.log_message(f"Listening on {socket_info[0]}:{socket_info[1]}") self.log_message(f"Listening on {socket_info[0]}:{socket_info[1]}")
async with server: async with server:
@ -332,8 +357,7 @@ class GeminiServer:
def generate_tls_certificate(hostname: str) -> typing.Tuple[str, str]: def generate_tls_certificate(hostname: str) -> typing.Tuple[str, str]:
""" """
Utility function to generate a self-signed SSL certificate key pair if Utility function to generate a self-signed SSL certificate key pair if
one isn't provided. This should only be used for development, know what one isn't provided. Results may vary depending on your version of OpenSSL.
you're doing if you plan to make your server public!
""" """
certfile = pathlib.Path(tempfile.gettempdir()) / f"{hostname}.crt" certfile = pathlib.Path(tempfile.gettempdir()) / f"{hostname}.crt"
keyfile = pathlib.Path(tempfile.gettempdir()) / f"{hostname}.key" keyfile = pathlib.Path(tempfile.gettempdir()) / f"{hostname}.key"
@ -360,16 +384,17 @@ def run_server() -> None:
epilog=EPILOG, epilog=EPILOG,
formatter_class=argparse.ArgumentDefaultsHelpFormatter, formatter_class=argparse.ArgumentDefaultsHelpFormatter,
) )
parser.add_argument("--host", help="server host", default="127.0.0.1") parser.add_argument("--host", help="Address to bind server to", default="127.0.0.1")
parser.add_argument("--port", help="server port", type=int, default=1965) parser.add_argument("--port", help="Port to bind server to", type=int, default=1965)
parser.add_argument("--tls-certfile", help="TLS certificate file", metavar="FILE") parser.add_argument("--tls-certfile", help="TLS certificate file", metavar="FILE")
parser.add_argument("--tls-keyfile", help="TLS private key file", metavar="FILE") parser.add_argument("--tls-keyfile", help="TLS private key file", metavar="FILE")
parser.add_argument("--hostname", help="Server hostname", default="localhost")
parser.add_argument("--dir", help="local directory to serve", default="/var/gemini") parser.add_argument("--dir", help="local directory to serve", default="/var/gemini")
args = parser.parse_args() args = parser.parse_args()
certfile, keyfile = args.tls_certfile, args.tls_keyfile certfile, keyfile = args.tls_certfile, args.tls_keyfile
if not certfile: if not certfile:
certfile, keyfile = generate_tls_certificate("localhost") certfile, keyfile = generate_tls_certificate(args.hostname)
ssl_context = ssl.SSLContext() ssl_context = ssl.SSLContext()
ssl_context.load_cert_chain(certfile, keyfile) ssl_context.load_cert_chain(certfile, keyfile)
@ -378,6 +403,7 @@ def run_server() -> None:
host=args.host, host=args.host,
port=args.port, port=args.port,
ssl_context=ssl_context, ssl_context=ssl_context,
hostname=args.hostname,
app=StaticDirectoryApp.serve_directory(args.dir), app=StaticDirectoryApp.serve_directory(args.dir),
) )
asyncio.run(server.run()) asyncio.run(server.run())