Repository: aio-libs/aiohttp

Issue:
Adding routes to subapps is broken after attaching subapp to the parent app
## Long story short

If one adds routes to subapp before `app.add_subapp` accessing them works well, otherwise it results in 404.

## Expected behaviour

It's naturally expected that routing will work well regardless of the order of function calls.

## Actual behaviour

Given `sub_app_cbv.py`:
```python
import sys
import aiohttp.web

API_URL_PREFIX = r'/api/v1'

class PingView(aiohttp.web.View):
    async def get(self):
        return aiohttp.web.Response(text='pong')

def just_app_with_routes_def(app, api_app):
    app.router.add_route('GET', API_URL_PREFIX + r'/ping/', PingView)

def sub_app_after_routes_def(app, api_app):
    api_app.router.add_route('GET', r'/ping/', PingView)
    app.add_subapp(API_URL_PREFIX, api_app)

def sub_app_before_routes_def(app, api_app):
    app.add_subapp(API_URL_PREFIX, api_app)
    api_app.router.add_route('GET', r'/ping/', PingView)

def run_app(coro_name):
    print(f'Going to run {coro_name}')
    setup_app = globals()[coro_name]
    app = aiohttp.web.Application()
    api_app = aiohttp.web.Application()
    setup_app(app, api_app)
    aiohttp.web.run_app(app)

def main():
    if len(sys.argv) < 2:
        raise RuntimeError('Supply one cli argument plz [just_app_with_routes_def|sub_app_after_routes_def|sub_app_before_routes_def]')
    coro_name = sys.argv[1]
    run_app(coro_name)

__name__ == '__main__' and main()
```

##### Demo 1

```shell
$ python sub_app_cbv.py just_app_with_routes_def
Going to run just_app_with_routes_def
======== Running on http://0.0.0.0:8080 ========
(Press CTRL+C to quit)
```

```shell
$ http :8080/api/v1/ping/
HTTP/1.1 200 OK
Content-Length: 4
Content-Type: text/plain; charset=utf-8
Date: Thu, 11 Jan 2018 23:17:52 GMT
Server: Python/3.6 aiohttp/2.3.7
pong
```

##### Demo 2

```shell
$ python sub_app_cbv.py sub_app_after_routes_def
Going to run sub_app_after_routes_def
======== Running on http://0.0.0.0:8080 ========
(Press CTRL+C to quit)
```

```shell
$ http :8080/api/v1/ping/
HTTP/1.1 200 OK
Content-Length: 4
Content-Type: text/plain; charset=utf-8
Date: Thu, 11 Jan 2018 23:20:58 GMT
Server: Python/3.6 aiohttp/2.3.7
pong
```

##### Demo 3

```shell
$ python sub_app_cbv.py sub_app_before_routes_def
Going to run sub_app_before_routes_def
======== Running on http://0.0.0.0:8080 ========
(Press CTRL+C to quit)
```

```shell
$ http :8080/api/v1/ping/
HTTP/1.1 404 Not Found
Content-Length: 14
Content-Type: text/plain; charset=utf-8
Date: Thu, 11 Jan 2018 23:21:41 GMT
Server: Python/3.6 aiohttp/2.3.7
404: Not Found
```

## Steps to reproduce

See above.

## Your environment

Gentoo laptop, pyenv, 3.6. Not really env-related.



Discussion:

Resolving patch:
diff --git a/aiohttp/web_app.py b/aiohttp/web_app.py
--- a/aiohttp/web_app.py
+++ b/aiohttp/web_app.py
@@ -163,6 +163,7 @@ def add_subapp(self, prefix, subapp):
         self.router.register_resource(resource)
         self._reg_subapp_signals(subapp)
         self._subapps.append(subapp)
+        subapp.freeze()
         if self._loop is not None:
             subapp._set_loop(self._loop)
         return resource


Test patch:
diff --git a/tests/test_web_app.py b/tests/test_web_app.py
--- a/tests/test_web_app.py
+++ b/tests/test_web_app.py
@@ -236,3 +236,11 @@ async def middleware(request, handler):
     root.add_subapp('/sub', sub)
     root.freeze()
     assert root._run_middlewares is True
+
+
+def test_subapp_frozen_after_adding():
+    app = web.Application()
+    subapp = web.Application()
+
+    app.add_subapp('/prefix', subapp)
+    assert subapp.frozen
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
@@ -1188,11 +1188,11 @@ async def middleware(request):
     subapp1 = web.Application(middlewares=[middleware_factory])
     subapp2 = web.Application(middlewares=[middleware_factory])
     subapp2.router.add_get('/to', handler)
-    subapp1.add_subapp('/b/', subapp2)
-    app.add_subapp('/a/', subapp1)
-
     with pytest.warns(DeprecationWarning):
+        subapp1.add_subapp('/b/', subapp2)
+        app.add_subapp('/a/', subapp1)
         client = await test_client(app)
+
     resp = await client.get('/a/b/to')
     assert resp.status == 200
     assert [(1, app), (1, subapp1), (1, subapp2),