Example #1
0
def set_cookie(name, _, *args):
    cookie = SimpleCookie()
    cookie[name] = base64.b64encode(":".join(args))
    cookie[name]['path'] = "/"
    cookie[name]["expires"] = _expiration(5)  # 5 minutes from now
    logger.debug("Cookie expires: %s", cookie[name]["expires"])
    return tuple(cookie.output().split(": ", 1))
Example #2
0
    def _prepare_response(self, res_info, url):

        res = requests.Response()
        res.url = res_info.get('url', url)
        res.status_code = res_info.get('status_code', 200)
        res.reason_phrase = res_info.get('reason_phrase', REASON_PHRASES.get(res.status_code, 'UNKNOWN STATUS CODE'))

        if 'reason' in res_info:
            res.reason = res_info['reason']

        if 'headers' in res_info:
            res.headers.update(res_info['headers'])

            if 'Set-Cookie' in res_info['headers'] and 'cookies' not in res_info:
                cookies = SimpleCookie()
                for entry in res_info['headers']['Set-Cookie'].split(','):
                    cookies.load(str(entry))
                res.cookies.update(cookies)

        if 'cookies' in res_info:
            res.cookies.update(res_info['cookies'])

        res.raw = StreamContent(res_info.get('content', ''))

        return res
Example #3
0
    def build_wsgi_environ(self, method, path, body, content_type, cookies=None, **kw):

        # NOTE that in Pando (request.py make_franken_headers) only headers
        # beginning with ``HTTP`` are included in the request - and those are
        # changed to no longer include ``HTTP``. There are currently 2
        # exceptions to this: ``'CONTENT_TYPE'``, ``'CONTENT_LENGTH'`` which
        # are explicitly checked for.

        if isinstance(cookies, dict) and not isinstance(cookies, SimpleCookie):
            cookies, d = SimpleCookie(), cookies
            for k, v in d.items():
                cookies[str(k)] = str(v)

        typecheck(path, (bytes, text_type), method, text_type, content_type, bytes, body, bytes)
        environ = {}
        environ[b'CONTENT_TYPE'] = content_type
        if cookies is not None:
            environ[b'HTTP_COOKIE'] = cookies.output(header='', sep='; ')
        environ[b'HTTP_HOST'] = b'localhost'
        environ[b'PATH_INFO'] = path.encode('ascii') if type(path) != bytes else path
        environ[b'REMOTE_ADDR'] = b'0.0.0.0'
        environ[b'REQUEST_METHOD'] = method.encode('ascii')
        environ[b'SERVER_PROTOCOL'] = b'HTTP/1.1'
        environ[b'wsgi.input'] = BytesIO(body)
        environ[b'HTTP_CONTENT_LENGTH'] = str(len(body)).encode('ascii')
        environ.update((k.encode('ascii'), v) for k, v in kw.items())
        return environ
Example #4
0
def make_cookie(name, load, seed, expire=0, domain="", path="",
                timestamp=""):
    """
    Create and return a cookie

    :param name: Cookie name
    :param load: Cookie load
    :param seed: A seed for the HMAC function
    :param expire: Number of minutes before this cookie goes stale
    :param domain: The domain of the cookie
    :param path: The path specification for the cookie
    :return: A tuple to be added to headers
    """
    cookie = SimpleCookie()
    if not timestamp:
        timestamp = str(int(time.mktime(time.gmtime())))
    signature = cookie_signature(seed, load, timestamp)
    cookie[name] = "|".join([load, timestamp, signature])
    if path:
        cookie[name]["path"] = path
    if domain:
        cookie[name]["domain"] = domain
    if expire:
        cookie[name]["expires"] = _expiration(expire,
                                              "%a, %d-%b-%Y %H:%M:%S GMT")

    return tuple(cookie.output().split(": ", 1))
Example #5
0
def make_cookie(name, load, seed, expire=0, domain="", path="", timestamp=""):
    """
    Create and return a cookie

    :param name: Cookie name
    :param load: Cookie load
    :param seed: A seed for the HMAC function
    :param expire: Number of minutes before this cookie goes stale
    :param domain: The domain of the cookie
    :param path: The path specification for the cookie
    :return: A tuple to be added to headers
    """
    cookie = SimpleCookie()
    if not timestamp:
        timestamp = str(int(time.mktime(time.gmtime())))
    signature = cookie_signature(seed, load, timestamp)
    cookie[name] = "|".join([load, timestamp, signature])
    if path:
        cookie[name]["path"] = path
    if domain:
        cookie[name]["domain"] = domain
    if expire:
        cookie[name]["expires"] = _expiration(expire,
                                              "%a, %d-%b-%Y %H:%M:%S GMT")

    return tuple(cookie.output().split(": ", 1))
Example #6
0
def set_cookie(name, _, *args):
    cookie = SimpleCookie()
    cookie[name] = base64.b64encode(":".join(args))
    cookie[name]['path'] = "/"
    cookie[name]["expires"] = _expiration(5)  # 5 minutes from now
    logger.debug("Cookie expires: %s", cookie[name]["expires"])
    return tuple(cookie.output().split(": ", 1))
Example #7
0
def parse_cookie(name, seed, kaka):
    """Parses and verifies a cookie value

    :param seed: A seed used for the HMAC signature
    :param kaka: The cookie
    :return: A tuple consisting of (payload, timestamp)
    """
    if not kaka:
        return None

    cookie_obj = SimpleCookie(kaka)
    morsel = cookie_obj.get(name)

    if morsel:
        parts = morsel.value.split("|")
        if len(parts) != 3:
            return None
            # verify the cookie signature
        sig = cookie_signature(seed, parts[0], parts[1])
        if sig != parts[2]:
            raise SAMLError("Invalid cookie signature")

        try:
            return parts[0].strip(), parts[1]
        except KeyError:
            return None
    else:
        return None
Example #8
0
    def build_wsgi_environ(self,
                           method,
                           path,
                           body,
                           content_type,
                           cookies=None,
                           **kw):

        # NOTE that in Pando (request.py make_franken_headers) only headers
        # beginning with ``HTTP`` are included in the request - and those are
        # changed to no longer include ``HTTP``. There are currently 2
        # exceptions to this: ``'CONTENT_TYPE'``, ``'CONTENT_LENGTH'`` which
        # are explicitly checked for.

        if isinstance(cookies, dict) and not isinstance(cookies, SimpleCookie):
            cookies, d = SimpleCookie(), cookies
            for k, v in d.items():
                cookies[str(k)] = str(v)

        typecheck(path, (bytes, text_type), method, text_type, content_type,
                  bytes, body, bytes)
        environ = {}
        environ[b'CONTENT_TYPE'] = content_type
        if cookies is not None:
            environ[b'HTTP_COOKIE'] = cookies.output(header='', sep='; ')
        environ[b'HTTP_HOST'] = b'localhost'
        environ[b'PATH_INFO'] = path.encode(
            'ascii') if type(path) != bytes else path
        environ[b'REMOTE_ADDR'] = b'0.0.0.0'
        environ[b'REQUEST_METHOD'] = method.encode('ascii')
        environ[b'SERVER_PROTOCOL'] = b'HTTP/1.1'
        environ[b'wsgi.input'] = BytesIO(body)
        environ[b'HTTP_CONTENT_LENGTH'] = str(len(body)).encode('ascii')
        environ.update((k.encode('ascii'), v) for k, v in kw.items())
        return environ
Example #9
0
def cookie_parts(name, kaka):
    cookie_obj = SimpleCookie(kaka)
    morsel = cookie_obj.get(name)
    if morsel:
        return morsel.value.split("|")
    else:
        return None
