def __handle_request(self): """Handle GET or POST request.""" path = self.__normalize_path(self.path) # Try "/path" and "/path/" paths_to_try = [path] if path.endswith('/'): paths_to_try.append(path[:-1]) else: paths_to_try.append(path + '/') # Try URLdecoded versions too unquoted_paths = [] for path_to_try in paths_to_try: unquoted_paths.append(unquote(path_to_try)) paths_to_try = paths_to_try + unquoted_paths page = None for path_to_try in paths_to_try: if path_to_try in self._pages: page = self._pages[path_to_try] path = path_to_try break if page is None: self.send_response(HTTPStatus.NOT_FOUND) self.send_header("Content-Type", "text/plain") self.end_headers() self.__write_response_string("Not found :(") return page = self._pages[path] if isinstance(page, str) or isinstance(page, bytes): page = {'content': page} # HTTP auth if not self.__request_passed_authentication(page=page): self.send_response(HTTPStatus.UNAUTHORIZED) self.send_header("WWW-Authenticate", 'Basic realm="HashServer"') self.end_headers() return # MC_REWRITE_TO_PYTHON: Decode strings from Perl's bytes if b'redirect' in page: # noinspection PyTypeChecker page['redirect'] = decode_object_from_bytes_if_needed( page[b'redirect']) if b'http_status_code' in page: # noinspection PyTypeChecker page['http_status_code'] = page[b'http_status_code'] if b'callback' in page: # noinspection PyTypeChecker page['callback'] = page[b'callback'] if b'content' in page: # noinspection PyTypeChecker page['content'] = page[b'content'] if b'header' in page: # noinspection PyTypeChecker page['header'] = decode_object_from_bytes_if_needed( page[b'header']) if 'redirect' in page: redirect_url = page['redirect'] http_status_code = page.get( 'http_status_code', HashServer._DEFAULT_REDIRECT_STATUS_CODE) self.send_response(http_status_code) self.send_header("Content-Type", "text/html; charset=UTF-8") self.send_header('Location', redirect_url) self.end_headers() self.__write_response_string("Redirecting.") return elif 'callback' in page: callback_function = page['callback'] post_data = None if self.command.lower() == 'post': post_data = self.rfile.read( int(self.headers['Content-Length'])).decode('utf-8') request = HashServer.Request( port=self._port, method=self.command, path=self.path, headers=dict(self.headers.items()), content=post_data, ) response = callback_function(request) if isinstance(response, str): response = str.encode(response) # log.debug("Raw callback response: %s" % str(response)) if b"\r\n\r\n" not in response: raise McHashServerException( "Response must include both HTTP headers and data, separated by CRLF." ) response_headers, response_content = response.split( b"\r\n\r\n", 1) for response_header in response_headers.split(b"\r\n"): if response_header.startswith(b'HTTP/'): first_response_line = response_header.split(b' ', maxsplit=2) if not 2 <= len(first_response_line) <= 3: raise McHashServerException( "Unexpected response line: %s" % response_header) # protocol = first_response_line[0] http_status_code = first_response_line[1] if len(first_response_line) == 3: http_status_message = first_response_line[2] else: # Some responses might not have status message, e.g. respond with just "HTTP 200" http_status_message = b'' self.send_response( code=int(http_status_code.decode('utf-8')), message=http_status_message.decode('utf-8')) else: header_name, header_value = response_header.split( b':', 1) header_value = header_value.strip() self.send_header(header_name.decode('utf-8'), header_value.decode('utf-8')) self.end_headers() self.__write_response_string(response_content) return elif 'content' in page: content = page['content'] headers = page.get('header', 'Content-Type: text/html; charset=UTF-8') if not isinstance(headers, list): headers = [headers] http_status_code = page.get('http_status_code', HTTPStatus.OK) self.send_response(http_status_code) for header in headers: header_name, header_value = header.split(':', 1) header_value = header_value.strip() self.send_header(header_name, header_value) self.end_headers() self.__write_response_string(content) return else: log.info("Invalid page for path %s" % self.path) raise McHashServerException('Invalid page: %s' % str(page))
def __handle_request(self): """Handle GET or POST request.""" path = self.__normalize_path(self.path) # Try "/path" and "/path/" paths_to_try = [path] if path.endswith('/'): paths_to_try.append(path[:-1]) else: paths_to_try.append(path + '/') # Try URLdecoded versions too unquoted_paths = [] for path_to_try in paths_to_try: unquoted_paths.append(unquote(path_to_try)) paths_to_try = paths_to_try + unquoted_paths page = None for path_to_try in paths_to_try: if path_to_try in self._pages: page = self._pages[path_to_try] path = path_to_try break if page is None: self.send_response(HTTPStatus.NOT_FOUND) self.send_header("Content-Type", "text/plain") self.end_headers() self.__write_response_string("Not found :(") return page = self._pages[path] if isinstance(page, str) or isinstance(page, bytes): page = {'content': page} # HTTP auth if not self.__request_passed_authentication(page=page): self.send_response(HTTPStatus.UNAUTHORIZED) self.send_header("WWW-Authenticate", 'Basic realm="HashServer"') self.end_headers() return # MC_REWRITE_TO_PYTHON: Decode strings from Perl's bytes if b'redirect' in page: # noinspection PyTypeChecker page['redirect'] = decode_object_from_bytes_if_needed(page[b'redirect']) if b'http_status_code' in page: # noinspection PyTypeChecker page['http_status_code'] = page[b'http_status_code'] if b'callback' in page: # noinspection PyTypeChecker page['callback'] = page[b'callback'] if b'content' in page: # noinspection PyTypeChecker page['content'] = page[b'content'] if b'header' in page: # noinspection PyTypeChecker page['header'] = decode_object_from_bytes_if_needed(page[b'header']) if 'redirect' in page: redirect_url = page['redirect'] http_status_code = page.get('http_status_code', HashServer._DEFAULT_REDIRECT_STATUS_CODE) self.send_response(http_status_code) self.send_header("Content-Type", "text/html; charset=UTF-8") self.send_header('Location', redirect_url) self.end_headers() self.__write_response_string("Redirecting.") return elif 'callback' in page: callback_function = page['callback'] post_data = None if self.command.lower() == 'post': post_data = self.rfile.read(int(self.headers['Content-Length'])).decode('utf-8') request = HashServer.Request( port=self._port, method=self.command, path=self.path, headers=dict(self.headers.items()), content=post_data, ) response = callback_function(request) if isinstance(response, str): response = str.encode(response) log.debug("Raw callback response: %s" % str(response)) if b"\r\n\r\n" not in response: raise McHashServerException("Response must include both HTTP headers and data, separated by CRLF.") response_headers, response_content = response.split(b"\r\n\r\n", 1) for response_header in response_headers.split(b"\r\n"): if response_header.startswith(b'HTTP/'): first_response_line = response_header.split(b' ', maxsplit=2) if not 2 <= len(first_response_line) <= 3: raise McHashServerException("Unexpected response line: %s" % response_header) # protocol = first_response_line[0] http_status_code = first_response_line[1] if len(first_response_line) == 3: http_status_message = first_response_line[2] else: # Some responses might not have status message, e.g. respond with just "HTTP 200" http_status_message = b'' self.send_response( code=int(http_status_code.decode('utf-8')), message=http_status_message.decode('utf-8') ) else: header_name, header_value = response_header.split(b':', 1) header_value = header_value.strip() self.send_header(header_name.decode('utf-8'), header_value.decode('utf-8')) self.end_headers() self.__write_response_string(response_content) return elif 'content' in page: content = page['content'] headers = page.get('header', 'Content-Type: text/html; charset=UTF-8') if not isinstance(headers, list): headers = [headers] http_status_code = page.get('http_status_code', HTTPStatus.OK) self.send_response(http_status_code) for header in headers: header_name, header_value = header.split(':', 1) header_value = header_value.strip() self.send_header(header_name, header_value) self.end_headers() self.__write_response_string(content) return else: log.info("Invalid page for path %s" % self.path) raise McHashServerException('Invalid page: %s' % str(page))