Esempio n. 1
0
	def decode(cls, data, charset=None, mimetype=None):
		boundary = mimetype.boundary.encode('ISO8859-1')
		parts = data.split(b'--%s' % (boundary,))
		part = parts.pop(0)
		if part:
			raise DecodeError(_(u'Data before boundary: %r'), part.decode('ISO8859-1'))
		try:
			part = parts.pop()
		except IndexError:
			raise DecodeError(u'No end of boundary')
		if part not in (b'--', b'--\r\n'):
			raise DecodeError(_(u'Invalid multipart end: %r'), part.decode('ISO8859-1'))

		from httoop.messages.body import Body
		multiparts = []
		for part in parts:
			if not part.startswith('\r\n'):
				raise DecodeError(_(u'Invalid boundary end: %r'), part[:2].decode('ISO8859-1'))
			part = part[2:]
			headers, separator, content = part.partition(b'\r\n\r\n')
			if not separator:
				raise DecodeError(_(u'Multipart does not contain CRLF header separator'))
			if not content.endswith('\r\n'):
				raise DecodeError(_(u'Multipart does not end with CRLF: %r'), content[-2].decode('ISO8859-1'))
			content = content[:-2]
			body = Body()
			body.headers.clear()
			body.headers.parse(headers)
			body.headers.setdefault('Content-Type', cls.default_content_type)
			body.parse(content)
			multiparts.append(body)
		return multiparts
Esempio n. 2
0
	def _unquote_host(self, host):
		# IPv6 / IPvFuture
		if host.startswith(b'[') and host.endswith(b']'):
			host = host[1:-1]
			try:
				return u'[%s]' % inet_ntop(AF_INET6, inet_pton(AF_INET6, host)).decode('ascii')
			except SocketError:
				# IPvFuture
				if host.startswith(b'v') and b'.' in host and host[1:].split(b'.', 1)[0].isdigit():
					try:
						return u'[%s]' % host.decode('ascii')
					except UnicodeDecodeError:
						raise InvalidURI(_('Invalid IPvFuture address: must be ASCII.'))
				raise InvalidURI(_('Invalid IP address in URI.'))
		# IPv4
		if all(x.isdigit() for x in host.split(b'.')):
			try:
				return inet_ntop(AF_INET, inet_pton(AF_INET, host)).decode('ascii')
			except SocketError:
				raise InvalidURI(_('Invalid IPv4 address in URI.'))

		if host.strip(Percent.UNRESERVED + Percent.SUB_DELIMS + b'%'):
			raise InvalidURI(_('Invalid URI host.'))

		# DNS hostname
		host = self.unquote(host)
		try:
			return host.encode('ascii').decode('idna').lower()
		except UnicodeError:
			raise InvalidURI(_('Invalid host.'))
Esempio n. 3
0
	def parse(self, data):
		r"""parses HTTP headers

			:param data:
				the header string containing headers separated by "\r\n"
				without trailing "\r\n"
			:type  data: bytes
		"""

		lines = data.split(b'\r\n')

		while lines:
			curr = lines.pop(0)
			name, __, value = curr.partition(b':')
			if __ != b':':
				raise InvalidHeader(_(u"Invalid header line: %r"), curr.decode('ISO8859-1'))

			if self.HEADER_RE.search(name):
				raise InvalidHeader(_(u"Invalid header name: %r"), name.decode('ISO8859-1'))

			name, value = name.strip(), [value.lstrip()]

			# continuation lines
			while lines and lines[0].startswith((' ', '\t')):
				value.append(lines.pop(0)[1:])
			value = b''.join(value).rstrip()
			Element = HEADER.get(name, HeaderElement)
			value = Element.decode(value)

			self.append(name, value)
Esempio n. 4
0
	def parse(self, data):
		r"""parses HTTP headers

			:param data:
				the header string containing headers separated by "\r\n"
				without trailing "\r\n"
			:type  data: bytes
		"""

		lines = data.split(b'\r\n')

		while lines:
			curr = lines.pop(0)
			name, __, value = curr.partition(b':')
			if __ != b':':
				raise InvalidHeader(_(u"Invalid header line: %r"), curr.decode('ISO8859-1'))

			if self.HEADER_RE.search(name):
				raise InvalidHeader(_(u"Invalid header name: %r"), name.decode('ISO8859-1'))

			name, value = name.strip(), [value.lstrip()]

			# continuation lines
			while lines and lines[0].startswith((b' ', b'\t')):
				value.append(lines.pop(0)[1:])
			value = b''.join(value).rstrip()
			Element = HEADER.get(name, HeaderElement)
			value = Element.decode(value)

			self.append(name, value)
