Issue #4718: Adapt the wsgiref package so that it actually works with Python 3.x,
in accordance with http://www.wsgi.org/wsgi/Amendments_1.0
diff --git a/Lib/wsgiref/handlers.py b/Lib/wsgiref/handlers.py
index 8231620..e082823 100644
--- a/Lib/wsgiref/handlers.py
+++ b/Lib/wsgiref/handlers.py
@@ -157,19 +157,29 @@
         elif self.headers is not None:
             raise AssertionError("Headers already set!")
 
-        assert type(status) is str,"Status must be a string"
+        status = self._convert_string_type(status, "Status")
         assert len(status)>=4,"Status must be at least 4 characters"
         assert int(status[:3]),"Status message must begin w/3-digit code"
         assert status[3]==" ", "Status message must have a space after code"
-        if __debug__:
-            for name,val in headers:
-                assert type(name) is str,"Header names must be strings"
-                assert type(val) is str,"Header values must be strings"
-                assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"
+
+        str_headers = []
+        for name,val in headers:
+            name = self._convert_string_type(name, "Header name")
+            val = self._convert_string_type(val, "Header value")
+            str_headers.append((name, val))
+            assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"
+
         self.status = status
-        self.headers = self.headers_class(headers)
+        self.headers = self.headers_class(str_headers)
         return self.write
 
+    def _convert_string_type(self, value, title):
+        """Convert/check value type."""
+        if isinstance(value, str):
+            return value
+        assert isinstance(value, bytes), \
+            "{0} must be a string or bytes object (not {1})".format(title, value)
+        return str(value, "iso-8859-1")
 
     def send_preamble(self):
         """Transmit version/status/date/server, via self._write()"""
@@ -188,7 +198,8 @@
     def write(self, data):
         """'write()' callable as specified by PEP 333"""
 
-        assert type(data) is str,"write() argument must be string"
+        assert isinstance(data, (str, bytes)), \
+            "write() argument must be a string or bytes"
 
         if not self.status:
             raise AssertionError("write() before start_response()")
@@ -382,8 +393,13 @@
         self.environ.update(self.base_env)
 
     def _write(self,data):
+        if isinstance(data, str):
+            try:
+                data = data.encode("iso-8859-1")
+            except UnicodeEncodeError:
+                raise ValueError("Unicode data must contain only code points"
+                    " representable in ISO-8859-1 encoding")
         self.stdout.write(data)
-        self._write = self.stdout.write
 
     def _flush(self):
         self.stdout.flush()
diff --git a/Lib/wsgiref/headers.py b/Lib/wsgiref/headers.py
index c3774bb..83074af 100644
--- a/Lib/wsgiref/headers.py
+++ b/Lib/wsgiref/headers.py
@@ -44,7 +44,19 @@
     def __init__(self,headers):
         if not isinstance(headers, list):
             raise TypeError("Headers must be a list of name/value tuples")
-        self._headers = headers
+        self._headers = []
+        for k, v in headers:
+            k = self._convert_string_type(k)
+            v = self._convert_string_type(v)
+            self._headers.append((k, v))
+
+    def _convert_string_type(self, value):
+        """Convert/check value type."""
+        if isinstance(value, str):
+            return value
+        assert isinstance(value, bytes), ("Header names/values must be"
+            " a string or bytes object (not {0})".format(value))
+        return str(value, "iso-8859-1")
 
     def __len__(self):
         """Return the total number of headers, including duplicates."""
@@ -53,7 +65,8 @@
     def __setitem__(self, name, val):
         """Set the value of a header."""
         del self[name]
-        self._headers.append((name, val))
+        self._headers.append(
+            (self._convert_string_type(name), self._convert_string_type(val)))
 
     def __delitem__(self,name):
         """Delete all occurrences of a header, if present.
@@ -152,7 +165,8 @@
         and value 'value'."""
         result = self.get(name)
         if result is None:
-            self._headers.append((name,value))
+            self._headers.append((self._convert_string_type(name),
+                self._convert_string_type(value)))
             return value
         else:
             return result
