Repository: cherrypy/cherrypy

Issue:
disabling wsgi interface fails under python3
Originally reported by: **Tim Miller (Bitbucket: [lashni](http://bitbucket.org/lashni), GitHub: Unknown)**

---

http://docs.cherrypy.org/en/latest/advanced.html#no-need-for-the-wsgi-interface

Error is encountered when the example code from the link above is run under a python 3.4.3 virtualenv on archlinux, traceback triggers when the server is accessed. Error doesn't occur with python 2.7.10.

Happens with both current head and CherryPy 3.8.0.

```
[19/Jul/2015:17:41:01] ENGINE Listening for SIGUSR1.
[19/Jul/2015:17:41:01] ENGINE Listening for SIGTERM.
[19/Jul/2015:17:41:01] ENGINE Listening for SIGHUP.
[19/Jul/2015:17:41:01] ENGINE Bus STARTING
CherryPy Checker:
The Application mounted at '' has an empty config.

[19/Jul/2015:17:41:01] ENGINE Started monitor thread '_TimeoutMonitor'.
[19/Jul/2015:17:41:01] ENGINE Started monitor thread 'Autoreloader'.
[19/Jul/2015:17:41:01] ENGINE Serving on http://127.0.0.1:8080
[19/Jul/2015:17:41:01] ENGINE Bus STARTED
[19/Jul/2015:17:41:05] NATIVE_ADAPTER Traceback (most recent call last):
  File "/home/lashni/dev/serve/lib/python3.4/site-packages/cherrypy/_cpnative_server.py", line 27, in respond
    sn = cherrypy.tree.script_name(req.uri or "/")
  File "/home/lashni/dev/serve/lib/python3.4/site-packages/cherrypy/_cptree.py", line 257, in script_name
    path = path[:path.rfind("/")]
TypeError: 'str' does not support the buffer interface

ValueError('invalid literal for int() with base 10: "b\'5"',)
Traceback (most recent call last):
  File "/home/lashni/dev/serve/lib/python3.4/site-packages/cherrypy/_cpnative_server.py", line 27, in respond
    sn = cherrypy.tree.script_name(req.uri or "/")
  File "/home/lashni/dev/serve/lib/python3.4/site-packages/cherrypy/_cptree.py", line 257, in script_name
    path = path[:path.rfind("/")]
TypeError: 'str' does not support the buffer interface

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/lashni/dev/serve/lib/python3.4/site-packages/cherrypy/wsgiserver/wsgiserver3.py", line 1068, in communicate
    req.respond()
  File "/home/lashni/dev/serve/lib/python3.4/site-packages/cherrypy/wsgiserver/wsgiserver3.py", line 856, in respond
    self.server.gateway(self).respond()
  File "/home/lashni/dev/serve/lib/python3.4/site-packages/cherrypy/_cpnative_server.py", line 88, in respond
    self.send_response(s, h, b)
  File "/home/lashni/dev/serve/lib/python3.4/site-packages/cherrypy/_cpnative_server.py", line 101, in send_response
    req.send_headers()
  File "/home/lashni/dev/serve/lib/python3.4/site-packages/cherrypy/wsgiserver/wsgiserver3.py", line 912, in send_headers
    status = int(self.status[:3])
ValueError: invalid literal for int() with base 10: "b'5"
```

---
- Bitbucket: https://bitbucket.org/cherrypy/cherrypy/issue/1377


Discussion:

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 7862fc93..f9961319 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,9 @@
+v18.1.2 (unreleased)
+--------------------
+
+* Fixed :issue:`1377` via :pr:`1785`: Restore a native WSGI-less
+  HTTP server support.
+
 v18.1.1
 -------
 
diff --git a/cherrypy/_cpnative_server.py b/cherrypy/_cpnative_server.py
index 55653c35..e9671d28 100644
--- a/cherrypy/_cpnative_server.py
+++ b/cherrypy/_cpnative_server.py
@@ -9,6 +9,7 @@ import cheroot.server
 import cherrypy
 from cherrypy._cperror import format_exc, bare_error
 from cherrypy.lib import httputil
+from ._cpcompat import tonative
 
 
 class NativeGateway(cheroot.server.Gateway):
@@ -21,21 +22,25 @@ class NativeGateway(cheroot.server.Gateway):
         req = self.req
         try:
             # Obtain a Request object from CherryPy
-            local = req.server.bind_addr
+            local = req.server.bind_addr  # FIXME: handle UNIX sockets
+            local = tonative(local[0]), local[1]
             local = httputil.Host(local[0], local[1], '')
-            remote = req.conn.remote_addr, req.conn.remote_port
+            remote = tonative(req.conn.remote_addr), req.conn.remote_port
             remote = httputil.Host(remote[0], remote[1], '')
 
-            scheme = req.scheme
-            sn = cherrypy.tree.script_name(req.uri or '/')
+            scheme = tonative(req.scheme)
+            sn = cherrypy.tree.script_name(tonative(req.uri or '/'))
             if sn is None:
                 self.send_response('404 Not Found', [], [''])
             else:
                 app = cherrypy.tree.apps[sn]
-                method = req.method
-                path = req.path
-                qs = req.qs or ''
-                headers = req.inheaders.items()
+                method = tonative(req.method)
+                path = tonative(req.path)
+                qs = tonative(req.qs or '')
+                headers = (
+                    (tonative(h), tonative(v))
+                    for h, v in req.inheaders.items()
+                )
                 rfile = req.rfile
                 prev = None
 
@@ -52,8 +57,11 @@ class NativeGateway(cheroot.server.Gateway):
                         # Run the CherryPy Request object and obtain the
                         # response
                         try:
-                            request.run(method, path, qs,
-                                        req.request_protocol, headers, rfile)
+                            request.run(
+                                method, path, qs,
+                                tonative(req.request_protocol),
+                                headers, rfile,
+                            )
                             break
                         except cherrypy.InternalRedirect:
                             ir = sys.exc_info()[1]


Test patch:
diff --git a/cherrypy/test/test_native.py b/cherrypy/test/test_native.py
index caebc3f4..08bf9997 100644
--- a/cherrypy/test/test_native.py
+++ b/cherrypy/test/test_native.py
@@ -32,4 +32,7 @@ def cp_native_server(request):
 
 def test_basic_request(cp_native_server):
     """A request to a native server should succeed."""
-    cp_native_server.get('/')
+    resp = cp_native_server.get('/')
+    assert resp.ok
+    assert resp.status_code == 200
+    assert resp.text == 'Hello World!'