initial commit
This commit is contained in:
commit
b6ef339d89
|
@ -0,0 +1,21 @@
|
|||
# pygem
|
||||
Spaghetti Python gemini server
|
||||
|
||||
## FAQ
|
||||
Q: Does this work?
|
||||
A: Yes.
|
||||
|
||||
Q: How much does the spaghetti weigh?
|
||||
A: About 2kg
|
||||
|
||||
Q: What does this not support?
|
||||
A:
|
||||
- Proxying
|
||||
- CGI
|
||||
- Directory indexes
|
||||
- Multiple domains
|
||||
- Redirects
|
||||
- Input in any way
|
||||
|
||||
Q: This is too spaghetti
|
||||
A: Can't do much about this.
|
|
@ -0,0 +1,2 @@
|
|||
# Hello world
|
||||
It works!
|
|
@ -0,0 +1,12 @@
|
|||
[listen]
|
||||
host = 0.0.0.0
|
||||
port = 1965
|
||||
cert = cert.pem
|
||||
key = key.pem
|
||||
|
||||
[server]
|
||||
root = /absolute/path/to/geminiroot
|
||||
|
||||
[gemini]
|
||||
domain = localhost
|
||||
allowProxy = no
|
|
@ -0,0 +1,62 @@
|
|||
from server import TLS_ThreadingTCPServer
|
||||
import socketserver
|
||||
import configparser
|
||||
from urllib.parse import urlparse
|
||||
import os.path
|
||||
import mimetypes
|
||||
|
||||
mimetypes.init()
|
||||
mimetypes.add_type("text/gemini", ".gmi")
|
||||
|
||||
def is_safe_path(basedir, path, follow_symlinks=True):
|
||||
# resolves symbolic links
|
||||
if follow_symlinks:
|
||||
return os.path.realpath(path).startswith(basedir)
|
||||
|
||||
return os.path.abspath(path).startswith(basedir)
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read("pygem.ini")
|
||||
|
||||
class GeminiHandler(socketserver.StreamRequestHandler):
|
||||
def handle(self):
|
||||
self.url = urlparse(self.rfile.readline().strip())
|
||||
if self.url.netloc == b"" and self.url.scheme == b"":
|
||||
self.url = urlparse("gemini://%s" % self.rfile.readline().strip())
|
||||
# thorough check of the URL
|
||||
if self.url.scheme != b"gemini" and self.url.scheme != b"":
|
||||
self.wfile.write(("59 URL scheme is not Gemini %s\r\n" % str(self.url)).encode("utf-8"))
|
||||
return
|
||||
if self.url.netloc.decode("utf-8") != config["gemini"]["domain"] and config["gemini"]["allowProxy"] == "no":
|
||||
self.wfile.write("53 \r\n".encode("utf-8"))
|
||||
return
|
||||
elif self.url.netloc.decode("utf-8") != config["gemini"]["domain"]:
|
||||
self.wfile.write("50 Proxying is not implemented yet.\r\n".encode("utf-8"))
|
||||
return
|
||||
# Actual serving
|
||||
file_path = config["server"]["root"] + "/" + self.url.path.decode("utf-8")
|
||||
if not is_safe_path(config["server"]["root"], file_path):
|
||||
self.wfile.write("50 No.\r\n".encode("utf-8"))
|
||||
return
|
||||
if os.path.isdir(file_path):
|
||||
file_path = file_path + "/index.gmi"
|
||||
if not os.path.exists(file_path):
|
||||
self.wfile.write("40 File not found.\r\n".encode("utf-8"))
|
||||
return
|
||||
with open(file_path) as f:
|
||||
self.wfile.write(("20 %s\r\n" % mimetypes.guess_type(file_path)[0]).encode("utf-8"))
|
||||
self.wfile.write(f.read().encode("utf-8"))
|
||||
|
||||
server = TLS_ThreadingTCPServer((config['listen']['host'], int(config['listen']['port'])),
|
||||
GeminiHandler,
|
||||
config['listen']['cert'],
|
||||
config['listen']['key'])
|
||||
|
||||
try:
|
||||
print("Starting server...")
|
||||
server.serve_forever()
|
||||
finally:
|
||||
print("Server shutting down...")
|
||||
server.shutdown()
|
||||
server.server_close()
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import ssl
|
||||
import socketserver
|
||||
|
||||
class TLS_ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
"""TCPServer wrapped in TLS and ThreadingMixIn"""
|
||||
def __init__(self,
|
||||
server_address,
|
||||
RequestHandlerClass,
|
||||
certfile,
|
||||
keyfile,
|
||||
bind_and_activate=True):
|
||||
socketserver.TCPServer.__init__(self, server_address, RequestHandlerClass, bind_and_activate)
|
||||
self.certfile = certfile
|
||||
self.keyfile = keyfile
|
||||
def get_request(self):
|
||||
newsocket, fromaddr = self.socket.accept()
|
||||
connstream = ssl.wrap_socket(newsocket,
|
||||
server_side=True,
|
||||
certfile=self.certfile,
|
||||
keyfile=self.keyfile,
|
||||
ssl_version = ssl.PROTOCOL_TLS_SERVER)
|
||||
return connstream, fromaddr
|
Loading…
Reference in New Issue