예제 #1
0
파일: request.py 프로젝트: jarpineh/aspen
    def __init__(self, raw):
        """Extend BaseHeaders to add extra attributes.
        """
        BaseHeaders.__init__(self, raw)
      

        # Cookie
        # ======

        self.cookie = SimpleCookie()
        try:
            self.cookie.load(self.get('Cookie', ''))
        except CookieError:
            pass # XXX really?


        # Host
        # ====
        # Per the spec, respond with 400 if no Host header is given. However,
        # we prefer X-Forwarded-For if that is available.
        
        host = self.get('X-Forwarded-Host', self['Host']) # KeyError raises 400
        self.host = UnicodeWithRaw(host, encoding='idna')


        # Scheme
        # ======
        # http://docs.python.org/library/wsgiref.html#wsgiref.util.guess_scheme

        scheme = 'https' if self.get('HTTPS', False) else 'http'
        self.scheme = UnicodeWithRaw(scheme)
예제 #2
0
    def __init__(self, code=200, body='', headers=None, charset="UTF-8"):
        """Takes an int, a string, a dict, and a basestring.

            - code      an HTTP response code, e.g., 404
            - body      the message body as a string
            - headers   a Headers instance
            - charset   string that will be set in the Content-Type in the future at some point but not now

        Code is first because when you're raising your own Responses, they're
        usually error conditions. Body is second because one more often wants
        to specify a body without headers, than a header without a body.

        """
        if not isinstance(code, int):
            raise TypeError("'code' must be an integer")
        elif not isinstance(body, str) and not hasattr(body, '__iter__'):
            raise TypeError("'body' must be a bytestring or iterable of "
                            "bytestrings")
        elif headers is not None and not isinstance(headers, (dict, list)):
            raise TypeError("'headers' must be a dictionary or a list of " +
                            "2-tuples")
        elif charset_re.match(charset) is None:
            raise TypeError("'charset' must match " + charset_re.pattern)

        Exception.__init__(self)
        self.code = code
        self.body = body
        self.headers = Headers('')
        self.charset = charset
        if headers:
            if isinstance(headers, dict):
                headers = headers.items()
            for k, v in headers:
                self.headers[k] = v
        self.headers.cookie.load(self.headers.get('Cookie', ''))
예제 #3
0
    def __init__(self, code=200, body='', headers=None, charset="UTF-8"):
        """Takes an int, a string, a dict, and a basestring.

            - code      an HTTP response code, e.g., 404
            - body      the message body as a string
            - headers   a Headers instance
            - charset   string that will be set in the Content-Type in the future at some point but not now

        Code is first because when you're raising your own Responses, they're
        usually error conditions. Body is second because one more often wants
        to specify a body without headers, than a header without a body.

        """
        if not isinstance(code, int):
            raise TypeError("'code' must be an integer")
        elif not isinstance(body, basestring) and not hasattr(
                body, '__iter__'):
            raise TypeError("'body' must be a string or iterable of strings")
        elif headers is not None and not isinstance(headers, (dict, list)):
            raise TypeError("'headers' must be a dictionary or a list of " +
                            "2-tuples")
        elif charset_re.match(charset) is None:
            raise TypeError("'charset' must match " + charset_re.pattern)

        Exception.__init__(self)
        self.code = code
        self.body = body
        self.headers = Headers(b'')
        self.charset = charset
        if headers:
            if isinstance(headers, dict):
                headers = headers.items()
            for k, v in headers:
                self.headers[k] = v
        self.headers.cookie.load(self.headers.get('Cookie', b''))
예제 #4
0
    def __init__(self, raw):
        """Extend BaseHeaders to add extra attributes.
        """
        BaseHeaders.__init__(self, raw)

        # Host
        # ====
        # Per the spec, respond with 400 if no Host header is given. However,
        # we prefer X-Forwarded-For if that is available.

        host = self.get("X-Forwarded-Host", self["Host"])  # KeyError raises 400
        self.host = UnicodeWithRaw(host, encoding="idna")

        # Scheme
        # ======
        # http://docs.python.org/library/wsgiref.html#wsgiref.util.guess_scheme

        scheme = "https" if self.get("HTTPS", False) else "http"
        self.scheme = UnicodeWithRaw(scheme)
