prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Requests does not follow on an HTTP 301
  We had already an issue when kickass redirected from https to http I seem to remember. Or it was on another engine. And the request wasn't followed so we had an empty response with status_code 301 in the engine.
  
  Anyway, that is still the case in kickass if we try to perform a search when the query contains capitalized letters.
  
  So why isn't 301 followed ? Is there a way to force it ?
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `searx/engines/kickass.py`
  Content:
  ```
  1 ## Kickass Torrent (Videos, Music, Files)
  2 #
  3 # @website     https://kickass.so
  4 # @provide-api no (nothing found)
  5 #
  6 # @using-api   no
  7 # @results     HTML (using search portal)
  8 # @stable      yes (HTML can change)
  9 # @parse       url, title, content, seed, leech, magnetlink
  10 
  11 from urlparse import urljoin
  12 from cgi import escape
  13 from urllib import quote
  14 from lxml import html
  15 from operator import itemgetter
  16 from searx.engines.xpath import extract_text
  17 
  18 # engine dependent config
  19 categories = ['videos', 'music', 'files']
  20 paging = True
  21 
  22 # search-url
  23 url = 'https://kickass.so/'
  24 search_url = url + 'search/{search_term}/{pageno}/'
  25 
  26 # specific xpath variables
  27 magnet_xpath = './/a[@title="Torrent magnet link"]'
  28 torrent_xpath = './/a[@title="Download torrent file"]'
  29 content_xpath = './/span[@class="font11px lightgrey block"]'
  30 
  31 
  32 # do search-request
  33 def request(query, params):
  34     params['url'] = search_url.format(search_term=quote(query),
  35                                       pageno=params['pageno'])
  36 
  37     # FIX: SSLError: hostname 'kickass.so'
  38     # doesn't match either of '*.kickass.to', 'kickass.to'
  39     params['verify'] = False
  40 
  41     return params
  42 
  43 
  44 # get response from search-request
  45 def response(resp):
  46     results = []
  47 
  48     dom = html.fromstring(resp.text)
  49 
  50     search_res = dom.xpath('//table[@class="data"]//tr')
  51 
  52     # return empty array if nothing is found
  53     if not search_res:
  54         return []
  55 
  56     # parse results
  57     for result in search_res[1:]:
  58         link = result.xpath('.//a[@class="cellMainLink"]')[0]
  59         href = urljoin(url, link.attrib['href'])
  60         title = extract_text(link)
  61         content = escape(extract_text(result.xpath(content_xpath)))
  62         seed = result.xpath('.//td[contains(@class, "green")]/text()')[0]
  63         leech = result.xpath('.//td[contains(@class, "red")]/text()')[0]
  64         filesize = result.xpath('.//td[contains(@class, "nobr")]/text()')[0]
  65         filesize_multiplier = result.xpath('.//td[contains(@class, "nobr")]//span/text()')[0]
  66         files = result.xpath('.//td[contains(@class, "center")][2]/text()')[0]
  67 
  68         # convert seed to int if possible
  69         if seed.isdigit():
  70             seed = int(seed)
  71         else:
  72             seed = 0
  73 
  74         # convert leech to int if possible
  75         if leech.isdigit():
  76             leech = int(leech)
  77         else:
  78             leech = 0
  79 
  80         # convert filesize to byte if possible
  81         try:
  82             filesize = float(filesize)
  83 
  84             # convert filesize to byte
  85             if filesize_multiplier == 'TB':
  86                 filesize = int(filesize * 1024 * 1024 * 1024 * 1024)
  87             elif filesize_multiplier == 'GB':
  88                 filesize = int(filesize * 1024 * 1024 * 1024)
  89             elif filesize_multiplier == 'MB':
  90                 filesize = int(filesize * 1024 * 1024)
  91             elif filesize_multiplier == 'KB':
  92                 filesize = int(filesize * 1024)
  93         except:
  94             filesize = None
  95 
  96         # convert files to int if possible
  97         if files.isdigit():
  98             files = int(files)
  99         else:
  100             files = None
  101 
  102         magnetlink = result.xpath(magnet_xpath)[0].attrib['href']
  103 
  104         torrentfile = result.xpath(torrent_xpath)[0].attrib['href']
  105         torrentfileurl = quote(torrentfile, safe="%/:=&?~#+!$,;'@()*")
  106 
  107         # append result
  108         results.append({'url': href,
  109                         'title': title,
  110                         'content': content,
  111                         'seed': seed,
  112                         'leech': leech,
  113                         'filesize': filesize,
  114                         'files': files,
  115                         'magnetlink': magnetlink,
  116                         'torrentfile': torrentfileurl,
  117                         'template': 'torrent.html'})
  118 
  119     # return results sorted by seeder
  120     return sorted(results, key=itemgetter('seed'), reverse=True)
  121
  ```
  --- 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/searx/engines/kickass.py b/searx/engines/kickass.py
  --- a/searx/engines/kickass.py
  +++ b/searx/engines/kickass.py
  @@ -20,7 +20,7 @@
   paging = True
   
   # search-url
  -url = 'https://kickass.so/'
  +url = 'https://kickass.to/'
   search_url = url + 'search/{search_term}/{pageno}/'
   
   # specific xpath variables
  @@ -45,6 +45,11 @@
   def response(resp):
       results = []
   
  +    # check if redirect comparing to the True value,
  +    # because resp can be a Mock object, and any attribut name returns something.
  +    if resp.is_redirect is True:
  +        return results
  +
       dom = html.fromstring(resp.text)
   
       search_res = dom.xpath('//table[@class="data"]//tr')
  
verification_info: {"golden_diff": "diff --git a/searx/engines/kickass.py b/searx/engines/kickass.py\n--- a/searx/engines/kickass.py\n+++ b/searx/engines/kickass.py\n@@ -20,7 +20,7 @@\n paging = True\n \n # search-url\n-url = 'https://kickass.so/'\n+url = 'https://kickass.to/'\n search_url = url + 'search/{search_term}/{pageno}/'\n \n # specific xpath variables\n@@ -45,6 +45,11 @@\n def response(resp):\n     results = []\n \n+    # check if redirect comparing to the True value,\n+    # because resp can be a Mock object, and any attribut name returns something.\n+    if resp.is_redirect is True:\n+        return results\n+\n     dom = html.fromstring(resp.text)\n \n     search_res = dom.xpath('//table[@class=\"data\"]//tr')\n", "issue": "Requests does not follow on an HTTP 301\nWe had already an issue when kickass redirected from https to http I seem to remember. Or it was on another engine. And the request wasn't followed so we had an empty response with status_code 301 in the engine.\n\nAnyway, that is still the case in kickass if we try to perform a search when the query contains capitalized letters.\n\nSo why isn't 301 followed ? Is there a way to force it ?\n\n", "before_files": [{"content": "## Kickass Torrent (Videos, Music, Files)\n#\n# @website     https://kickass.so\n# @provide-api no (nothing found)\n#\n# @using-api   no\n# @results     HTML (using search portal)\n# @stable      yes (HTML can change)\n# @parse       url, title, content, seed, leech, magnetlink\n\nfrom urlparse import urljoin\nfrom cgi import escape\nfrom urllib import quote\nfrom lxml import html\nfrom operator import itemgetter\nfrom searx.engines.xpath import extract_text\n\n# engine dependent config\ncategories = ['videos', 'music', 'files']\npaging = True\n\n# search-url\nurl = 'https://kickass.so/'\nsearch_url = url + 'search/{search_term}/{pageno}/'\n\n# specific xpath variables\nmagnet_xpath = './/a[@title=\"Torrent magnet link\"]'\ntorrent_xpath = './/a[@title=\"Download torrent file\"]'\ncontent_xpath = './/span[@class=\"font11px lightgrey block\"]'\n\n\n# do search-request\ndef request(query, params):\n    params['url'] = search_url.format(search_term=quote(query),\n                                      pageno=params['pageno'])\n\n    # FIX: SSLError: hostname 'kickass.so'\n    # doesn't match either of '*.kickass.to', 'kickass.to'\n    params['verify'] = False\n\n    return params\n\n\n# get response from search-request\ndef response(resp):\n    results = []\n\n    dom = html.fromstring(resp.text)\n\n    search_res = dom.xpath('//table[@class=\"data\"]//tr')\n\n    # return empty array if nothing is found\n    if not search_res:\n        return []\n\n    # parse results\n    for result in search_res[1:]:\n        link = result.xpath('.//a[@class=\"cellMainLink\"]')[0]\n        href = urljoin(url, link.attrib['href'])\n        title = extract_text(link)\n        content = escape(extract_text(result.xpath(content_xpath)))\n        seed = result.xpath('.//td[contains(@class, \"green\")]/text()')[0]\n        leech = result.xpath('.//td[contains(@class, \"red\")]/text()')[0]\n        filesize = result.xpath('.//td[contains(@class, \"nobr\")]/text()')[0]\n        filesize_multiplier = result.xpath('.//td[contains(@class, \"nobr\")]//span/text()')[0]\n        files = result.xpath('.//td[contains(@class, \"center\")][2]/text()')[0]\n\n        # convert seed to int if possible\n        if seed.isdigit():\n            seed = int(seed)\n        else:\n            seed = 0\n\n        # convert leech to int if possible\n        if leech.isdigit():\n            leech = int(leech)\n        else:\n            leech = 0\n\n        # convert filesize to byte if possible\n        try:\n            filesize = float(filesize)\n\n            # convert filesize to byte\n            if filesize_multiplier == 'TB':\n                filesize = int(filesize * 1024 * 1024 * 1024 * 1024)\n            elif filesize_multiplier == 'GB':\n                filesize = int(filesize * 1024 * 1024 * 1024)\n            elif filesize_multiplier == 'MB':\n                filesize = int(filesize * 1024 * 1024)\n            elif filesize_multiplier == 'KB':\n                filesize = int(filesize * 1024)\n        except:\n            filesize = None\n\n        # convert files to int if possible\n        if files.isdigit():\n            files = int(files)\n        else:\n            files = None\n\n        magnetlink = result.xpath(magnet_xpath)[0].attrib['href']\n\n        torrentfile = result.xpath(torrent_xpath)[0].attrib['href']\n        torrentfileurl = quote(torrentfile, safe=\"%/:=&?~#+!$,;'@()*\")\n\n        # append result\n        results.append({'url': href,\n                        'title': title,\n                        'content': content,\n                        'seed': seed,\n                        'leech': leech,\n                        'filesize': filesize,\n                        'files': files,\n                        'magnetlink': magnetlink,\n                        'torrentfile': torrentfileurl,\n                        'template': 'torrent.html'})\n\n    # return results sorted by seeder\n    return sorted(results, key=itemgetter('seed'), reverse=True)\n", "path": "searx/engines/kickass.py"}], "after_files": [{"content": "## Kickass Torrent (Videos, Music, Files)\n#\n# @website     https://kickass.so\n# @provide-api no (nothing found)\n#\n# @using-api   no\n# @results     HTML (using search portal)\n# @stable      yes (HTML can change)\n# @parse       url, title, content, seed, leech, magnetlink\n\nfrom urlparse import urljoin\nfrom cgi import escape\nfrom urllib import quote\nfrom lxml import html\nfrom operator import itemgetter\nfrom searx.engines.xpath import extract_text\n\n# engine dependent config\ncategories = ['videos', 'music', 'files']\npaging = True\n\n# search-url\nurl = 'https://kickass.to/'\nsearch_url = url + 'search/{search_term}/{pageno}/'\n\n# specific xpath variables\nmagnet_xpath = './/a[@title=\"Torrent magnet link\"]'\ntorrent_xpath = './/a[@title=\"Download torrent file\"]'\ncontent_xpath = './/span[@class=\"font11px lightgrey block\"]'\n\n\n# do search-request\ndef request(query, params):\n    params['url'] = search_url.format(search_term=quote(query),\n                                      pageno=params['pageno'])\n\n    # FIX: SSLError: hostname 'kickass.so'\n    # doesn't match either of '*.kickass.to', 'kickass.to'\n    params['verify'] = False\n\n    return params\n\n\n# get response from search-request\ndef response(resp):\n    results = []\n\n    # check if redirect comparing to the True value,\n    # because resp can be a Mock object, and any attribut name returns something.\n    if resp.is_redirect is True:\n        return results\n\n    dom = html.fromstring(resp.text)\n\n    search_res = dom.xpath('//table[@class=\"data\"]//tr')\n\n    # return empty array if nothing is found\n    if not search_res:\n        return []\n\n    # parse results\n    for result in search_res[1:]:\n        link = result.xpath('.//a[@class=\"cellMainLink\"]')[0]\n        href = urljoin(url, link.attrib['href'])\n        title = extract_text(link)\n        content = escape(extract_text(result.xpath(content_xpath)))\n        seed = result.xpath('.//td[contains(@class, \"green\")]/text()')[0]\n        leech = result.xpath('.//td[contains(@class, \"red\")]/text()')[0]\n        filesize = result.xpath('.//td[contains(@class, \"nobr\")]/text()')[0]\n        filesize_multiplier = result.xpath('.//td[contains(@class, \"nobr\")]//span/text()')[0]\n        files = result.xpath('.//td[contains(@class, \"center\")][2]/text()')[0]\n\n        # convert seed to int if possible\n        if seed.isdigit():\n            seed = int(seed)\n        else:\n            seed = 0\n\n        # convert leech to int if possible\n        if leech.isdigit():\n            leech = int(leech)\n        else:\n            leech = 0\n\n        # convert filesize to byte if possible\n        try:\n            filesize = float(filesize)\n\n            # convert filesize to byte\n            if filesize_multiplier == 'TB':\n                filesize = int(filesize * 1024 * 1024 * 1024 * 1024)\n            elif filesize_multiplier == 'GB':\n                filesize = int(filesize * 1024 * 1024 * 1024)\n            elif filesize_multiplier == 'MB':\n                filesize = int(filesize * 1024 * 1024)\n            elif filesize_multiplier == 'KB':\n                filesize = int(filesize * 1024)\n        except:\n            filesize = None\n\n        # convert files to int if possible\n        if files.isdigit():\n            files = int(files)\n        else:\n            files = None\n\n        magnetlink = result.xpath(magnet_xpath)[0].attrib['href']\n\n        torrentfile = result.xpath(torrent_xpath)[0].attrib['href']\n        torrentfileurl = quote(torrentfile, safe=\"%/:=&?~#+!$,;'@()*\")\n\n        # append result\n        results.append({'url': href,\n                        'title': title,\n                        'content': content,\n                        'seed': seed,\n                        'leech': leech,\n                        'filesize': filesize,\n                        'files': files,\n                        'magnetlink': magnetlink,\n                        'torrentfile': torrentfileurl,\n                        'template': 'torrent.html'})\n\n    # return results sorted by seeder\n    return sorted(results, key=itemgetter('seed'), reverse=True)\n", "path": "searx/engines/kickass.py"}]}