We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
PY3: error decoding Content-Disposition header
This request

```
scrapy shell 'http://npe.com.cn/plus/save_to_doc.php?id=1666'
```

raises this error:

```
Traceback (most recent call last):
  File "/Users/kmike/envs/dl/bin/scrapy", line 9, in <module>
    load_entry_point('Scrapy', 'console_scripts', 'scrapy')()
  File "/Users/kmike/svn/scrapy/scrapy/cmdline.py", line 142, in execute
    _run_print_help(parser, _run_command, cmd, args, opts)
  File "/Users/kmike/svn/scrapy/scrapy/cmdline.py", line 88, in _run_print_help
    func(*a, **kw)
  File "/Users/kmike/svn/scrapy/scrapy/cmdline.py", line 149, in _run_command
    cmd.run(args, opts)
  File "/Users/kmike/svn/scrapy/scrapy/commands/shell.py", line 71, in run
    shell.start(url=url)
  File "/Users/kmike/svn/scrapy/scrapy/shell.py", line 47, in start
    self.fetch(url, spider)
  File "/Users/kmike/svn/scrapy/scrapy/shell.py", line 112, in fetch
    reactor, self._schedule, request, spider)
  File "/Users/kmike/envs/dl/lib/python3.5/site-packages/Twisted-15.5.0-py3.5.egg/twisted/internet/threads.py", line 122, in blockingCallFromThread
    result.raiseException()
  File "/Users/kmike/envs/dl/lib/python3.5/site-packages/Twisted-15.5.0-py3.5.egg/twisted/python/failure.py", line 368, in raiseException
    raise self.value.with_traceback(self.tb)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb8 in position 25: invalid start byte
```