Esempio n. 5
0
	def _unquote_host(self, host):
		# IPv6 / IPvFuture
		if host.startswith(b'[') and host.endswith(b']'):
			host = host[1:-1]
			try:
				return u'[%s]' % inet_ntop(AF_INET6, inet_pton(AF_INET6, host)).decode('ascii')
			except SocketError:
				# IPvFuture
				if host.startswith(b'v') and b'.' in host and host[1:].split('.', 1)[0].isdigit():
					try:
						return u'[%s]' % host.decode('ascii')
					except UnicodeDecodeError:
						raise InvalidURI(_('Invalid IPvFuture address: must be ASCII.'))
				raise InvalidURI(_('Invalid IP address in URI.'))
		# IPv4
		if all(x.isdigit() for x in host.split(b'.')):
			try:
				return inet_ntop(AF_INET, inet_pton(AF_INET, host)).decode('ascii')
			except SocketError:
				raise InvalidURI(_('Invalid IPv4 address in URI.'))

		if host.strip(Percent.UNRESERVED + Percent.SUB_DELIMS + b'%'):
			raise InvalidURI(_('Invalid URI host.'))

		# DNS hostname
		host = self.unquote(host)
		try:
			return host.encode('ascii').decode('idna').lower()
		except UnicodeError:
			raise InvalidURI(_('Invalid host.'))
Esempio n. 6
0
    def parse(self, line):
        """parses the request line and sets method, uri and protocol version
			:param line: the request line
			:type  line: bytes
		"""
        bits = line.strip().split(None, 2)
        try:
            method, uri, version = bits
        except ValueError:
            raise InvalidLine(_(u'Invalid request line: %r'),
                              line.decode('ISO8859-1'))

        # protocol version
        super(Request, self).parse(version)

        # method
        self.method.parse(method)

        # URI
        if uri.startswith(b'//'):
            raise InvalidURI(
                _(u'The request URI must be an absolute path or contain a scheme.'
                  ))
        if self.method == u'CONNECT':
            uri = b'//%s' % (uri, )
        self.uri.parse(uri)
        self.validate_request_uri()
Esempio n. 7
0
	def prevent_denial_of_service(self):
		if len([x for x in self.ranges if x[0] is None]) > 1 or len([x for x in self.ranges if x[1] is None]) > 1:
			raise InvalidHeader(_(u'too many overlapping ranges.'))
		byterange = set()
		for start, stop in ((x, y) for x, y in self.ranges if x is not None and y is not None):
			range_ = set(range(start, stop))
			if any(x in byterange for x in range_):
				raise InvalidHeader(_(u'duplicated range.'))
			byterange.update(range_)
Esempio n. 8
0
 def prevent_denial_of_service(self):
     if len([x for x in self.ranges if x[0] is None]) > 1 or len(
         [x for x in self.ranges if x[1] is None]) > 1:
         raise InvalidHeader(_(u'too many overlapping ranges.'))
     byterange = set()
     for start, stop in ((x, y) for x, y in self.ranges
                         if x is not None and y is not None):
         range_ = set(range(start, stop))
         if any(x in byterange for x in range_):
             raise InvalidHeader(_(u'duplicated range.'))
         byterange.update(range_)
Esempio n. 9
0
	def compose(self):
		try:
			scheme = self.schemes[self.value.lower()]
		except KeyError:
			raise InvalidHeader(_(u'Unsupported authentication scheme: %r'), self.value)

		try:
			authinfo = scheme.compose(self.params)
		except KeyError as key:
			raise InvalidHeader(_(u'Missing parameter %r for authentication scheme %r'), str(key), self.value)

		return b'%s %s' % (self.value.title(), authinfo)
Esempio n. 10
0
	def validate_request_uri(self):
		uri = self.uri
		if not isinstance(uri, (uri.SCHEMES['http'], uri.SCHEMES['https'])):
			raise InvalidURI(_(u'The request URI scheme must be HTTP based.'))
		if uri.fragment or uri.username or uri.password:
			raise InvalidURI(_(u'The request URI must not contain fragments or user information.'))
		if uri.path.startswith(b'//'):
			raise InvalidURI(_(u'The request URI path must not start with //.'))
		if uri.path and uri.path != u'*' and uri.path[0] != u'/':
			raise InvalidURI(_(u'The request URI path must start with /.'))
		if self.method == u'CONNECT' and (uri.scheme or uri.path or uri.query_string or not uri.host):
			raise InvalidURI(_(u'The request URI of an CONNECT request must be a authority.'))
Esempio n. 11
0
	def sanitize(self):
		self.value = self.value.lower()
		if self.attachment:
			if b'inline' in self.params:
				raise InvalidHeader(_(u'Mixed Content-Disposition'))
		elif self.inline:
			if b'attachment' in self.params:
				raise InvalidHeader(_(u'Mixed Content-Disposition'))
		elif self.form_data:
			if b'form-data' in self.params:
				raise InvalidHeader(_(u'Mixed Content-Disposition'))
		else:
			raise InvalidHeader(_(u'Unknown Content-Disposition: %r'), self.value,)
Esempio n. 12
0
	def sanitize(self):
		self.value = self.value.lower()
		if self.attachment:
			if b'inline' in self.params:
				raise InvalidHeader(_(u'Mixed Content-Disposition'))
		elif self.inline:
			if b'attachment' in self.params:
				raise InvalidHeader(_(u'Mixed Content-Disposition'))
		elif self.form_data:
			if b'form-data' in self.params:
				raise InvalidHeader(_(u'Mixed Content-Disposition'))
		else:
			raise InvalidHeader(_(u'Unknown Content-Disposition: %r'), self.value,)