Example #10
0
def extract_macaroons(headers):
    ''' Returns an array of any macaroons found in the given slice of cookies.
    @param headers: dict of headers
    @return: An array of array of mpy macaroons
    '''
    mss = []

    def add_macaroon(data):
        data = utils.b64decode(data)
        data_as_objs = json.loads(data.decode('utf-8'))
        ms = [utils.macaroon_from_dict(x) for x in data_as_objs]
        mss.append(ms)

    cookieHeader = headers.get('Cookie')
    if cookieHeader is not None:
        cs = SimpleCookie()
        # The cookie might be a unicode object, so convert it
        # to ASCII. This may cause an exception under Python 2.
        # TODO is that a problem?
        cs.load(str(cookieHeader))
        for c in cs:
            if c.startswith('macaroon-'):
                add_macaroon(cs[c].value)
    # Python doesn't make it easy to have multiple values for a
    # key, so split the header instead, which is necessary
    # for HTTP1.1 compatibility anyway.
    macaroonHeader = headers.get('Macaroons')
    if macaroonHeader is not None:
        for h in macaroonHeader.split(','):
            add_macaroon(h)
    return mss
Example #11
0
def cookie_parts(name, kaka):
    cookie_obj = SimpleCookie(kaka)
    morsel = cookie_obj.get(name)
    if morsel:
        return morsel.value.split("|")
    else:
        return None
Example #12
0
    def _load_session(self, environ):
        session = None

        try:
            # Attempt to load existing cookie from environ
            C = SimpleCookie(environ.get('HTTP_COOKIE'))
            morsel = C.get(self._cookie_key)
        except CookieError:
            morsel = None

        if morsel is not None:
            try:
                # Attempt to decrypt and decode
                session_data = self._crypto.decrypt(morsel.value.encode('ascii'), ttl=self._session_ttl)
                session = self._session_cls(self._serializer.decode(self._compressor[1](session_data)))
            except (InvalidToken, SessionSerializerException, zlib.error):
                # Start anew
                session = self._session_cls()
                # And ensure invalid cookie is overwritten
                session.save()

        if session is None:
            session = self._session_cls()

        return session
Example #13
0
        def visit(url, request):
            if request.headers.get('Accept') == 'application/json':
                return {'status_code': 200, 'content': {'agent': request.url}}
            cs = SimpleCookie()
            cookies = request.headers.get('Cookie')
            if cookies is not None:
                cs.load(str(cookies))
            public_key = None
            for c in cs:
                if c == 'agent-login':
                    json_cookie = json.loads(
                        base64.b64decode(cs[c].value).decode('utf-8'))
                    public_key = bakery.PublicKey.deserialize(
                        json_cookie.get('public_key'))
            ms = httpbakery.extract_macaroons(request.headers)
            if len(ms) == 0:
                b = bakery.Bakery(key=discharge_key)
                m = b.oven.macaroon(
                    version=bakery.LATEST_VERSION,
                    expiry=datetime.utcnow() + timedelta(days=1),
                    caveats=[
                        bakery.local_third_party_caveat(
                            public_key,
                            version=httpbakery.request_version(
                                request.headers))
                    ],
                    ops=[bakery.Op(entity='agent', action='login')])
                content, headers = httpbakery.discharge_required_response(
                    m, '/', 'test', 'message')
                resp = response(status_code=401,
                                content=content,
                                headers=headers)
                return request.hooks['response'][0](resp)

            return {'status_code': 200, 'content': {'agent-login': True}}
Example #14
0
    def _prepare_response(self, res_info, url):

        res = requests.Response()
        res.url = res_info.get('url', url)
        res.status_code = res_info.get('status_code', 200)
        res.reason_phrase = res_info.get(
            'reason_phrase',
            REASON_PHRASES.get(res.status_code, 'UNKNOWN STATUS CODE'))

        if 'reason' in res_info:
            res.reason = res_info['reason']

        if 'headers' in res_info:
            res.headers.update(res_info['headers'])

            if 'Set-Cookie' in res_info[
                    'headers'] and 'cookies' not in res_info:
                cookies = SimpleCookie()
                for entry in res_info['headers']['Set-Cookie'].split(','):
                    cookies.load(str(entry))
                res.cookies.update(cookies)

        if 'cookies' in res_info:
            res.cookies.update(res_info['cookies'])

        res.raw = StreamContent(res_info.get('content', ''))

        return res
Example #15
0
 def __init__(self):
     """
     Create a new Response defaulting to HTML content and "200 OK" status
     """
     self.status = "200 OK"
     self.headers = HeaderDict({"content-type": "text/html"})
     self.cookies = SimpleCookie()
class CookieHandler(object):

    def __init__(self, *args, **kw):
        # Somewhere to store cookies between consecutive requests
        self.cookies = SimpleCookie()
        super(CookieHandler, self).__init__(*args, **kw)

    def httpCookie(self, path):
        """Return self.cookies as an HTTP_COOKIE environment value."""
        l = [m.OutputString().split(';')[0] for m in self.cookies.values()
             if path.startswith(m['path'])]
        return '; '.join(l)

    def loadCookies(self, envstring):
        self.cookies.load(envstring)

    def saveCookies(self, response):
        """Save cookies from the response."""
        # Urgh - need to play with the response's privates to extract
        # cookies that have been set
        # TODO: extend the IHTTPRequest interface to allow access to all
        # cookies
        # TODO: handle cookie expirations
        for k, v in response._cookies.items():
            k = k.encode('utf8') if bytes is str else k
            val = v['value']
            val = val.encode('utf8') if bytes is str else val
            self.cookies[k] = val
            if 'path' in v:
                self.cookies[k]['path'] = v['path']
Example #17
0
    def __init__(self, d):
        """Takes headers as a dict, list, or bytestring.
        """
        if isinstance(d, bytes):
            from pando.exceptions import MalformedHeader

            def genheaders():
                for line in d.splitlines():
                    if b':' not in line:
                        # no colon separator in header
                        raise MalformedHeader(line)
                    k, v = line.split(b':', 1)
                    if k != k.strip():
                        # disallowed leading or trailing whitspace
                        # (per http://tools.ietf.org/html/rfc7230#section-3.2.4)
                        raise MalformedHeader(line)
                    yield k, v.strip()

            headers = genheaders()
        else:
            headers = d
        CaseInsensitiveMapping.__init__(self, headers)

        # Cookie
        # ======

        self.cookie = SimpleCookie()
        cookie = self.get(b'Cookie', b'')
        if PY3 and isinstance(cookie, bytes):
            cookie = cookie.decode('ascii', 'replace')
        try:
            self.cookie.load(cookie)
        except CookieError:
            pass  # XXX really?
Example #18
0
 def _wrap(response):
     cookie = SimpleCookie()
     cookie.load(response.headers['Set-Cookie'])
     for key, value in cookie.items():
         if key == ngw_env.pyramid.options['session.cookie.name']:
             return value.value
     return None
Example #19
0
def parse_cookie(name, seed, kaka):
    """Parses and verifies a cookie value

    :param seed: A seed used for the HMAC signature
    :param kaka: The cookie
    :return: A tuple consisting of (payload, timestamp)
    """
    if not kaka:
        return None

    cookie_obj = SimpleCookie(kaka)
    morsel = cookie_obj.get(name)

    if morsel:
        parts = morsel.value.split("|")
        if len(parts) != 3:
            return None
            # verify the cookie signature
        sig = cookie_signature(seed, parts[0], parts[1])
        if sig != parts[2]:
            raise SAMLError("Invalid cookie signature")

        try:
            return parts[0].strip(), parts[1]
        except KeyError:
            return None
    else:
        return None
Example #20
0
 def set_cookie(self, user):
     uid = rndstr(32)
     self.uid2user[uid] = user
     cookie = SimpleCookie()
     cookie[self.cookie_name] = uid
     cookie[self.cookie_name]["path"] = "/"
     cookie[self.cookie_name]["expires"] = _expiration(480)
     logger.debug("Cookie expires: %s", cookie[self.cookie_name]["expires"])
     return cookie.output().split(": ", 1)
