sawine@0: #! /usr/bin/env python3 sawine@0: """ sawine@0: Name: sawine@0: Author: Eugen Sawin sawine@0: """ sawine@0: sawine@0: import socketserver sawine@0: import logging sawine@0: import os sawine@0: import os.path sawine@0: import shelve sawine@0: import json sawine@0: import re sawine@0: import urllib.request sawine@0: import subprocess sawine@0: import time sawine@0: from operator import itemgetter sawine@0: from urllib.parse import urlparse sawine@0: from urllib.request import urlopen sawine@0: from tempfile import NamedTemporaryFile sawine@0: from http.server import BaseHTTPRequestHandler, HTTPServer sawine@0: from pseudoword import Database sawine@0: sawine@0: try: sawine@0: from argparse import ArgumentParser sawine@0: except ImportError: sawine@0: from external.argparse import ArgumentParser sawine@0: sawine@0: ENCODING = 'utf-8' sawine@0: sawine@0: def parse_args(): sawine@0: parser = ArgumentParser(description='') sawine@0: parser.add_argument('-p', type=int, default=8080, help='port') sawine@0: parser.add_argument('-a', default='localhost', help='host address') sawine@0: parser.add_argument('-l', default='log/server.log', help='log file') sawine@0: parser.add_argument('-w', default='data/names', help='word database') sawine@0: parser.add_argument('-d', default='httpdocs', help='http docs path') sawine@2: parser.add_argument('--pid', default=None, help='pid file') sawine@0: return parser.parse_args() sawine@0: sawine@0: sawine@2: def main(): sawine@0: args = parse_args() sawine@0: logging.basicConfig(filename=args.l, level=logging.DEBUG, sawine@0: format='[%(levelname)s@%(asctime)s] %(message)s', sawine@0: datefmt='%d.%m.%Y %I:%M:%S') sawine@2: if args.pid: sawine@2: with open(args.pid, 'w') as f: sawine@2: f.write(str(os.getpid())) sawine@0: db = Database(args.w) sawine@0: server = Server(args.a, args.p, args.d, db, GetHandler) sawine@0: server.run() sawine@0: sawine@0: sawine@0: class GetHandler(BaseHTTPRequestHandler): sawine@0: sawine@0: def do_GET(self): sawine@0: request = Request(self.path, self.server.docs_path, self.server.db) sawine@0: logging.info('Request: %s' % request) sawine@0: code, content_type, data = request() sawine@0: self.send_response(code) sawine@0: self.send_header('Content-type', content_type) sawine@0: self.end_headers() sawine@0: self.wfile.write(data) sawine@0: sawine@0: def log_message(self, format, *args): sawine@0: pass sawine@0: sawine@0: sawine@0: class Server(HTTPServer): sawine@0: sawine@0: def __init__(self, host, port, docs_path, db, handler): sawine@0: HTTPServer.__init__(self, (host, port), handler) sawine@0: self.host = host sawine@0: self.port = port sawine@0: self.docs_path = docs_path sawine@0: self.db = db sawine@0: sawine@0: def run(self): sawine@0: logging.info('Server ready and listening at port {0}.'.format(self.port)) sawine@0: self.serve_forever() sawine@0: sawine@0: sawine@0: class Request: sawine@0: sawine@0: @classmethod sawine@0: def _content_type(cls, doc): sawine@0: if doc.endswith('.html'): sawine@0: return 'text/html' sawine@0: elif doc.endswith('.css'): sawine@0: return 'text/css' sawine@0: elif doc.endswith('.js'): sawine@0: return 'application/javascript' sawine@0: elif doc.endswith('.ttf'): sawine@0: return 'application/octet-stream' sawine@0: else: sawine@0: return 'text/plain' sawine@0: sawine@0: def __init__(self, query, docs_path, db): sawine@0: self.docs_path = docs_path sawine@0: self.db = db sawine@0: self.parsed = urlparse(query) sawine@0: self.args = dict() sawine@0: if len(self.parsed.query): sawine@0: self.args = dict([a.split('=') for a in self.parsed.query.split('&')]) sawine@0: if self.parsed.path[1:] in commands: sawine@0: self.__class__ = commands[self.parsed.path[1:]] sawine@0: else: sawine@0: self.__class__ = WebRequest sawine@0: self.args['doc'] = self.parsed.path sawine@0: sawine@0: def __str__(self): sawine@0: return '{0}({1})'.format(str(self.__class__).partition('.')[-1].rstrip('">'), sawine@0: ', '.join(['{0}: {1}'.format(k, str(v)) sawine@0: for k, v in self.args.items()])) sawine@0: sawine@0: sawine@0: def client_rating(rating): sawine@0: return int(rating * 1000) sawine@0: sawine@0: sawine@0: class WordsRequest(Request): sawine@0: sawine@0: def __call__(self): sawine@0: w1, w2 = self.db.pick_words() sawine@0: data = '{{"words":[["{w1}", {r1}], ["{w2}", {r2}]]}}'.format(w1=w1[0], sawine@0: r1=w1[1], sawine@0: w2=w2[0], sawine@0: r2=w2[1]) sawine@0: return (200, 'application/javascript', bytes(data, ENCODING)) sawine@0: sawine@0: sawine@0: class RateRequest(Request): sawine@0: sawine@0: def __call__(self): sawine@0: w3 = self.args['bn'] sawine@0: w4 = self.args['wn'] sawine@0: w3, w4 = self.db.rate(w3, w4) sawine@0: w1, w2 = self.db.pick_words() sawine@0: data = '{{"words":[["{w1}",{r1}],["{w2}",{r2}],'.format(w1=w1[0], r1=client_rating(w1[1]), sawine@0: w2=w2[0], r2=client_rating(w2[1])) sawine@0: data += '["{w3}",{r3}],["{w4}",{r4}]]}}'.format(w3=w3[0], r3=client_rating(w3[1]), sawine@0: w4=w4[0], r4=client_rating(w4[1])) sawine@0: return (200, 'application/javascript', bytes(data, ENCODING)) sawine@0: sawine@0: sawine@0: commands = {'words': WordsRequest, sawine@0: 'rate': RateRequest} sawine@0: sawine@0: sawine@0: class WebRequest(Request): sawine@0: sawine@0: def __call__(self): sawine@0: if self.args['doc'] == '/': sawine@0: self.args['doc'] = '/index.html' sawine@0: content_type = self._content_type(self.args['doc']) sawine@0: data = bytes('', ENCODING) sawine@0: rel_path = self.docs_path + self.args['doc'] sawine@0: if os.path.exists(rel_path): sawine@0: with open(rel_path, 'r+b') as f: sawine@0: data = f.read() sawine@0: code = 200 sawine@0: else: sawine@0: code = 404 sawine@0: return (code, content_type, data) sawine@0: sawine@0: sawine@0: if __name__ == '__main__': sawine@0: main()