def serve_client(self, client, req: Request): try: Logger.debug_info(f'Request {req}') if req.insufficient(): raise Errors.MALFORMED_REQ a = time.perf_counter() res = self.handle_req(req) Logger.debug_info( f'Request handling time {time.perf_counter() - a}') Logger.debug_info(f'Response prepared {res}') Response.send_response(client, res) Logger.debug_info(f'Response sent') Logger.info(f'Source Requested', extra={ 'method': req.method, 'url': req.path, 'code': res.status, 'ip': client.getpeername() }) if req.headers.get('Connection') == 'keep-alive': client.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) if req.headers.get('Connection') == 'close': self.close(client) except KeepAliveExpire: self.close(client) except Exception as e: Logger.exception(f'Client handling failed') send_error(client, e, self.configurator)
def test_guest_book_post(self): req_line = ( b'POST /post HTTP/1.1\r\n' b'Host: 0.0.0.0:8000\r\n' b'Content-Type: application/x-www-form-urlencoded\r\n' b'Accept-Encoding: gzip, deflate\r\n' b'Upgrade-Insecure-Requests: 1\r\n' b'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,' b'*/*;q=0.8\r\n' b'Content-Length: 17\r\n' b'Accept-Language: en-us\r\n' b'\r\n' b'username=a&post=b') server = self.make_server() req = Request.fill_from_line(req_line) res: Response = server.handle_req(req) self.assertEqual(res.body, b"POST ADDED {'username': ['a'], 'post': ['b']}") res = self.guest_book_get(server) self.assertEqual(json.loads(res.body), [{ "username": "******", "post": "Post" }, { 'post': ['b'], 'username': ['a'] }])
def test_get_res(self): line = (b'GET / HTTP/1.1\r\n' b'Host: 0.0.0.0:8000\r\n' b'Accept: */*\r\n\r\n') server = self.make_server() req = Request() split = Request.split_keep_sep(line, b'\r\n') for s in split: if req.dynamic_fill(s): res = server.handle_req(req) self.assertEqual(res.status, 200) self.assertEqual(res.reason, 'OK') headers_dict = dict(res.headers) self.assertTrue(headers_dict.get('Content-Type')) self.assertTrue(headers_dict.get('Content-Length'))
def test_get_picture(self): req_line = b'GET /c.png HTTP/1.1\r\n' \ b'Host: 0.0.0.0\r\n' \ b'Accept: */*\r\n\r\n' server = self.make_server() req = Request.fill_from_line(req_line) res: Response = server.handle_req(req) with open(os.path.join(TEST_DATA_DIR, 'c.png'), 'rb') as pic: self.assertEqual(res.body, pic.read())
def guest_book_get(self, server): req_line = (b'GET /posts HTTP/1.1\r\n' b'Host: 0.0.0.0:8000\r\n' b'Accept: */*\r\n' b'Accept-Language: en-us\r\n' b'Accept-Encoding: gzip, deflate\r\n' b'\r\n') req = Request.fill_from_line(req_line) return server.handle_req(req)
def save(req: Request, server): h = re.compile( b'-+?.+?\r\n' b'Content-Disposition: form-data; ' b'name=\"(?P<name>.*?)\"; ' b'filename=\"(?P<filename>.*?)\"\r\n' b'.+?: (?P<ct>.+?)\r\n' b'\r\n', re.S) tail_pos = 3 while True: req.body_file.seek(-tail_pos, 2) tail = req.body_file.tell() t = req.body_file.read(tail_pos) if t.startswith(b'\r\n'): break tail_pos += 1 head_crlf_count = 4 head = b'' req.body_file.seek(0) i = 0 while True: if i == head_crlf_count: break head += req.body_file.read(1) if head.endswith(b'\r\n'): i += 1 data_start = req.body_file.tell() match = h.search(head) if not match: raise Errors.MALFORMED_REQ groups = match.groupdict() ftype = groups.get('name') fname = Request.decode(groups.get('filename')) if fname: with open(os.path.join(ROOT_DIR, 'tmp', 'saved', fname), 'wb') as f: f.write(req.body_file.read(tail - data_start)) Logger.debug_info(f'{ftype} saved as {fname} ') body = f'{os.path.join(ROOT_DIR, "tmp", "saved")}' \ f' - >' \ f' {os.listdir(os.path.join(ROOT_DIR, "tmp", "saved"))}' else: body = 'Empty file' headers = [ ('Content-Type', f'application/json'), ('Content-Disposition', f'inline; filename=Echo query'), ('Content-Length', len(body)), ('Location', '/upload'), ] return Response(301, 'Moved Permanently', headers, body.encode())
def _accept(self, sock): (client, addr) = sock.accept() num = client.fileno() self.conns[num] = client self.requests[num] = Request() self.out_buff[num] = [] Logger.debug_info(f'Connected {addr}') client.setblocking(False) self.poller.register(client, selectors.EVENT_READ, self._read) Logger.debug_info(f'EVENT_READ Registered {addr}')
def _read(self, client): try: line: bytes = client.recv(self.MAX_LINE) if not line: self.poller.unregister(client) del self.conns[client.fileno()] return num = client.fileno() req_builder: Request = self.requests[num] split = Request.split_keep_sep(line, bytes(os.linesep, 'utf-8')) for s in split: if req_builder.dynamic_fill(s): self.requests[num] = Request() # todo Request() change to req_builder return self.serve_client(client, req_builder) print(req_builder) except Exception as e: Logger.exception('Read from client failed') send_error(client, e, self.configurator)
def handle_post(req: Request, server): req.body_file.seek(0) body = req.body_file.read() body = Request.decode(body) query = parse_qs(f'{body}') POSTS.append(query) body = f'POST ADDED {query}'.encode() headers = [ ('Content-Type', f'application/json'), ('Content-Disposition', f'inline; filename=Post'), ('Content-Length', len(body)), ('Location', '/my_guest_book'), ] return Response(301, 'Moved Permanently', headers, body)
def post(req: Request, server): Logger.debug_info(f'POST---------') req.body_file.seek(0) body = req.body_file.read() body = Request.decode(body) Logger.debug_info(f'POSTED -> {body}') post_data = json.loads(body) BOOKS.append({ 'id': uuid.uuid4().hex, 'title': post_data.get('title'), 'author': post_data.get('author'), 'read': post_data.get('read') }) SUCCESS['message'] = 'Books added!' body = f'{json.dumps(SUCCESS)}'.encode() headers = [ ('Content-Type', f'application/json'), ('Content-Disposition', f'inline; filename=Get posts'), ('Content-Length', len(body)), ("Access-Control-Allow-Origin", '*') ] return Response(200, 'OK', headers, body)