Esempio n. 13
0
    def _rfc2231_and_continuation_params(cls, params):  # TODO: complexity
        count = set()
        continuations = dict()
        for key, value, quoted in params:
            if key in count:
                raise InvalidHeader(_(u'Parameter given twice: %r'),
                                    key.decode('ISO8859-1'))
            count.add(key)
            if '*' in key:
                if key.endswith('*') and not quoted:
                    charset, language, value_ = decode_rfc2231(
                        value.encode('ISO8859-1'))
                    if not charset:
                        yield key, value
                        continue
                    encoding = sanitize_encoding(charset)
                    if encoding is None:
                        raise InvalidHeader(_(u'Unknown encoding: %r'),
                                            charset)
                    try:
                        key, value = key[:-1], Percent.unquote(value_).decode(
                            encoding)
                    except UnicodeDecodeError as exc:
                        raise InvalidHeader(_(u'%s') % (exc, ))
                key_, asterisk, num = key.rpartition('*')
                if asterisk:
                    try:
                        if num != '0' and num.startswith('0'):
                            raise ValueError
                        num = int(num)
                    except ValueError:
                        yield key, value
                        continue
                    continuations.setdefault(key_, {})[num] = value
                    continue
            yield key, value

        for key, lines in iteritems(continuations):
            value = b''
            for i in xrange(len(lines)):
                try:
                    value += lines.pop(i)
                except KeyError:
                    break
            if not key:
                raise InvalidHeader(_(u'...'))
            if value:
                yield key, value
            for k, v in iteritems(lines):
                yield '%s*%d' % (key, k), v
Esempio n. 14
0
	def parseparams(cls, elementstr):
		try:
			scheme, authinfo = elementstr.split(b' ', 1)
		except ValueError:
			raise InvalidHeader(_(u'Authorization headers must contain authentication scheme'))
		try:
			parser = cls.schemes[scheme.lower()]
		except KeyError:
			raise InvalidHeader(_(u'Unsupported authentication scheme: %r'), scheme)

		try:
			authinfo = parser.parse(authinfo)
		except KeyError as key:
			raise InvalidHeader(_(u'Missing parameter %r for authentication scheme %r'), str(key), scheme)

		return scheme.title(), authinfo
Esempio n. 15
0
	def parseparam(cls, atom):
		key, __, val  = atom.partition(b'=')
		try:
			val, quoted = cls.unescape_param(val.strip())
		except InvalidHeader:
			raise InvalidHeader(_(u'Unquoted parameter %r in %r containing TSPECIALS: %r'), key, cls.__name__, val)
		return cls.unescape_key(key), val, quoted
Esempio n. 16
0
	def _rfc2231_and_continuation_params(cls, params):  # TODO: complexity
		count = set()
		continuations = dict()
		for key, value, quoted in params:
			if key in count:
				raise InvalidHeader(_(u'Parameter given twice: %r'), key.decode('ISO8859-1'))
			count.add(key)
			if '*' in key:
				if key.endswith('*') and not quoted:
					charset, language, value_ = decode_rfc2231(value.encode('ISO8859-1'))
					if not charset:
						yield key, value
						continue
					encoding = sanitize_encoding(charset)
					if encoding is None:
						raise InvalidHeader(_(u'Unknown encoding: %r'), charset)
					try:
						key, value = key[:-1], Percent.unquote(value_).decode(encoding)
					except UnicodeDecodeError as exc:
						raise InvalidHeader(_(u'%s') % (exc,))
				key_, asterisk, num = key.rpartition('*')
				if asterisk:
					try:
						if num != '0' and num.startswith('0'):
							raise ValueError
						num = int(num)
					except ValueError:
						yield key, value
						continue
					continuations.setdefault(key_, {})[num] = value
					continue
			yield key, value

		for key, lines in iteritems(continuations):
			value = b''
			for i in xrange(len(lines)):
				try:
					value += lines.pop(i)
				except KeyError:
					break
			if not key:
				raise InvalidHeader(_(u'...'))
			if value:
				yield key, value
			for k, v in iteritems(lines):
				yield '%s*%d' % (key, k), v
Esempio n. 17
0
	def unescape_param(cls, value):
		quoted = value.startswith(b'"') and value.endswith(b'"')
		if quoted:
			value = re.sub(r'\\(?!\\)', '', value.strip(b'"'))
		else:
			if cls.RE_TSPECIALS.search(value):
				raise InvalidHeader(_(u'Unquoted parameter in %r containing TSPECIALS: %r'), cls.__name__, value)
		return value, quoted
Esempio n. 18
0
	def decode(cls, data, charset=None, mimetype=None):
		try:
			with gzip.GzipFile(fileobj=io.BytesIO(data)) as fd:
				return fd.read()
			# data = zlib.decompress(data, 16 + zlib.MAX_WBITS)
		except (zlib.error, IOError, EOFError):
			raise DecodeError(_(u'Invalid gzip data.'))
		return Codec.decode(data, charset)
