Repository: aio-libs/aiohttp

Issue:
web.FileResponse overrides content_type
## Long story short

When returning a `web.FileResponse` from a route, any changes to the `content_type` member is not taken into account when the file is served. Instead, the file extension is always used to determine the content type of the response.

## Expected behaviour

`Content-Type` header of the response be that of `web.FileResponse.content_type` when such has been set.

## Actual behaviour

`Content-Type` header is guessed from the file extension of the served file.

## Steps to reproduce

Create a route that responds something along the lines of

```python
    response = web.FileResponse('/path/to/some/image.png')
    response.content_type = 'application/octet-stream'
    return response
```

then `curl -i` it and notice the `Content-Type` is `image/png`.

## Your environment

```
[~]$ uname -mrs
Linux 4.11.9-1-ARCH x86_64
[~]$ python --version
Python 3.6.2
[~]$ pip show aiohttp | grep Version
Version: 2.2.5
```



Discussion:
I'm inclined to opening a PR to resolve this issue, suggestions are highly appreciated.
The only problem is figuring out if content type was explicitly set.
User could do it by `resp.content_type = '...'` and `resp.headers['Content-Type']` modifications.
Default value for property is `'application/octet-stream'`.

Well, the following code should work:
```
if hdrs.CONTENT_TYPE not in self.headers:
        ct, encoding = mimetypes.guess_type(str(filepath))
        if not ct:
            ct = 'application/octet-stream'
        self.content_type = ct
        ....
```
~I think setting the content type header based on the `path` parameter during `__init__` is a better idea.  This follows the same behaviour as `web.Response`, and gives us access to the guessed content type from the property. As such, this seems like the least suprising way for `web.FileResponse` to behave.~

~Same goes for encoding~.

Ok, I see the flaw behind this, nevermind.


Resolving patch:
diff --git a/aiohttp/web_fileresponse.py b/aiohttp/web_fileresponse.py
--- a/aiohttp/web_fileresponse.py
+++ b/aiohttp/web_fileresponse.py
@@ -182,9 +182,13 @@ def prepare(self, request):
             self._length_check = False
             return (yield from super().prepare(request))
 
-        ct, encoding = mimetypes.guess_type(str(filepath))
-        if not ct:
-            ct = 'application/octet-stream'
+        if hdrs.CONTENT_TYPE not in self.headers:
+            ct, encoding = mimetypes.guess_type(str(filepath))
+            if not ct:
+                ct = 'application/octet-stream'
+        else:
+            ct = self.headers[hdrs.CONTENT_TYPE]
+            encoding = 'gzip' if gzip else None
 
         status = HTTPOk.status_code
         file_size = st.st_size


Test patch:
diff --git a/tests/test_web_sendfile_functional.py b/tests/test_web_sendfile_functional.py
--- a/tests/test_web_sendfile_functional.py
+++ b/tests/test_web_sendfile_functional.py
@@ -122,6 +122,54 @@ def handler(request):
     resp.close()
 
 
+@asyncio.coroutine
+def test_static_file_custom_content_type(loop, test_client, sender):
+    filepath = (pathlib.Path(__file__).parent / 'hello.txt.gz')
+
+    @asyncio.coroutine
+    def handler(request):
+        resp = sender(filepath, chunk_size=16)
+        resp.content_type = 'application/pdf'
+        return resp
+
+    app = web.Application()
+    app.router.add_get('/', handler)
+    client = yield from test_client(lambda loop: app)
+
+    resp = yield from client.get('/')
+    assert resp.status == 200
+    body = yield from resp.read()
+    with filepath.open('rb') as f:
+        content = f.read()
+        assert content == body
+    assert resp.headers['Content-Type'] == 'application/pdf'
+    assert resp.headers.get('Content-Encoding') is None
+    resp.close()
+
+
+@asyncio.coroutine
+def test_static_file_custom_content_type_compress(loop, test_client, sender):
+    filepath = (pathlib.Path(__file__).parent / 'hello.txt')
+
+    @asyncio.coroutine
+    def handler(request):
+        resp = sender(filepath, chunk_size=16)
+        resp.content_type = 'application/pdf'
+        return resp
+
+    app = web.Application()
+    app.router.add_get('/', handler)
+    client = yield from test_client(lambda loop: app)
+
+    resp = yield from client.get('/')
+    assert resp.status == 200
+    body = yield from resp.read()
+    assert b'hello aiohttp\n' == body
+    assert resp.headers['Content-Type'] == 'application/pdf'
+    assert resp.headers.get('Content-Encoding') == 'gzip'
+    resp.close()
+
+
 @asyncio.coroutine
 def test_static_file_with_content_encoding(loop, test_client, sender):
     filepath = pathlib.Path(__file__).parent / 'hello.txt.gz'