Example #21
0
    def _assert_cookies_expired(self, http_headers):
        cookies_string = ";".join([c[1] for c in http_headers if c[0] == "Set-Cookie"])
        all_cookies = SimpleCookie()
        all_cookies.load(cookies_string)

        now = datetime.datetime.utcnow()  #
        for c in [self.provider.cookie_name, self.provider.session_cookie_name]:
            dt = datetime.datetime.strptime(all_cookies[c]["expires"], "%a, %d-%b-%Y %H:%M:%S GMT")
            assert dt < now  # make sure the cookies have expired to be cleared
Example #22
0
 def set_cookie(self, user):
     uid = rndstr(32)
     self.uid2user[uid] = user
     cookie = SimpleCookie()
     cookie[self.cookie_name] = uid
     cookie[self.cookie_name]['path'] = "/"
     cookie[self.cookie_name]["expires"] = _expiration(480)
     logger.debug("Cookie expires: %s", cookie[self.cookie_name]["expires"])
     return cookie.output().encode("UTF-8").split(": ", 1)
Example #23
0
    def cookies(self):
        if self._cookies is None:
            parser = SimpleCookie(self.headers("Cookie"))
            cookies = {}
            for morsel in parser.values():
                cookies[morsel.key] = morsel.value

            self._cookies = cookies

        return self._cookies.copy()
Example #24
0
    def _assert_cookies_expired(self, http_headers):
        cookies_string = ";".join(
            [c[1] for c in http_headers if c[0] == "Set-Cookie"])
        all_cookies = SimpleCookie()
        all_cookies.load(cookies_string)

        now = datetime.datetime.now()
        for c in [self.provider.cookie_name, self.provider.session_cookie_name]:
            dt = datetime.datetime.strptime(all_cookies[c]["expires"],
                                            "%a, %d-%b-%Y %H:%M:%S GMT")
            assert dt < now  # make sure the cookies have expired to be cleared
Example #25
0
 def cookies(self):
     cookies = SimpleCookie()
     cookie_header = self.environ.get("HTTP_COOKIE")
     if cookie_header:
         galaxy_cookies = "; ".join(x.strip() for x in cookie_header.split('; ') if x.startswith('galaxy'))
         if galaxy_cookies:
             try:
                 cookies.load(galaxy_cookies)
             except CookieError:
                 pass
     return cookies
    def store_cookies(self, response):
        headers = filter(lambda h: h[0].lower() == "set-cookie", response.getheaders())

        for header in headers:
            cookie = SimpleCookie(header[1])
            for morsel in cookie.values():
                if morsel.key not in self._keys:
                    self._keys.append(morsel.key)
                self._content[morsel.key] = morsel.value
                logger.debug("--> Set cookie %s: %s" % (morsel.key, morsel.value))

        logger.debug("CookieJar contents: %s\n%s" % (self._keys, self._content))
Example #27
0
 def fromRawString(raw):
     try:
         sc = SimpleCookie(raw)
         for key, cookie in sc.items():
             try:
                 return Cookie(key, cookie['domain'], cookie['path'], raw,
                               cookie['expires'], True)
             except:
                 return None
     except CookieError as e:
         print(e)
         return None
Example #28
0
 def _process_cookies(self, headers):
     """
     Process and store cookies from response headers
     :param headers:
     :return:
     """
     cdata = headers.get("Set-Cookie")
     if not cdata:
         return
     if not self.cookies:
         self.cookies = SimpleCookie()
     self.cookies.load(cdata)
Example #29
0
    def _handle_cookies(self, response):
        # type: (httplib.HTTPResponse) -> None
        """
		Parse cookies from |HTTP| response and store for next request.

		:param httplib.HTTPResponse: The |HTTP| response.
		"""
        # FIXME: this cookie handling doesn't respect path, domain and expiry
        cookies = SimpleCookie()
        cookies.load(response.getheader('set-cookie', ''))
        self.cookies.update(
            dict((cookie.key, cookie.value) for cookie in cookies.values()))
 def test_session_is_regularly_refreshed(self):
     alice = self.make_participant('alice')
     alice.authenticated = True
     alice.sign_in(SimpleCookie())
     cookies = SimpleCookie()
     alice.keep_signed_in(cookies)
     assert SESSION not in cookies
     cookies = SimpleCookie()
     expires = alice.session_expires
     alice.set_session_expires(expires - SESSION_REFRESH)
     alice.keep_signed_in(cookies)
     assert SESSION in cookies
Example #31
0
    def cookies(self):
        if self._cookies is None:
            # NOTE(tbug): We might want to look into parsing
            # cookies ourselves. The SimpleCookie is doing a
            # lot if stuff only required to SEND cookies.
            parser = SimpleCookie(self.get_header("Cookie"))
            cookies = {}
            for morsel in parser.values():
                cookies[morsel.key] = morsel.value

            self._cookies = cookies

        return self._cookies.copy()
Example #32
0
    def cookies(self):
        if self._cookies is None:
            # NOTE(tbug): We might want to look into parsing
            # cookies ourselves. The SimpleCookie is doing a
            # lot if stuff only required to SEND cookies.
            parser = SimpleCookie(self.get_header("Cookie"))
            cookies = {}
            for morsel in parser.values():
                cookies[morsel.key] = morsel.value

            self._cookies = cookies

        return self._cookies.copy()
Example #33
0
def delete_cookie(environ, name):
    kaka = environ.get("HTTP_COOKIE", "")
    logger.debug("delete KAKA: %s", kaka)
    if kaka:
        cookie_obj = SimpleCookie(kaka)
        morsel = cookie_obj.get(name, None)
        cookie = SimpleCookie()
        cookie[name] = ""
        cookie[name]["path"] = "/"
        logger.debug("Expire: %s", morsel)
        cookie[name]["expires"] = _expiration("dawn")
        return tuple(cookie.output().split(": ", 1))
    return None
Example #34
0
class BaseHeaders(CaseInsensitiveMapping):
    """Represent the headers in an HTTP Request or Response message.

    `How to send non-English unicode string using HTTP header?
    <http://stackoverflow.com/q/5423223/>`_ and
    `What character encoding should I use for a HTTP header?
    <http://stackoverflow.com/q/4400678/>`_
    have good notes on why we do everything as pure bytes here.
    """
    def __init__(self, headers=()):
        """Takes headers as a dict, or list of items.
        """
        CaseInsensitiveMapping.__init__(self, headers)

        # Cookie
        # ======

        self.cookie = SimpleCookie()
        cookie = self.get(b'Cookie', b'')
        if PY3 and isinstance(cookie, bytes):
            cookie = cookie.decode('ascii', 'replace')
        try:
            self.cookie.load(cookie)
        except CookieError:
            pass  # XXX really?

    def __setitem__(self, name, value):
        """Checks for CRLF in ``value``, then calls the superclass method:

        .. automethod:: pando.http.mapping.CaseInsensitiveMapping.__setitem__
        """
        _check_for_CRLF(value)
        super(BaseHeaders, self).__setitem__(name, value)

    def add(self, name, value):
        """Checks for CRLF in ``value``, then calls the superclass method:

        .. automethod:: pando.http.mapping.CaseInsensitiveMapping.add
        """
        _check_for_CRLF(value)
        super(BaseHeaders, self).add(name, value)

    @property
    def raw(self):
        """Return the headers as a bytestring, formatted for an HTTP message.
        """
        out = []
        for header, values in sorted(self.items()):
            for value in values:
                out.append(header + b': ' + value)
        return b'\r\n'.join(out)
Example #35
0
def info_from_cookie(kaka):
    logger.debug("KAKA: %s", kaka)
    if kaka:
        cookie_obj = SimpleCookie(kaka)
        morsel = cookie_obj.get("idpauthn", None)
        if morsel:
            try:
                key, ref = base64.b64decode(morsel.value).split(":")
                return IDP.cache.uid2user[key], ref
            except (KeyError, TypeError):
                return None, None
        else:
            logger.debug("No idpauthn cookie")
    return None, None
Example #36
0
def info_from_cookie(kaka):
    logger.debug("KAKA: %s", kaka)
    if kaka:
        cookie_obj = SimpleCookie(kaka)
        morsel = cookie_obj.get("idpauthn", None)
        if morsel:
            try:
                key, ref = base64.b64decode(morsel.value).split(":")
                return IDP.cache.uid2user[key], ref
            except (KeyError, TypeError):
                return None, None
        else:
            logger.debug("No idpauthn cookie")
    return None, None
Example #37
0
 def delete_cookie(self, environ):
     cookie = environ.get("HTTP_COOKIE", '')
     logger.debug("delete cookie: %s", cookie)
     if cookie:
         _name = self.cookie_name
         cookie_obj = SimpleCookie(cookie)
         morsel = cookie_obj.get(_name, None)
         cookie = SimpleCookie()
         cookie[_name] = ""
         cookie[_name]['path'] = "/"
         logger.debug("Expire: %s", morsel)
         cookie[_name]["expires"] = _expiration("now")
         return cookie.output().split(": ", 1)
     return None
Example #38
0
    def __init__(self):
        self.status = None
        self.header_list = None
        self._body = []
        self.time = time.time()

        self.headers = httputil.HeaderMap()
        # Since we know all our keys are titled strings, we can
        # bypass HeaderMap.update and get a big speed boost.
        dict.update(self.headers, {
            'Content-Type': 'text/html',
            'Server': 'CherryPy/' + cherrypy.__version__,
            'Date': httputil.HTTPDate(self.time),
        })
        self.cookie = SimpleCookie()
Example #39
0
    def get_user(self, environ):
        cookie = environ.get("HTTP_COOKIE", '')
        logger.debug("Cookie: %s", cookie)
        if cookie:
            cookie_obj = SimpleCookie(cookie)
            morsel = cookie_obj.get(self.cookie_name, None)
            if morsel:
                try:
                    return self.uid2user[morsel.value]
                except KeyError:
                    return None
            else:
                logger.debug("No %s cookie", self.cookie_name)

        return None
Example #40
0
    def get_user(self, environ):
        cookie = environ.get("HTTP_COOKIE", "")
        logger.debug("Cookie: %s", cookie)
        if cookie:
            cookie_obj = SimpleCookie(cookie)
            morsel = cookie_obj.get(self.cookie_name, None)
            if morsel:
                try:
                    return self.uid2user[morsel.value]
                except KeyError:
                    return None
            else:
                logger.debug("No %s cookie", self.cookie_name)

        return None
Example #41
0
    def http_request(self, url, method="GET", **kwargs):
        _kwargs = copy.copy(self.request_args)
        if kwargs:
            _kwargs.update(kwargs)

        if self.cookiejar:
            _kwargs["cookies"] = self._cookies()
            logger.debug("SENT COOKIEs: %s" % (_kwargs["cookies"], ))

        try:
            r = requests.request(method, url, **_kwargs)
        except Exception as err:
            logger.error(
                "http_request failed: %s, url: %s, htargs: %s, method: %s" %
                (err, url, _kwargs, method))
            raise

        try:
            _cookie = r.headers["set-cookie"]
            # Telekom fix
            # set_cookie = set_cookie.replace(
            # "=;Path=/;Expires=Thu, 01-Jan-1970 00:00:01 GMT;HttpOnly,", "")
            logger.debug("RECEIVED COOKIEs: %s" % _cookie)
            try:
                set_cookie(self.cookiejar, SimpleCookie(_cookie))
            except CookieError as err:
                logger.error(err)
                raise NonFatalException(r, "{}".format(err))
        except (AttributeError, KeyError) as err:
            pass

        return r
Example #42
0
 def __init__( self ):
     """
     Create a new Response defaulting to HTML content and "200 OK" status
     """
     self.status = "200 OK"
     self.headers = HeaderDict( { "content-type": "text/html" } )
     self.cookies = SimpleCookie()
 def test_log_in_with_old_session(self):
     alice = self.make_participant('alice')
     self.db.run("UPDATE user_secrets SET mtime = mtime - interval '1 day'")
     alice.authenticated = True
     cookies = SimpleCookie()
     alice.sign_in(cookies)
     self.check_with_about_me('alice', cookies)
Example #44
0
    def __init__(self, d):
        """Takes headers as a dict, list, or bytestring.
        """
        if isinstance(d, bytes):
            from pando.exceptions import MalformedHeader

            def genheaders():
                for line in d.splitlines():
                    if b':' not in line:
                        # no colon separator in header
                        raise MalformedHeader(line)
                    k, v = line.split(b':', 1)
                    if k != k.strip():
                        # disallowed leading or trailing whitspace
                        # (per http://tools.ietf.org/html/rfc7230#section-3.2.4)
                        raise MalformedHeader(line)
                    yield k, v.strip()

            headers = genheaders()
        else:
            headers = d
        CaseInsensitiveMapping.__init__(self, headers)

        # Cookie
        # ======

        self.cookie = SimpleCookie()
        cookie = self.get(b'Cookie', b'')
        if PY3 and isinstance(cookie, bytes):
            cookie = cookie.decode('ascii', 'replace')
        try:
            self.cookie.load(cookie)
        except CookieError:
            pass  # XXX really?
Example #45
0
    def __init__(self, headers=()):
        """Takes headers as a dict, or list of items.
        """
        CaseInsensitiveMapping.__init__(self, headers)

        # Cookie
        # ======

        self.cookie = SimpleCookie()
        cookie = self.get(b'Cookie', b'')
        if PY3 and isinstance(cookie, bytes):
            cookie = cookie.decode('ascii', 'replace')
        try:
            self.cookie.load(cookie)
        except CookieError:
            pass  # XXX really?
Example #46
0
def set_cookie(name, _, *args):
    cookie = SimpleCookie()

    data = ":".join(args)
    if not isinstance(data, six.binary_type):
        data = data.encode("ascii")

    data64 = base64.b64encode(data)
    if not isinstance(data64, six.string_types):
        data64 = data64.decode("ascii")

    cookie[name] = data64
    cookie[name]["path"] = "/"
    cookie[name]["expires"] = _expiration(5)  # 5 minutes from now
    logger.debug("Cookie expires: %s", cookie[name]["expires"])
    return tuple(cookie.output().split(": ", 1))
Example #47
0
def quiet_rewrite(self, cookie_str, header='Set-Cookie'):
   # begin Perma customization
    from pywb.rewrite.cookie_rewriter import six
    from six.moves.http_cookies import SimpleCookie, CookieError
    import logging
    # end Perma customization

    results = []
    cookie_str = self.REMOVE_EXPIRES.sub('', cookie_str)
    try:
        cookie = SimpleCookie(cookie_str)
    except CookieError as e:
        # begin Perma customization
        logger = logging.getLogger(__name__)
        logger.info(e, exc_info=True)
        # end Perma customization
        return results

    for name, morsel in six.iteritems(cookie):
        morsel = self.rewrite_cookie(name, morsel)

        self._filter_morsel(morsel)

        if not self.add_prefix_cookie_for_all_mods(morsel, results, header):
            value = morsel.OutputString()
            results.append((header, value))

    return results
Example #48
0
def info_from_cookie(kaka):
    logger.debug("KAKA: %s", kaka)
    if kaka:
        cookie_obj = SimpleCookie(kaka)
        morsel = cookie_obj.get("idpauthn", None)
        if morsel:
            try:
                data = base64.b64decode(morsel.value)
                if not isinstance(data, six.string_types):
                    data = data.decode("ascii")
                key, ref = data.split(":", 1)
                return IDP.cache.uid2user[key], ref
            except (KeyError, TypeError):
                return None, None
        else:
            logger.debug("No idpauthn cookie")
    return None, None