The error points to a wrong location (similar to #1760); the real traceback is

```
Traceback (most recent call last):
  File "/Users/kmike/envs/dl/lib/python3.5/site-packages/Twisted-15.5.0-py3.5.egg/twisted/internet/defer.py", line 1126, in _inlineCallbacks
    result = result.throwExceptionIntoGenerator(g)
  File "/Users/kmike/envs/dl/lib/python3.5/site-packages/Twisted-15.5.0-py3.5.egg/twisted/python/failure.py", line 389, in throwExceptionIntoGenerator
    return g.throw(self.type, self.value, self.tb)
  File "/Users/kmike/svn/scrapy/scrapy/core/downloader/middleware.py", line 43, in process_request
    defer.returnValue((yield download_func(request=request,spider=spider)))
  File "/Users/kmike/envs/dl/lib/python3.5/site-packages/Twisted-15.5.0-py3.5.egg/twisted/internet/defer.py", line 588, in _runCallbacks
    current.result = callback(current.result, *args, **kw)
  File "/Users/kmike/svn/scrapy/scrapy/core/downloader/handlers/http11.py", line 272, in _cb_bodydone
    respcls = responsetypes.from_args(headers=headers, url=url)
  File "/Users/kmike/svn/scrapy/scrapy/responsetypes.py", line 110, in from_args
    cls = self.from_headers(headers)
  File "/Users/kmike/svn/scrapy/scrapy/responsetypes.py", line 78, in from_headers
    cls = self.from_content_disposition(headers[b'Content-Disposition'])
  File "/Users/kmike/svn/scrapy/scrapy/responsetypes.py", line 62, in from_content_disposition
    filename = to_native_str(content_disposition).split(';')[1].split('=')[1]
  File "/Users/kmike/svn/scrapy/scrapy/utils/python.py", line 129, in to_native_str
    return to_unicode(text, encoding, errors)
  File "/Users/kmike/svn/scrapy/scrapy/utils/python.py", line 107, in to_unicode
    return text.decode(encoding, errors)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb8 in position 25: invalid start byte
```

It looks like Content-Disposition is decoded using utf-8, but the encoding was not UTF-8.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `scrapy/responsetypes.py`
Content:
```
1 """
2 This module implements a class which returns the appropriate Response class
3 based on different criteria.
4 """
5 from __future__ import absolute_import
6 from mimetypes import MimeTypes
7 from pkgutil import get_data
8 from io import StringIO
9 import six
10 
11 from scrapy.http import Response
12 from scrapy.utils.misc import load_object
13 from scrapy.utils.python import isbinarytext, to_bytes, to_native_str
14 
15 
16 class ResponseTypes(object):
17 
18     CLASSES = {
19         'text/html': 'scrapy.http.HtmlResponse',
20         'application/atom+xml': 'scrapy.http.XmlResponse',
21         'application/rdf+xml': 'scrapy.http.XmlResponse',
22         'application/rss+xml': 'scrapy.http.XmlResponse',
23         'application/xhtml+xml': 'scrapy.http.HtmlResponse',
24         'application/vnd.wap.xhtml+xml': 'scrapy.http.HtmlResponse',
25         'application/xml': 'scrapy.http.XmlResponse',
26         'application/json': 'scrapy.http.TextResponse',
27         'application/x-json': 'scrapy.http.TextResponse',
28         'application/javascript': 'scrapy.http.TextResponse',
29         'application/x-javascript': 'scrapy.http.TextResponse',
30         'text/xml': 'scrapy.http.XmlResponse',
31         'text/*': 'scrapy.http.TextResponse',
32     }
33 
34     def __init__(self):
35         self.classes = {}
36         self.mimetypes = MimeTypes()
37         mimedata = get_data('scrapy', 'mime.types').decode('utf8')
38         self.mimetypes.readfp(StringIO(mimedata))
39         for mimetype, cls in six.iteritems(self.CLASSES):
40             self.classes[mimetype] = load_object(cls)
41 
42     def from_mimetype(self, mimetype):
43         """Return the most appropriate Response class for the given mimetype"""
44         if mimetype is None:
45             return Response
46         elif mimetype in self.classes:
47             return self.classes[mimetype]
48         else:
49             basetype = "%s/*" % mimetype.split('/')[0]
50             return self.classes.get(basetype, Response)
51 
52     def from_content_type(self, content_type, content_encoding=None):
53         """Return the most appropriate Response class from an HTTP Content-Type
54         header """
55         if content_encoding:
56             return Response
57         mimetype = to_native_str(content_type).split(';')[0].strip().lower()
58         return self.from_mimetype(mimetype)
59 
60     def from_content_disposition(self, content_disposition):
61         try:
62             filename = to_native_str(content_disposition).split(';')[1].split('=')[1]
63             filename = filename.strip('"\'')
64             return self.from_filename(filename)
65         except IndexError:
66             return Response
67 
68     def from_headers(self, headers):
69         """Return the most appropriate Response class by looking at the HTTP
70         headers"""
71         cls = Response
72         if b'Content-Type' in headers:
73             cls = self.from_content_type(
74                 content_type=headers[b'Content-type'],
75                 content_encoding=headers.get(b'Content-Encoding')
76             )
77         if cls is Response and b'Content-Disposition' in headers:
78             cls = self.from_content_disposition(headers[b'Content-Disposition'])
79         return cls
80 
81     def from_filename(self, filename):
82         """Return the most appropriate Response class from a file name"""
83         mimetype, encoding = self.mimetypes.guess_type(filename)
84         if mimetype and not encoding:
85             return self.from_mimetype(mimetype)
86         else:
87             return Response
88 
89     def from_body(self, body):
90         """Try to guess the appropriate response based on the body content.
91         This method is a bit magic and could be improved in the future, but
92         it's not meant to be used except for special cases where response types
93         cannot be guess using more straightforward methods."""
94         chunk = body[:5000]
95         chunk = to_bytes(chunk)
96         if isbinarytext(chunk):
97             return self.from_mimetype('application/octet-stream')
98         elif b"<html>" in chunk.lower():
99             return self.from_mimetype('text/html')
100         elif b"<?xml" in chunk.lower():
101             return self.from_mimetype('text/xml')
102         else:
103             return self.from_mimetype('text')
104 
105     def from_args(self, headers=None, url=None, filename=None, body=None):
106         """Guess the most appropriate Response class based on
107         the given arguments."""
108         cls = Response
109         if headers is not None:
110             cls = self.from_headers(headers)
111         if cls is Response and url is not None:
112             cls = self.from_filename(url)
113         if cls is Response and filename is not None:
114             cls = self.from_filename(filename)
115         if cls is Response and body is not None:
116             cls = self.from_body(body)
117         return cls
118 
119 responsetypes = ResponseTypes()
120
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/scrapy/responsetypes.py b/scrapy/responsetypes.py
--- a/scrapy/responsetypes.py
+++ b/scrapy/responsetypes.py
@@ -59,7 +59,8 @@
 
     def from_content_disposition(self, content_disposition):
         try:
-            filename = to_native_str(content_disposition).split(';')[1].split('=')[1]
+            filename = to_native_str(content_disposition,
+                encoding='latin-1', errors='replace').split(';')[1].split('=')[1]
             filename = filename.strip('"\'')
             return self.from_filename(filename)
         except IndexError:

{"golden_diff": "diff --git a/scrapy/responsetypes.py b/scrapy/responsetypes.py\n--- a/scrapy/responsetypes.py\n+++ b/scrapy/responsetypes.py\n@@ -59,7 +59,8 @@\n \n     def from_content_disposition(self, content_disposition):\n         try:\n-            filename = to_native_str(content_disposition).split(';')[1].split('=')[1]\n+            filename = to_native_str(content_disposition,\n+                encoding='latin-1', errors='replace').split(';')[1].split('=')[1]\n             filename = filename.strip('\"\\'')\n             return self.from_filename(filename)\n         except IndexError:\n", "issue": "PY3: error decoding Content-Disposition header\nThis request\n\n```\nscrapy shell 'http://npe.com.cn/plus/save_to_doc.php?id=1666'\n```\n\nraises this error:\n\n```\nTraceback (most recent call last):\n  File \"/Users/kmike/envs/dl/bin/scrapy\", line 9, in <module>\n    load_entry_point('Scrapy', 'console_scripts', 'scrapy')()\n  File \"/Users/kmike/svn/scrapy/scrapy/cmdline.py\", line 142, in execute\n    _run_print_help(parser, _run_command, cmd, args, opts)\n  File \"/Users/kmike/svn/scrapy/scrapy/cmdline.py\", line 88, in _run_print_help\n    func(*a, **kw)\n  File \"/Users/kmike/svn/scrapy/scrapy/cmdline.py\", line 149, in _run_command\n    cmd.run(args, opts)\n  File \"/Users/kmike/svn/scrapy/scrapy/commands/shell.py\", line 71, in run\n    shell.start(url=url)\n  File \"/Users/kmike/svn/scrapy/scrapy/shell.py\", line 47, in start\n    self.fetch(url, spider)\n  File \"/Users/kmike/svn/scrapy/scrapy/shell.py\", line 112, in fetch\n    reactor, self._schedule, request, spider)\n  File \"/Users/kmike/envs/dl/lib/python3.5/site-packages/Twisted-15.5.0-py3.5.egg/twisted/internet/threads.py\", line 122, in blockingCallFromThread\n    result.raiseException()\n  File \"/Users/kmike/envs/dl/lib/python3.5/site-packages/Twisted-15.5.0-py3.5.egg/twisted/python/failure.py\", line 368, in raiseException\n    raise self.value.with_traceback(self.tb)\nUnicodeDecodeError: 'utf-8' codec can't decode byte 0xb8 in position 25: invalid start byte\n```\n\nThe error points to a wrong location (similar to #1760); the real traceback is\n\n```\nTraceback (most recent call last):\n  File \"/Users/kmike/envs/dl/lib/python3.5/site-packages/Twisted-15.5.0-py3.5.egg/twisted/internet/defer.py\", line 1126, in _inlineCallbacks\n    result = result.throwExceptionIntoGenerator(g)\n  File \"/Users/kmike/envs/dl/lib/python3.5/site-packages/Twisted-15.5.0-py3.5.egg/twisted/python/failure.py\", line 389, in throwExceptionIntoGenerator\n    return g.throw(self.type, self.value, self.tb)\n  File \"/Users/kmike/svn/scrapy/scrapy/core/downloader/middleware.py\", line 43, in process_request\n    defer.returnValue((yield download_func(request=request,spider=spider)))\n  File \"/Users/kmike/envs/dl/lib/python3.5/site-packages/Twisted-15.5.0-py3.5.egg/twisted/internet/defer.py\", line 588, in _runCallbacks\n    current.result = callback(current.result, *args, **kw)\n  File \"/Users/kmike/svn/scrapy/scrapy/core/downloader/handlers/http11.py\", line 272, in _cb_bodydone\n    respcls = responsetypes.from_args(headers=headers, url=url)\n  File \"/Users/kmike/svn/scrapy/scrapy/responsetypes.py\", line 110, in from_args\n    cls = self.from_headers(headers)\n  File \"/Users/kmike/svn/scrapy/scrapy/responsetypes.py\", line 78, in from_headers\n    cls = self.from_content_disposition(headers[b'Content-Disposition'])\n  File \"/Users/kmike/svn/scrapy/scrapy/responsetypes.py\", line 62, in from_content_disposition\n    filename = to_native_str(content_disposition).split(';')[1].split('=')[1]\n  File \"/Users/kmike/svn/scrapy/scrapy/utils/python.py\", line 129, in to_native_str\n    return to_unicode(text, encoding, errors)\n  File \"/Users/kmike/svn/scrapy/scrapy/utils/python.py\", line 107, in to_unicode\n    return text.decode(encoding, errors)\nUnicodeDecodeError: 'utf-8' codec can't decode byte 0xb8 in position 25: invalid start byte\n```\n\nIt looks like Content-Disposition is decoded using utf-8, but the encoding was not UTF-8.\n\n", "before_files": [{"content": "\"\"\"\nThis module implements a class which returns the appropriate Response class\nbased on different criteria.\n\"\"\"\nfrom __future__ import absolute_import\nfrom mimetypes import MimeTypes\nfrom pkgutil import get_data\nfrom io import StringIO\nimport six\n\nfrom scrapy.http import Response\nfrom scrapy.utils.misc import load_object\nfrom scrapy.utils.python import isbinarytext, to_bytes, to_native_str\n\n\nclass ResponseTypes(object):\n\n    CLASSES = {\n        'text/html': 'scrapy.http.HtmlResponse',\n        'application/atom+xml': 'scrapy.http.XmlResponse',\n        'application/rdf+xml': 'scrapy.http.XmlResponse',\n        'application/rss+xml': 'scrapy.http.XmlResponse',\n        'application/xhtml+xml': 'scrapy.http.HtmlResponse',\n        'application/vnd.wap.xhtml+xml': 'scrapy.http.HtmlResponse',\n        'application/xml': 'scrapy.http.XmlResponse',\n        'application/json': 'scrapy.http.TextResponse',\n        'application/x-json': 'scrapy.http.TextResponse',\n        'application/javascript': 'scrapy.http.TextResponse',\n        'application/x-javascript': 'scrapy.http.TextResponse',\n        'text/xml': 'scrapy.http.XmlResponse',\n        'text/*': 'scrapy.http.TextResponse',\n    }\n\n    def __init__(self):\n        self.classes = {}\n        self.mimetypes = MimeTypes()\n        mimedata = get_data('scrapy', 'mime.types').decode('utf8')\n        self.mimetypes.readfp(StringIO(mimedata))\n        for mimetype, cls in six.iteritems(self.CLASSES):\n            self.classes[mimetype] = load_object(cls)\n\n    def from_mimetype(self, mimetype):\n        \"\"\"Return the most appropriate Response class for the given mimetype\"\"\"\n        if mimetype is None:\n            return Response\n        elif mimetype in self.classes:\n            return self.classes[mimetype]\n        else:\n            basetype = \"%s/*\" % mimetype.split('/')[0]\n            return self.classes.get(basetype, Response)\n\n    def from_content_type(self, content_type, content_encoding=None):\n        \"\"\"Return the most appropriate Response class from an HTTP Content-Type\n        header \"\"\"\n        if content_encoding:\n            return Response\n        mimetype = to_native_str(content_type).split(';')[0].strip().lower()\n        return self.from_mimetype(mimetype)\n\n    def from_content_disposition(self, content_disposition):\n        try:\n            filename = to_native_str(content_disposition).split(';')[1].split('=')[1]\n            filename = filename.strip('\"\\'')\n            return self.from_filename(filename)\n        except IndexError:\n            return Response\n\n    def from_headers(self, headers):\n        \"\"\"Return the most appropriate Response class by looking at the HTTP\n        headers\"\"\"\n        cls = Response\n        if b'Content-Type' in headers:\n            cls = self.from_content_type(\n                content_type=headers[b'Content-type'],\n                content_encoding=headers.get(b'Content-Encoding')\n            )\n        if cls is Response and b'Content-Disposition' in headers:\n            cls = self.from_content_disposition(headers[b'Content-Disposition'])\n        return cls\n\n    def from_filename(self, filename):\n        \"\"\"Return the most appropriate Response class from a file name\"\"\"\n        mimetype, encoding = self.mimetypes.guess_type(filename)\n        if mimetype and not encoding:\n            return self.from_mimetype(mimetype)\n        else:\n            return Response\n\n    def from_body(self, body):\n        \"\"\"Try to guess the appropriate response based on the body content.\n        This method is a bit magic and could be improved in the future, but\n        it's not meant to be used except for special cases where response types\n        cannot be guess using more straightforward methods.\"\"\"\n        chunk = body[:5000]\n        chunk = to_bytes(chunk)\n        if isbinarytext(chunk):\n            return self.from_mimetype('application/octet-stream')\n        elif b\"<html>\" in chunk.lower():\n            return self.from_mimetype('text/html')\n        elif b\"<?xml\" in chunk.lower():\n            return self.from_mimetype('text/xml')\n        else:\n            return self.from_mimetype('text')\n\n    def from_args(self, headers=None, url=None, filename=None, body=None):\n        \"\"\"Guess the most appropriate Response class based on\n        the given arguments.\"\"\"\n        cls = Response\n        if headers is not None:\n            cls = self.from_headers(headers)\n        if cls is Response and url is not None:\n            cls = self.from_filename(url)\n        if cls is Response and filename is not None:\n            cls = self.from_filename(filename)\n        if cls is Response and body is not None:\n            cls = self.from_body(body)\n        return cls\n\nresponsetypes = ResponseTypes()\n", "path": "scrapy/responsetypes.py"}], "after_files": [{"content": "\"\"\"\nThis module implements a class which returns the appropriate Response class\nbased on different criteria.\n\"\"\"\nfrom __future__ import absolute_import\nfrom mimetypes import MimeTypes\nfrom pkgutil import get_data\nfrom io import StringIO\nimport six\n\nfrom scrapy.http import Response\nfrom scrapy.utils.misc import load_object\nfrom scrapy.utils.python import isbinarytext, to_bytes, to_native_str\n\n\nclass ResponseTypes(object):\n\n    CLASSES = {\n        'text/html': 'scrapy.http.HtmlResponse',\n        'application/atom+xml': 'scrapy.http.XmlResponse',\n        'application/rdf+xml': 'scrapy.http.XmlResponse',\n        'application/rss+xml': 'scrapy.http.XmlResponse',\n        'application/xhtml+xml': 'scrapy.http.HtmlResponse',\n        'application/vnd.wap.xhtml+xml': 'scrapy.http.HtmlResponse',\n        'application/xml': 'scrapy.http.XmlResponse',\n        'application/json': 'scrapy.http.TextResponse',\n        'application/x-json': 'scrapy.http.TextResponse',\n        'application/javascript': 'scrapy.http.TextResponse',\n        'application/x-javascript': 'scrapy.http.TextResponse',\n        'text/xml': 'scrapy.http.XmlResponse',\n        'text/*': 'scrapy.http.TextResponse',\n    }\n\n    def __init__(self):\n        self.classes = {}\n        self.mimetypes = MimeTypes()\n        mimedata = get_data('scrapy', 'mime.types').decode('utf8')\n        self.mimetypes.readfp(StringIO(mimedata))\n        for mimetype, cls in six.iteritems(self.CLASSES):\n            self.classes[mimetype] = load_object(cls)\n\n    def from_mimetype(self, mimetype):\n        \"\"\"Return the most appropriate Response class for the given mimetype\"\"\"\n        if mimetype is None:\n            return Response\n        elif mimetype in self.classes:\n            return self.classes[mimetype]\n        else:\n            basetype = \"%s/*\" % mimetype.split('/')[0]\n            return self.classes.get(basetype, Response)\n\n    def from_content_type(self, content_type, content_encoding=None):\n        \"\"\"Return the most appropriate Response class from an HTTP Content-Type\n        header \"\"\"\n        if content_encoding:\n            return Response\n        mimetype = to_native_str(content_type).split(';')[0].strip().lower()\n        return self.from_mimetype(mimetype)\n\n    def from_content_disposition(self, content_disposition):\n        try:\n            filename = to_native_str(content_disposition,\n                encoding='latin-1', errors='replace').split(';')[1].split('=')[1]\n            filename = filename.strip('\"\\'')\n            return self.from_filename(filename)\n        except IndexError:\n            return Response\n\n    def from_headers(self, headers):\n        \"\"\"Return the most appropriate Response class by looking at the HTTP\n        headers\"\"\"\n        cls = Response\n        if b'Content-Type' in headers:\n            cls = self.from_content_type(\n                content_type=headers[b'Content-type'],\n                content_encoding=headers.get(b'Content-Encoding')\n            )\n        if cls is Response and b'Content-Disposition' in headers:\n            cls = self.from_content_disposition(headers[b'Content-Disposition'])\n        return cls\n\n    def from_filename(self, filename):\n        \"\"\"Return the most appropriate Response class from a file name\"\"\"\n        mimetype, encoding = self.mimetypes.guess_type(filename)\n        if mimetype and not encoding:\n            return self.from_mimetype(mimetype)\n        else:\n            return Response\n\n    def from_body(self, body):\n        \"\"\"Try to guess the appropriate response based on the body content.\n        This method is a bit magic and could be improved in the future, but\n        it's not meant to be used except for special cases where response types\n        cannot be guess using more straightforward methods.\"\"\"\n        chunk = body[:5000]\n        chunk = to_bytes(chunk)\n        if isbinarytext(chunk):\n            return self.from_mimetype('application/octet-stream')\n        elif b\"<html>\" in chunk.lower():\n            return self.from_mimetype('text/html')\n        elif b\"<?xml\" in chunk.lower():\n            return self.from_mimetype('text/xml')\n        else:\n            return self.from_mimetype('text')\n\n    def from_args(self, headers=None, url=None, filename=None, body=None):\n        \"\"\"Guess the most appropriate Response class based on\n        the given arguments.\"\"\"\n        cls = Response\n        if headers is not None:\n            cls = self.from_headers(headers)\n        if cls is Response and url is not None:\n            cls = self.from_filename(url)\n        if cls is Response and filename is not None:\n            cls = self.from_filename(filename)\n        if cls is Response and body is not None:\n            cls = self.from_body(body)\n        return cls\n\nresponsetypes = ResponseTypes()\n", "path": "scrapy/responsetypes.py"}]}