예제 #5
0
    def __init__(self, raw):
        """Extend BaseHeaders to add extra attributes.
        """
        BaseHeaders.__init__(self, raw)

        # Host
        # ====
        # Per the spec, respond with 400 if no Host header is given. However,
        # we prefer X-Forwarded-For if that is available.

        host = self.get('X-Forwarded-Host',
                        self['Host'])  # KeyError raises 400
        self.host = UnicodeWithRaw(host, encoding='idna')

        # Scheme
        # ======
        # http://docs.python.org/library/wsgiref.html#wsgiref.util.guess_scheme

        scheme = 'https' if self.get('HTTPS', False) else 'http'
        self.scheme = UnicodeWithRaw(scheme)
예제 #6
0
def est_headers_are_case_insensitive():
    headers = BaseHeaders('Foo: bar')
    expected = 'bar'
    actual = headers.one('foo')
    assert actual == expected, actual
예제 #7
0
class Response(Exception):
    """Represent an HTTP Response message.
    """

    request = None

    def __init__(self, code=200, body='', headers=None, charset="UTF-8"):
        """Takes an int, a string, a dict, and a basestring.

            - code      an HTTP response code, e.g., 404
            - body      the message body as a string
            - headers   a Headers instance
            - charset   string that will be set in the Content-Type in the future at some point but not now

        Code is first because when you're raising your own Responses, they're
        usually error conditions. Body is second because one more often wants
        to specify a body without headers, than a header without a body.

        """
        if not isinstance(code, int):
            raise TypeError("'code' must be an integer")
        elif not isinstance(body, str) and not hasattr(body, '__iter__'):
            raise TypeError("'body' must be a bytestring or iterable of "
                            "bytestrings")
        elif headers is not None and not isinstance(headers, (dict, list)):
            raise TypeError("'headers' must be a dictionary or a list of " +
                            "2-tuples")
        elif charset_re.match(charset) is None:
            raise TypeError("'charset' must match " + charset_re.pattern)

        Exception.__init__(self)
        self.code = code
        self.body = body
        self.headers = Headers('')
        self.charset = charset
        if headers:
            if isinstance(headers, dict):
                headers = headers.items()
            for k, v in headers:
                self.headers[k] = v
        self.headers.cookie.load(self.headers.get('Cookie', ''))

    def __call__(self, environ, start_response):
        wsgi_status = str(self)
        for morsel in self.headers.cookie.values():
            self.headers.add('Set-Cookie', morsel.OutputString())
        wsgi_headers = []
        for k, vals in self.headers.iteritems():
            try:        # XXX This is a hack. It's red hot, baby.
                k = k.encode('US-ASCII')
            except UnicodeEncodeError:
                k = ascii_dammit(k)
                raise ValueError("Header key %s must be US-ASCII.")
            for v in vals:
                try:    # XXX This also is a hack. It is also red hot, baby.
                    v = v.encode('US-ASCII')
                except UnicodeEncodeError:
                    v = ascii_dammit(v)
                    raise ValueError("Header value %s must be US-ASCII.")
                wsgi_headers.append((k, v))

        start_response(wsgi_status, wsgi_headers)
        body = self.body
        if isinstance(body, str):
            body = [body]
        return CloseWrapper(self.request, body)

    def __repr__(self):
        return "<Response: %s>" % str(self)

    def __str__(self):
        return "%d %s" % (self.code, self._status())

    def _status(self):
        return status_strings.get(self.code, 'Unknown HTTP status')

    def _to_http(self, version):
        """Given a version string like 1.1, return an HTTP message, a string.
        """
        status_line = "HTTP/%s" % version
        headers = self.headers.raw
        body = self.body
        if self.headers.get('Content-Type', '').startswith('text/'):
            body = body.replace('\n', '\r\n')
            body = body.replace('\r\r', '\r')
        return '\r\n'.join([status_line, headers, '', body])
