summaryrefslogtreecommitdiff
path: root/project1/proj1_s4498062/webhttp/server.py
blob: 5db8620320eec821c60a79ef3401451432bb9233 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
"""HTTP Server

This module contains a HTTP server
"""

import threading
import socket

from composer import ResponseComposer
from parser import RequestParser
import weblogging as logging
from config import config

class ConnectionHandler(threading.Thread):
    """Connection Handler for HTTP Server"""

    def __init__(self, conn_socket, addr, timeout):
        """Initialize the HTTP Connection Handler
        
        Args:
            conn_socket (socket): socket used for connection with client
            addr (str): ip address of client
            timeout (int): seconds until timeout
        """
        super(ConnectionHandler, self).__init__()
        self.daemon = True
        self.conn_socket = conn_socket
        self.addr = addr
        self.timeout = timeout
        self.done = False
    
    def handle_connection(self):
        """Handle a new connection"""
        self.rp = RequestParser()
        self.rc = ResponseComposer(timeout=self.timeout)

        self.conn_socket.settimeout(self.timeout)

        try:
            while not self.done:
                data = self.conn_socket.recv(32)
                if len(data) == 0:
                    break
                self.handle_data(data)
        except socket.timeout:
            logging.debug('%s connection timed out.' % self.addr[0])
        finally:
            self.conn_socket.shutdown(socket.SHUT_RDWR)
            self.conn_socket.close()
            logging.debug('%s connection closed.' % self.addr[0])

    def handle_data(self, data):
        for req in self.rp.parse_requests(data):
            logging.info("<-- (%s) %s" % (self.addr[0], req.startline()))
            resp = self.rc.compose_response(req)
            logging.info("--> (%s) %s" % (self.addr[0], resp.startline()))
            self.send(resp)

            if resp.get_header('Connection') == 'close':
                self.done = True
                return

    def send(self, data):
        sent = self.conn_socket.send(str(data))
        if sent == 0:
            raise RuntimeError('Socket broken')
        
    def run(self):
        """Run the thread of the connection handler"""
        try:
            self.handle_connection()
        except socket.error, e:
            logging.error('Error in handling connection with %s: %s' % 
                    (self.addr[0], str(e)))


class Server:
    """HTTP Server"""

    def __init__(self, configfile, **kwargs):
        """Initialize the HTTP server
        
        Args:
            hostname (str): hostname of the server
            server_port (int): port that the server is listening on
            timeout (int): seconds until timeout
        """
        self.read_config(configfile, **kwargs)
        self.done = False

    def read_config(self, configfile, **kwargs):
        config().read(configfile)
        
        if not config().has_section('webhttp'):
            config().add_section('webhttp')
        
        for name, val in kwargs.items():
            if val != None:
                config().set('webhttp', name, str(val))
    
    def run(self):
        """Run the HTTP Server and start listening"""
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.bind((config('hostname', default='localhost'), 
                config('port', type=int, default=8001)))
        self.socket.listen(config('max_connections', type=int, default=1000))
        while not self.done:
            (csocket, addr) = self.socket.accept()
            logging.debug('%s connection accepted.' % addr[0])
            ch = ConnectionHandler(csocket, addr, config('timeout', type=int))
            ch.start()
    
    def shutdown(self):
        """Safely shut down the HTTP server"""
        self.socket.shutdown(socket.SHUT_RDWR)
        self.socket.close()
        self.done = True