server.py
author Eugen Sawin <sawine@me73.com>
Mon, 20 Feb 2012 23:41:50 +0100
changeset 2 b25c62d210f5
parent 0 a2f88c3bd824
permissions -rw-r--r--
Added pid file writing.
sawine@0
     1
#! /usr/bin/env python3
sawine@0
     2
"""
sawine@0
     3
Name: 
sawine@0
     4
Author: Eugen Sawin <sawine@me73.com>
sawine@0
     5
"""
sawine@0
     6
sawine@0
     7
import socketserver
sawine@0
     8
import logging
sawine@0
     9
import os
sawine@0
    10
import os.path
sawine@0
    11
import shelve
sawine@0
    12
import json
sawine@0
    13
import re
sawine@0
    14
import urllib.request
sawine@0
    15
import subprocess
sawine@0
    16
import time
sawine@0
    17
from operator import itemgetter
sawine@0
    18
from urllib.parse import urlparse
sawine@0
    19
from urllib.request import urlopen
sawine@0
    20
from tempfile import NamedTemporaryFile
sawine@0
    21
from http.server import BaseHTTPRequestHandler, HTTPServer
sawine@0
    22
from pseudoword import Database
sawine@0
    23
sawine@0
    24
try:
sawine@0
    25
    from argparse import ArgumentParser
sawine@0
    26
except ImportError:
sawine@0
    27
    from external.argparse import ArgumentParser
sawine@0
    28
sawine@0
    29
ENCODING = 'utf-8'
sawine@0
    30
sawine@0
    31
def parse_args():
sawine@0
    32
    parser = ArgumentParser(description='')
sawine@0
    33
    parser.add_argument('-p', type=int, default=8080, help='port')
sawine@0
    34
    parser.add_argument('-a', default='localhost', help='host address')
sawine@0
    35
    parser.add_argument('-l', default='log/server.log', help='log file')
sawine@0
    36
    parser.add_argument('-w', default='data/names', help='word database')
sawine@0
    37
    parser.add_argument('-d', default='httpdocs', help='http docs path')
sawine@2
    38
    parser.add_argument('--pid', default=None, help='pid file')
sawine@0
    39
    return parser.parse_args()	
sawine@0
    40
sawine@0
    41
sawine@2
    42
def main():       
sawine@0
    43
    args = parse_args()
sawine@0
    44
    logging.basicConfig(filename=args.l, level=logging.DEBUG,
sawine@0
    45
                        format='[%(levelname)s@%(asctime)s] %(message)s',
sawine@0
    46
                        datefmt='%d.%m.%Y %I:%M:%S')
sawine@2
    47
    if args.pid:
sawine@2
    48
        with open(args.pid, 'w') as f:
sawine@2
    49
            f.write(str(os.getpid()))
sawine@0
    50
    db = Database(args.w)
sawine@0
    51
    server = Server(args.a, args.p, args.d, db, GetHandler)
sawine@0
    52
    server.run()
sawine@0
    53
sawine@0
    54
sawine@0
    55
class GetHandler(BaseHTTPRequestHandler):
sawine@0
    56
sawine@0
    57
    def do_GET(self):
sawine@0
    58
        request = Request(self.path, self.server.docs_path, self.server.db)
sawine@0
    59
        logging.info('Request: %s' % request)
sawine@0
    60
        code, content_type, data = request()
sawine@0
    61
        self.send_response(code)       
sawine@0
    62
        self.send_header('Content-type', content_type)
sawine@0
    63
        self.end_headers()
sawine@0
    64
        self.wfile.write(data)
sawine@0
    65
        
sawine@0
    66
    def log_message(self, format, *args):
sawine@0
    67
        pass
sawine@0
    68
sawine@0
    69
		
sawine@0
    70
class Server(HTTPServer):
sawine@0
    71
sawine@0
    72
    def __init__(self, host, port, docs_path, db, handler):
sawine@0
    73
        HTTPServer.__init__(self, (host, port), handler)
sawine@0
    74
        self.host = host
sawine@0
    75
        self.port = port
sawine@0
    76
        self.docs_path = docs_path
sawine@0
    77
        self.db = db
sawine@0
    78
sawine@0
    79
    def run(self):
sawine@0
    80
        logging.info('Server ready and listening at port {0}.'.format(self.port))
sawine@0
    81
        self.serve_forever()
sawine@0
    82
sawine@0
    83
sawine@0
    84
class Request:    
sawine@0
    85
sawine@0
    86
    @classmethod
sawine@0
    87
    def _content_type(cls, doc):