def extract_macaroons(headers_or_request):
    ''' Returns an array of any macaroons found in the given slice of cookies.
    If the argument implements a get_header method, that will be used
    instead of the get method to retrieve headers.
    @param headers_or_request: dict of headers or a
    urllib.request.Request-like object.
    @return: A list of list of mpy macaroons
    '''
    def get_header(key, default=None):
        try:
            return headers_or_request.get_header(key, default)
        except AttributeError:
            return headers_or_request.get(key, default)

    mss = []

    def add_macaroon(data):
        try:
            data = utils.b64decode(data)
            data_as_objs = json.loads(data.decode('utf-8'))
        except ValueError:
            return
        ms = [utils.macaroon_from_dict(x) for x in data_as_objs]
        mss.append(ms)

    cookie_header = get_header('Cookie')
    if cookie_header is not None:
        cs = SimpleCookie()
        # The cookie might be a unicode object, so convert it
        # to ASCII. This may cause an exception under Python 2.
        # TODO is that a problem?
        cs.load(str(cookie_header))
        for c in cs:
            if c.startswith('macaroon-'):
                add_macaroon(cs[c].value)
    # Python doesn't make it easy to have multiple values for a
    # key, so split the header instead, which is necessary
    # for HTTP1.1 compatibility anyway (see RFC 7230, section 3.2.2)
    macaroon_header = get_header('Macaroons')
    if macaroon_header is not None:
        for h in macaroon_header.split(','):
            add_macaroon(h)
    return mss
Example #50
0
    def _prepare_response(self, res_info, url):

        res = requests.Response()
        res.url = res_info.get("url", url)
        res.status_code = res_info.get("status_code", 200)
        res.reason_phrase = res_info.get("reason_phrase", REASON_PHRASES.get(res.status_code, "UNKNOWN STATUS CODE"))

        if "reason" in res_info:
            res.reason = res_info["reason"]

        if "headers" in res_info:
            res.headers.update(res_info["headers"])

            if "Set-Cookie" in res_info["headers"]:
                cookies = SimpleCookie()
                for entry in res_info["headers"]["Set-Cookie"].split(","):
                    cookies.load(str(entry))
                res.cookies.update(cookies)

        res.raw = StreamContent(res_info.get("content", ""))

        return res
Example #51
0
class Response(object):
    """
    Describes an HTTP response. Currently very simple since the actual body
    of the request is handled separately.
    """

    def __init__(self):
        """
        Create a new Response defaulting to HTML content and "200 OK" status
        """
        self.status = "200 OK"
        self.headers = HeaderDict({"content-type": "text/html"})
        self.cookies = SimpleCookie()

    def set_content_type(self, type_):
        """
        Sets the Content-Type header
        """
        self.headers["content-type"] = type_

    def get_content_type(self):
        return self.headers.get("content-type", None)

    def send_redirect(self, url):
        """
        Send an HTTP redirect response to (target `url`)
        """
        if "\n" in url or "\r" in url:
            raise webob.exc.HTTPInternalServerError("Invalid redirect URL encountered.")
        raise webob.exc.HTTPFound(location=url)

    def wsgi_headeritems(self):
        """
        Return headers in format appropriate for WSGI `start_response`
        """
        result = self.headers.headeritems()
        # Add cookie to header
        for name, crumb in self.cookies.items():
            header, value = str(crumb).split(': ', 1)
            result.append((header, value))
        return result

    def wsgi_status(self):
        """
        Return status line in format appropriate for WSGI `start_response`
        """
        if isinstance(self.status, int):
            exception = webob.exc.status_map.get(self.status)
            return "%d %s" % (exception.code, exception.title)
        else:
            return self.status
Example #52
0
class StatefulClient(Client):
    """This is a Client subclass that keeps cookies between calls."""

    def __init__(self, *a, **kw):
        super(StatefulClient, self).__init__(*a, **kw)
        self.cookie = SimpleCookie()

    def __enter__(self):
        return self

    def __exit__(self, *a):
        self.cookie.clear()

    def hit(self, *a, **kw):
        cookies = kw.pop('cookies', None)
        if cookies:
            cookie = SimpleCookie()
            # session cookies first
            cookie.update(self.cookie)
            # request cookies second
            if isinstance(cookies, SimpleCookie):
                cookie.update(cookies)
            else:
                for k, v in cookies.items():
                    cookie[str(k)] = str(v)
        else:
            cookie = self.cookie
        kw['cookies'] = cookie

        want = kw.pop('want', 'response')
        kw['want'] = 'state'
        state = super(StatefulClient, self).hit(*a, **kw)
        r = state.get('response')
        if isinstance(r, Response) and r.headers.cookie:
            self.cookie.update(r.headers.cookie)

        return self.resolve_want(state, want)
Example #53
0
    def __init__(self):
        self.status = None
        self.header_list = None
        self._body = []
        self.time = time.time()

        self.headers = httputil.HeaderMap()
        # Since we know all our keys are titled strings, we can
        # bypass HeaderMap.update and get a big speed boost.
        dict.update(self.headers, {
            'Content-Type': 'text/html',
            'Server': 'CherryPy/' + cherrypy.__version__,
            'Date': httputil.HTTPDate(self.time),
        })
        self.cookie = SimpleCookie()
Example #54
0
    def hit(self, *a, **kw):
        cookies = kw.pop('cookies', None)
        if cookies:
            cookie = SimpleCookie()
            # session cookies first
            cookie.update(self.cookie)
            # request cookies second
            if isinstance(cookies, SimpleCookie):
                cookie.update(cookies)
            else:
                for k, v in cookies.items():
                    cookie[str(k)] = str(v)
        else:
            cookie = self.cookie
        kw['cookies'] = cookie

        want = kw.pop('want', 'response')
        kw['want'] = 'state'
        state = super(StatefulClient, self).hit(*a, **kw)
        r = state.get('response')
        if isinstance(r, Response) and r.headers.cookie:
            self.cookie.update(r.headers.cookie)

        return self.resolve_want(state, want)
Example #55
0
 def _websocket_auth(self, queue_events_data: Dict[str, Dict[str, str]],
                     cookies: SimpleCookie) -> Generator[str, str, None]:
     message = {
         "req_id": self._get_request_id(),
         "type": "auth",
         "request": {
             "csrf_token": cookies.get(settings.CSRF_COOKIE_NAME),
             "queue_id": queue_events_data['queue_id'],
             "status_inquiries": []
         }
     }
     auth_frame_str = ujson.dumps(message)
     self.ws.write_message(ujson.dumps([auth_frame_str]))
     response_ack = yield self.ws.read_message()
     response_message = yield self.ws.read_message()
     raise gen.Return([response_ack, response_message])