Esempio n. 19
0
	def sanitize(self):
		self.value = self.value.lower()
		self.host, self.port = self.HOSTPORT.match(self.value).groups()
		if self.host.endswith(']') and self.host.startswith('['):
			self.host = self.host[1:-1]
		if self.port:
			self.port = int(self.port)
		if not self.hostname:
			raise InvalidHeader(_(u'Invalid Host header'))
Esempio n. 20
0
	def encode(cls, data, charset=None, mimetype=None):
		try:
			out = io.BytesIO()
			with gzip.GzipFile(fileobj=out, mode="w") as fd:
				fd.write(Codec.encode(data, charset))
			return out.getvalue()
			# return zlib.compress(Codec.encode(data, charset), cls.compression_level)
		except zlib.error:
			raise EncodeError(_(u'Invalid gzip data.'))
Esempio n. 21
0
 def decode(cls, data, charset=None):
     if any(
             in_table(x)
             for x in cls.unquote(data.encode('ISO8859-1'), 'ISO8859-1')
             for in_table in cls.INVALID):
         raise DecodeError(
             _(u'Invalid query string: contains invalid token'))
     data = super(QueryString, cls).decode(data, charset)
     return data
Esempio n. 22
0
	def validate_request_uri_scheme(self):
		if self.message.uri.scheme:
			if self.message.uri.scheme not in ('http', 'https'):
				exc = InvalidURI(_(u'Invalid URL: wrong scheme'))
				raise BAD_REQUEST(Unicode(exc))
		else:
			self.message.uri.scheme = self._default_scheme
			self.message.uri.host = self._default_host
			self.message.uri.port = self._default_port
Esempio n. 23
0
	def sanitize(self):
		self.value = self.value.lower()
		self.host, self.port = self.HOSTPORT.match(self.value).groups()
		if self.host.endswith(']') and self.host.startswith('['):
			self.host = self.host[1:-1]
		if self.port:
			self.port = int(self.port)
		if not self.hostname:
			raise InvalidHeader(_(u'Invalid Host header'))
Esempio n. 24
0
	def parse(authinfo):
		try:
			authinfo = authinfo.encode('ascii')
		except ValueError:
			raise InvalidHeader(_(u'Invalid base64 in basic authentication'))

		try:
			username, password = decode_base64(authinfo.strip()).split(':')
		except Base64Error:
			raise InvalidHeader(_(u'Basic authentication contains invalid base64'))
		except ValueError:
			raise InvalidHeader(_(u'No username:password provided'))

		authinfo = {
			'username': username.decode('ISO8859-1'),
			'password': password.decode('ISO8859-1')
		}
		return authinfo
Esempio n. 25
0
 def validate_request_uri(self):
     uri = self.uri
     if not isinstance(uri, (uri.SCHEMES['http'], uri.SCHEMES['https'])):
         raise InvalidURI(_(u'The request URI scheme must be HTTP based.'))
     if uri.fragment or uri.username or uri.password:
         raise InvalidURI(
             _(u'The request URI must not contain fragments or user information.'
               ))
     if uri.path.startswith(b'//'):
         raise InvalidURI(
             _(u'The request URI path must not start with //.'))
     if uri.path and uri.path != u'*' and uri.path[0] != u'/':
         raise InvalidURI(_(u'The request URI path must start with /.'))
     if self.method == u'CONNECT' and (uri.scheme or uri.path
                                       or uri.query_string or not uri.host):
         raise InvalidURI(
             _(u'The request URI of an CONNECT request must be a authority.'
               ))
Esempio n. 26
0
 def parseparam(cls, atom):
     key, __, val = atom.partition(b'=')
     try:
         val, quoted = cls.unescape_param(val.strip())
     except InvalidHeader:
         raise InvalidHeader(
             _(u'Unquoted parameter %r in %r containing TSPECIALS: %r'),
             key, cls.__name__, val)
     return cls.unescape_key(key), val, quoted
Esempio n. 27
0
 def validate_request_uri_scheme(self):
     if self.message.uri.scheme:
         if self.message.uri.scheme not in ('http', 'https'):
             exc = InvalidURI(_(u'Invalid URL: wrong scheme'))
             raise BAD_REQUEST(Unicode(exc))
     else:
         self.message.uri.scheme = self._default_scheme
         self.message.uri.host = self._default_host
         self.message.uri.port = self._default_port
Esempio n. 28
0
	def port(self, port):
		port = port or self.PORT
		if port:
			try:
				port = int(port)
				if not 0 < int(port) <= 65535:
					raise ValueError
			except ValueError:
				raise InvalidURI(_(u'Invalid port: %r'), port)  # TODO: TypeError
		self._port = port
