summaryrefslogtreecommitdiff
path: root/project1/proj1_s4498062/webhttp/composer.py
blob: b8b9f6ac29f9069112627382f442d3a452ca92f6 (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
""" Composer for HTTP responses

This module contains a composer, which can compose responses to
HTTP requests from a client.
"""

import re
import time

from config import config
import encodings
from message import Response
from resource import Resource, FileExistError, FileAccessError
import weblogging as logging

class ResponseComposer:
    """Class that composes a HTTP response to a HTTP request"""

    def __init__(self, timeout):
        """Initialize the ResponseComposer
        
        Args:
            timeout (int): connection timeout
        """
        self.timeout = timeout
    
    def compose_response(self, request):
        """Compose a response to a request
        
        Args:
            request (webhttp.Request): request from client

        Returns:
            webhttp.Response: response to request

        """
        response = Response()

        if re.search(r'\/\.\.?(\/|$)', request.uri):
            return self.serve_error(403)
        else:
            return self.serve(request.uri, request=request)

    def serve(self, uri, code=200, etag=None, request=None):
        resp = Response()

        try:
            resource = Resource(uri)
            req = request
            if req != None and (
                    resource.etag_match(req.get_header('If-None-Match')) or \
                    req.get_header('If-Match') != None and \
                    not resource.etag_match(req.get_header('If-Match'))):
                resp.code = 304
            else:
                resp.code = code
                resp.body = None
                encs = req.encodings() if req != None else [encodings.IDENTITY]
                for enc in encs:
                    try:
                        resp.body = resource.get_content(enc)
                        resp.set_header('Content-Encoding', encodings.str(enc))
                        break
                    except UnknownEncodingError:
                        pass
                if resp.body == None:
                    return self.serve_error(406)
            resp.set_header('ETag', resource.generate_etag())
            resp.set_header('Content-Type', resource.get_content_type())
            resp.set_content_length()
        except FileExistError:
            if code < 400:
                return self.serve_error(404)
            else:
                resp.code = code
                resp.body = 'Error %d' % code
                resp.set_content_length()
        except FileAccessError:
            return self.serve_error(403)
        
        conn = 'keep-alive'
        if req != None and req.get_header('Connection') == 'close':
            conn = 'close'
        resp.set_header('Connection', conn)

        return resp

    def serve_error(self, code):
        return self.serve(config('error%d' % code, default=None), code=code)

    def make_date_string(self):
        """Make string of date and time
        
        Returns:
            str: formatted string of date and time
        """
        return time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())