Blame server.py

Ivan Mahonin 3c1a85
#!/usr/bin/python3
Ivan Mahonin 3c1a85
Ivan Mahonin 4bc4d1
import os
Ivan Mahonin 4bc4d1
os.chdir(os.path.dirname(os.path.abspath(__file__)))
Ivan Mahonin 4bc4d1
import sys
Ivan Mahonin 4bc4d1
if len(sys.argv) == 2 and sys.argv[1] == "daemon":
Ivan Mahonin 4bc4d1
    sys.stdout = open('log/server.log', 'a')
Ivan Mahonin 4bc4d1
    sys.stderr = sys.stdout
Ivan Mahonin 4bc4d1
Ivan Mahonin 4bc4d1
Ivan Mahonin 3c1a85
import config
Ivan Mahonin 3c1a85
import game
Ivan Mahonin 3c1a85
import template
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
import json
Ivan Mahonin 3c1a85
import threading
Ivan Mahonin 3c1a85
import http.server
Ivan Mahonin 3c1a85
import urllib.parse
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
class Server(http.server.BaseHTTPRequestHandler):
Ivan Mahonin 3c1a85
    def write(self, *texts):
Ivan Mahonin 3c1a85
        for text in texts:
Ivan Mahonin 3c1a85
            self.wfile.write(bytes(str(text), "utf8"))
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
    def parsePath(self, path):
Ivan Mahonin 3c1a85
        if not path.startswith(config.prefix):
Ivan Mahonin 3c1a85
            return None
Ivan Mahonin 3c1a85
        path = path[len(config.prefix):]
Ivan Mahonin 3c1a85
        if len(path) and path[0] != '/':
Ivan Mahonin 3c1a85
            return None
Ivan Mahonin 3c1a85
        return list(filter(None, path.split("/")))
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
    def err(self, code = 404, message = ""):
Ivan Mahonin 3c1a85
        self.send_response(int(code))
Ivan Mahonin 3c1a85
        self.send_header("Content-type", "text/html")
Ivan Mahonin 3c1a85
        self.end_headers()
Ivan Mahonin ffff69
        tplErrorpage.write(self, {
Ivan Mahonin ffff69
            "host": config.externalHost,
Ivan Mahonin ffff69
            "prefix": config.prefix,
Ivan Mahonin ffff69
            "code": int(code),
Ivan Mahonin ffff69
            "message": str(message),
Ivan Mahonin ffff69
            "waiting": False });
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
    def writeJpeg(self, path):
Ivan Mahonin 3c1a85
        if not path.endswith(".jpg") or not os.path.isfile(path):
Ivan Mahonin 3c1a85
            return self.err()
Ivan Mahonin 3c1a85
        with open(path, "rb") as f:
Ivan Mahonin 3c1a85
            self.send_response(200)
Ivan Mahonin 3c1a85
            self.send_header("Content-type", "image/jpeg")
Ivan Mahonin 3c1a85
            self.send_header("Cache-Control", "max-age=3600")
Ivan Mahonin 3c1a85
            self.end_headers()
Ivan Mahonin 3c1a85
            self.wfile.write(f.read())
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
    def writeStatus(self, player):
Ivan Mahonin 3c1a85
        self.send_response(200)
Ivan Mahonin 3c1a85
        self.send_header("Content-type", "text/html")
Ivan Mahonin 3c1a85
        self.end_headers()
Ivan Mahonin 3c1a85
        self.write(json.dumps(player.status()))
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
    def do_POST(self):
Ivan Mahonin 3c1a85
        if self.command != 'POST':
Ivan Mahonin 3c1a85
            return self.err()
Ivan Mahonin 3c1a85
        path = self.parsePath(self.path)
Ivan Mahonin 3c1a85
        if path is None:
Ivan Mahonin 3c1a85
            return self.err()
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
        length = int(self.headers.get('content-length'))
Ivan Mahonin 3c1a85
        data = self.rfile.read(length)
Ivan Mahonin 3c1a85
        fields = urllib.parse.parse_qs(str(data,"UTF-8"))
Ivan Mahonin 33a545
        word = str(fields.get("word", [""])[0])
Ivan Mahonin 9ae076
        vocabulary = str(fields.get("vocabulary", [""])[0])
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
        with mutex:
Ivan Mahonin 3c1a85
            if not path: # create new game
Ivan Mahonin 9ae076
                if not vocabulary in game.words:
Ivan Mahonin 9ae076
                    return self.err()
Ivan Mahonin 9ae076
                g = game.Game(vocabulary, word)
Ivan Mahonin 3c1a85
                self.send_response(303)
Ivan Mahonin 3c1a85
                self.send_header("Location", str(config.prefix) + "/" + str(g.players[0].id))
Ivan Mahonin 3c1a85
                self.end_headers()
Ivan Mahonin 3c1a85
                return
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
            player = game.players.get(path[0], None)
Ivan Mahonin 3c1a85
            if not player:
Ivan Mahonin 3c1a85
                return self.err()
Ivan Mahonin 3c1a85
            del path[0]
Ivan Mahonin 3c1a85
Ivan Mahonin 33a545
            if not path:
Ivan Mahonin 33a545
                player.join(word)
Ivan Mahonin 33a545
            elif len(path) == 2 and path[0] == "select":
