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.'))
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()
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.' ))
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
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
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)
def parse(self, uri): r"""Parses a well formed absolute or relative URI. foo://example.com:8042/over/there?name=ferret#nose \_/ \______________/\_________/ \_________/ \__/ | | | | | scheme authority path query fragment | _____________________|__ / \ / \ urn:example:animal:ferret:nose https://username:password@[::1]:8090/some/path?query#fragment <scheme>://<username>:<password>@<host>:<port>/<path>?<query>#<fragment> [<scheme>:][//[<username>[:<password>]@][<host>][:<port>]/]<path>[?<query>][#<fragment>] """ if isinstance(uri, Unicode): try: uri = uri.encode('ascii') except UnicodeEncodeError: raise TypeError('URI must be ASCII bytes.') if type(self) is URI and b':' in uri: self.scheme = uri.split(b':', 1)[0].lower() if type(self) is not URI: return self.parse(uri) if uri and uri.strip(b'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'): raise InvalidURI(_(u'Invalid URI: must consist of printable ASCII characters without whitespace.')) uri, __, fragment = uri.partition(b'#') uri, __, query_string = uri.partition(b'?') scheme, authority_exists, uri = uri.rpartition(b'://') if not authority_exists and uri.startswith(b'//'): uri = uri[2:] authority_exists = True if not authority_exists and b':' in uri: scheme, __, uri = uri.partition(b':') authority, path = b'', uri if authority_exists: authority, __, path = uri.partition(b'/') path = b'%s%s' % (__, path) userinfo, __, hostport = authority.rpartition(b'@') username, __, password = userinfo.partition(b':') if b':' in hostport and not hostport.endswith(b']'): host, __, port = hostport.rpartition(b':') else: host, port = hostport, b'' unquote = self.unquote path = u'/'.join([unquote(seq).replace(u'/', u'%2f') for seq in path.split(b'/')]) try: scheme = scheme.decode('ascii').lower() except UnicodeDecodeError: raise InvalidURI(_(u'Invalid scheme: must be ASCII.')) if scheme and scheme.strip(u'abcdefghijklmnopqrstuvwxyz0123456789.-+'): raise InvalidURI(_(u'Invalid scheme: must only contain alphanumeric letters or plus, dash, dot.')) if query_string: query_string = QueryString.encode(QueryString.decode(query_string, self.encoding), self.encoding) self.tuple = ( scheme, unquote(username), unquote(password), self._unquote_host(host), port, path, query_string.decode(self.encoding), unquote(fragment) )