diff options
Diffstat (limited to 'netlib')
| -rw-r--r-- | netlib/http/http1/protocol.py | 14 | ||||
| -rw-r--r-- | netlib/http/semantics.py | 43 | ||||
| -rw-r--r-- | netlib/odict.py | 3 | ||||
| -rw-r--r-- | netlib/tutils.py | 4 | ||||
| -rw-r--r-- | netlib/utils.py | 56 | 
5 files changed, 87 insertions, 33 deletions
| diff --git a/netlib/http/http1/protocol.py b/netlib/http/http1/protocol.py index 2e85a762..31e9cc85 100644 --- a/netlib/http/http1/protocol.py +++ b/netlib/http/http1/protocol.py @@ -360,20 +360,6 @@ class HTTP1Protocol(semantics.ProtocolMixin):      @classmethod -    def request_preamble(self, method, resource, http_major="1", http_minor="1"): -        return '%s %s HTTP/%s.%s' % ( -            method, resource, http_major, http_minor -        ) - - -    @classmethod -    def response_preamble(self, code, message=None, http_major="1", http_minor="1"): -        if message is None: -            message = status_codes.RESPONSES.get(code) -        return 'HTTP/%s.%s %s %s' % (http_major, http_minor, code, message) - - -    @classmethod      def has_chunked_encoding(self, headers):          return "chunked" in [              i.lower() for i in utils.get_header_tokens(headers, "transfer-encoding") diff --git a/netlib/http/semantics.py b/netlib/http/semantics.py index e7ae2b5f..974fe6e6 100644 --- a/netlib/http/semantics.py +++ b/netlib/http/semantics.py @@ -7,7 +7,7 @@ import urllib  import urlparse  from .. import utils, odict -from . import cookies +from . import cookies, exceptions  from netlib import utils, encoding  HDR_FORM_URLENCODED = "application/x-www-form-urlencoded" @@ -18,10 +18,10 @@ CONTENT_MISSING = 0  class ProtocolMixin(object): -    def read_request(self): +    def read_request(self, *args, **kwargs):  # pragma: no cover          raise NotImplemented -    def read_response(self): +    def read_response(self, *args, **kwargs):  # pragma: no cover          raise NotImplemented      def assemble(self, message): @@ -32,14 +32,23 @@ class ProtocolMixin(object):          else:              raise ValueError("HTTP message not supported.") -    def assemble_request(self, request): +    def assemble_request(self, request):  # pragma: no cover          raise NotImplemented -    def assemble_response(self, response): +    def assemble_response(self, response):  # pragma: no cover          raise NotImplemented  class Request(object): +    # This list is adopted legacy code. +    # We probably don't need to strip off keep-alive. +    _headers_to_strip_off = [ +        'Proxy-Connection', +        'Keep-Alive', +        'Connection', +        'Transfer-Encoding', +        'Upgrade', +    ]      def __init__(          self, @@ -71,7 +80,6 @@ class Request(object):          self.timestamp_start = timestamp_start          self.timestamp_end = timestamp_end -      def __eq__(self, other):          try:              self_d = [self.__dict__[k] for k in self.__dict__ if k not in ('timestamp_start', 'timestamp_end')] @@ -114,7 +122,7 @@ class Request(object):                  self.httpversion[1],              )          else: -            raise http.HttpError(400, "Invalid request form") +            raise exceptions.HttpError(400, "Invalid request form")      def anticache(self):          """ @@ -143,7 +151,7 @@ class Request(object):          if self.headers["accept-encoding"]:              self.headers["accept-encoding"] = [                  ', '.join( -                    e for e in encoding.ENCODINGS if e in self.headers["accept-encoding"][0])] +                    e for e in encoding.ENCODINGS if e in self.headers.get_first("accept-encoding"))]      def update_host_header(self):          """ @@ -317,12 +325,12 @@ class Request(object):          self.scheme, self.host, self.port, self.path = parts      @property -    def content(self): +    def content(self):  # pragma: no cover          # TODO: remove deprecated getter          return self.body      @content.setter -    def content(self, content): +    def content(self, content):  # pragma: no cover          # TODO: remove deprecated setter          self.body = content @@ -343,6 +351,11 @@ class EmptyRequest(Request):  class Response(object): +    _headers_to_strip_off = [ +        'Proxy-Connection', +        'Alternate-Protocol', +        'Alt-Svc', +    ]      def __init__(          self, @@ -368,7 +381,6 @@ class Response(object):          self.timestamp_start = timestamp_start          self.timestamp_end = timestamp_end -      def __eq__(self, other):          try:              self_d = [self.__dict__[k] for k in self.__dict__ if k not in ('timestamp_start', 'timestamp_end')] @@ -393,7 +405,6 @@ class Response(object):              size=size          ) -      def get_cookies(self):          """              Get the contents of all Set-Cookie headers. @@ -430,21 +441,21 @@ class Response(object):          self.headers["Set-Cookie"] = values      @property -    def content(self): +    def content(self):  # pragma: no cover          # TODO: remove deprecated getter          return self.body      @content.setter -    def content(self, content): +    def content(self, content):  # pragma: no cover          # TODO: remove deprecated setter          self.body = content      @property -    def code(self): +    def code(self):  # pragma: no cover          # TODO: remove deprecated getter          return self.status_code      @code.setter -    def code(self, code): +    def code(self, code):  # pragma: no cover          # TODO: remove deprecated setter          self.status_code = code diff --git a/netlib/odict.py b/netlib/odict.py index d02de08d..11d5d52a 100644 --- a/netlib/odict.py +++ b/netlib/odict.py @@ -91,8 +91,9 @@ class ODict(object):          self.lst = self._filter_lst(k, self.lst)      def __contains__(self, k): +        k = self._kconv(k)          for i in self.lst: -            if self._kconv(i[0]) == self._kconv(k): +            if self._kconv(i[0]) == k:                  return True          return False diff --git a/netlib/tutils.py b/netlib/tutils.py index 5018b9e8..3c471d0d 100644 --- a/netlib/tutils.py +++ b/netlib/tutils.py @@ -119,7 +119,7 @@ def tresp(content="message"):          "OK",          headers,          content, -        time.time(), -        time.time(), +        timestamp_start=time.time(), +        timestamp_end=time.time(),      )      return resp diff --git a/netlib/utils.py b/netlib/utils.py index 35ea0ec7..2dfcafc6 100644 --- a/netlib/utils.py +++ b/netlib/utils.py @@ -4,6 +4,7 @@ import cgi  import urllib  import urlparse  import string +import re  def isascii(s): @@ -239,3 +240,58 @@ def urldecode(s):          Takes a urlencoded string and returns a list of (key, value) tuples.      """      return cgi.parse_qsl(s, keep_blank_values=True) + + +def parse_content_type(c): +    """ +        A simple parser for content-type values. Returns a (type, subtype, +        parameters) tuple, where type and subtype are strings, and parameters +        is a dict. If the string could not be parsed, return None. + +        E.g. the following string: + +            text/html; charset=UTF-8 + +        Returns: + +            ("text", "html", {"charset": "UTF-8"}) +    """ +    parts = c.split(";", 1) +    ts = parts[0].split("/", 1) +    if len(ts) != 2: +        return None +    d = {} +    if len(parts) == 2: +        for i in parts[1].split(";"): +            clause = i.split("=", 1) +            if len(clause) == 2: +                d[clause[0].strip()] = clause[1].strip() +    return ts[0].lower(), ts[1].lower(), d + + +def multipartdecode(hdrs, content): +    """ +        Takes a multipart boundary encoded string and returns list of (key, value) tuples. +    """ +    v = hdrs.get_first("content-type") +    if v: +        v = parse_content_type(v) +        if not v: +            return [] +        boundary = v[2].get("boundary") +        if not boundary: +            return [] + +        rx = re.compile(r'\bname="([^"]+)"') +        r = [] + +        for i in content.split("--" + boundary): +            parts = i.splitlines() +            if len(parts) > 1 and parts[0][0:2] != "--": +                match = rx.search(parts[1]) +                if match: +                    key = match.group(1) +                    value = "".join(parts[3 + parts[2:].index(""):]) +                    r.append((key, value)) +        return r +    return [] | 