Esempio n. 29
0
	def parse(cls, elementstr):
		bytesunit, __, byteranges = elementstr.partition(b'=')
		byteranges = cls.split(byteranges)
		ranges = set()
		for brange in byteranges:
			start, __, stop = (x.strip() for x in brange.partition(b'-'))
			if (not start and not stop) or not __:
				raise InvalidHeader(_(u'no range start/stop.'))
			try:
				start = int(start) if start else None
				stop = int(stop) if stop else None
				if start and start < 0 or stop and stop < 0:
					raise ValueError
			except ValueError:
				raise InvalidHeader(_(u'no range number.'))
			if start is not None and stop is not None and stop < start:
				raise InvalidHeader(_(u'range start must be smaller than end.'))
			ranges.add((start, stop))
		return cls(bytesunit, list(sorted(ranges)))
Esempio n. 30
0
	def port(self, port):
		port = port or self.PORT
		if port:
			try:
				port = int(port)
				if not 0 < int(port) <= 65535:
					raise ValueError
			except ValueError:
				raise InvalidURI(_(u'Invalid port: %r'), port)  # TODO: TypeError
		self._port = port
Esempio n. 31
0
 def unescape_param(cls, value):
     quoted = value.startswith(b'"') and value.endswith(b'"')
     if quoted:
         value = re.sub(r'\\(?!\\)', '', value.strip(b'"'))
     else:
         if cls.RE_TSPECIALS.search(value):
             raise InvalidHeader(
                 _(u'Unquoted parameter in %r containing TSPECIALS: %r'),
                 cls.__name__, value)
     return value, quoted
Esempio n. 32
0
    def parse(authinfo):
        try:
            authinfo = authinfo.encode('ascii')
        except ValueError:
            raise InvalidHeader(_(u'Invalid base64 in basic authentication'))

        try:
            username, password = decode_base64(authinfo.strip()).split(':')
        except Base64Error:
            raise InvalidHeader(
                _(u'Basic authentication contains invalid base64'))
        except ValueError:
            raise InvalidHeader(_(u'No username:password provided'))

        authinfo = {
            'username': username.decode('ISO8859-1'),
            'password': password.decode('ISO8859-1')
        }
        return authinfo
Esempio n. 33
0
	def parse(self, status):
		"""parse a Statuscode and Reason-Phrase

			:param status: the code and reason
			:type  status: bytes
		"""
		match = self.STATUS_RE.match(status)
		if match is None:
			raise InvalidLine(_(u"Invalid status %r"), status.decode('ISO8859-1'))

		self.set((int(match.group(1)), match.group(2).decode('ascii'),))
Esempio n. 34
0
 def parse(cls, elementstr):
     bytesunit, __, byteranges = elementstr.partition(b'=')
     byteranges = cls.split(byteranges)
     ranges = set()
     for brange in byteranges:
         start, __, stop = (x.strip() for x in brange.partition(b'-'))
         if (not start and not stop) or not __:
             raise InvalidHeader(_(u'no range start/stop.'))
         try:
             start = int(start) if start else None
             stop = int(stop) if stop else None
             if start and start < 0 or stop and stop < 0:
                 raise ValueError
         except ValueError:
             raise InvalidHeader(_(u'no range number.'))
         if start is not None and stop is not None and stop < start:
             raise InvalidHeader(
                 _(u'range start must be smaller than end.'))
         ranges.add((start, stop))
     return cls(bytesunit, list(sorted(ranges)))
Esempio n. 35
0
	def __parse_chunk_size(self):
		line, rest_chunk = self.buffer.split(self.line_end, 1)
		chunk_size = line.split(b";", 1)[0].strip()
		try:
			chunk_size = int(bytes(chunk_size), 16)
			if chunk_size < 0:
				raise ValueError
		except (ValueError, OverflowError):
			exc = InvalidHeader(_(u'Invalid chunk size: %r'), chunk_size.decode('ISO8859-1'))
			raise BAD_REQUEST(Unicode(exc))
		else:
			return chunk_size, rest_chunk
Esempio n. 36
0
	def __parse_chunk_size(self):
		line, rest_chunk = self.buffer.split(self.line_end, 1)
		chunk_size = line.split(b";", 1)[0].strip()
		try:
			chunk_size = int(bytes(chunk_size), 16)
			if chunk_size < 0:
				raise ValueError
		except (ValueError, OverflowError):
			exc = InvalidHeader(_(u'Invalid chunk size: %r'), chunk_size.decode('ISO8859-1'))
			raise BAD_REQUEST(Unicode(exc))
		else:
			return chunk_size, rest_chunk
Esempio n. 37
0
	def parse(self, line):
		u"""parses the response line"""

		bits = line.strip().split(None, 1)
		try:
			version, status = bits
		except ValueError:
			raise InvalidLine(_(u'Invalid response line: %r'), line.decode('ISO8859-1'))

		# version
		super(Response, self).parse(version)

		# status
		self.status.parse(status)
Esempio n. 38
0
 def parse(cls, elementstr):
     value, start, end, complete_length = None, None, None, None
     try:
         value, content_range = elementstr.split(None, 1)
         if value != b'bytes':
             raise InvalidHeader(_(u'Only "bytes" Content-Range supported'))
         byte_range, complete_length = content_range.split(b'/')
         if complete_length != b'*':
             complete_length = int(complete_length)
             if complete_length < 0:
                 raise ValueError
         else:
             complete_length = None
         if byte_range != b'*':
             start, end = byte_range.split(b'-', 1)
             start, end = int(start), int(end)
             if start >= end or start < 0 or end < 0:
                 raise ValueError
         if complete_length is None and start is None:
             raise ValueError
     except ValueError:
         raise InvalidHeader(_(u'Content-Range: %r'), elementstr)
     return cls(value, (start, end), complete_length)
