def _extractURLparts(request: IRequest) -> Tuple[str, str, int, str, str]: """ Extracts and decodes URI parts from C{request}. All strings must be UTF8-decodable. @param request: A Twisted Web request. @raise URLDecodeError: If one of the parts could not be decoded as UTF-8. @return: L{tuple} of the URL scheme, the server name, the server port, the path info and the script name. """ server_name = request.getRequestHostname() if hasattr(request.getHost(), "port"): server_port = request.getHost().port else: server_port = 0 if (bool(request.isSecure()), server_port) not in [ (True, 443), (False, 80), (False, 0), (True, 0), ]: server_name = server_name + b":" + intToBytes(server_port) script_name = b"" if request.prepath: script_name = b"/".join(request.prepath) if not script_name.startswith(b"/"): script_name = b"/" + script_name path_info = b"" if request.postpath: path_info = b"/".join(request.postpath) if not path_info.startswith(b"/"): path_info = b"/" + path_info url_scheme = "https" if request.isSecure() else "http" utf8Failures = [] try: server_name = server_name.decode("utf-8") except UnicodeDecodeError: utf8Failures.append(("SERVER_NAME", Failure())) try: path_text = path_info.decode("utf-8") except UnicodeDecodeError: utf8Failures.append(("PATH_INFO", Failure())) try: script_text = script_name.decode("utf-8") except UnicodeDecodeError: utf8Failures.append(("SCRIPT_NAME", Failure())) if utf8Failures: raise _URLDecodeError(utf8Failures) return url_scheme, server_name, server_port, path_text, script_text
def get_request_uri(request: IRequest) -> bytes: """Return the full URI that was requested by the client""" return b"%s://%s%s" % ( b"https" if request.isSecure() else b"http", _get_requested_host(request), # despite its name, "request.uri" is only the path and query-string. request.uri, )
def _get_requested_host(request: IRequest) -> bytes: hostname = request.getHeader(b"host") if hostname: return hostname # no Host header, use the address/port that the request arrived on host: Union[address.IPv4Address, address.IPv6Address] = request.getHost() hostname = host.host.encode("ascii") if request.isSecure() and host.port == 443: # default port for https return hostname if not request.isSecure() and host.port == 80: # default port for http return hostname return b"%s:%i" % ( hostname, host.port, )
def urlFromRequest(request: IRequest) -> DecodedURL: sentHeader = request.getHeader(b"host") if sentHeader is not None: sentHeader = sentHeader.decode("charmap") if ":" in sentHeader: host, port = sentHeader.split(":") port = int(port) else: host = sentHeader port = None else: host = request.client.host port = request.client.port url = DecodedURL.fromText(request.uri.decode("charmap")) url = url.replace( scheme="https" if request.isSecure() else "http", host=host, port=port, ) return url
def procureSession(self, request: IRequest, forceInsecure: bool = False) -> Any: alreadyProcured = request.getComponent(ISession) if alreadyProcured is not None: if not forceInsecure or not request.isSecure(): returnValue(alreadyProcured) if request.isSecure(): if forceInsecure: tokenHeader = self._insecureTokenHeader cookieName: Union[str, bytes] = self._insecureCookie sentSecurely = False else: tokenHeader = self._secureTokenHeader cookieName = self._secureCookie sentSecurely = True else: # Have we inadvertently disclosed a secure token over an insecure # transport, for example, due to a buggy client? allPossibleSentTokens: Sequence[str] = sum( [ request.requestHeaders.getRawHeaders(header, []) for header in [ self._secureTokenHeader, self._insecureTokenHeader, ] ], [], ) + [ it for it in [ request.getCookie(cookie) for cookie in [self._secureCookie, self._insecureCookie] ] if it ] # Does it seem like this check is expensive? It sure is! Don't want # to do it? Turn on your dang HTTPS! yield self._store.sentInsecurely(allPossibleSentTokens) tokenHeader = self._insecureTokenHeader cookieName = self._insecureCookie sentSecurely = False # Fun future feature: honeypot that does this over HTTPS, but sets # isSecure() to return false because it serves up a cert for the # wrong hostname or an invalid cert, to keep API clients honest # about chain validation. sentHeader = (request.getHeader(tokenHeader) or b"").decode("utf-8") sentCookie = (request.getCookie(cookieName) or b"").decode("utf-8") if sentHeader: mechanism = SessionMechanism.Header else: mechanism = SessionMechanism.Cookie if not (sentHeader or sentCookie): session = None else: try: session = yield self._store.loadSession( sentHeader or sentCookie, sentSecurely, mechanism) except NoSuchSession: if mechanism == SessionMechanism.Header: raise session = None if mechanism == SessionMechanism.Cookie and ( session is None or session.identifier != sentCookie): if session is None: if request.startedWriting: # At this point, if the mechanism is Header, we either have # a valid session or we bailed after NoSuchSession above. raise TooLateForCookies( "You tried initializing a cookie-based session too" " late in the request pipeline; the headers" " were already sent.") if request.method != b"GET": # Sessions should only ever be auto-created by GET # requests; there's no way that any meaningful data # manipulation could succeed (no CSRF token check could # ever succeed, for example). raise NoSuchSession( "Can't initialize a session on a " "{method} request.".format( method=request.method.decode("ascii"))) if not self._setCookieOnGET: # We don't have a session ID at all, and we're not allowed # by policy to set a cookie on the client. raise NoSuchSession( "Cannot auto-initialize a session for this request.") session = yield self._store.newSession(sentSecurely, mechanism) identifierInCookie = session.identifier if not isinstance(identifierInCookie, str): identifierInCookie = identifierInCookie.encode("ascii") if not isinstance(cookieName, str): cookieName = cookieName.decode("ascii") request.addCookie( cookieName, identifierInCookie, max_age=str(self._maxAge), domain=self._cookieDomain, path=self._cookiePath, secure=sentSecurely, httpOnly=True, ) if sentSecurely or not request.isSecure(): # Do not cache the insecure session on the secure request, thanks. request.setComponent(ISession, session) returnValue(session)