def test_dispatcher():
    def null_application(environ, start_response):
        start_response("404 NOT FOUND", [("Content-Type", "text/plain")])
        yield b"NOT FOUND"

    def dummy_application(environ, start_response):
        start_response("200 OK", [("Content-Type", "text/plain")])
        yield _to_bytes(environ["SCRIPT_NAME"])

    app = DispatcherMiddleware(
        null_application,
        {
            "/test1": dummy_application,
            "/test2/very": dummy_application
        },
    )
    tests = {
        "/test1": ("/test1", "/test1/asfd", "/test1/very"),
        "/test2/very":
        ("/test2/very", "/test2/very/long/path/after/script/name"),
    }

    for name, urls in tests.items():
        for p in urls:
            environ = create_environ(p)
            app_iter, status, headers = run_wsgi_app(app, environ)
            assert status == "200 OK"
            assert b"".join(app_iter).strip() == _to_bytes(name)

    app_iter, status, headers = run_wsgi_app(app, create_environ("/missing"))
    assert status == "404 NOT FOUND"
    assert b"".join(app_iter).strip() == b"NOT FOUND"
Example #2
0
def test_multiple_cookies():
    @Request.application
    def test_app(request):
        response = Response(repr(sorted(request.cookies.items())))
        response.set_cookie("test1", b"foo")
        response.set_cookie("test2", b"bar")
        return response

    client = Client(test_app)
    resp = client.get("/")
    assert resp.data == b"[]"
    resp = client.get("/")
    assert resp.data == _to_bytes(repr([("test1", "foo"), ("test2", "bar")]), "ascii")
Example #3
0
def handle_request(app, event, context=None):
    # if event.get("source") in ["scf.events", "serverless-plugin-warmup"]:
    #     print("Tencent Cloud Function warming event received, skipping handler")
    #     return {}

    if "multiValueHeaders" in event:
        headers = Headers(event["multiValueHeaders"])
    else:
        headers = Headers(event["headers"])

    strip_stage_path = os.environ.get("STRIP_STAGE_PATH", "").lower().strip() in [
        "yes",
        "y",
        "true",
        "t",
        "1",
    ]
    if (
        headers.get("Host", "").endswith(".apigw.tencentcs.com")
        and not strip_stage_path
    ):
        script_name = "/{}".format(event["requestContext"].get("stage", ""))
    else:
        script_name = ""

    # If a user is using a custom domain on API Gateway, they may have a base
    # path in their URL. This allows us to strip it out via an optional
    # environment variable.
    path_info = event["path"]
    base_path = os.environ.get("API_GATEWAY_BASE_PATH")
    print(base_path)
    if base_path:
        script_name = "/" + base_path

        if path_info.startswith(script_name):
            path_info = path_info[len(script_name) :] or "/"

    if "body" in event:
        body = event["body"] or ""
    else:
        body = ""

    if event.get("isBase64Encoded", False):
        body = base64.b64decode(body)
    if isinstance(body, str):
        body = _to_bytes(body, charset="utf-8")

    environ = {
        "CONTENT_LENGTH": str(len(body)),
        "CONTENT_TYPE": headers.get("Content-Type", ""),
        "PATH_INFO": url_unquote(path_info),
        "QUERY_STRING": encode_query_string(event),
        "REMOTE_ADDR": event["requestContext"].get("identity", {}).get("sourceIp", ""),
        "REMOTE_USER": event["requestContext"]
        .get("authorizer", {})
        .get("principalId", ""),
        "REQUEST_METHOD": event["httpMethod"],
        "SCRIPT_NAME": script_name,
        "SERVER_NAME": headers.get("Host", "lambda"),
        "SERVER_PORT": headers.get("X-Forwarded-Port", "80"),
        "SERVER_PROTOCOL": "HTTP/1.1",
        "wsgi.errors": sys.stderr,
        "wsgi.input": BytesIO(body),
        "wsgi.multiprocess": False,
        "wsgi.multithread": False,
        "wsgi.run_once": False,
        "wsgi.url_scheme": headers.get("X-Forwarded-Proto", "http"),
        "wsgi.version": (1, 0),
        "serverless.authorizer": event["requestContext"].get("authorizer"),
        "serverless.event": event,
        "serverless.context": context,
        # TODO: Deprecate the following entries, as they do not comply with the WSGI
        # spec. For custom variables, the spec says:
        #
        #   Finally, the environ dictionary may also contain server-defined variables.
        #   These variables should be named using only lower-case letters, numbers,
        #   dots, and underscores, and should be prefixed with a name that is unique
        #   to the defining server or gateway.
        "API_GATEWAY_AUTHORIZER": event["requestContext"].get("authorizer"),
        "event": event,
        "context": context,
    }

    for key, value in environ.items():
        if isinstance(value, str):
            environ[key] = _wsgi_encoding_dance(value)

    for key, value in headers.items():
        key = "HTTP_" + key.upper().replace("-", "_")
        if key not in ("HTTP_CONTENT_TYPE", "HTTP_CONTENT_LENGTH"):
            environ[key] = value

    response = Response.from_app(app, environ)

    returndict = {"statusCode": response.status_code}

    if "multiValueHeaders" in event:
        returndict["multiValueHeaders"] = group_headers(response.headers)
    else:
        returndict["headers"] = split_headers(response.headers)

    if event.get("requestContext").get("elb"):
        # If the request comes from ALB we need to add a status description
        returndict["statusDescription"] = "%d %s" % (
            response.status_code,
            HTTP_STATUS_CODES[response.status_code],
        )

    if response.data:
        mimetype = response.mimetype or "text/plain"
        if (
            mimetype.startswith("text/") or mimetype in TEXT_MIME_TYPES
        ) and not response.headers.get("Content-Encoding", ""):
            returndict["body"] = response.get_data(as_text=True)
            returndict["isBase64Encoded"] = False
        else:
            returndict["body"] = base64.b64encode(response.data).decode("utf-8")
            returndict["isBase64Encoded"] = True

    return returndict