Example #56
0
class BaseHeaders(CaseInsensitiveMapping):
    """Represent the headers in an HTTP Request or Response message.

    `How to send non-English unicode string using HTTP header?
    <http://stackoverflow.com/q/5423223/>`_ and
    `What character encoding should I use for a HTTP header?
    <http://stackoverflow.com/q/4400678/>`_
    have good notes on why we do everything as pure bytes here.
    """

    def __init__(self, d):
        """Takes headers as a dict, list, or bytestring.
        """
        if isinstance(d, bytes):
            from pando.exceptions import MalformedHeader

            def genheaders():
                for line in d.splitlines():
                    if b':' not in line:
                        # no colon separator in header
                        raise MalformedHeader(line)
                    k, v = line.split(b':', 1)
                    if k != k.strip():
                        # disallowed leading or trailing whitspace
                        # (per http://tools.ietf.org/html/rfc7230#section-3.2.4)
                        raise MalformedHeader(line)
                    yield k, v.strip()

            headers = genheaders()
        else:
            headers = d
        CaseInsensitiveMapping.__init__(self, headers)

        # Cookie
        # ======

        self.cookie = SimpleCookie()
        cookie = self.get(b'Cookie', b'')
        if PY3 and isinstance(cookie, bytes):
            cookie = cookie.decode('ascii', 'replace')
        try:
            self.cookie.load(cookie)
        except CookieError:
            pass  # XXX really?

    def __setitem__(self, name, value):
        """Checks for CRLF in ``value``, then calls the superclass method:

        .. automethod:: pando.http.mapping.CaseInsensitiveMapping.__setitem__
        """
        _check_for_CRLF(value)
        super(BaseHeaders, self).__setitem__(name, value)

    def add(self, name, value):
        """Checks for CRLF in ``value``, then calls the superclass method:

        .. automethod:: pando.http.mapping.CaseInsensitiveMapping.add
        """
        _check_for_CRLF(value)
        super(BaseHeaders, self).add(name, value)

    @property
    def raw(self):
        """Return the headers as a bytestring, formatted for an HTTP message.
        """
        out = []
        for header, values in sorted(self.items()):
            for value in values:
                out.append(header + b': ' + value)
        return b'\r\n'.join(out)
Example #57
0
    def set_cookie(self, name, value, expires=None, max_age=None,
                   domain=None, path=None, secure=True, http_only=True):
        """Set a response cookie.

        Note:
            This method can be called multiple times to add one or
            more cookies to the response.

        See Also:
            To learn more about setting cookies, see
            :ref:`Setting Cookies <setting-cookies>`. The parameters listed
            below correspond to those defined in `RFC 6265`_.

        Args:
            name (str):
                Cookie name
            value (str):
                Cookie value
            expires (datetime): Specifies when the cookie should expire. By
                default, cookies expire when the user agent exits.
            max_age (int): Defines the lifetime of the cookie in seconds.
                After the specified number of seconds elapse, the client
                should discard the cookie.
            domain (str): Specifies the domain for which the cookie is valid.
                An explicitly specified domain must always start with a dot.
                A value of 0 means the cookie should be discarded immediately.
            path (str): Specifies the subset of URLs to
                which this cookie applies.
            secure (bool): Direct the client to use only secure means to
                contact the origin server whenever it sends back this cookie
                (default: ``True``). Warning: You will also need to enforce
                HTTPS for the cookies to be transfered securely.
            http_only (bool): Direct the client to only transfer the cookie
                with unscripted HTTP requests (default: ``True``). This is
                intended to mitigate some forms of cross-site scripting.

        Raises:
            KeyError: `name` is not a valid cookie name.
            ValueError: `value` is not a valid cookie value.

        .. _RFC 6265:
            http://tools.ietf.org/html/rfc6265

        """

        if not is_ascii_encodable(name):
            raise KeyError('"name" is not ascii encodable')
        if not is_ascii_encodable(value):
            raise ValueError('"value" is not ascii encodable')

        if PY2:  # pragma: no cover
            name = str(name)
            value = str(value)

        if self._cookies is None:
            self._cookies = SimpleCookie()

        try:
            self._cookies[name] = value
        except CookieError as e:  # pragma: no cover
            # NOTE(tbug): we raise a KeyError here, to avoid leaking
            # the CookieError to the user. SimpleCookie (well, BaseCookie)
            # only throws CookieError on issues with the cookie key
            raise KeyError(str(e))

        if expires:
            # set Expires on cookie. Format is Wdy, DD Mon YYYY HH:MM:SS GMT

            # NOTE(tbug): we never actually need to
            # know that GMT is named GMT when formatting cookies.
            # It is a function call less to just write "GMT" in the fmt string:
            fmt = "%a, %d %b %Y %H:%M:%S GMT"
            if expires.tzinfo is None:
                # naive
                self._cookies[name]["expires"] = expires.strftime(fmt)
            else:
                # aware
                gmt_expires = expires.astimezone(GMT_TIMEZONE)
                self._cookies[name]["expires"] = gmt_expires.strftime(fmt)

        if max_age:
            self._cookies[name]["max-age"] = max_age

        if domain:
            self._cookies[name]["domain"] = domain

        if path:
            self._cookies[name]["path"] = path

        if secure:
            self._cookies[name]["secure"] = secure

        if http_only:
            self._cookies[name]["httponly"] = http_only
Example #58
0
def _generate_proxy(current_path, value):
    where = value.split(':', 1)[1]
    cookie_name, routes = where.split(':', 1)
    routes = dict([ route.strip().split('=', 1) for route in routes.split(',') if route.strip() ])

    # cookie_name = 'weblabsessionid'
    # routes = {
    #    'route1' : 'http://localhost:10000/weblab/json/',
    #    'route2' : 'http://localhost:10001/weblab/json/',
    #    'route3' : 'http://localhost:10002/weblab/json/',
    # }

    current_cookie_value = request.cookies.get(cookie_name, '')

    chosen_url = None
    for route in routes:
        if current_cookie_value.endswith(route):
            chosen_url = routes[route]
            break

    if chosen_url is None:
        chosen_url = random.choice(list(routes.values()))

    headers = dict(request.headers)
    headers['X-Forwarded-For'] = request.remote_addr
    headers['X-Forwarded-Host'] = request.host
    headers.pop('Host', None)
    headers.pop('host', None)

    kwargs = dict(headers = headers, cookies = dict(request.cookies), allow_redirects=False)

    if request.method == 'GET':
        method = requests.get
    elif request.method == 'POST':
        kwargs['data'] = request.data

        if request.files:
            kwargs['files'] = {}
            for f, f_contents in six.iteritems(request.files):
                kwargs['files'][f] = [f_contents.filename, f_contents.stream, f_contents.content_type, f_contents.headers]

        if request.form:
            headers.pop('Content-Type', None)
            kwargs['data'] = request.form

        method = requests.post
    else:
        raise Exception("Method not supported")

    MAX_RETRIES = 5
    retry = 0
    full_url = chosen_url + current_path
    if request.args:
        full_url += '?' + '&'.join([ '%s=%s' % (key, requests.utils.quote(value, '')) for key, value in request.args.items() ])

    while True:
        try:
            req = method(full_url, **kwargs)
            break
        except requests.ConnectionError:
            if request.method != 'GET':
                raise
            retry += 1
            if retry >= MAX_RETRIES:
                raise
            time.sleep(0.5)

    cookies = list(req.cookies)
   
    headers = dict(req.headers)
    headers.pop('set-cookie', None)
    response_kwargs = {
        'headers' : headers,
        'status' : req.status_code,
    }
    if 'content-type' in req.headers:
        response_kwargs['content_type'] = req.headers['content-type']
    
    response = Response(req.content, **response_kwargs)
    
    existing_cookies = SimpleCookie()
    for header in response.headers:
        if header[0].lower() == 'set-cookie':
            try:
                if six.PY2:
                    cookie_header = header[1].encode('utf8')
                else:
                    cookie_header = header[1]
                existing_cookies.load(cookie_header)
            except Exception as e:
                print("Error processing cookie header: {}".format(cookie_header))
                import traceback
                traceback.print_exc()

    for c in req.cookies:
        if c.name not in existing_cookies.keys():
            response.set_cookie(c.name, c.value, path=c.path, expires=c.expires, secure=c.secure)

    return response
