def test_unauthorized_www_authenticate(): basic = WWWAuthenticate() basic.set_basic("test") digest = WWWAuthenticate() digest.set_digest("test", "test") exc = exceptions.Unauthorized(www_authenticate=basic) h = Headers(exc.get_headers({})) assert h["WWW-Authenticate"] == str(basic) exc = exceptions.Unauthorized(www_authenticate=[digest, basic]) h = Headers(exc.get_headers({})) assert h.get_all("WWW-Authenticate") == [str(digest), str(basic)] exc = exceptions.Unauthorized() h = Headers(exc.get_headers({})) assert "WWW-Authenticate" not in h
class Request(object): """ A Request object. This should not be manually created. Instead, it is automatically provided by Kyokai. If you must create one, use :meth:`from_data` or :meth:`parse`. :ivar method: The HTTP method (GET, POST, PUT, etc) :ivar path: The full path of the request (``/api/v1/whatever``) :ivar headers: A :class:`IOrderedDict` representing the headers of the request. :ivar query: The raw query string (``a=b&c=d``) :ivar body: The raw body of the request. :ivar cookies: A :class:`cookies.SimpleCookie` containing the cookies of the request. :ivar args: The arguments from the query string, parsed out. :ivar form: The form data for the request. If the request was JSON, this is automatically parsed out. :ivar values: THe arguments and the form combined. :ivar source: The source IP of the request. """ def __init__(self): """ Creates a new request. The request is probably useless right now, but the HTTP parser will then go on to set the right attributes on it. """ # Empty values. self.method = "" # This differs from path/query because it's the full `/a/b/?c=d`. # This is then urlsplit into a path and query string in _parse_path. self.full_path = b"" self.path = "" self.query = "" self.version = "" # Empty body, as this isn't known until it's passed in. self.body = "" self.cookies = cookies.SimpleCookie() # We use a Headers object here as it serves our purposes the best. self.headers = Headers() # Args, values, and forms are OrderedMultiDicts. # So are files. self.args = OrderedMultiDict() self._form = OrderedMultiDict() self.values = OrderedMultiDict() self.files = OrderedMultiDict() # Protocol-specific data. self.ip = "" self.port = 0 # Extra values, for hooks. self.extra = {} self.should_keep_alive = False @property def form(self) -> dict: """ Returns the form data for the specified request. JSON forms are lazy loaded. This means that parsing is done in the first call to `.form`, rather than when the request is created. """ if self._form: return self._form # Parse JSON, otherwise. if self.headers.get("Content-Type") == "application/json": self._form = json.loads(self.body) self.values.update(self._form if self._form else {}) return self._form def _parse_path(self): """ urlsplits the full path. """ split = uparse.urlsplit(self.full_path.decode()) self.path = split.path self.query = split.query def _parse_query(self): """ Parses the query string, and updates `args` with it as appropriate. """ new_args = uparse.parse_qs(self.query) # Unpack the urlparsed arguments. for name, value in new_args.items(): if len(value) == 1: self.args[name] = value[0] elif len(value) == 0: self.args[name] = None else: self.args[name] = value def _parse_body(self): """ Parses the body data. """ if self.headers.get("Content-Type") != "application/json": # Parse the form data out. f_parser = formparser.FormDataParser() # Wrap the body in a BytesIO. body = BytesIO(self.body.encode()) # The headers can't be directly passed into Werkzeug. # Instead, we have to get a the custom content type, then pass in some fake WSGI options. mimetype, c_t_args = parse_options_header( self.headers.get("Content-Type")) if mimetype: # We have a valid mimetype. # This is good! # Now parse the body. # Construct a fake WSGI environment. env = { "Content-Type": self.headers.get("Content-Type"), "Content-Length": self.headers.get("Content-Length") } # Take the boundary out of the Content-Type, if applicable. boundary = c_t_args.get("boundary") if boundary is not None: env["boundary"] = boundary # Get a good content length. content_length = self.headers.get("Content-Length") try: content_length = int(content_length) except ValueError: content_length = len(self.body) except TypeError: # NoneType... raise HTTPException(411) # Then, the form body itself is parsed. data = f_parser.parse(body, mimetype, content_length, options=env) # Extract the new data from the form parser. self._form.update(data[1]) self.files.update(data[2]) def parse_all(self): """ Called when all data is parsed. This tells the request to re-parse everything based off of the raw data. This is an internal method. .. versionadded:: 1.9 """ # Call _parse_path to parse the path. self._parse_path() # Call _parse_query to parse the query string. self._parse_query() # Call _parse_body to parse the body. self._parse_body() # Load cookies. cookie_header = self.headers.get_all("Cookie") for c in cookie_header: self.cookies.load(c)