Esempio n. 39
0
	def parse(cls, elementstr):
		value, start, end, complete_length = None, None, None, None
		try:
			value, content_range = elementstr.split(None, 1)
			if value != b'bytes':
				raise InvalidHeader(_(u'Only "bytes" Content-Range supported'))
			byte_range, complete_length = content_range.split(b'/')
			if complete_length != b'*':
				complete_length = int(complete_length)
				if complete_length < 0:
					raise ValueError
			else:
				complete_length = None
			if byte_range != b'*':
				start, end = byte_range.split(b'-', 1)
				start, end = int(start), int(end)
				if start >= end or start < 0 or end < 0:
					raise ValueError
			if complete_length is None and start is None:
				raise ValueError
		except ValueError:
			raise InvalidHeader(_(u'Content-Range: %r'), elementstr)
		return cls(value, (start, end), complete_length)
Esempio n. 40
0
    def decode(cls, data, charset=None, mimetype=None):
        boundary = mimetype.boundary.encode('ISO8859-1')
        parts = data.split(b'--%s' % (boundary, ))
        part = parts.pop(0)
        if part:
            raise DecodeError(_(u'Data before boundary: %r'),
                              part.decode('ISO8859-1'))
        try:
            part = parts.pop()
        except IndexError:
            raise DecodeError(u'No end of boundary')
        if part not in (b'--', b'--\r\n'):
            raise DecodeError(_(u'Invalid multipart end: %r'),
                              part.decode('ISO8859-1'))

        from httoop.messages.body import Body
        multiparts = []
        for part in parts:
            if not part.startswith('\r\n'):
                raise DecodeError(_(u'Invalid boundary end: %r'),
                                  part[:2].decode('ISO8859-1'))
            part = part[2:]
            headers, separator, content = part.partition(b'\r\n\r\n')
            if not separator:
                raise DecodeError(
                    _(u'Multipart does not contain CRLF header separator'))
            if not content.endswith('\r\n'):
                raise DecodeError(_(u'Multipart does not end with CRLF: %r'),
                                  content[-2].decode('ISO8859-1'))
            content = content[:-2]
            body = Body()
            body.headers.clear()
            body.headers.parse(headers)
            body.headers.setdefault('Content-Type', cls.default_content_type)
            body.parse(content)
            multiparts.append(body)
        return multiparts
Esempio n. 41
0
	def parse(self, line):
		"""parses the request line and sets method, uri and protocol version
			:param line: the request line
			:type  line: bytes
		"""
		bits = line.strip().split(None, 2)
		try:
			method, uri, version = bits
		except ValueError:
			raise InvalidLine(_(u'Invalid request line: %r'), line.decode('ISO8859-1'))

		# protocol version
		super(Request, self).parse(version)

		# method
		self.method.parse(method)

		# URI
		if uri.startswith(b'//'):
			raise InvalidURI(_(u'The request URI must be an absolute path or contain a scheme.'))
		if self.method == u'CONNECT':
			uri = b'//%s' % (uri,)
		self.uri.parse(uri)
		self.validate_request_uri()
Esempio n. 42
0
    def parse(self, line):
        u"""parses the response line"""

        bits = line.strip().split(None, 1)
        try:
            version, status = bits
        except ValueError:
            raise InvalidLine(_(u'Invalid response line: %r'),
                              line.decode('ISO8859-1'))

        # version
        super(Response, self).parse(version)

        # status
        self.status.parse(status)
Esempio n. 43
0
    def parse(self, status):
        """parse a Statuscode and Reason-Phrase

			:param status: the code and reason
			:type  status: bytes
		"""
        match = self.STATUS_RE.match(status)
        if match is None:
            raise InvalidLine(_(u"Invalid status %r"),
                              status.decode('ISO8859-1'))

        self.set((
            int(match.group(1)),
            match.group(2).decode('ascii'),
        ))
Esempio n. 44
0
	def _compose_authority_iter(self):
		if not self.host:
			return
		username, password, host, port, quote = self.username, self.password, self.host, self.port, self.quote
		if username:
			yield quote(username, Percent.USERINFO)
			if password:
				yield b':'
				yield quote(password, Percent.USERINFO)
			yield b'@'
		try:
			yield host.encode('idna')
		except UnicodeError:  # u'..'.encode('idna')
			raise InvalidURI(_(u'Invalid URI: cannot encode host as IDNA.'))
		if port and int(port) != self.PORT:
			yield b':%d' % int(port)
