Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cookies not supported for other than localhost domain #162

Open
vlcinsky opened this issue Jul 24, 2016 · 0 comments
Open

Cookies not supported for other than localhost domain #162

vlcinsky opened this issue Jul 24, 2016 · 0 comments

Comments

@vlcinsky
Copy link

vlcinsky commented Jul 24, 2016

WebTest claims to support tests of WSGI as well as non WSGI applications, see Testing a non wsgi application.

However, when trying to test non-WSGI app from external domain (localhosts works well), it does not work well with cookies, which are not resent to the server with subsequent requests.

The problem can be resolved if extra_environ["HTTP_HOST"] is set properly to name of target host.

The tests were run on httpbin running on http://httpbin.org`, https://httpbin.org and on local instance on http://localhost:5000.
Local instance of httpbin was run by:

$ pip install httpbin
$ export FLASK_APP=httpbin
$ flask run
 * Serving Flask app "httpbin"
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Using pytest I have created simple test suite:

import pytest
from webtest import TestApp
import httpbin


apps = [("wsgiapp", httpbin.app),
        ("http", "http://httpbin.org"),
        ("https", "https://httpbin.org"),
        ("http#requests", "http://httpbin.org#requests"),
        ("https#requests", "https://httpbin.org#requests"),
        ("http#urllib3", "http://httpbin.org#urllib3"),
        ("http#restkit", "http://httpbin.org#restkit"),
        ("localhost_http", "http://localhost:5000"),
        ("localhost_http#requests", "http://localhost:5000#requests"),
        ("localhost_http#urllib3", "http://localhost:5000#urllib3"),
        ("localhost_http#restkit", "http://localhost:5000#restkit"),
        ]


@pytest.fixture(params=apps, ids=lambda arg: arg[0])
def testapp(request):
    testid, app = request.param
    testapp = TestApp(app)
    return testapp


def amend_testapp(testapp):
    """Amend TestApp instance to deal with cookies properly.
    `testapp` is instance of webtest.TestApp.
    """
    if hasattr(testapp.app, "uri"):
        from urlparse import urlparse
        uri = testapp.app.uri
        testapp.extra_environ["HTTP_HOST"] = urlparse(uri).hostname
    return testapp


@pytest.mark.parametrize("amend", ["original", "amended"])
def test_bin(testapp, amend):
    if amend == "amended":
        testapp = amend_testapp(testapp)

    assert not testapp.cookies, "Cookies in the testapp shall be empty initially"

    # list existing cookies
    res = testapp.get("/cookies")
    assert not res.json["cookies"], "no cookies reported in response"

    # set some cookie
    cname, cval = "james", "cook"
    res = testapp.get("/cookies/set", {cname: cval})
    # set-cookie header shall be present in the response
    print res.headers.keys()
    print res.headers["set-cookie"]
    print "Set-Cookie" in res.headers, "Header Set-Cookie expected in response headers"
    # make sure, it is set in test testapp cookiejar
    assert testapp.cookies
    assert cname in testapp.cookies
    assert testapp.cookies[cname] == cval
    # make sure, testapp reports the cookies as being set
    res = testapp.get("/cookies")
    cookies = res.json["cookies"]
    assert cname in cookies
    assert cookies[cname] == cval

To run it, install required dependencies:

$ pip install webtest pytest wsgiproxy2 httpbin requests restkit urllib3

and run the test:

$ py.test -v tests/test_it.py
...
tests/test_it.py::test_bin[wsgiapp-original] PASSED
tests/test_it.py::test_bin[wsgiapp-amended] PASSED
tests/test_it.py::test_bin[http-original] FAILED
tests/test_it.py::test_bin[http-amended] PASSED
tests/test_it.py::test_bin[https-original] FAILED
tests/test_it.py::test_bin[https-amended] PASSED
tests/test_it.py::test_bin[http#requests-original] FAILED
tests/test_it.py::test_bin[http#requests-amended] PASSED
tests/test_it.py::test_bin[https#requests-original] FAILED
tests/test_it.py::test_bin[https#requests-amended] PASSED
tests/test_it.py::test_bin[http#urllib3-original] FAILED
tests/test_it.py::test_bin[http#urllib3-amended] PASSED
tests/test_it.py::test_bin[http#restkit-original] FAILED
tests/test_it.py::test_bin[http#restkit-amended] PASSED
tests/test_it.py::test_bin[localhost_http-original] PASSED
tests/test_it.py::test_bin[localhost_http-amended] PASSED
tests/test_it.py::test_bin[localhost_http#requests-original] PASSED
tests/test_it.py::test_bin[localhost_http#requests-amended] PASSED
tests/test_it.py::test_bin[localhost_http#urllib3-original] PASSED
tests/test_it.py::test_bin[localhost_http#urllib3-amended] PASSED
tests/test_it.py::test_bin[localhost_http#restkit-original] PASSED
tests/test_it.py::test_bin[localhost_http#restkit-amended] PASSED

Typical failure looks like:

testapp = <webtest.app.TestApp object at 0x7f046ab84350>, amend = 'original'

    @pytest.mark.parametrize("amend", ["original", "amended"])
    def test_bin(testapp, amend):
        if amend == "amended":
            testapp = amend_testapp(testapp)

        assert not testapp.cookies, "Cookies in the testapp shall be empty initially"

        # list existing cookies
        res = testapp.get("/cookies")
        assert not res.json["cookies"], "no cookies reported in response"

        # set some cookie
        cname, cval = "james", "cook"
        res = testapp.get("/cookies/set", {cname: cval})
        # set-cookie header shall be present in the response
        print res.headers.keys()
        print res.headers["set-cookie"]
        print "Set-Cookie" in res.headers, "Header Set-Cookie expected in response headers"
        # make sure, it is set in test testapp cookiejar
        assert testapp.cookies
        assert cname in testapp.cookies
        assert testapp.cookies[cname] == cval
        # make sure, testapp reports the cookies as being set
        res = testapp.get("/cookies")
        cookies = res.json["cookies"]
>       assert cname in cookies
E       assert 'james' in {}

Workaround and how it works

Whole workaround is provided in following function amend_testapp:

def amend_testapp(testapp):
    """Amend TestApp instance to deal with cookies properly.
    `testapp` is instance of webtest.TestApp.
    """
    if hasattr(testapp.app, "uri"):
        from urlparse import urlparse
        uri = testapp.app.uri
        testapp.extra_environ["HTTP_HOST"] = urlparse(uri).hostname
    return testapp

It gets an instance of TestApp and tries to detect, if it was created using url and not wsgi application. If the attribute "uri" of testapp.app" is present, it is likely to be based on url and not WSGI app. From theurivalue is detected hostname and set astestapp.extra_environ["HTTP_HOST"]`.

This solution shall work well in following circumstances:

  • application was created either as WSGI one or by providing url (which is allowed to contain the #{clientlibname} part like #requests at the end.
  • using environmental variable WEBTEST_TARGET_URL shall work well as we fix the testapp after it is created.
  • if (during creation of testapp was any extra_environ set, it is preserved (with only exception being HTTP_HOST).

How to fix it in WebTest library

Being able to test WSGI app as well as external ones is great feature and I think, WebTest shall keep this functional.

The question is, where shall be this fix put and options are:

  • WebTest library itself.
  • WsgiProxy2 library, which is used to handle applications accessible via url (it provides WSGI application by proxying requests to real url).
  • some CookieJar implementation.

I think, that it cannot be fixed on WsgiProxy2 and CookieJar level as it is too late to fix anything.

The only place to fix is probably the WebTest library itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant