def get_headers( self, version="1.1", keep_alive=False, keep_alive_timeout=None ): # This is all returned in a kind-of funky way # We tried to make this as fast as possible in pure python timeout_header = b"" if keep_alive and keep_alive_timeout is not None: timeout_header = b"Keep-Alive: %d\r\n" % keep_alive_timeout self.headers["Transfer-Encoding"] = "chunked" self.headers.pop("Content-Length", None) self.headers["Content-Type"] = self.headers.get( "Content-Type", self.content_type ) headers = self._parse_headers() if self.status is 200: status = b"OK" else: status = STATUS_CODES.get(self.status) return (b"HTTP/%b %d %b\r\n" b"%b" b"%b\r\n") % ( version.encode(), self.status, status, timeout_header, headers, )
def output(self, version="1.1", keep_alive=False, keep_alive_timeout=None): # This is all returned in a kind-of funky way # We tried to make this as fast as possible in pure python timeout_header = b'' if keep_alive and keep_alive_timeout is not None: timeout_header = b'Keep-Alive: %d\r\n' % keep_alive_timeout body = b'' if has_message_body(self.status): body = self.body self.headers['Content-Length'] = self.headers.get( 'Content-Length', len(self.body)) self.headers['Content-Type'] = self.headers.get( 'Content-Type', self.content_type) if self.status in (304, 412): self.headers = remove_entity_headers(self.headers) headers = self._parse_headers() if self.status is 200: status = b'OK' else: status = STATUS_CODES.get(self.status, b'UNKNOWN RESPONSE') return (b'HTTP/%b %d %b\r\n' b'Connection: %b\r\n' b'%b' b'%b\r\n' b'%b') % (version.encode(), self.status, status, b'keep-alive' if keep_alive else b'close', timeout_header, headers, body)
def get_headers(self, version="1.1", keep_alive=False, keep_alive_timeout=None): # This is all returned in a kind-of funky way # We tried to make this as fast as possible in pure python timeout_header = b'' if keep_alive and keep_alive_timeout is not None: timeout_header = b'Keep-Alive: %d\r\n' % keep_alive_timeout self.headers['Transfer-Encoding'] = 'chunked' self.headers.pop('Content-Length', None) self.headers['Content-Type'] = self.headers.get( 'Content-Type', self.content_type) headers = self._parse_headers() if self.status is 200: status = b'OK' else: status = STATUS_CODES.get(self.status) return (b'HTTP/%b %d %b\r\n' b'%b' b'%b\r\n') % (version.encode(), self.status, status, timeout_header, headers)
def __init__( self, message: Optional[Union[str, bytes]] = None, status_code: Optional[int] = None, quiet: Optional[bool] = None, context: Optional[Dict[str, Any]] = None, extra: Optional[Dict[str, Any]] = None, ) -> None: self.context = context self.extra = extra if message is None: if self.message: message = self.message elif status_code is not None: msg: bytes = STATUS_CODES.get(status_code, b"") message = msg.decode("utf8") super().__init__(message) if status_code is not None: self.status_code = status_code # quiet=None/False/True with None meaning choose by status if quiet or quiet is None and status_code not in (None, 500): self.quiet = True
def output(self, version="1.1", keep_alive=False, keep_alive_timeout=None): # This is all returned in a kind-of funky way # We tried to make this as fast as possible in pure python timeout_header = b"" if keep_alive and keep_alive_timeout is not None: timeout_header = b"Keep-Alive: %d\r\n" % keep_alive_timeout body = b"" if has_message_body(self.status): body = self.body self.headers["Content-Length"] = self.headers.get( "Content-Length", len(self.body)) # self.headers get priority over content_type if self.content_type and "Content-Type" not in self.headers: self.headers["Content-Type"] = self.content_type if self.status in (304, 412): self.headers = remove_entity_headers(self.headers) headers = self._parse_headers() status = STATUS_CODES.get(self.status, b"UNKNOWN RESPONSE") return (b"HTTP/%b %d %b\r\n" b"Connection: %b\r\n" b"%b" b"%b\r\n" b"%b") % ( version.encode(), self.status, status, b"keep-alive" if keep_alive else b"close", timeout_header, headers, body, )
def exception_response(request, exception, debug): status = 500 text = ("The server encountered an internal error " "and cannot complete your request.") headers = {} if isinstance(exception, SanicException): text = f"{exception}" status = getattr(exception, "status_code", status) headers = getattr(exception, "headers", headers) elif debug: text = f"{exception}" status_text = STATUS_CODES.get(status, b"Error Occurred").decode() title = escape(f"{status} — {status_text}") text = escape(text) if debug and not getattr(exception, "quiet", False): return html( f"<!DOCTYPE html><meta charset=UTF-8><title>{title}</title>" f"<style>{TRACEBACK_STYLE}</style>\n" f"<h1>⚠️ {title}</h1><p>{text}\n" f"{_render_traceback_html(request, exception)}", status=status, ) # Keeping it minimal with trailing newline for pretty curl/console output return html( f"<!DOCTYPE html><meta charset=UTF-8><title>{title}</title>" "<style>html { font-family: sans-serif }</style>\n" f"<h1>⚠️ {title}</h1><p>{text}\n", status=status, headers=headers, )
def format_http1_response(status: int, headers: HeaderIterable, body=b"") -> bytes: """Format a full HTTP/1.1 response. - If `body` is included, content-length must be specified in headers. """ headerbytes = format_http1(headers) return b"HTTP/1.1 %d %b\r\n%b\r\n%b" % ( status, STATUS_CODES.get(status, b"UNKNOWN"), headerbytes, body, )
def abort(status_code, message=None): """ Raise an exception based on SanicException. Returns the HTTP response message appropriate for the given status code, unless provided. :param status_code: The HTTP status code to return. :param message: The HTTP response body. Defaults to the messages in response.py for the given status code. """ if message is None: message = STATUS_CODES.get(status_code) # These are stored as bytes in the STATUS_CODES dict message = message.decode("utf8") sanic_exception = _sanic_exceptions.get(status_code, SanicException) raise sanic_exception(message=message, status_code=status_code)
def exception_response(request, exception, debug): status = 500 text = ( "The server encountered an internal error " "and cannot complete your request." ) headers = {} if isinstance(exception, SanicException): text = f"{exception}" status = getattr(exception, "status_code", status) headers = getattr(exception, "headers", headers) elif debug: text = f"{exception}" status_text = STATUS_CODES.get(status, b"Error Occurred").decode() title = escape(f"{status} — {status_text}") text = escape(text) if debug and not getattr(exception, "quiet", False): return html(f"{text}", status=status) return html(f"{text}", status=status, headers=headers)
def handle_error(self, request, e): """ Error handler for the API transforms a raised exception into a Sanic response, with the appropriate HTTP status code and body. :param request: The Sanic Request object :type request: sanic.request.Request :param e: the raised Exception object :type e: Exception """ context = restplus.get_context_from_spf(self.spf_reg) app = context.app #got_request_exception.send(app._get_current_object(), exception=e) if not isinstance(e, SanicException) and app.config.get( 'PROPAGATE_EXCEPTIONS', False): exc_type, exc_value, tb = sys.exc_info() if exc_value is e: raise else: raise e include_message_in_response = app.config.get("ERROR_INCLUDE_MESSAGE", True) include_code_in_response = app.config.get("ERROR_INCLUDE_CODE", True) default_data = {} headers = Header() for typecheck, handler in self._own_and_child_error_handlers.items(): if isinstance(e, typecheck): result = handler(e) default_data, code, headers = unpack( result, HTTPStatus.INTERNAL_SERVER_ERROR) break else: if isinstance(e, SanicException): sanic_code = code = e.status_code try: status = e.args[0] assert isinstance(status, (str, bytes)) except (AttributeError, LookupError, AssertionError): if sanic_code is 200: status = b'OK' # x is y comparison only works between -5 and 256 elif sanic_code == 404: status = b'Not Found' elif sanic_code == 500: status = b'Internal Server Error' else: status = ALL_STATUS_CODES.get(int(sanic_code)) code = HTTPStatus(sanic_code, None) if status and isinstance(status, bytes): status = status.decode('ascii') if include_message_in_response: default_data = {'message': getattr(e, 'message', status)} elif self._default_error_handler: result = self._default_error_handler(e) default_data, code, headers = unpack( result, HTTPStatus.INTERNAL_SERVER_ERROR) else: code = HTTPStatus.INTERNAL_SERVER_ERROR status = ALL_STATUS_CODES.get(code.value, str(e)) if status and isinstance(status, bytes): status = status.decode('ascii') if include_message_in_response: default_data = { 'message': status, } if include_message_in_response: default_data['message'] = default_data.get('message', str(e)) if include_code_in_response: default_data['code'] = int(code) data = getattr(e, 'data', default_data) fallback_mediatype = None if code >= HTTPStatus.INTERNAL_SERVER_ERROR: exc_info = sys.exc_info() if exc_info[1] is None or exc_info[0] is None: e_type = e.__class__ e_value = e e_traceback = e.__traceback__ else: e_type, e_value, e_traceback = exc_info context.log(logging.ERROR, "Caught Exception: {}".format(str(e_type))) context.log(logging.ERROR, "Detail: {}".format(str(e_value))) tb = traceback.format_tb(e_traceback) tb = "".join(tb) context.log(logging.ERROR, "Traceback:\n{}".format(tb)) elif code == HTTPStatus.NOT_FOUND and app.config.get("ERROR_404_HELP", False) \ and include_message_in_response: data['message'] = self._help_on_404(request, data.get('message', None)) elif code == HTTPStatus.NOT_ACCEPTABLE and self.default_mediatype is None: # if we are handling NotAcceptable (406), make sure that # make_response uses a representation we support as the # default mediatype (so that make_response doesn't throw # another NotAcceptable error). supported_mediatypes = list(self.representations.keys()) fallback_mediatype = supported_mediatypes[ 0] if supported_mediatypes else "text/plain" # Remove blacklisted headers for header in HEADERS_BLACKLIST: headers.pop(header, None) resp = self.make_response(request, data, code, headers, fallback_mediatype=fallback_mediatype) if code == HTTPStatus.UNAUTHORIZED: resp = self.unauthorized(resp) return resp
addr = f"[{addr}]" # bracket IPv6 return addr.lower() def parse_host(host: str) -> Tuple[Optional[str], Optional[int]]: """Split host:port into hostname and port. :return: None in place of missing elements """ m = _host_re.fullmatch(host) if not m: return None, None host, port = m.groups() return host.lower(), int(port) if port is not None else None _HTTP1_STATUSLINES = [ b"HTTP/1.1 %d %b\r\n" % (status, STATUS_CODES.get(status, b"UNKNOWN")) for status in range(1000) ] def format_http1_response(status: int, headers: HeaderBytesIterable) -> bytes: """Format a HTTP/1.1 response header.""" # Note: benchmarks show that here bytes concat is faster than bytearray, # b"".join() or %-formatting. %timeit any changes you make. ret = _HTTP1_STATUSLINES[status] for h in headers: ret += b"%b: %b\r\n" % h ret += b"\r\n" return ret
def title(self): status_text = STATUS_CODES.get(self.status, b"Error Occurred").decode() return f"{self.status} — {status_text}"
def title(self): return STATUS_CODES.get(self.status, b"Error Occurred").decode()