Esempio n. 45
0
	def _compose_authority_iter(self):
		if not self.host:
			return
		username, password, host, port, quote = self.username, self.password, self.host, self.port, self.quote
		if username:
			yield quote(username, Percent.USERINFO)
			if password:
				yield b':'
				yield quote(password, Percent.USERINFO)
			yield b'@'
		try:
			yield host.encode('idna')
		except UnicodeError:  # u'..'.encode('idna')
			raise InvalidURI(_(u'Invalid URI: cannot encode host as IDNA.'))
		if port and int(port) != self.PORT:
			yield b':%d' % int(port)
Esempio n. 46
0
 def parse(cls, authinfo):
     params = super(cls, cls).parse(authinfo)
     if '"' in params['nonce']:
         raise InvalidHeader(_(u'Nonce must not contain double quote'))
     stale = params.get('stale')
     if stale:
         stale = {'false': False, 'true': True}.get(stale.lower())
     params = [
         ('realm', params['realm']),
         ('domain', params.get('domain', '').split()),
         ('nonce', params['nonce']),
         ('opaque', params.get('opaque')),
         ('stale', stale),
         ('algorithm', params.get('algorithm')),
         ('qop', [p.strip() for p in params.get('qop', '').split(',')]),
     ]
     return dict([(k, v) for k, v in params if v is not None])
Esempio n. 47
0
	def parse(cls, authinfo):
		params = super(cls, cls).parse(authinfo)
		if '"' in params['nonce']:
			raise InvalidHeader(_(u'Nonce must not contain double quote'))
		stale = params.get('stale')
		if stale:
			stale = {'false': False, 'true': True}.get(stale.lower())
		params = [
			('realm', params['realm']),
			('domain', params.get('domain', '').split()),
			('nonce', params['nonce']),
			('opaque', params.get('opaque')),
			('stale', stale),
			('algorithm', params.get('algorithm')),
			('qop', [p.strip() for p in params.get('qop', '').split(',')]),
		]
		return dict([(k, v) for k, v in params if v is not None])
Esempio n. 48
0
	def determine_message_length(self):
		# RFC 2616 Section 4.4
		# get message length

		# TODO: check if both is set
		message = self.message
		if 'Transfer-Encoding' in message.headers and message.protocol >= (1, 1):
			# chunked transfer in HTTP/1.1
			te = message.headers['Transfer-Encoding'].lower()
			self.chunked = 'chunked' == te
			if not self.chunked:
				raise NOT_IMPLEMENTED(u'Unknown HTTP/1.1 Transfer-Encoding: %r' % te)
		else:
			# Content-Length header defines the length of the message body
			try:
				self.message_length = int(message.headers.get("Content-Length", "0"))
				if self.message_length < 0:
					self.message_length = None
					raise ValueError
			except ValueError:
				raise BAD_REQUEST(_(u'Invalid Content-Length header.'))
Esempio n. 49
0
	def determine_message_length(self):
		# RFC 2616 Section 4.4
		# get message length

		# TODO: check if both is set
		message = self.message
		if 'Transfer-Encoding' in message.headers and message.protocol >= (1, 1):
			# chunked transfer in HTTP/1.1
			te = message.headers['Transfer-Encoding'].lower()
			self.chunked = 'chunked' == te
			if not self.chunked:
				raise NOT_IMPLEMENTED(u'Unknown HTTP/1.1 Transfer-Encoding: %r' % te)
		else:
			# Content-Length header defines the length of the message body
			try:
				self.message_length = int(message.headers.get("Content-Length", "0"))
				if self.message_length < 0:
					self.message_length = None
					raise ValueError
			except ValueError:
				raise BAD_REQUEST(_(u'Invalid Content-Length header.'))
Esempio n. 50
0
	def parse_trailers(self):
		# TODO: the code is exactly the same as parse_headers but
		# we have to make sure no invalid header fields are send (only values told in Trailer header allowed)
		if self.buffer.startswith(self.line_end):
			self.buffer = self.buffer[len(self.line_end):]
			return False # no trailers

		trailer_end = self.line_end + self.line_end
		if trailer_end not in self.buffer:
			# not received yet
			return NOT_RECEIVED_YET

		trailers, self.buffer = self.buffer.split(trailer_end, 1)
		self.trailers = Headers()
		try:
			self.trailers.parse(bytes(trailers))
		except InvalidHeader as exc:
			exc = InvalidHeader(_(u'Invalid trailers: %r'), Unicode(exc))
			raise BAD_REQUEST(Unicode(exc))

		self.merge_trailer_into_header()
		return False
Esempio n. 51
0
	def parse_trailers(self):
		# TODO: the code is exactly the same as parse_headers but
		# we have to make sure no invalid header fields are send (only values told in Trailer header allowed)
		if self.buffer.startswith(self.line_end):
			self.buffer = self.buffer[len(self.line_end):]
			return False  # no trailers

		trailer_end = self.line_end + self.line_end
		if trailer_end not in self.buffer:
			# not received yet
			return NOT_RECEIVED_YET

		trailers, self.buffer = self.buffer.split(trailer_end, 1)
		self.trailers = Headers()
		try:
			self.trailers.parse(bytes(trailers))
		except InvalidHeader as exc:
			exc = InvalidHeader(_(u'Invalid trailers: %r'), Unicode(exc))
			raise BAD_REQUEST(Unicode(exc))

		self.merge_trailer_into_header()
		return False