@@ -176,13 +190,16 @@
         """
         parts = []
         if _value is not None:
+            _value = self._convert_string_type(_value)
             parts.append(_value)
         for k, v in _params.items():
+            k = self._convert_string_type(k)
             if v is None:
                 parts.append(k.replace('_', '-'))
             else:
+                v = self._convert_string_type(v)
                 parts.append(_formatparam(k.replace('_', '-'), v))
-        self._headers.append((_name, "; ".join(parts)))
+        self._headers.append((self._convert_string_type(_name), "; ".join(parts)))
 
 
 
diff --git a/Lib/wsgiref/simple_server.py b/Lib/wsgiref/simple_server.py
index a82c80a..da14144 100644
--- a/Lib/wsgiref/simple_server.py
+++ b/Lib/wsgiref/simple_server.py
@@ -111,8 +111,7 @@
         if length:
             env['CONTENT_LENGTH'] = length
 
-        for h in self.headers:
-            k,v = h.split(':',1)
+        for k, v in self.headers.items():
             k=k.replace('-','_').upper(); v=v.strip()
             if k in env:
                 continue                    # skip content length, type,etc.
@@ -168,11 +167,11 @@
     stdout = StringIO()
     print("Hello world!", file=stdout)
     print(file=stdout)
-    h = environ.items(); h.sort()
+    h = sorted(environ.items())
     for k,v in h:
         print(k,'=',repr(v), file=stdout)
-    start_response("200 OK", [('Content-Type','text/plain')])
-    return [stdout.getvalue()]
+    start_response(b"200 OK", [(b'Content-Type',b'text/plain; charset=utf-8')])
+    return [stdout.getvalue().encode("utf-8")]
 
 
 def make_server(
diff --git a/Lib/wsgiref/util.py b/Lib/wsgiref/util.py
index 2686b66..937be60 100644
--- a/Lib/wsgiref/util.py
+++ b/Lib/wsgiref/util.py
@@ -149,8 +149,8 @@
     environ.setdefault('wsgi.multithread', 0)
     environ.setdefault('wsgi.multiprocess', 0)
 
-    from io import StringIO
-    environ.setdefault('wsgi.input', StringIO(""))
+    from io import StringIO, BytesIO
+    environ.setdefault('wsgi.input', BytesIO())
     environ.setdefault('wsgi.errors', StringIO())
     environ.setdefault('wsgi.url_scheme',guess_scheme(environ))
 
diff --git a/Lib/wsgiref/validate.py b/Lib/wsgiref/validate.py
index fbd3536..2df3f9f 100644
--- a/Lib/wsgiref/validate.py
+++ b/Lib/wsgiref/validate.py
@@ -127,6 +127,13 @@
     if not cond:
         raise AssertionError(*args)
 
+def check_string_type(value, title):
+    if isinstance(value, str):
+        return value
+    assert isinstance(value, bytes), \
+        "{0} must be a string or bytes object (not {1})".format(title, value)
+    return str(value, "iso-8859-1")
+
 def validator(application):
 
     """
@@ -188,14 +195,14 @@
         self.input = wsgi_input
 
     def read(self, *args):
-        assert_(len(args) <= 1)
+        assert_(len(args) == 1)
         v = self.input.read(*args)
-        assert_(isinstance(v, str))
+        assert_(isinstance(v, bytes))
         return v
 
     def readline(self):
         v = self.input.readline()
-        assert_(isinstance(v, str))
+        assert_(isinstance(v, bytes))
         return v
 
     def readlines(self, *args):
@@ -203,7 +210,7 @@
         lines = self.input.readlines(*args)
         assert_(isinstance(lines, list))
         for line in lines:
-            assert_(isinstance(line, str))
+            assert_(isinstance(line, bytes))
         return lines
 
     def __iter__(self):
@@ -241,7 +248,7 @@
         self.writer = wsgi_writer
 
     def __call__(self, s):
-        assert_(isinstance(s, str))
+        assert_(isinstance(s, (str, bytes)))
         self.writer(s)
 
 class PartialIteratorWrapper:
@@ -364,8 +371,7 @@
             % (wsgi_errors, attr))
 
 def check_status(status):
-    assert_(isinstance(status, str),
-        "Status must be a string (not %r)" % status)
+    status = check_string_type(status, "Status")
     # Implicitly check that we can turn it into an integer:
     status_code = status.split(None, 1)[0]
     assert_(len(status_code) == 3,
@@ -389,6 +395,8 @@
             % (item, type(item)))
         assert_(len(item) == 2)
         name, value = item
+        name = check_string_type(name, "Header name")
+        value = check_string_type(value, "Header value")
         assert_(name.lower() != 'status',
             "The Status header cannot be used; it conflicts with CGI "
             "script, and HTTP status is not given through headers "
@@ -404,11 +412,13 @@
             % (value, bad_header_value_re.search(value).group(0)))
 
 def check_content_type(status, headers):
+    status = check_string_type(status, "Status")
     code = int(status.split(None, 1)[0])
     # @@: need one more person to verify this interpretation of RFC 2616
     #     http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
     NO_MESSAGE_BODY = (204, 304)
     for name, value in headers:
+        name = check_string_type(name, "Header name")
         if name.lower() == 'content-type':
             if code not in NO_MESSAGE_BODY:
                 return
@@ -426,6 +436,6 @@
     # Technically a string is legal, which is why it's a really bad
     # idea, because it may cause the response to be returned
     # character-by-character
-    assert_(not isinstance(iterator, str),
+    assert_(not isinstance(iterator, (str, bytes)),
         "You should not return a string as your application iterator, "
         "instead return a single-item list containing that string.")