Remove external library dependencies

This commit is contained in:
Neale Pickett 2019-02-26 16:27:20 -07:00
parent 0ff3679b2c
commit 041b582935
3 changed files with 1502 additions and 135 deletions

View File

@ -3,7 +3,8 @@
import asyncio import asyncio
import cgitb import cgitb
import html import html
from aiohttp import web import cgi
import http.server
import io import io
import json import json
import mimetypes import mimetypes
@ -17,32 +18,42 @@ import socketserver
import sys import sys
import traceback import traceback
import mothballer import mothballer
import parse
from http import HTTPStatus
sys.dont_write_bytecode = True # Don't write .pyc files sys.dont_write_bytecode = True # Don't write .pyc files
def get_seed(request): class MothServer(http.server.ThreadingHTTPServer):
seedstr = request.match_info.get("seed") def __init__(self, server_address, RequestHandlerClass):
if seedstr == "random": super().__init__(server_address, RequestHandlerClass)
return random.getrandbits(32) self.args = {}
else:
return int(seedstr)
def get_puzzle(request, data=None): class MothRequestHandler(http.server.SimpleHTTPRequestHandler):
seed = get_seed(request) endpoints = []
if not data:
data = request.match_info def __init__(self, request, client_address, server):
category = data.get("cat") super().__init__(request, client_address, server, directory=server.args["theme_dir"])
points = int(data.get("points"))
filename = data.get("filename")
cat = moth.Category(request.app["puzzles_dir"].joinpath(category), seed) def get_seed(self):
return seed
def get_puzzle(self):
category = self.req.get("cat")
points = int(self.req.get("points"))
cat = moth.Category(self.server.args["puzzles_dir"].joinpath(category), self.seed)
puzzle = cat.puzzle(points) puzzle = cat.puzzle(points)
return puzzle return puzzle
async def handle_answer(request):
data = await request.post() def handle_answer(self):
puzzle = get_puzzle(request, data) for f in ("cat", "points", "answer"):
self.req[f] = self.fields.getfirst(f)
puzzle = self.get_puzzle()
ret = { ret = {
"status": "success", "status": "success",
"data": { "data": {
@ -51,40 +62,37 @@ async def handle_answer(request):
}, },
} }
if data.get("answer") in puzzle.answers: if self.req.get("answer") in puzzle.answers:
ret["data"]["description"] = "Answer is correct" ret["data"]["description"] = "Answer is correct"
return web.Response( self.send_response(200)
content_type="application/json", self.send_header("Content-Type", "application/json")
body=json.dumps(ret), self.end_headers()
) self.wfile.write(json.dumps(ret).encode("utf-8"))
endpoints.append(('/{seed}/answer', handle_answer))
async def handle_puzzlelist(request): def handle_puzzlelist(self):
seed = get_seed(request)
puzzles = { puzzles = {
"__devel__": [[0, ""]], "__devel__": [[0, ""]],
} }
for p in request.app["puzzles_dir"].glob("*"): for p in self.server.args["puzzles_dir"].glob("*"):
if not p.is_dir() or p.match(".*"): if not p.is_dir() or p.match(".*"):
continue continue
catName = p.parts[-1] catName = p.parts[-1]
cat = moth.Category(p, seed) cat = moth.Category(p, self.seed)
puzzles[catName] = [[i, str(i)] for i in cat.pointvals()] puzzles[catName] = [[i, str(i)] for i in cat.pointvals()]
puzzles[catName].append([0, ""]) puzzles[catName].append([0, ""])
if len(puzzles) <= 1: if len(puzzles) <= 1:
logging.warning("No directories found matching {}/*".format(request.app["puzzles_dir"])) logging.warning("No directories found matching {}/*".format(self.server.args["puzzles_dir"]))
return web.Response( self.send_response(200)
content_type="application/json", self.send_header("Content-Type", "application/json")
body=json.dumps(puzzles), self.end_headers()
) self.wfile.write(json.dumps(puzzles).encode("utf-8"))
endpoints.append(('/{seed}/puzzles.json', handle_puzzlelist))
async def handle_puzzle(request): def handle_puzzle(self):
seed = get_seed(request) puzzle = self.get_puzzle()
category = request.match_info.get("cat")
points = int(request.match_info.get("points"))
cat = moth.Category(request.app["puzzles_dir"].joinpath(category), seed)
puzzle = cat.puzzle(points)
obj = puzzle.package() obj = puzzle.package()
obj["answers"] = puzzle.answers obj["answers"] = puzzle.answers
@ -92,38 +100,34 @@ async def handle_puzzle(request):
obj["summary"] = puzzle.summary obj["summary"] = puzzle.summary
obj["logs"] = puzzle.logs obj["logs"] = puzzle.logs
return web.Response( self.send_response(200)
content_type="application/json", self.send_header("Content-Type", "application/json")
body=json.dumps(obj), self.end_headers()
) self.wfile.write(json.dumps(obj).encode("utf-8"))
endpoints.append(('/{seed}/content/{cat}/{points}/puzzle.json', handle_puzzle))
async def handle_puzzlefile(request): def handle_puzzlefile(self):
seed = get_seed(request) puzzle = self.get_puzzle()
category = request.match_info.get("cat")
points = int(request.match_info.get("points"))
filename = request.match_info.get("filename")
cat = moth.Category(request.app["puzzles_dir"].joinpath(category), seed)
puzzle = cat.puzzle(points)
try: try:
file = puzzle.files[filename] file = puzzle.files[self.req["filename"]]
except KeyError: except KeyError:
return web.Response(status=404) return web.Response(status=404)
content_type, _ = mimetypes.guess_type(file.name) self.send_response(200)
return web.Response( self.send_header("Content-Type", mimetypes.guess_type(file.name))
body=file.stream.read(), # Is there no way to pipe this, must we slurp the whole thing into memory? self.end_headers()
content_type=content_type, shutil.copyfileobj(file.stream, self.wfile)
) endpoints.append(("/{seed}/content/{cat}/{points}/{filename}", handle_puzzlefile))
async def handle_mothballer(request):
seed = get_seed(request) def handle_mothballer(self):
category = request.match_info.get("cat") category = self.req.get("cat")
try: try:
catdir = request.app["puzzles_dir"].joinpath(category) catdir = self.server.args["puzzles_dir"].joinpath(category)
mb = mothballer.package(category, catdir, seed) mb = mothballer.package(category, catdir, self.seed)
except: except:
body = cgitb.html(sys.exc_info()) body = cgitb.html(sys.exc_info())
resp = web.Response(text=body, content_type="text/html") resp = web.Response(text=body, content_type="text/html")
@ -136,10 +140,11 @@ async def handle_mothballer(request):
content_type="application/octet_stream", content_type="application/octet_stream",
) )
return resp return resp
endpoints.append(("/{seed}/mothballer/{cat}", handle_mothballer))
async def handle_index(request): def handle_index(self):
seed = random.getrandbits(32) seed = self.get_seed()
body = """<!DOCTYPE html> body = """<!DOCTYPE html>
<html> <html>
<head> <head>
@ -177,19 +182,51 @@ sessionStorage.setItem("id", "devel-server")
</body> </body>
</html> </html>
""".format(seed=seed) """.format(seed=seed)
return web.Response(
content_type="text/html", self.send_response(200)
body=body, self.send_header("Content-Type", "text/html; charset=utf-8")
self.end_headers()
self.wfile.write(body.encode('utf-8'))
endpoints.append((r"/", handle_index))
def handle_theme_file(self):
self.path = "/" + self.req.get("path", "")
super().do_GET()
endpoints.append(("/{seed}/", handle_theme_file))
endpoints.append(("/{seed}/{path}", handle_theme_file))
def do_GET(self):
self.fields = cgi.FieldStorage(
fp=self.rfile,
headers=self.headers,
environ={
"REQUEST_METHOD": self.command,
"CONTENT_TYPE": self.headers["Content-Type"],
},
) )
for pattern, function in self.endpoints:
result = parse.parse(pattern, self.path)
if result:
self.req = result.named
seed = self.req.get("seed", "random")
if seed == "random":
self.seed = random.getrandbits(32)
else:
self.seed = int(seed)
return function(self)
super().do_GET()
async def handle_static(request): def do_POST(self):
themes = request.app["theme_dir"] self.do_GET()
fn = request.match_info.get("filename")
if not fn: def do_HEAD(self):
fn = "index.html" self.send_error(
path = themes.joinpath(fn) HTTPStatus.NOT_IMPLEMENTED,
return web.FileResponse(path) "Unsupported method (%r)" % self.command,
)
if __name__ == '__main__': if __name__ == '__main__':
@ -220,15 +257,10 @@ if __name__ == '__main__':
mydir = os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0]))) mydir = os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))
app = web.Application() server = MothServer((addr, port), MothRequestHandler)
app["base_url"] = args.base server.args["base_url"] = args.base
app["puzzles_dir"] = pathlib.Path(args.puzzles) server.args["puzzles_dir"] = pathlib.Path(args.puzzles)
app["theme_dir"] = pathlib.Path(args.theme) server.args["theme_dir"] = args.theme
app.router.add_route("GET", "/", handle_index)
app.router.add_route("*", "/{seed}/answer", handle_answer) logging.info("Listening on %s:%d", addr, port)
app.router.add_route("*", "/{seed}/puzzles.json", handle_puzzlelist) server.serve_forever()
app.router.add_route("GET", "/{seed}/content/{cat}/{points}/puzzle.json", handle_puzzle)
app.router.add_route("GET", "/{seed}/content/{cat}/{points}/{filename}", handle_puzzlefile)
app.router.add_route("GET", "/{seed}/mothballer/{cat}", handle_mothballer)
app.router.add_route("GET", "/{seed}/{filename:.*}", handle_static)
web.run_app(app, host=addr, port=port)

1335
devel/parse.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
package main package main
import ( import (
"github.com/namsral/flag" "flag"
"log" "log"
"math/rand" "math/rand"
"mime" "mime"