sawine@0
    88
        if doc.endswith('.html'):
sawine@0
    89
            return 'text/html'
sawine@0
    90
        elif doc.endswith('.css'):
sawine@0
    91
            return 'text/css'
sawine@0
    92
        elif doc.endswith('.js'):
sawine@0
    93
            return 'application/javascript'
sawine@0
    94
        elif doc.endswith('.ttf'):           
sawine@0
    95
            return 'application/octet-stream'
sawine@0
    96
        else:            
sawine@0
    97
            return 'text/plain'
sawine@0
    98
            
sawine@0
    99
    def __init__(self, query, docs_path, db):       
sawine@0
   100
        self.docs_path = docs_path
sawine@0
   101
        self.db = db
sawine@0
   102
        self.parsed = urlparse(query)
sawine@0
   103
        self.args = dict()
sawine@0
   104
        if len(self.parsed.query):
sawine@0
   105
            self.args = dict([a.split('=') for a in self.parsed.query.split('&')])
sawine@0
   106
        if self.parsed.path[1:] in commands:
sawine@0
   107
            self.__class__ = commands[self.parsed.path[1:]]
sawine@0
   108
        else:
sawine@0
   109
            self.__class__ = WebRequest     
sawine@0
   110
            self.args['doc'] = self.parsed.path    
sawine@0
   111
sawine@0
   112
    def __str__(self):
sawine@0
   113
        return '{0}({1})'.format(str(self.__class__).partition('.')[-1].rstrip('">'),
sawine@0
   114
                           ', '.join(['{0}: {1}'.format(k, str(v))
sawine@0
   115
                                      for k, v in self.args.items()]))
sawine@0
   116
sawine@0
   117
sawine@0
   118
def client_rating(rating):
sawine@0
   119
    return int(rating * 1000)
sawine@0
   120
sawine@0
   121
sawine@0
   122
class WordsRequest(Request):
sawine@0
   123
sawine@0
   124
    def __call__(self):   
sawine@0
   125
        w1, w2 = self.db.pick_words()    
sawine@0
   126
        data = '{{"words":[["{w1}", {r1}], ["{w2}", {r2}]]}}'.format(w1=w1[0],
sawine@0
   127
                                                                     r1=w1[1], 
sawine@0
   128
                                                                     w2=w2[0],
sawine@0
   129
                                                                     r2=w2[1])
sawine@0
   130
        return (200, 'application/javascript', bytes(data, ENCODING))
sawine@0
   131
sawine@0
   132
sawine@0
   133
class RateRequest(Request):
sawine@0
   134
sawine@0
   135
    def __call__(self):
sawine@0
   136
        w3 = self.args['bn']
sawine@0
   137
        w4 = self.args['wn']
sawine@0
   138
        w3, w4 = self.db.rate(w3, w4)
sawine@0
   139
        w1, w2 = self.db.pick_words()
sawine@0
   140
        data = '{{"words":[["{w1}",{r1}],["{w2}",{r2}],'.format(w1=w1[0], r1=client_rating(w1[1]), 
sawine@0
   141
                                                                   w2=w2[0], r2=client_rating(w2[1]))
sawine@0
   142
        data += '["{w3}",{r3}],["{w4}",{r4}]]}}'.format(w3=w3[0], r3=client_rating(w3[1]), 
sawine@0
   143
                                                           w4=w4[0], r4=client_rating(w4[1]))
sawine@0
   144
        return (200, 'application/javascript', bytes(data, ENCODING))
sawine@0
   145
sawine@0
   146
sawine@0
   147
commands = {'words': WordsRequest,
sawine@0
   148
            'rate': RateRequest}
sawine@0
   149
sawine@0
   150
sawine@0
   151
class WebRequest(Request):
sawine@0
   152
sawine@0
   153
    def __call__(self):
sawine@0
   154
        if self.args['doc'] == '/':
sawine@0
   155
            self.args['doc'] = '/index.html'
sawine@0
   156
        content_type = self._content_type(self.args['doc'])
sawine@0
   157
        data = bytes('', ENCODING)
sawine@0
   158
        rel_path = self.docs_path + self.args['doc']
sawine@0
   159
        if os.path.exists(rel_path):
sawine@0
   160
            with open(rel_path, 'r+b') as f:
sawine@0
   161
                data = f.read()
sawine@0
   162
            code = 200
sawine@0
   163
        else:
sawine@0
   164
            code = 404
sawine@0
   165
        return (code, content_type, data)
sawine@0
   166
sawine@0
   167
sawine@0
   168
if __name__ == '__main__':
sawine@0
   169
	main()