Example #4
0
    def __init__(self, key: str, **kwargs):
        charset = kwargs.get('charset', 'utf-8')
        key = _to_bytes(key, charset=charset)
        value = _to_bytes(kwargs.get('value'), charset=charset)

        path = kwargs.get('path', None)
        if path is not None:
            path = iri_to_uri(path, charset)

        domain = _make_cookie_domain(kwargs.get('domain'))

        max_age = kwargs.get('max_age', None)
        if isinstance(max_age, timedelta):
            max_age = int(max_age.total_seconds())

        expires = kwargs.get('expires')
        if expires is not None:
            if not isinstance(expires, str):
                expires = http_date(expires)
        elif max_age is not None and kwargs.get('sync_expires', False):
            expires = http_date(
                datetime.now(tz=datetime.timezone.utc).timestamp() + max_age)

        same_site = kwargs.get('same_site', None)
        if same_site is not None:
            same_site = same_site.title()

            if same_site not in ['Strict', 'Lax', 'None']:
                raise ValueError('same site must be one of ')

        buffer = [key + b'=' + _cookie_quote(value)]

        cookie_attrs = [
            (b'Domain', domain, True),
            (b'Expires', expires, False),
            (b'Max-Age', max_age, False),
            (b'Secure', kwargs.get('secure', False), None),
            (b'HttpOnly', kwargs.get('http_only', False), None),
            (b'Path', path, False),
            (b'SameSite', same_site, False),
        ]

        for key, value, f in cookie_attrs:
            if f is None:
                if value:
                    buffer.append(key)
                continue

            if value is None:
                continue

            temp = bytearray(key)
            if not isinstance(value, (bytes, bytearray)):
                value = _to_bytes(str(value), charset)
            if f:
                value = _cookie_quote(value)
            temp += b'=' + value
            buffer.append(bytes(temp))

        return_value = b'; '.join(buffer)
        return_value = return_value.decode('latin1')

        cookie_size = len(return_value)

        max_size = kwargs.get('max_size', 4093)
        if max_size and cookie_size > max_size:
            value_size = len(value)
            warnings.warn(
                f"The {key.decode(charset)!r} cookie is too large: the value was"
                f" {value_size} bytes but the"
                f" header required {cookie_size - value_size} extra bytes. The final size"
                f" was {cookie_size} bytes but the limit is {max_size} bytes. Browsers may"
                f" silently ignore cookies larger than this.",
                stacklevel=2,
            )

        self.return_value = return_value
 def dummy_application(environ, start_response):
     start_response("200 OK", [("Content-Type", "text/plain")])
     yield _to_bytes(environ["SCRIPT_NAME"])
Example #6
0
 def inner(environ, start_response):
     if environ["PATH_INFO"] == "/_getpid":
         start_response("200 OK", [("Content-Type", "text/plain")])
         pid_logger.info("pid=%s", os.getpid())
         return [_to_bytes(str(os.getpid()))]
     return f(environ, start_response)