Ivan Mahonin 3c1a85
                i = -1
Ivan Mahonin 3c1a85
                try:
Ivan Mahonin 3c1a85
                    i = int(path[1])
Ivan Mahonin 3c1a85
                except ValueError:
Ivan Mahonin 3c1a85
                    return self.err()
Ivan Mahonin 3c1a85
                player.select(i)
Ivan Mahonin 3c1a85
            elif len(path) == 1 and path[0] == "ready":
Ivan Mahonin 3c1a85
                player.nextturn(word)
Ivan Mahonin 3c1a85
            else:
Ivan Mahonin 3c1a85
                return self.err()
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
            self.send_response(303)
Ivan Mahonin 3c1a85
            self.send_header("Location", str(config.prefix) + "/" + str(player.id))
Ivan Mahonin 3c1a85
            self.end_headers()
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
    def do_GET(self):
Ivan Mahonin 33a545
        #loadTemplates()
Ivan Mahonin 33a545
Ivan Mahonin 3c1a85
        if self.command != 'GET':
Ivan Mahonin 3c1a85
            return self.err()
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
        if self.path.startswith(config.cardsPrefix):
Ivan Mahonin 3c1a85
            p = str(self.path[len(config.cardsPrefix):])
Ivan Mahonin 3c1a85
            if p and p[0] == "/":
Ivan Mahonin 3c1a85
                if p.count("/") > 1 or p.count("..") > 0:
Ivan Mahonin 3c1a85
                    return self.err()
Ivan Mahonin 3c1a85
                return self.writeJpeg("cards" + p)
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
        path = self.parsePath(self.path)
Ivan Mahonin 3c1a85
        if path is None:
Ivan Mahonin 3c1a85
            return self.err()
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
        if not path:
Ivan Mahonin 3c1a85
            self.send_response(200)
Ivan Mahonin 3c1a85
            self.send_header("Content-type", "text/html")
Ivan Mahonin 3c1a85
            self.end_headers()
Ivan Mahonin 9ae076
            tplStartpage.write(self, {
Ivan Mahonin 9ae076
                "host": config.externalHost,
Ivan Mahonin 9ae076
                "prefix": config.prefix,
Ivan Mahonin 9ae076
                "vocabularies": list(sorted(game.words.keys())),
Ivan Mahonin 9ae076
                "waiting": False });
Ivan Mahonin 3c1a85
            return
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
        with mutex:
Ivan Mahonin 3c1a85
            if len(path) != 1:
Ivan Mahonin 3c1a85
                return self.err()
Ivan Mahonin 3c1a85
            j = False
Ivan Mahonin 3c1a85
            if path[0].endswith(".json"):
Ivan Mahonin 3c1a85
                j = True
Ivan Mahonin 3c1a85
                path[0] = path[0][0:-5]
Ivan Mahonin 3c1a85
            player = game.players.get(path[0], None)
Ivan Mahonin 3c1a85
            if not player:
Ivan Mahonin 3c1a85
                return self.err()
Ivan Mahonin 3c1a85
            if j:
Ivan Mahonin 3c1a85
                return self.writeStatus(player)
Ivan Mahonin 3c1a85
            self.send_response(200)
Ivan Mahonin 3c1a85
            self.send_header("Content-type", "text/html")
Ivan Mahonin 3c1a85
            self.end_headers()
Ivan Mahonin 33a545
            if player.game.started:
Ivan Mahonin 33a545
                tplPlayerpage.write(self, player.status());
Ivan Mahonin 33a545
            else:
Ivan Mahonin 33a545
                tplJoinpage.write(self, player.status());
Ivan Mahonin 33a545
Ivan Mahonin 33a545
Ivan Mahonin 33a545
def loadTemplates():
Ivan Mahonin ffff69
    global tplStartpage, tplJoinpage, tplPlayerpage, tplErrorpage
Ivan Mahonin 33a545
    tplStartpage = template.TplLoader.load("tpl/startpage.tpl")
Ivan Mahonin 33a545
    tplJoinpage = template.TplLoader.load("tpl/joinpage.tpl")
Ivan Mahonin 33a545
    tplPlayerpage = template.TplLoader.load("tpl/playerpage.tpl")
Ivan Mahonin ffff69
    tplErrorpage = template.TplLoader.load("tpl/errorpage.tpl")
Ivan Mahonin 33a545
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
mutex = threading.Lock()
Ivan Mahonin 3c1a85
Ivan Mahonin 33a545
loadTemplates()
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
game.mergeCards("cards")
Ivan Mahonin 9ae076
game.mergeWords("words")
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
webServer = http.server.HTTPServer((
Ivan Mahonin 3c1a85
    config.hostName,
Ivan Mahonin 3c1a85
    config.serverPort ), Server )
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
Ivan Mahonin 3c1a85
print("Server started http://%s:%s" % (
Ivan Mahonin 3c1a85
    config.hostName,
Ivan Mahonin 3c1a85
    config.serverPort ))
Ivan Mahonin 3c1a85
try:
Ivan Mahonin 3c1a85
    webServer.serve_forever()
Ivan Mahonin 3c1a85
except KeyboardInterrupt:
Ivan Mahonin 3c1a85
    pass
Ivan Mahonin 3c1a85
webServer.server_close()
Ivan Mahonin 3c1a85
print("Server stopped.")