class MyWebServer(socketserver.BaseRequestHandler): """ Web server. """ class ClientDisconnected(Exception): """ Raised when a client disconnects while the server is waiting for it to transmit. """ pass class SocketIterator: """ Decoding iterator over a SocketBuffer yielding lines. """ def __init__(self, sio): self.sio = io.TextIOWrapper( io.BufferedReader(sio), newline='\r\n') def __next__(self): """ Read a line from the socket. Returns a line of text (str), with the trailing newline removed. Raises ClientDisconnected if the socket has been disconnected from the other side. """ text = self.sio.readline() if not text: raise self.ClientDisconnected() stripped = text.rstrip('\r\n') return stripped def handle(self): self.handler = MyHTTPHandler(WEB_ROOT) self.sio = SocketIO(self.request) try: req = Request.read_from(self.SocketIterator(self.sio)) if LOG_REQUESTS: print(('{}:{} {} {}').format( self.client_address[0], self.client_address[1], req.method, req.path), file=sys.stderr) resp = self.handler.handle_request(req) except myhttp.ParseError: resp = Response(Response.BAD_REQUEST) except self.ClientDisconnected: # nothing to do, really return except: # Try to send a valid response even if something went wrong if LOG_REQUESTS: print('500 Internal Server Error', file=sys.stderr) self.sio.write(b'HTTP/1.1 500 Internal Server Error\r\n') raise # Attach required header resp.attach_header('Connection', 'close') # send self._send_response(resp) def _send_response(self, resp): """ Write an HTTP response as represented by a Response object to the socket. resp: Response """ assert isinstance(resp, Response) # Attach the current date gmt_now = time.gmtime() ftime = time.strftime('%a, %d %b %Y %H:%M:%S GMT', gmt_now) resp.attach_header('Date', ftime) # write if LOG_REQUESTS: print(' {} {}'.format( resp.code, resp.status_message()), file=sys.stderr) self.sio.write(bytes(resp))