prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Error 302 redirection with headers location starts with 3 slash
  
  ### Description
  
  when the 302 response return a headers's location startswith 3 slash, the scrapy redirect to a url different from what the browser do.
  
  ### Steps to Reproduce
  
  1.  scrapy shell https://www.hjenglish.com/new/p1285798/
  
  **Expected behavior:** 
  redirect to `https://fr.hujiang.com/new/p1285798/` as browser `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36`  do.
  
  
  **Actual behavior:** 
  redirct to `https://www.hjenglish.com/fr.hujiang.com/new/p1285798`
  
  **Reproduces how often:** 
  
  everytime
  
  ### Versions
  Scrapy       : 1.7.3
  lxml         : 4.3.2.0
  libxml2      : 2.9.9
  cssselect    : 1.1.0
  parsel       : 1.5.2
  w3lib        : 1.20.0
  Twisted      : 19.7.0
  Python       : 3.7.3 (default, Mar 27 2019, 17:13:21) [MSC v.1915 64 bit (AMD64)]
  pyOpenSSL    : 19.0.0 (OpenSSL 1.1.1c  28 May 2019)
  cryptography : 2.6.1
  Platform     : Windows-10-10.0.17134-SP0
  
  
  ### Additional context
  
  I check the defination of [Location in rfc](https://tools.ietf.org/html/rfc7231#section-7.1.2) and end with [reference resolution](https://tools.ietf.org/html/rfc3986#section-5.3). But I fail to findout how to resolve the Location startswith `///`. So I don't know why Chrome did so.
  
  The behavior of scrapy is determined by [redirect.py#L73](https://github.com/scrapy/scrapy/blob/master/scrapy/downloadermiddlewares/redirect.py#L73), which will truncate `///` to `/`。
  
  I'm wandering the differents betweent scarpy and browser...
  
  
  --- 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/downloadermiddlewares/redirect.py`
  Content:
  ```
  1 import logging
  2 from six.moves.urllib.parse import urljoin
  3 
  4 from w3lib.url import safe_url_string
  5 
  6 from scrapy.http import HtmlResponse
  7 from scrapy.utils.response import get_meta_refresh
  8 from scrapy.exceptions import IgnoreRequest, NotConfigured
  9 
  10 logger = logging.getLogger(__name__)
  11 
  12 
  13 class BaseRedirectMiddleware(object):
  14 
  15     enabled_setting = 'REDIRECT_ENABLED'
  16 
  17     def __init__(self, settings):
  18         if not settings.getbool(self.enabled_setting):
  19             raise NotConfigured
  20 
  21         self.max_redirect_times = settings.getint('REDIRECT_MAX_TIMES')
  22         self.priority_adjust = settings.getint('REDIRECT_PRIORITY_ADJUST')
  23 
  24     @classmethod
  25     def from_crawler(cls, crawler):
  26         return cls(crawler.settings)
  27 
  28     def _redirect(self, redirected, request, spider, reason):
  29         ttl = request.meta.setdefault('redirect_ttl', self.max_redirect_times)
  30         redirects = request.meta.get('redirect_times', 0) + 1
  31 
  32         if ttl and redirects <= self.max_redirect_times:
  33             redirected.meta['redirect_times'] = redirects
  34             redirected.meta['redirect_ttl'] = ttl - 1
  35             redirected.meta['redirect_urls'] = request.meta.get('redirect_urls', []) + \
  36                 [request.url]
  37             redirected.meta['redirect_reasons'] = request.meta.get('redirect_reasons', []) + \
  38                 [reason]
  39             redirected.dont_filter = request.dont_filter
  40             redirected.priority = request.priority + self.priority_adjust
  41             logger.debug("Redirecting (%(reason)s) to %(redirected)s from %(request)s",
  42                          {'reason': reason, 'redirected': redirected, 'request': request},
  43                          extra={'spider': spider})
  44             return redirected
  45         else:
  46             logger.debug("Discarding %(request)s: max redirections reached",
  47                          {'request': request}, extra={'spider': spider})
  48             raise IgnoreRequest("max redirections reached")
  49 
  50     def _redirect_request_using_get(self, request, redirect_url):
  51         redirected = request.replace(url=redirect_url, method='GET', body='')
  52         redirected.headers.pop('Content-Type', None)
  53         redirected.headers.pop('Content-Length', None)
  54         return redirected
  55 
  56 
  57 class RedirectMiddleware(BaseRedirectMiddleware):
  58     """
  59     Handle redirection of requests based on response status
  60     and meta-refresh html tag.
  61     """
  62     def process_response(self, request, response, spider):
  63         if (request.meta.get('dont_redirect', False) or
  64                 response.status in getattr(spider, 'handle_httpstatus_list', []) or
  65                 response.status in request.meta.get('handle_httpstatus_list', []) or
  66                 request.meta.get('handle_httpstatus_all', False)):
  67             return response
  68 
  69         allowed_status = (301, 302, 303, 307, 308)
  70         if 'Location' not in response.headers or response.status not in allowed_status:
  71             return response
  72 
  73         location = safe_url_string(response.headers['location'])
  74 
  75         redirected_url = urljoin(request.url, location)
  76 
  77         if response.status in (301, 307, 308) or request.method == 'HEAD':
  78             redirected = request.replace(url=redirected_url)
  79             return self._redirect(redirected, request, spider, response.status)
  80 
  81         redirected = self._redirect_request_using_get(request, redirected_url)
  82         return self._redirect(redirected, request, spider, response.status)
  83 
  84 
  85 class MetaRefreshMiddleware(BaseRedirectMiddleware):
  86 
  87     enabled_setting = 'METAREFRESH_ENABLED'
  88 
  89     def __init__(self, settings):
  90         super(MetaRefreshMiddleware, self).__init__(settings)
  91         self._ignore_tags = settings.getlist('METAREFRESH_IGNORE_TAGS')
  92         self._maxdelay = settings.getint('REDIRECT_MAX_METAREFRESH_DELAY',
  93                                          settings.getint('METAREFRESH_MAXDELAY'))
  94 
  95     def process_response(self, request, response, spider):
  96         if request.meta.get('dont_redirect', False) or request.method == 'HEAD' or \
  97                 not isinstance(response, HtmlResponse):
  98             return response
  99 
  100         interval, url = get_meta_refresh(response,
  101                                          ignore_tags=self._ignore_tags)
  102         if url and interval < self._maxdelay:
  103             redirected = self._redirect_request_using_get(request, url)
  104             return self._redirect(redirected, request, spider, 'meta refresh')
  105 
  106         return response
  107
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/scrapy/downloadermiddlewares/redirect.py b/scrapy/downloadermiddlewares/redirect.py
  --- a/scrapy/downloadermiddlewares/redirect.py
  +++ b/scrapy/downloadermiddlewares/redirect.py
  @@ -1,5 +1,5 @@
   import logging
  -from six.moves.urllib.parse import urljoin
  +from six.moves.urllib.parse import urljoin, urlparse
   
   from w3lib.url import safe_url_string
   
  @@ -70,7 +70,10 @@
           if 'Location' not in response.headers or response.status not in allowed_status:
               return response
   
  -        location = safe_url_string(response.headers['location'])
  +        location = safe_url_string(response.headers['Location'])
  +        if response.headers['Location'].startswith(b'//'):
  +            request_scheme = urlparse(request.url).scheme
  +            location = request_scheme + '://' + location.lstrip('/')
   
           redirected_url = urljoin(request.url, location)
  
verification_info: {"golden_diff": "diff --git a/scrapy/downloadermiddlewares/redirect.py b/scrapy/downloadermiddlewares/redirect.py\n--- a/scrapy/downloadermiddlewares/redirect.py\n+++ b/scrapy/downloadermiddlewares/redirect.py\n@@ -1,5 +1,5 @@\n import logging\n-from six.moves.urllib.parse import urljoin\n+from six.moves.urllib.parse import urljoin, urlparse\n \n from w3lib.url import safe_url_string\n \n@@ -70,7 +70,10 @@\n         if 'Location' not in response.headers or response.status not in allowed_status:\n             return response\n \n-        location = safe_url_string(response.headers['location'])\n+        location = safe_url_string(response.headers['Location'])\n+        if response.headers['Location'].startswith(b'//'):\n+            request_scheme = urlparse(request.url).scheme\n+            location = request_scheme + '://' + location.lstrip('/')\n \n         redirected_url = urljoin(request.url, location)\n", "issue": "Error 302 redirection with headers location starts with 3 slash\n\r\n### Description\r\n\r\nwhen the 302 response return a headers's location startswith 3 slash, the scrapy redirect to a url different from what the browser do.\r\n\r\n### Steps to Reproduce\r\n\r\n1.  scrapy shell https://www.hjenglish.com/new/p1285798/\r\n\r\n**Expected behavior:** \r\nredirect to `https://fr.hujiang.com/new/p1285798/` as browser `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36`  do.\r\n\r\n\r\n**Actual behavior:** \r\nredirct to `https://www.hjenglish.com/fr.hujiang.com/new/p1285798`\r\n\r\n**Reproduces how often:** \r\n\r\neverytime\r\n\r\n### Versions\r\nScrapy       : 1.7.3\r\nlxml         : 4.3.2.0\r\nlibxml2      : 2.9.9\r\ncssselect    : 1.1.0\r\nparsel       : 1.5.2\r\nw3lib        : 1.20.0\r\nTwisted      : 19.7.0\r\nPython       : 3.7.3 (default, Mar 27 2019, 17:13:21) [MSC v.1915 64 bit (AMD64)]\r\npyOpenSSL    : 19.0.0 (OpenSSL 1.1.1c  28 May 2019)\r\ncryptography : 2.6.1\r\nPlatform     : Windows-10-10.0.17134-SP0\r\n\r\n\r\n### Additional context\r\n\r\nI check the defination of [Location in rfc](https://tools.ietf.org/html/rfc7231#section-7.1.2) and end with [reference resolution](https://tools.ietf.org/html/rfc3986#section-5.3). But I fail to findout how to resolve the Location startswith `///`. So I don't know why Chrome did so.\r\n\r\nThe behavior of scrapy is determined by [redirect.py#L73](https://github.com/scrapy/scrapy/blob/master/scrapy/downloadermiddlewares/redirect.py#L73), which will truncate `///` to `/`\u3002\r\n\r\nI'm wandering the differents betweent scarpy and browser...\r\n\n", "before_files": [{"content": "import logging\nfrom six.moves.urllib.parse import urljoin\n\nfrom w3lib.url import safe_url_string\n\nfrom scrapy.http import HtmlResponse\nfrom scrapy.utils.response import get_meta_refresh\nfrom scrapy.exceptions import IgnoreRequest, NotConfigured\n\nlogger = logging.getLogger(__name__)\n\n\nclass BaseRedirectMiddleware(object):\n\n    enabled_setting = 'REDIRECT_ENABLED'\n\n    def __init__(self, settings):\n        if not settings.getbool(self.enabled_setting):\n            raise NotConfigured\n\n        self.max_redirect_times = settings.getint('REDIRECT_MAX_TIMES')\n        self.priority_adjust = settings.getint('REDIRECT_PRIORITY_ADJUST')\n\n    @classmethod\n    def from_crawler(cls, crawler):\n        return cls(crawler.settings)\n\n    def _redirect(self, redirected, request, spider, reason):\n        ttl = request.meta.setdefault('redirect_ttl', self.max_redirect_times)\n        redirects = request.meta.get('redirect_times', 0) + 1\n\n        if ttl and redirects <= self.max_redirect_times:\n            redirected.meta['redirect_times'] = redirects\n            redirected.meta['redirect_ttl'] = ttl - 1\n            redirected.meta['redirect_urls'] = request.meta.get('redirect_urls', []) + \\\n                [request.url]\n            redirected.meta['redirect_reasons'] = request.meta.get('redirect_reasons', []) + \\\n                [reason]\n            redirected.dont_filter = request.dont_filter\n            redirected.priority = request.priority + self.priority_adjust\n            logger.debug(\"Redirecting (%(reason)s) to %(redirected)s from %(request)s\",\n                         {'reason': reason, 'redirected': redirected, 'request': request},\n                         extra={'spider': spider})\n            return redirected\n        else:\n            logger.debug(\"Discarding %(request)s: max redirections reached\",\n                         {'request': request}, extra={'spider': spider})\n            raise IgnoreRequest(\"max redirections reached\")\n\n    def _redirect_request_using_get(self, request, redirect_url):\n        redirected = request.replace(url=redirect_url, method='GET', body='')\n        redirected.headers.pop('Content-Type', None)\n        redirected.headers.pop('Content-Length', None)\n        return redirected\n\n\nclass RedirectMiddleware(BaseRedirectMiddleware):\n    \"\"\"\n    Handle redirection of requests based on response status\n    and meta-refresh html tag.\n    \"\"\"\n    def process_response(self, request, response, spider):\n        if (request.meta.get('dont_redirect', False) or\n                response.status in getattr(spider, 'handle_httpstatus_list', []) or\n                response.status in request.meta.get('handle_httpstatus_list', []) or\n                request.meta.get('handle_httpstatus_all', False)):\n            return response\n\n        allowed_status = (301, 302, 303, 307, 308)\n        if 'Location' not in response.headers or response.status not in allowed_status:\n            return response\n\n        location = safe_url_string(response.headers['location'])\n\n        redirected_url = urljoin(request.url, location)\n\n        if response.status in (301, 307, 308) or request.method == 'HEAD':\n            redirected = request.replace(url=redirected_url)\n            return self._redirect(redirected, request, spider, response.status)\n\n        redirected = self._redirect_request_using_get(request, redirected_url)\n        return self._redirect(redirected, request, spider, response.status)\n\n\nclass MetaRefreshMiddleware(BaseRedirectMiddleware):\n\n    enabled_setting = 'METAREFRESH_ENABLED'\n\n    def __init__(self, settings):\n        super(MetaRefreshMiddleware, self).__init__(settings)\n        self._ignore_tags = settings.getlist('METAREFRESH_IGNORE_TAGS')\n        self._maxdelay = settings.getint('REDIRECT_MAX_METAREFRESH_DELAY',\n                                         settings.getint('METAREFRESH_MAXDELAY'))\n\n    def process_response(self, request, response, spider):\n        if request.meta.get('dont_redirect', False) or request.method == 'HEAD' or \\\n                not isinstance(response, HtmlResponse):\n            return response\n\n        interval, url = get_meta_refresh(response,\n                                         ignore_tags=self._ignore_tags)\n        if url and interval < self._maxdelay:\n            redirected = self._redirect_request_using_get(request, url)\n            return self._redirect(redirected, request, spider, 'meta refresh')\n\n        return response\n", "path": "scrapy/downloadermiddlewares/redirect.py"}], "after_files": [{"content": "import logging\nfrom six.moves.urllib.parse import urljoin, urlparse\n\nfrom w3lib.url import safe_url_string\n\nfrom scrapy.http import HtmlResponse\nfrom scrapy.utils.response import get_meta_refresh\nfrom scrapy.exceptions import IgnoreRequest, NotConfigured\n\nlogger = logging.getLogger(__name__)\n\n\nclass BaseRedirectMiddleware(object):\n\n    enabled_setting = 'REDIRECT_ENABLED'\n\n    def __init__(self, settings):\n        if not settings.getbool(self.enabled_setting):\n            raise NotConfigured\n\n        self.max_redirect_times = settings.getint('REDIRECT_MAX_TIMES')\n        self.priority_adjust = settings.getint('REDIRECT_PRIORITY_ADJUST')\n\n    @classmethod\n    def from_crawler(cls, crawler):\n        return cls(crawler.settings)\n\n    def _redirect(self, redirected, request, spider, reason):\n        ttl = request.meta.setdefault('redirect_ttl', self.max_redirect_times)\n        redirects = request.meta.get('redirect_times', 0) + 1\n\n        if ttl and redirects <= self.max_redirect_times:\n            redirected.meta['redirect_times'] = redirects\n            redirected.meta['redirect_ttl'] = ttl - 1\n            redirected.meta['redirect_urls'] = request.meta.get('redirect_urls', []) + \\\n                [request.url]\n            redirected.meta['redirect_reasons'] = request.meta.get('redirect_reasons', []) + \\\n                [reason]\n            redirected.dont_filter = request.dont_filter\n            redirected.priority = request.priority + self.priority_adjust\n            logger.debug(\"Redirecting (%(reason)s) to %(redirected)s from %(request)s\",\n                         {'reason': reason, 'redirected': redirected, 'request': request},\n                         extra={'spider': spider})\n            return redirected\n        else:\n            logger.debug(\"Discarding %(request)s: max redirections reached\",\n                         {'request': request}, extra={'spider': spider})\n            raise IgnoreRequest(\"max redirections reached\")\n\n    def _redirect_request_using_get(self, request, redirect_url):\n        redirected = request.replace(url=redirect_url, method='GET', body='')\n        redirected.headers.pop('Content-Type', None)\n        redirected.headers.pop('Content-Length', None)\n        return redirected\n\n\nclass RedirectMiddleware(BaseRedirectMiddleware):\n    \"\"\"\n    Handle redirection of requests based on response status\n    and meta-refresh html tag.\n    \"\"\"\n    def process_response(self, request, response, spider):\n        if (request.meta.get('dont_redirect', False) or\n                response.status in getattr(spider, 'handle_httpstatus_list', []) or\n                response.status in request.meta.get('handle_httpstatus_list', []) or\n                request.meta.get('handle_httpstatus_all', False)):\n            return response\n\n        allowed_status = (301, 302, 303, 307, 308)\n        if 'Location' not in response.headers or response.status not in allowed_status:\n            return response\n\n        location = safe_url_string(response.headers['Location'])\n        if response.headers['Location'].startswith(b'//'):\n            request_scheme = urlparse(request.url).scheme\n            location = request_scheme + '://' + location.lstrip('/')\n\n        redirected_url = urljoin(request.url, location)\n\n        if response.status in (301, 307, 308) or request.method == 'HEAD':\n            redirected = request.replace(url=redirected_url)\n            return self._redirect(redirected, request, spider, response.status)\n\n        redirected = self._redirect_request_using_get(request, redirected_url)\n        return self._redirect(redirected, request, spider, response.status)\n\n\nclass MetaRefreshMiddleware(BaseRedirectMiddleware):\n\n    enabled_setting = 'METAREFRESH_ENABLED'\n\n    def __init__(self, settings):\n        super(MetaRefreshMiddleware, self).__init__(settings)\n        self._ignore_tags = settings.getlist('METAREFRESH_IGNORE_TAGS')\n        self._maxdelay = settings.getint('REDIRECT_MAX_METAREFRESH_DELAY',\n                                         settings.getint('METAREFRESH_MAXDELAY'))\n\n    def process_response(self, request, response, spider):\n        if request.meta.get('dont_redirect', False) or request.method == 'HEAD' or \\\n                not isinstance(response, HtmlResponse):\n            return response\n\n        interval, url = get_meta_refresh(response,\n                                         ignore_tags=self._ignore_tags)\n        if url and interval < self._maxdelay:\n            redirected = self._redirect_request_using_get(request, url)\n            return self._redirect(redirected, request, spider, 'meta refresh')\n\n        return response\n", "path": "scrapy/downloadermiddlewares/redirect.py"}]}