예제 #8
0
class Response(Exception):
    """Represent an HTTP Response message.
    """

    request = None

    def __init__(self, code=200, body='', headers=None, charset="UTF-8"):
        """Takes an int, a string, a dict, and a basestring.

            - code      an HTTP response code, e.g., 404
            - body      the message body as a string
            - headers   a Headers instance
            - charset   string that will be set in the Content-Type in the future at some point but not now

        Code is first because when you're raising your own Responses, they're
        usually error conditions. Body is second because one more often wants
        to specify a body without headers, than a header without a body.

        """
        if not isinstance(code, int):
            raise TypeError("'code' must be an integer")
        elif not isinstance(body, basestring) and not hasattr(
                body, '__iter__'):
            raise TypeError("'body' must be a string or iterable of strings")
        elif headers is not None and not isinstance(headers, (dict, list)):
            raise TypeError("'headers' must be a dictionary or a list of " +
                            "2-tuples")
        elif charset_re.match(charset) is None:
            raise TypeError("'charset' must match " + charset_re.pattern)

        Exception.__init__(self)
        self.code = code
        self.body = body
        self.headers = Headers(b'')
        self.charset = charset
        if headers:
            if isinstance(headers, dict):
                headers = headers.items()
            for k, v in headers:
                self.headers[k] = v
        self.headers.cookie.load(self.headers.get('Cookie', b''))

    def __call__(self, environ, start_response):
        wsgi_status = str(self)
        for morsel in self.headers.cookie.values():
            self.headers.add('Set-Cookie', morsel.OutputString())
        wsgi_headers = []
        for k, vals in self.headers.iteritems():
            try:  # XXX This is a hack. It's red hot, baby.
                k = k.encode('US-ASCII')
            except UnicodeEncodeError:
                k = ascii_dammit(k)
                raise ValueError("Header key %s must be US-ASCII.")
            for v in vals:
                try:  # XXX This also is a hack. It is also red hot, baby.
                    v = v.encode('US-ASCII')
                except UnicodeEncodeError:
                    v = ascii_dammit(v)
                    raise ValueError("Header value %s must be US-ASCII.")
                wsgi_headers.append((k, v))

        start_response(wsgi_status, wsgi_headers)
        body = self.body
        if isinstance(body, basestring):
            body = [body]
        body = (x.encode('ascii') if isinstance(x, unicode) else x
                for x in body)
        return CloseWrapper(self.request, body)

    def __repr__(self):
        return "<Response: %s>" % str(self)

    def __str__(self):
        return "%d %s" % (self.code, self._status())

    def _status(self):
        return status_strings.get(self.code, 'Unknown HTTP status')

    def _to_http(self, version):
        """Given a version string like 1.1, return an HTTP message, a string.
        """
        status_line = "HTTP/%s" % version
        headers = self.headers.raw
        body = self.body
        if self.headers.get('Content-Type', '').startswith('text/'):
            body = body.replace('\n', '\r\n')
            body = body.replace('\r\r', '\r')
        return '\r\n'.join([status_line, headers, '', body])

    def whence_raised(self):
        """Return a tuple, (filename, linenum) where we were raised from.

        If we're not the exception currently being handled then the return
        value is (None, None).

        """
        tb = filepath = linenum = None
        try:
            cls, response, tb = sys.exc_info()
            if response is self:
                while tb.tb_next is not None:
                    tb = tb.tb_next
                frame = tb.tb_frame

                # filepath
                pathparts = tb.tb_frame.f_code.co_filename.split(os.sep)[-2:]
                # XXX It'd be nice to use www_root and project_root here, but
                # self.request is None at this point afaict, and it's enough to
                # show the last two parts just to differentiate index.html or
                # __init__.py.
                filepath = os.sep.join(pathparts)

                # linenum
                linenum = frame.f_lineno
        finally:
            del tb  # http://docs.python.org/2/library/sys.html#sys.exc_info
        return filepath, linenum