Esempio n. 52
0
    def parse(cls, timestr=None):
        u"""parses a HTTP date string and returns a :class:`Date` object

			:param timestr: the time string in one of the http formats
			:type  timestr: str

			:returns: the HTTP Date object
			:rtype  : :class:`Date`

		"""

        # parse the most common HTTP Date format (RFC 2822)
        date = parsedate(timestr)
        if date is not None:
            return cls(date[:9])

        old = locale.getlocale(locale.LC_TIME)
        locale.setlocale(locale.LC_TIME, (None, None))
        try:
            # parse RFC 1036 date format
            try:
                date = time.strptime(timestr, '%A, %d-%b-%y %H:%M:%S GMT')
            except ValueError:
                pass
            else:
                return cls(date)

            # parse C's asctime format
            try:
                date = time.strptime(timestr, '%a %b %d %H:%M:%S %Y')
            except ValueError:
                pass
            else:
                return cls(date)
        finally:
            locale.setlocale(locale.LC_TIME, old)

        raise InvalidDate(_(u'Invalid date: %r'), date)
Esempio n. 53
0
	def parse(cls, timestr=None):
		u"""parses a HTTP date string and returns a :class:`Date` object

			:param timestr: the time string in one of the http formats
			:type  timestr: str

			:returns: the HTTP Date object
			:rtype  : :class:`Date`

		"""

		# parse the most common HTTP Date format (RFC 2822)
		date = parsedate(timestr)
		if date is not None:
			return cls(date[:9])

		old = locale.getlocale(locale.LC_TIME)
		locale.setlocale(locale.LC_TIME, (None, None))
		try:
			# parse RFC 1036 date format
			try:
				date = time.strptime(timestr, '%A, %d-%b-%y %H:%M:%S GMT')
			except ValueError:
				pass
			else:
				return cls(date)

			# parse C's asctime format
			try:
				date = time.strptime(timestr, '%a %b %d %H:%M:%S %Y')
			except ValueError:
				pass
			else:
				return cls(date)
		finally:
			locale.setlocale(locale.LC_TIME, old)

		raise InvalidDate(_(u'Invalid date: %r'), date)
Esempio n. 54
0
	def parse_chunked_body(self):
		if self.line_end not in self.buffer:
			# chunk size info not received yet
			return NOT_RECEIVED_YET

		chunk_size, rest_chunk = self.__parse_chunk_size()

		if len(rest_chunk) < (len(self.line_end) + chunk_size):
			# chunk not received completely
			return NOT_RECEIVED_YET

		body_part, rest_chunk = rest_chunk[:chunk_size], rest_chunk[chunk_size:]
		self.message.body.parse(bytes(body_part))
		self.buffer = rest_chunk

		if chunk_size == 0:
			return self.parse_trailers()

		if not rest_chunk.startswith(self.line_end):
			raise InvalidBody(_(u'Invalid chunk terminator: %r'), repr(rest_chunk[:2]))
		self.buffer = self.buffer[len(self.line_end):]

		# next chunk
		return self.parse_chunked_body()
Esempio n. 55
0
	def parse_chunked_body(self):
		if self.line_end not in self.buffer:
			# chunk size info not received yet
			return NOT_RECEIVED_YET

		chunk_size, rest_chunk = self.__parse_chunk_size()

		if len(rest_chunk) < (len(self.line_end) + chunk_size):
			# chunk not received completely
			return NOT_RECEIVED_YET

		body_part, rest_chunk = rest_chunk[:chunk_size], rest_chunk[chunk_size:]
		self.message.body.parse(bytes(body_part))
		self.buffer = rest_chunk

		if chunk_size == 0:
			return self.parse_trailers()

		if not rest_chunk.startswith(self.line_end):
			raise InvalidBody(_(u'Invalid chunk terminator: %r'), repr(rest_chunk[:2]))
		self.buffer = self.buffer[len(self.line_end):]

		# next chunk
		return self.parse_chunked_body()
Esempio n. 56
0
	def parse(self, protocol):
		match = self.PROTOCOL_RE.match(protocol)
		if match is None:
			raise InvalidLine(_(u"Invalid HTTP protocol: %r"), protocol.decode('ISO8859-1'))
		self.__protocol = (int(match.group(2)), int(match.group(3)))
		self.name = match.group(1)
Esempio n. 57
0
	def parse(self, protocol):
		match = self.PROTOCOL_RE.match(protocol)
		if match is None:
			raise InvalidLine(_(u"Invalid HTTP protocol: %r"), protocol.decode('ISO8859-1'))
		self.__protocol = (int(match.group(2)), int(match.group(3)))
		self.name = match.group(1)
Esempio n. 58
0
	def decode(cls, data, charset=None, mimetype=None):
		try:
			data = zlib.decompress(data, 16 + zlib.MAX_WBITS)
		except zlib.error:
			raise DecodeError(_(u'Invalid gzip data.'))
		return Codec.decode(data, charset)