Repository: aio-libs/aiohttp

Issue:
[bug] URL parssing error in the web server
If you run this simple server example :

``` python
import asyncio
from aiohttp import web

@asyncio.coroutine
def handle(request):
    return webResponse(body=request.path.encode('utf8'))

@asyncio.coroutine
def init(loop):
    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', handle)
    srv = yield from loop.create_server(app.make_handler(), '127.0.0.1', 5555)
    return srv

loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))

try:
    loop.run_forever()
except KeyboardInterrupt:
    pass
```

The following requests will get a `200 OK` with the considered path as content :

```
$ curl -X GET http://127.0.0.1:5555///
/
$ curl -X GET http://127.0.0.1:555//foo/
/
```

As you can see, the path of the URL is not well parsed.

This bug resides in the `_splitted_path` non-data descriptor which uses `urlsplit` on a path rather than a full URL. The consequence is a second segment interpreted as a network location if the first one is empty.

I've not quite investigated a fix at the moment, but  `_splitted_path` only being used by `raw_path` and `query_string` seems to be a plea to use a fake scheme and netloc for `urlsplit` to behave the expected way.



Discussion:
I'll take a look


Resolving patch:
diff --git a/aiohttp/web_reqrep.py b/aiohttp/web_reqrep.py
--- a/aiohttp/web_reqrep.py
+++ b/aiohttp/web_reqrep.py
@@ -173,7 +173,8 @@ def path_qs(self):
 
     @reify
     def _splitted_path(self):
-        return urlsplit(self._path_qs)
+        url = '{}://{}{}'.format(self.scheme, self.host, self._path_qs)
+        return urlsplit(url)
 
     @property
     def raw_path(self):


Test patch:
diff --git a/tests/test_web.py b/tests/test_web.py
--- a/tests/test_web.py
+++ b/tests/test_web.py
@@ -16,6 +16,7 @@ def setUp(self):
     def tearDown(self):
         self.loop.close()
 
+    @unittest.skip('moved to test_web_functional')
     def test_handler_returns_not_response(self):
         app = web.Application(loop=self.loop)
 
diff --git a/tests/test_web_functional.py b/tests/test_web_functional.py
--- a/tests/test_web_functional.py
+++ b/tests/test_web_functional.py
@@ -66,6 +66,20 @@ def go():
 
         self.loop.run_until_complete(go())
 
+    def test_handler_returns_not_response(self):
+
+        @asyncio.coroutine
+        def handler(request):
+            return 'abc'
+
+        @asyncio.coroutine
+        def go():
+            _, _, url = yield from self.create_server('GET', '/', handler)
+            resp = yield from request('GET', url, loop=self.loop)
+            self.assertEqual(500, resp.status)
+
+        self.loop.run_until_complete(go())
+
     def test_post_form(self):
 
         @asyncio.coroutine
diff --git a/tests/test_web_request.py b/tests/test_web_request.py
--- a/tests/test_web_request.py
+++ b/tests/test_web_request.py
@@ -64,6 +64,10 @@ def test_ctor(self):
         self.assertIs(self.transport, req.transport)
         self.assertTrue(req.keep_alive)
 
+    def test_doubleslashes(self):
+        req = self.make_request('GET', '//foo/')
+        self.assertEqual('//foo/', req.path)
+
     def test_POST(self):
         req = self.make_request('POST', '/')
         with self.assertRaises(RuntimeError):