예제 #9
0
class Response(Exception):
    """Represent an HTTP Response message.
    """

    request = None

    def __init__(self, code=200, body='', headers=None, charset="UTF-8"):
        """Takes an int, a string, a dict, and a basestring.

            - code      an HTTP response code, e.g., 404
            - body      the message body as a string
            - headers   a Headers instance
            - charset   string that will be set in the Content-Type in the future at some point but not now

        Code is first because when you're raising your own Responses, they're
        usually error conditions. Body is second because one more often wants
        to specify a body without headers, than a header without a body.

        """
        if not isinstance(code, int):
            raise TypeError("'code' must be an integer")
        elif not isinstance(body, basestring) and not hasattr(body, '__iter__'):
            raise TypeError("'body' must be a string or iterable of strings")
        elif headers is not None and not isinstance(headers, (dict, list)):
            raise TypeError("'headers' must be a dictionary or a list of " +
                            "2-tuples")
        elif charset_re.match(charset) is None:
            raise TypeError("'charset' must match " + charset_re.pattern)

        Exception.__init__(self)
        self.code = code
        self.body = body
        self.headers = Headers(b'')
        self.charset = charset
        if headers:
            if isinstance(headers, dict):
                headers = headers.items()
            for k, v in headers:
                self.headers[k] = v
        self.headers.cookie.load(self.headers.get('Cookie', b''))

    def __call__(self, environ, start_response):
        wsgi_status = str(self)
        for morsel in self.headers.cookie.values():
            self.headers.add('Set-Cookie', morsel.OutputString())
        wsgi_headers = []
        for k, vals in self.headers.iteritems():
            try:        # XXX This is a hack. It's red hot, baby.
                k = k.encode('US-ASCII')
            except UnicodeEncodeError:
                k = ascii_dammit(k)
                raise ValueError("Header key %s must be US-ASCII.")
            for v in vals:
                try:    # XXX This also is a hack. It is also red hot, baby.
                    v = v.encode('US-ASCII')
                except UnicodeEncodeError:
                    v = ascii_dammit(v)
                    raise ValueError("Header value %s must be US-ASCII.")
                wsgi_headers.append((k, v))

        start_response(wsgi_status, wsgi_headers)
        body = self.body
        if isinstance(body, basestring):
            body = [body]
        body = (x.encode('ascii') if isinstance(x, unicode) else x for x in body)
        return CloseWrapper(self.request, body)

    def __repr__(self):
        return "<Response: %s>" % str(self)

    def __str__(self):
        return "%d %s" % (self.code, self._status())

    def _status(self):
        return status_strings.get(self.code, 'Unknown HTTP status')

    def _to_http(self, version):
        """Given a version string like 1.1, return an HTTP message, a string.
        """
        status_line = "HTTP/%s" % version
        headers = self.headers.raw
        body = self.body
        if self.headers.get('Content-Type', '').startswith('text/'):
            body = body.replace('\n', '\r\n')
            body = body.replace('\r\r', '\r')
        return '\r\n'.join([status_line, headers, '', body])

    def whence_raised(self):
        """Return a tuple, (filename, linenum) where we were raised from.

        If we're not the exception currently being handled then the return
        value is (None, None).

        """
        tb = filepath = linenum = None
        try:
            cls, response, tb = sys.exc_info()
            if response is self:
                while tb.tb_next is not None:
                    tb = tb.tb_next
                frame = tb.tb_frame

                # filepath
                pathparts = tb.tb_frame.f_code.co_filename.split(os.sep)[-2:]
                # XXX It'd be nice to use www_root and project_root here, but
                # self.request is None at this point afaict, and it's enough to
                # show the last two parts just to differentiate index.html or
                # __init__.py.
                filepath = os.sep.join(pathparts)

                # linenum
                linenum = frame.f_lineno
        finally:
            del tb  # http://docs.python.org/2/library/sys.html#sys.exc_info
        return filepath, linenum
예제 #10
0
def bodified(raw):
    """Convert a plain dict into a cgi.FieldStorage per Aspen.
    """
    encoded = encode_multipart(BOUNDARY, raw)
    headers = BaseHeaders({b'Content-Type': b'multipart/form-data; boundary=' + BOUNDARY})
    return aspen.body_parsers.formdata(encoded, headers)
예제 #11
0
def test_headers_dont_unicodify_cookie():
    headers = BaseHeaders(b"Cookie: somecookiedata")
    expected = b"somecookiedata"
    actual = headers[b'Cookie']
    assert actual == expected
예제 #12
0
def test_headers_access_is_case_insensitive():
    headers = BaseHeaders(b"Foo: Bar")
    expected = b"Bar"
    actual = headers['foo']
    assert actual == expected
예제 #13
0
def test_headers_access_gets_last_value():
    headers = BaseHeaders(b"Foo: Bar\r\nFoo: Baz")
    expected = b"Baz"
    actual = headers['Foo']
    assert actual == expected
예제 #14
0
def test_headers_access_gets_a_value():
    headers = BaseHeaders("Foo: Bar")
    expected = "Bar"
    actual = headers['Foo']
    assert actual == expected, actual
예제 #15
0
class Response(Exception):
    """Represent an HTTP Response message.
    """

    request = None

    def __init__(self, code=200, body='', headers=None, charset="UTF-8"):
        """Takes an int, a string, a dict, and a basestring.

            - code      an HTTP response code, e.g., 404
            - body      the message body as a string
            - headers   a Headers instance
            - charset   string that will be set in the Content-Type in the future at some point but not now

        Code is first because when you're raising your own Responses, they're
        usually error conditions. Body is second because one more often wants
        to specify a body without headers, than a header without a body.

        """
        if not isinstance(code, int):
            raise TypeError("'code' must be an integer")
        elif not isinstance(body, str) and not hasattr(body, '__iter__'):
            raise TypeError("'body' must be a bytestring or iterable of "
                            "bytestrings")
        elif headers is not None and not isinstance(headers, (dict, list)):
            raise TypeError("'headers' must be a dictionary or a list of " +
                            "2-tuples")
        elif charset_re.match(charset) is None:
            raise TypeError("'charset' must match " + charset_re.pattern)

        Exception.__init__(self)
        self.code = code
        self.body = body
        self.headers = Headers('')
        self.charset = charset
        if headers:
            if isinstance(headers, dict):
                headers = headers.items()
            for k, v in headers:
                self.headers[k] = v
        self.headers.cookie.load(self.headers.get('Cookie', ''))

    def __call__(self, environ, start_response):
        wsgi_status = str(self)
        for morsel in self.headers.cookie.values():
            self.headers.add('Set-Cookie', morsel.OutputString())
        wsgi_headers = []
        for k, vals in self.headers.iteritems():
            try:  # XXX This is a hack. It's red hot, baby.
                k = k.encode('US-ASCII')
            except UnicodeEncodeError:
                k = ascii_dammit(k)
                raise ValueError("Header key %s must be US-ASCII.")
            for v in vals:
                try:  # XXX This also is a hack. It is also red hot, baby.
                    v = v.encode('US-ASCII')
                except UnicodeEncodeError:
                    v = ascii_dammit(v)
                    raise ValueError("Header value %s must be US-ASCII.")
                wsgi_headers.append((k, v))

        start_response(wsgi_status, wsgi_headers)
        body = self.body
        if isinstance(body, str):
            body = [body]
        return CloseWrapper(self.request, body)

    def __repr__(self):
        return "<Response: %s>" % str(self)

    def __str__(self):
        return "%d %s" % (self.code, self._status())

    def _status(self):
        return status_strings.get(self.code, 'Unknown HTTP status')

    def _to_http(self, version):
        """Given a version string like 1.1, return an HTTP message, a string.
        """
        status_line = "HTTP/%s" % version
        headers = self.headers.raw
        body = self.body
        if self.headers.get('Content-Type', '').startswith('text/'):
            body = body.replace('\n', '\r\n')
            body = body.replace('\r\r', '\r')
        return '\r\n'.join([status_line, headers, '', body])