Example #59
0
class Response(object):
    """Represents an HTTP response to a client request.

    Note:
        `Response` is not meant to be instantiated directly by responders.

    Attributes:
        status (str): HTTP status line (e.g., '200 OK'). Falcon requires the
            full status line, not just the code (e.g., 200). This design
            makes the framework more efficient because it does not have to
            do any kind of conversion or lookup when composing the WSGI
            response.

            If not set explicitly, the status defaults to '200 OK'.

            Note:
                Falcon provides a number of constants for common status
                codes. They all start with the ``HTTP_`` prefix, as in:
                ``falcon.HTTP_204``.

        body (str or unicode): String representing response content. If
            Unicode, Falcon will encode as UTF-8 in the response. If
            data is already a byte string, use the data attribute
            instead (it's faster).
        body_encoded (bytes): Returns a UTF-8 encoded version of `body`.
        data (bytes): Byte string representing response content.

            Use this attribute in lieu of `body` when your content is
            already a byte string (``str`` or ``bytes`` in Python 2, or
            simply ``bytes`` in Python 3). See also the note below.

            Note:
                Under Python 2.x, if your content is of type ``str``, using
                the `data` attribute instead of `body` is the most
                efficient approach. However, if
                your text is of type ``unicode``, you will need to use the
                `body` attribute instead.

                Under Python 3.x, on the other hand, the 2.x ``str`` type can
                be thought of as
                having been replaced by what was once the ``unicode`` type,
                and so you will need to always use the `body` attribute for
                strings to
                ensure Unicode characters are properly encoded in the
                HTTP response.

        stream: Either a file-like object with a `read()` method that takes
            an optional size argument and returns a block of bytes, or an
            iterable object, representing response content, and yielding
            blocks as byte strings. Falcon will use *wsgi.file_wrapper*, if
            provided by the WSGI server, in order to efficiently serve
            file-like objects.

        stream_len (int): Expected length of `stream` (e.g., file size).
    """

    __slots__ = (
        '_body',  # Stuff
        '_body_encoded',  # Stuff
        'data',
        '_headers',
        '_cookies',
        'status',
        'stream',
        'stream_len'
    )

    def __init__(self):
        self.status = '200 OK'
        self._headers = {}

        # NOTE(tbug): will be set to a SimpleCookie object
        # when cookie is set via set_cookie
        self._cookies = None

        self._body = None
        self._body_encoded = None
        self.data = None
        self.stream = None
        self.stream_len = None

    def _get_body(self):
        return self._body

    def _set_body(self, value):
        self._body = value
        self._body_encoded = None

    # NOTE(flaper87): Lets use a property
    # for the body in case its content was
    # encoded and then modified.
    body = property(_get_body, _set_body)

    @property
    def body_encoded(self):
        # NOTE(flaper87): Notice this property
        # is not thread-safe. If body is modified
        # before this property returns, we might
        # end up returning None.
        body = self._body
        if body and self._body_encoded is None:

            # NOTE(flaper87): Assume it is an
            # encoded str, then check and encode
            # if it isn't.
            self._body_encoded = body
            if isinstance(body, TEXT_TYPE):
                self._body_encoded = body.encode('utf-8')

        return self._body_encoded

    def set_stream(self, stream, stream_len):
        """Convenience method for setting both `stream` and `stream_len`.

        Although the `stream` and `stream_len` properties may be set
        directly, using this method ensures `stream_len` is not
        accidentally neglected.

        """

        self.stream = stream
        self.stream_len = stream_len

    def set_cookie(self, name, value, expires=None, max_age=None,
                   domain=None, path=None, secure=True, http_only=True):
        """Set a response cookie.

        Note:
            This method can be called multiple times to add one or
            more cookies to the response.

        See Also:
            To learn more about setting cookies, see
            :ref:`Setting Cookies <setting-cookies>`. The parameters listed
            below correspond to those defined in `RFC 6265`_.

        Args:
            name (str):
                Cookie name
            value (str):
                Cookie value
            expires (datetime): Specifies when the cookie should expire. By
                default, cookies expire when the user agent exits.
            max_age (int): Defines the lifetime of the cookie in seconds.
                After the specified number of seconds elapse, the client
                should discard the cookie.
            domain (str): Specifies the domain for which the cookie is valid.
                An explicitly specified domain must always start with a dot.
                A value of 0 means the cookie should be discarded immediately.
            path (str): Specifies the subset of URLs to
                which this cookie applies.
            secure (bool): Direct the client to use only secure means to
                contact the origin server whenever it sends back this cookie
                (default: ``True``). Warning: You will also need to enforce
                HTTPS for the cookies to be transfered securely.
            http_only (bool): Direct the client to only transfer the cookie
                with unscripted HTTP requests (default: ``True``). This is
                intended to mitigate some forms of cross-site scripting.

        Raises:
            KeyError: `name` is not a valid cookie name.
            ValueError: `value` is not a valid cookie value.

        .. _RFC 6265:
            http://tools.ietf.org/html/rfc6265

        """

        if not is_ascii_encodable(name):
            raise KeyError('"name" is not ascii encodable')
        if not is_ascii_encodable(value):
            raise ValueError('"value" is not ascii encodable')

        if PY2:  # pragma: no cover
            name = str(name)
            value = str(value)

        if self._cookies is None:
            self._cookies = SimpleCookie()

        try:
            self._cookies[name] = value
        except CookieError as e:  # pragma: no cover
            # NOTE(tbug): we raise a KeyError here, to avoid leaking
            # the CookieError to the user. SimpleCookie (well, BaseCookie)
            # only throws CookieError on issues with the cookie key
            raise KeyError(str(e))

        if expires:
            # set Expires on cookie. Format is Wdy, DD Mon YYYY HH:MM:SS GMT

            # NOTE(tbug): we never actually need to
            # know that GMT is named GMT when formatting cookies.
            # It is a function call less to just write "GMT" in the fmt string:
            fmt = "%a, %d %b %Y %H:%M:%S GMT"
            if expires.tzinfo is None:
                # naive
                self._cookies[name]["expires"] = expires.strftime(fmt)
            else:
                # aware
                gmt_expires = expires.astimezone(GMT_TIMEZONE)
                self._cookies[name]["expires"] = gmt_expires.strftime(fmt)

        if max_age:
            self._cookies[name]["max-age"] = max_age

        if domain:
            self._cookies[name]["domain"] = domain

        if path:
            self._cookies[name]["path"] = path

        if secure:
            self._cookies[name]["secure"] = secure

        if http_only:
            self._cookies[name]["httponly"] = http_only

    def unset_cookie(self, name):
        """Unset a cookie in the response."""
        if self._cookies is not None and name in self._cookies:
            del self._cookies[name]

    def get_header(self, name):
        """Retrieve the raw string value for the given header.

        Args:
            name (str): Header name, case-insensitive. Must be of type ``str``
                or ``StringType``, and only character values 0x00 through 0xFF
                may be used on platforms that use wide characters.

        Returns:
            str: The header's value if set, otherwise ``None``.
        """
        return self._headers.get(name.lower(), None)

    def set_header(self, name, value):
        """Set a header for this response to a given value.

        Warning:
            Calling this method overwrites the existing value, if any.

        Warning:
            For setting cookies, see instead :meth:`~.set_cookie`

        Args:
            name (str): Header name to set (case-insensitive). Must be of
                type ``str`` or ``StringType``, and only character values 0x00
                through 0xFF may be used on platforms that use wide
                characters.
            value (str): Value for the header. Must be of type ``str`` or
                ``StringType``, and only character values 0x00 through 0xFF
                may be used on platforms that use wide characters.

        """

        # NOTE(kgriffs): normalize name by lowercasing it
        self._headers[name.lower()] = value

    def append_header(self, name, value):
        """Set or append a header for this response.

        Warning:
            If the header already exists, the new value will be appended
            to it, delimited by a comma. Most header specifications support
            this format, Cookie and Set-Cookie being the notable exceptions.

        Warning:
            For setting cookies, see :py:meth:`~.set_cookie`

        Args:
            name (str): Header name to set (case-insensitive). Must be of
                type ``str`` or ``StringType``, and only character values 0x00
                through 0xFF may be used on platforms that use wide
                characters.
            value (str): Value for the header. Must be of type ``str`` or
                ``StringType``, and only character values 0x00 through 0xFF
                may be used on platforms that use wide characters.

        """
        name = name.lower()
        if name in self._headers:
            value = self._headers[name] + ',' + value

        self._headers[name] = value

    def set_headers(self, headers):
        """Set several headers at once.

        Warning:
            Calling this method overwrites existing values, if any.

        Args:
            headers (dict or list): A dictionary of header names and values
                to set, or ``list`` of (*name*, *value*) tuples. Both *name*
                and *value* must be of type ``str`` or ``StringType``, and
                only character values 0x00 through 0xFF may be used on
                platforms that use wide characters.

                Note:
                    Falcon can process a list of tuples slightly faster
                    than a dict.

        Raises:
            ValueError: `headers` was not a ``dict`` or ``list`` of ``tuple``.

        """

        if isinstance(headers, dict):
            headers = headers.items()

        # NOTE(kgriffs): We can't use dict.update because we have to
        # normalize the header names.
        _headers = self._headers
        for name, value in headers:
            _headers[name.lower()] = value

    def add_link(self, target, rel, title=None, title_star=None,
                 anchor=None, hreflang=None, type_hint=None):
        """
        Add a link header to the response.

        See also: https://tools.ietf.org/html/rfc5988

        Note:
            Calling this method repeatedly will cause each link to be
            appended to the Link header value, separated by commas.

        Note:
            So-called "link-extension" elements, as defined by RFC 5988,
            are not yet supported. See also Issue #288.

        Args:
            target (str): Target IRI for the resource identified by the
                link. Will be converted to a URI, if necessary, per
                RFC 3987, Section 3.1.
            rel (str): Relation type of the link, such as "next" or
                "bookmark". See also http://goo.gl/618GHr for a list
                of registered link relation types.

        Kwargs:
            title (str): Human-readable label for the destination of
                the link (default ``None``). If the title includes non-ASCII
                characters, you will need to use `title_star` instead, or
                provide both a US-ASCII version using `title` and a
                Unicode version using `title_star`.
            title_star (tuple of str): Localized title describing the
                destination of the link (default ``None``). The value must be a
                two-member tuple in the form of (*language-tag*, *text*),
                where *language-tag* is a standard language identifier as
                defined in RFC 5646, Section 2.1, and *text* is a Unicode
                string.

                Note:
                    *language-tag* may be an empty string, in which case the
                    client will assume the language from the general context
                    of the current request.

                Note:
                    *text* will always be encoded as UTF-8. If the string
                    contains non-ASCII characters, it should be passed as
                    a ``unicode`` type string (requires the 'u' prefix in
                    Python 2).

            anchor (str): Override the context IRI with a different URI
                (default None). By default, the context IRI for the link is
                simply the IRI of the requested resource. The value
                provided may be a relative URI.
            hreflang (str or iterable): Either a single *language-tag*, or
                a ``list`` or ``tuple`` of such tags to provide a hint to the
                client as to the language of the result of following the link.
                A list of tags may be given in order to indicate to the
                client that the target resource is available in multiple
                languages.
            type_hint(str): Provides a hint as to the media type of the
                result of dereferencing the link (default ``None``). As noted
                in RFC 5988, this is only a hint and does not override the
                Content-Type header returned when the link is followed.

        """

        # PERF(kgriffs): Heuristic to detect possiblity of an extension
        # relation type, in which case it will be a URL that may contain
        # reserved characters. Otherwise, don't waste time running the
        # string through uri.encode
        #
        # Example values for rel:
        #
        #     "next"
        #     "http://example.com/ext-type"
        #     "https://example.com/ext-type"
        #     "alternate http://example.com/ext-type"
        #     "http://example.com/ext-type alternate"
        #
        if '//' in rel:
            if ' ' in rel:
                rel = ('"' +
                       ' '.join([uri_encode(r) for r in rel.split()]) +
                       '"')
            else:
                rel = '"' + uri_encode(rel) + '"'

        value = '<' + uri_encode(target) + '>; rel=' + rel

        if title is not None:
            value += '; title="' + title + '"'

        if title_star is not None:
            value += ("; title*=UTF-8'" + title_star[0] + "'" +
                      uri_encode_value(title_star[1]))

        if type_hint is not None:
            value += '; type="' + type_hint + '"'

        if hreflang is not None:
            if isinstance(hreflang, STRING_TYPES):
                value += '; hreflang=' + hreflang
            else:
                value += '; '
                value += '; '.join(['hreflang=' + lang for lang in hreflang])

        if anchor is not None:
            value += '; anchor="' + uri_encode(anchor) + '"'

        _headers = self._headers
        if 'link' in _headers:
            _headers['link'] += ', ' + value
        else:
            _headers['link'] = value

    cache_control = header_property(
        'Cache-Control',
        """Sets the Cache-Control header.

        Used to set a list of cache directives to use as the value of the
        Cache-Control header. The list will be joined with ", " to produce
        the value for the header.

        """,
        lambda v: ', '.join(v))

    content_location = header_property(
        'Content-Location',
        'Sets the Content-Location header.',
        uri_encode)

    content_range = header_property(
        'Content-Range',
        """A tuple to use in constructing a value for the Content-Range header.

        The tuple has the form (*start*, *end*, *length*), where *start* and
        *end* designate the byte range (inclusive), and *length* is the
        total number of bytes, or '\*' if unknown. You may pass ``int``'s for
        these numbers (no need to convert to ``str`` beforehand).

        Note:
            You only need to use the alternate form, 'bytes \*/1234', for
            responses that use the status '416 Range Not Satisfiable'. In this
            case, raising ``falcon.HTTPRangeNotSatisfiable`` will do the right
            thing.

            See also: http://goo.gl/Iglhp
        """,
        format_range)

    content_type = header_property(
        'Content-Type',
        'Sets the Content-Type header.')

    etag = header_property(
        'ETag',
        'Sets the ETag header.')

    last_modified = header_property(
        'Last-Modified',
        """Sets the Last-Modified header. Set to a ``datetime`` (UTC) instance.

        Note:
            Falcon will format the ``datetime`` as an HTTP date string.
        """,
        dt_to_http)

    location = header_property(
        'Location',
        'Sets the Location header.',
        uri_encode)

    retry_after = header_property(
        'Retry-After',
        """Sets the Retry-After header.

        The expected value is an integral number of seconds to use as the
        value for the header. The HTTP-date syntax is not supported.
        """,
        str)

    vary = header_property(
        'Vary',
        """Value to use for the Vary header.

        Set this property to an iterable of header names. For a single
        asterisk or field value, simply pass a single-element ``list`` or
        ``tuple``.

        "Tells downstream proxies how to match future request headers
        to decide whether the cached response can be used rather than
        requesting a fresh one from the origin server."

        (Wikipedia)

        See also: http://goo.gl/NGHdL

        """,
        lambda v: ', '.join(v))

    def _wsgi_headers(self, media_type=None, py2=PY2):
        """Convert headers into the format expected by WSGI servers.

        Args:
            media_type: Default media type to use for the Content-Type
                header if the header was not set explicitly (default ``None``).

        """

        headers = self._headers

        # PERF(kgriffs): Using "in" like this is faster than using
        # dict.setdefault (tested on py27).
        set_content_type = (media_type is not None and
                            'content-type' not in headers)

        if set_content_type:
            headers['content-type'] = media_type

        if py2:  # pragma: no cover
            # PERF(kgriffs): Don't create an extra list object if
            # it isn't needed.
            items = headers.items()
        else:
            items = list(headers.items())  # pragma: no cover

        if self._cookies is not None:
            # PERF(tbug):
            # The below implementation is ~23% faster than
            # the alternative:
            #
            #     self._cookies.output().split("\\r\\n")
            #
            # Even without the .split("\\r\\n"), the below
            # is still ~17% faster, so don't use .output()
            items += [("set-cookie", c.OutputString())
                      for c in self._cookies.values()]
        return items