Add virtual hosting

This commit is contained in:
Michael Lazar 2020-03-11 00:06:22 -04:00
parent 2b9f98adf7
commit 478fb81094
3 changed files with 54 additions and 11 deletions

View File

@ -2,7 +2,10 @@
### Unreleased ### Unreleased
N/A - Allow virtual hosting by specifying an alternate hostname in the application
route pattern.
- Jetforce will no longer raise an exception when attempting to log dropped
connections or other malformed requests.
### v0.2.0 (2012-01-21) ### v0.2.0 (2012-01-21)

31
examples/virtualhost.py Normal file
View File

@ -0,0 +1,31 @@
"""
This is an example of using virtual hosting to serve URLs for multiple
subdomains from a single jetforce server.
"""
import asyncio
import jetforce
from jetforce import Response, Status
app = jetforce.JetforceApplication()
@app.route(hostname="apple.localhost")
def serve_apple_domain(request):
return Response(Status.SUCCESS, "text/plain", f"apple\n{request.path}")
@app.route(hostname="banana.localhost")
def serve_banana_domain(request):
return Response(Status.SUCCESS, "text/plain", f"banana\n{request.path}")
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, app=app
)
asyncio.run(server.run())

View File

@ -152,6 +152,7 @@ class RoutePattern:
path: str = "" path: str = ""
scheme: str = "gemini" scheme: str = "gemini"
hostname: typing.Optional[str] = None
strict_hostname: bool = True strict_hostname: bool = True
strict_port: bool = True strict_port: bool = True
@ -161,7 +162,10 @@ class RoutePattern:
""" """
Check if the given request URL matches this route pattern. Check if the given request URL matches this route pattern.
""" """
server_hostname = request.environ["HOSTNAME"] if self.hostname is None:
server_hostname = request.environ["HOSTNAME"]
else:
server_hostname = self.hostname
server_port = int(request.environ["SERVER_PORT"]) server_port = int(request.environ["SERVER_PORT"])
if self.strict_hostname and request.hostname != server_hostname: if self.strict_hostname and request.hostname != server_hostname:
@ -225,6 +229,7 @@ class JetforceApplication:
self, self,
path: str = "", path: str = "",
scheme: str = "gemini", scheme: str = "gemini",
hostname: typing.Optional[str] = None,
strict_hostname: bool = True, strict_hostname: bool = True,
strict_trailing_slash: bool = False, strict_trailing_slash: bool = False,
) -> typing.Callable: ) -> typing.Callable:
@ -238,7 +243,7 @@ class JetforceApplication:
return Response(Status.SUCCESS, 'text/plain', 'Hello world!') return Response(Status.SUCCESS, 'text/plain', 'Hello world!')
""" """
route_pattern = RoutePattern( route_pattern = RoutePattern(
path, scheme, strict_hostname, strict_trailing_slash path, scheme, hostname, strict_hostname, strict_trailing_slash
) )
def wrap(func: typing.Callable) -> typing.Callable: def wrap(func: typing.Callable) -> typing.Callable:
@ -591,14 +596,18 @@ class GeminiRequestHandler:
""" """
Log a gemini request using a format derived from the Common Log Format. Log a gemini request using a format derived from the Common Log Format.
""" """
self.server.log_message( try:
f"{self.remote_addr} " self.server.log_message(
f"[{time.strftime(self.TIMESTAMP_FORMAT, self.received_timestamp)}] " f"{self.remote_addr} "
f'"{self.url}" ' f"[{time.strftime(self.TIMESTAMP_FORMAT, self.received_timestamp)}] "
f"{self.status} " f'"{self.url}" '
f'"{self.meta}" ' f"{self.status} "
f"{self.response_size}" f'"{self.meta}" '
) f"{self.response_size}"
)
except AttributeError:
# Malformed request or dropped connection
pass
class GeminiServer: class GeminiServer: