We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Returning iterable of string instead of bytes in HEAD response
**Describe the bug**
uWSGI is expecting an iterable of bytes instead of (empty) string which is returned in case of HEAD request. This results in logging `[ERROR] Unhandled object from iterator: ''` because  [_The iterator returned something that we were not able to handle._](https://github.com/unbit/uwsgi/blob/d58a832c81c2c96ae0f6e72614e1cc47f4b5d332/plugins/python/wsgi_subhandler.c#L355)

**To Reproduce**
1. Create HEAD request
2. Check uwsgi logs for mentioned error log

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gluon/http.py`
Content:
```
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 
4 """
5 | This file is part of the web2py Web Framework
6 | Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
7 | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
8 
9 HTTP statuses helpers
10 --------------------------------------------
11 """
12 
13 import re
14 from gluon._compat import iteritems, unicodeT, to_bytes
15 
16 __all__ = ['HTTP', 'redirect']
17 
18 defined_status = {
19     200: 'OK',
20     201: 'CREATED',
21     202: 'ACCEPTED',
22     203: 'NON-AUTHORITATIVE INFORMATION',
23     204: 'NO CONTENT',
24     205: 'RESET CONTENT',
25     206: 'PARTIAL CONTENT',
26     301: 'MOVED PERMANENTLY',
27     302: 'FOUND',
28     303: 'SEE OTHER',
29     304: 'NOT MODIFIED',
30     305: 'USE PROXY',
31     307: 'TEMPORARY REDIRECT',
32     400: 'BAD REQUEST',
33     401: 'UNAUTHORIZED',
34     402: 'PAYMENT REQUIRED',
35     403: 'FORBIDDEN',
36     404: 'NOT FOUND',
37     405: 'METHOD NOT ALLOWED',
38     406: 'NOT ACCEPTABLE',
39     407: 'PROXY AUTHENTICATION REQUIRED',
40     408: 'REQUEST TIMEOUT',
41     409: 'CONFLICT',
42     410: 'GONE',
43     411: 'LENGTH REQUIRED',
44     412: 'PRECONDITION FAILED',
45     413: 'REQUEST ENTITY TOO LARGE',
46     414: 'REQUEST-URI TOO LONG',
47     415: 'UNSUPPORTED MEDIA TYPE',
48     416: 'REQUESTED RANGE NOT SATISFIABLE',
49     417: 'EXPECTATION FAILED',
50     422: 'UNPROCESSABLE ENTITY',
51     429: 'TOO MANY REQUESTS',
52     451: 'UNAVAILABLE FOR LEGAL REASONS',  # http://www.451unavailable.org/
53     500: 'INTERNAL SERVER ERROR',
54     501: 'NOT IMPLEMENTED',
55     502: 'BAD GATEWAY',
56     503: 'SERVICE UNAVAILABLE',
57     504: 'GATEWAY TIMEOUT',
58     505: 'HTTP VERSION NOT SUPPORTED',
59     509: 'BANDWIDTH LIMIT EXCEEDED',
60 }
61 
62 regex_status = re.compile('^\d{3} [0-9A-Z ]+$')
63 
64 
65 class HTTP(Exception):
66     """Raises an HTTP response
67 
68     Args:
69         status: usually an integer. If it's a well known status code, the ERROR
70           message will be automatically added. A string can also be passed
71           as `510 Foo Bar` and in that case the status code and the error
72           message will be parsed accordingly
73         body: what to return as body. If left as is, will return the error code
74           and the status message in the body itself
75         cookies: pass cookies along (usually not needed)
76         headers: pass headers as usual dict mapping
77     """
78 
79     def __init__(
80         self,
81         status,
82         body='',
83         cookies=None,
84         **headers
85     ):
86         self.status = status
87         self.body = body
88         self.headers = headers
89         self.cookies2headers(cookies)
90 
91     def cookies2headers(self, cookies):
92         if cookies and len(cookies) > 0:
93             self.headers['Set-Cookie'] = [
94                 str(cookie)[11:] for cookie in cookies.values()]
95 
96     def to(self, responder, env=None):
97         env = env or {}
98         status = self.status
99         headers = self.headers
100         if status in defined_status:
101             status = '%d %s' % (status, defined_status[status])
102         elif isinstance(status, int):
103             status = '%d UNKNOWN ERROR' % status
104         else:
105             status = str(status)
106             if not regex_status.match(status):
107                 status = '500 %s' % (defined_status[500])
108         headers.setdefault('Content-Type', 'text/html; charset=UTF-8')
109         body = self.body
110         if status[:1] == '4':
111             if not body:
112                 body = status
113             if isinstance(body, (str, bytes, bytearray)):
114                 if isinstance(body, unicodeT):
115                     body = to_bytes(body) # This must be done before len
116                 headers['Content-Length'] = len(body)
117         rheaders = []
118         for k, v in iteritems(headers):
119             if isinstance(v, list):
120                 rheaders += [(k, str(item)) for item in v]
121             elif v is not None:
122                 rheaders.append((k, str(v)))
123         responder(status, rheaders)
124         if env.get('request_method', '') == 'HEAD':
125             return ['']
126         elif isinstance(body, (str, bytes, bytearray)):
127             if isinstance(body, unicodeT):
128                 body = to_bytes(body)
129             return [body]
130         elif hasattr(body, '__iter__'):
131             return body
132         else:
133             body = str(body)
134             if isinstance(body, unicodeT):
135                 body = to_bytes(body)
136             return [body]
137 
138     @property
139     def message(self):
140         """
141         compose a message describing this exception
142 
143             "status defined_status [web2py_error]"
144 
145         message elements that are not defined are omitted
146         """
147         msg = '%(status)s'
148         if self.status in defined_status:
149             msg = '%(status)s %(defined_status)s'
150         if 'web2py_error' in self.headers:
151             msg += ' [%(web2py_error)s]'
152         return msg % dict(
153             status=self.status,
154             defined_status=defined_status.get(self.status),
155             web2py_error=self.headers.get('web2py_error'))
156 
157     def __str__(self):
158         """stringify me"""
159         return self.message
160 
161 
162 def redirect(location='', how=303, client_side=False, headers=None):
163     """Raises a redirect (303)
164 
165     Args:
166         location: the url where to redirect
167         how: what HTTP status code to use when redirecting
168         client_side: if set to True, it triggers a reload of the entire page when the fragment has been loaded as a component
169         headers: dictionary for request headers
170     """
171     headers = headers or {}
172     if location:
173         from gluon.globals import current
174         loc = location.replace('\r', '%0D').replace('\n', '%0A')
175         if client_side and current.request.ajax:
176             headers['web2py-redirect-location'] = loc
177             raise HTTP(200, **headers)
178         else:
179             headers['Location'] = loc
180             raise HTTP(how,
181                        'You are being redirected <a href="%s">here</a>' % loc,
182                        **headers)
183     else:
184         from gluon.globals import current
185         if client_side and current.request.ajax:
186             headers['web2py-component-command'] = 'window.location.reload(true)'
187             raise HTTP(200, **headers)
188
```
--- 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/gluon/http.py b/gluon/http.py
--- a/gluon/http.py
+++ b/gluon/http.py
@@ -122,7 +122,7 @@
                 rheaders.append((k, str(v)))
         responder(status, rheaders)
         if env.get('request_method', '') == 'HEAD':
-            return ['']
+            return [to_bytes('')]
         elif isinstance(body, (str, bytes, bytearray)):
             if isinstance(body, unicodeT):
                 body = to_bytes(body)

{"golden_diff": "diff --git a/gluon/http.py b/gluon/http.py\n--- a/gluon/http.py\n+++ b/gluon/http.py\n@@ -122,7 +122,7 @@\n                 rheaders.append((k, str(v)))\n         responder(status, rheaders)\n         if env.get('request_method', '') == 'HEAD':\n-            return ['']\n+            return [to_bytes('')]\n         elif isinstance(body, (str, bytes, bytearray)):\n             if isinstance(body, unicodeT):\n                 body = to_bytes(body)\n", "issue": "Returning iterable of string instead of bytes in HEAD response\n**Describe the bug**\r\nuWSGI is expecting an iterable of bytes instead of (empty) string which is returned in case of HEAD request. This results in logging `[ERROR] Unhandled object from iterator: ''` because  [_The iterator returned something that we were not able to handle._](https://github.com/unbit/uwsgi/blob/d58a832c81c2c96ae0f6e72614e1cc47f4b5d332/plugins/python/wsgi_subhandler.c#L355)\r\n\r\n**To Reproduce**\r\n1. Create HEAD request\r\n2. Check uwsgi logs for mentioned error log\n", "before_files": [{"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"\n| This file is part of the web2py Web Framework\n| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>\n| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)\n\nHTTP statuses helpers\n--------------------------------------------\n\"\"\"\n\nimport re\nfrom gluon._compat import iteritems, unicodeT, to_bytes\n\n__all__ = ['HTTP', 'redirect']\n\ndefined_status = {\n    200: 'OK',\n    201: 'CREATED',\n    202: 'ACCEPTED',\n    203: 'NON-AUTHORITATIVE INFORMATION',\n    204: 'NO CONTENT',\n    205: 'RESET CONTENT',\n    206: 'PARTIAL CONTENT',\n    301: 'MOVED PERMANENTLY',\n    302: 'FOUND',\n    303: 'SEE OTHER',\n    304: 'NOT MODIFIED',\n    305: 'USE PROXY',\n    307: 'TEMPORARY REDIRECT',\n    400: 'BAD REQUEST',\n    401: 'UNAUTHORIZED',\n    402: 'PAYMENT REQUIRED',\n    403: 'FORBIDDEN',\n    404: 'NOT FOUND',\n    405: 'METHOD NOT ALLOWED',\n    406: 'NOT ACCEPTABLE',\n    407: 'PROXY AUTHENTICATION REQUIRED',\n    408: 'REQUEST TIMEOUT',\n    409: 'CONFLICT',\n    410: 'GONE',\n    411: 'LENGTH REQUIRED',\n    412: 'PRECONDITION FAILED',\n    413: 'REQUEST ENTITY TOO LARGE',\n    414: 'REQUEST-URI TOO LONG',\n    415: 'UNSUPPORTED MEDIA TYPE',\n    416: 'REQUESTED RANGE NOT SATISFIABLE',\n    417: 'EXPECTATION FAILED',\n    422: 'UNPROCESSABLE ENTITY',\n    429: 'TOO MANY REQUESTS',\n    451: 'UNAVAILABLE FOR LEGAL REASONS',  # http://www.451unavailable.org/\n    500: 'INTERNAL SERVER ERROR',\n    501: 'NOT IMPLEMENTED',\n    502: 'BAD GATEWAY',\n    503: 'SERVICE UNAVAILABLE',\n    504: 'GATEWAY TIMEOUT',\n    505: 'HTTP VERSION NOT SUPPORTED',\n    509: 'BANDWIDTH LIMIT EXCEEDED',\n}\n\nregex_status = re.compile('^\\d{3} [0-9A-Z ]+$')\n\n\nclass HTTP(Exception):\n    \"\"\"Raises an HTTP response\n\n    Args:\n        status: usually an integer. If it's a well known status code, the ERROR\n          message will be automatically added. A string can also be passed\n          as `510 Foo Bar` and in that case the status code and the error\n          message will be parsed accordingly\n        body: what to return as body. If left as is, will return the error code\n          and the status message in the body itself\n        cookies: pass cookies along (usually not needed)\n        headers: pass headers as usual dict mapping\n    \"\"\"\n\n    def __init__(\n        self,\n        status,\n        body='',\n        cookies=None,\n        **headers\n    ):\n        self.status = status\n        self.body = body\n        self.headers = headers\n        self.cookies2headers(cookies)\n\n    def cookies2headers(self, cookies):\n        if cookies and len(cookies) > 0:\n            self.headers['Set-Cookie'] = [\n                str(cookie)[11:] for cookie in cookies.values()]\n\n    def to(self, responder, env=None):\n        env = env or {}\n        status = self.status\n        headers = self.headers\n        if status in defined_status:\n            status = '%d %s' % (status, defined_status[status])\n        elif isinstance(status, int):\n            status = '%d UNKNOWN ERROR' % status\n        else:\n            status = str(status)\n            if not regex_status.match(status):\n                status = '500 %s' % (defined_status[500])\n        headers.setdefault('Content-Type', 'text/html; charset=UTF-8')\n        body = self.body\n        if status[:1] == '4':\n            if not body:\n                body = status\n            if isinstance(body, (str, bytes, bytearray)):\n                if isinstance(body, unicodeT):\n                    body = to_bytes(body) # This must be done before len\n                headers['Content-Length'] = len(body)\n        rheaders = []\n        for k, v in iteritems(headers):\n            if isinstance(v, list):\n                rheaders += [(k, str(item)) for item in v]\n            elif v is not None:\n                rheaders.append((k, str(v)))\n        responder(status, rheaders)\n        if env.get('request_method', '') == 'HEAD':\n            return ['']\n        elif isinstance(body, (str, bytes, bytearray)):\n            if isinstance(body, unicodeT):\n                body = to_bytes(body)\n            return [body]\n        elif hasattr(body, '__iter__'):\n            return body\n        else:\n            body = str(body)\n            if isinstance(body, unicodeT):\n                body = to_bytes(body)\n            return [body]\n\n    @property\n    def message(self):\n        \"\"\"\n        compose a message describing this exception\n\n            \"status defined_status [web2py_error]\"\n\n        message elements that are not defined are omitted\n        \"\"\"\n        msg = '%(status)s'\n        if self.status in defined_status:\n            msg = '%(status)s %(defined_status)s'\n        if 'web2py_error' in self.headers:\n            msg += ' [%(web2py_error)s]'\n        return msg % dict(\n            status=self.status,\n            defined_status=defined_status.get(self.status),\n            web2py_error=self.headers.get('web2py_error'))\n\n    def __str__(self):\n        \"\"\"stringify me\"\"\"\n        return self.message\n\n\ndef redirect(location='', how=303, client_side=False, headers=None):\n    \"\"\"Raises a redirect (303)\n\n    Args:\n        location: the url where to redirect\n        how: what HTTP status code to use when redirecting\n        client_side: if set to True, it triggers a reload of the entire page when the fragment has been loaded as a component\n        headers: dictionary for request headers\n    \"\"\"\n    headers = headers or {}\n    if location:\n        from gluon.globals import current\n        loc = location.replace('\\r', '%0D').replace('\\n', '%0A')\n        if client_side and current.request.ajax:\n            headers['web2py-redirect-location'] = loc\n            raise HTTP(200, **headers)\n        else:\n            headers['Location'] = loc\n            raise HTTP(how,\n                       'You are being redirected <a href=\"%s\">here</a>' % loc,\n                       **headers)\n    else:\n        from gluon.globals import current\n        if client_side and current.request.ajax:\n            headers['web2py-component-command'] = 'window.location.reload(true)'\n            raise HTTP(200, **headers)\n", "path": "gluon/http.py"}], "after_files": [{"content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n\"\"\"\n| This file is part of the web2py Web Framework\n| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>\n| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)\n\nHTTP statuses helpers\n--------------------------------------------\n\"\"\"\n\nimport re\nfrom gluon._compat import iteritems, unicodeT, to_bytes\n\n__all__ = ['HTTP', 'redirect']\n\ndefined_status = {\n    200: 'OK',\n    201: 'CREATED',\n    202: 'ACCEPTED',\n    203: 'NON-AUTHORITATIVE INFORMATION',\n    204: 'NO CONTENT',\n    205: 'RESET CONTENT',\n    206: 'PARTIAL CONTENT',\n    301: 'MOVED PERMANENTLY',\n    302: 'FOUND',\n    303: 'SEE OTHER',\n    304: 'NOT MODIFIED',\n    305: 'USE PROXY',\n    307: 'TEMPORARY REDIRECT',\n    400: 'BAD REQUEST',\n    401: 'UNAUTHORIZED',\n    402: 'PAYMENT REQUIRED',\n    403: 'FORBIDDEN',\n    404: 'NOT FOUND',\n    405: 'METHOD NOT ALLOWED',\n    406: 'NOT ACCEPTABLE',\n    407: 'PROXY AUTHENTICATION REQUIRED',\n    408: 'REQUEST TIMEOUT',\n    409: 'CONFLICT',\n    410: 'GONE',\n    411: 'LENGTH REQUIRED',\n    412: 'PRECONDITION FAILED',\n    413: 'REQUEST ENTITY TOO LARGE',\n    414: 'REQUEST-URI TOO LONG',\n    415: 'UNSUPPORTED MEDIA TYPE',\n    416: 'REQUESTED RANGE NOT SATISFIABLE',\n    417: 'EXPECTATION FAILED',\n    422: 'UNPROCESSABLE ENTITY',\n    429: 'TOO MANY REQUESTS',\n    451: 'UNAVAILABLE FOR LEGAL REASONS',  # http://www.451unavailable.org/\n    500: 'INTERNAL SERVER ERROR',\n    501: 'NOT IMPLEMENTED',\n    502: 'BAD GATEWAY',\n    503: 'SERVICE UNAVAILABLE',\n    504: 'GATEWAY TIMEOUT',\n    505: 'HTTP VERSION NOT SUPPORTED',\n    509: 'BANDWIDTH LIMIT EXCEEDED',\n}\n\nregex_status = re.compile('^\\d{3} [0-9A-Z ]+$')\n\n\nclass HTTP(Exception):\n    \"\"\"Raises an HTTP response\n\n    Args:\n        status: usually an integer. If it's a well known status code, the ERROR\n          message will be automatically added. A string can also be passed\n          as `510 Foo Bar` and in that case the status code and the error\n          message will be parsed accordingly\n        body: what to return as body. If left as is, will return the error code\n          and the status message in the body itself\n        cookies: pass cookies along (usually not needed)\n        headers: pass headers as usual dict mapping\n    \"\"\"\n\n    def __init__(\n        self,\n        status,\n        body='',\n        cookies=None,\n        **headers\n    ):\n        self.status = status\n        self.body = body\n        self.headers = headers\n        self.cookies2headers(cookies)\n\n    def cookies2headers(self, cookies):\n        if cookies and len(cookies) > 0:\n            self.headers['Set-Cookie'] = [\n                str(cookie)[11:] for cookie in cookies.values()]\n\n    def to(self, responder, env=None):\n        env = env or {}\n        status = self.status\n        headers = self.headers\n        if status in defined_status:\n            status = '%d %s' % (status, defined_status[status])\n        elif isinstance(status, int):\n            status = '%d UNKNOWN ERROR' % status\n        else:\n            status = str(status)\n            if not regex_status.match(status):\n                status = '500 %s' % (defined_status[500])\n        headers.setdefault('Content-Type', 'text/html; charset=UTF-8')\n        body = self.body\n        if status[:1] == '4':\n            if not body:\n                body = status\n            if isinstance(body, (str, bytes, bytearray)):\n                if isinstance(body, unicodeT):\n                    body = to_bytes(body) # This must be done before len\n                headers['Content-Length'] = len(body)\n        rheaders = []\n        for k, v in iteritems(headers):\n            if isinstance(v, list):\n                rheaders += [(k, str(item)) for item in v]\n            elif v is not None:\n                rheaders.append((k, str(v)))\n        responder(status, rheaders)\n        if env.get('request_method', '') == 'HEAD':\n            return [to_bytes('')]\n        elif isinstance(body, (str, bytes, bytearray)):\n            if isinstance(body, unicodeT):\n                body = to_bytes(body)\n            return [body]\n        elif hasattr(body, '__iter__'):\n            return body\n        else:\n            body = str(body)\n            if isinstance(body, unicodeT):\n                body = to_bytes(body)\n            return [body]\n\n    @property\n    def message(self):\n        \"\"\"\n        compose a message describing this exception\n\n            \"status defined_status [web2py_error]\"\n\n        message elements that are not defined are omitted\n        \"\"\"\n        msg = '%(status)s'\n        if self.status in defined_status:\n            msg = '%(status)s %(defined_status)s'\n        if 'web2py_error' in self.headers:\n            msg += ' [%(web2py_error)s]'\n        return msg % dict(\n            status=self.status,\n            defined_status=defined_status.get(self.status),\n            web2py_error=self.headers.get('web2py_error'))\n\n    def __str__(self):\n        \"\"\"stringify me\"\"\"\n        return self.message\n\n\ndef redirect(location='', how=303, client_side=False, headers=None):\n    \"\"\"Raises a redirect (303)\n\n    Args:\n        location: the url where to redirect\n        how: what HTTP status code to use when redirecting\n        client_side: if set to True, it triggers a reload of the entire page when the fragment has been loaded as a component\n        headers: dictionary for request headers\n    \"\"\"\n    headers = headers or {}\n    if location:\n        from gluon.globals import current\n        loc = location.replace('\\r', '%0D').replace('\\n', '%0A')\n        if client_side and current.request.ajax:\n            headers['web2py-redirect-location'] = loc\n            raise HTTP(200, **headers)\n        else:\n            headers['Location'] = loc\n            raise HTTP(how,\n                       'You are being redirected <a href=\"%s\">here</a>' % loc,\n                       **headers)\n    else:\n        from gluon.globals import current\n        if client_side and current.request.ajax:\n            headers['web2py-component-command'] = 'window.location.reload(true)'\n            raise HTTP(200, **headers)\n", "path": "gluon/http.py"}]}