""" 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): response.code = 403 response.set_header('Connection', 'keep-alive') response.body = 'Don\'t even think about it.' response.set_content_length() return response 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 \ not resource.etag_match(req.get_header('If-Match') or '*')): 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('Connection', 'keep-alive') 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.set_header('Connection', 'keep-alive') resp.body = 'Error %d' % code resp.set_content_length() except FileAccessError: return self.serve_error(403) 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())