# Buggy code
def environ(request, body=None):
    """
    Converts a ``tornado.httputil.HTTPServerRequest`` to a WSGI environment.

    An optional ``body`` to be used for populating ``wsgi.input`` can be supplied (either a string or a stream). If not
    supplied, ``request.body`` will be wrapped into a ``io.BytesIO`` stream and used instead.

    :param request: the ``tornado.httpserver.HTTPServerRequest`` to derive the WSGI environment from
    :param body: an optional body  to use as ``wsgi.input`` instead of ``request.body``, can be a string or a stream
    """
    from tornado.wsgi import to_wsgi_str
    import sys
    import io

    # determine the request_body to supply as wsgi.input
    if body is not None:
        if isinstance(body, (bytes, str)):
            request_body = io.BytesIO(tornado.escape.utf8(body))
        else:
            request_body = body
    else:
        request_body = io.BytesIO(tornado.escape.utf8(request.body))

    hostport = request.host.split(":")
    if len(hostport) == 2:
        host = hostport[0]
        port = int(hostport[1])
    else:
        host = request.host
        port = 443 if request.protocol == "https" else 80
    environ = {
        "REQUEST_METHOD": request.method,
        "SCRIPT_NAME": "",
        "PATH_INFO": to_wsgi_str(
            tornado.escape.url_unescape(request.path, encoding=None, plus=False)
        ),
        "QUERY_STRING": request.query,
        "REMOTE_ADDR": request.remote_ip,
        "SERVER_NAME": host,
        "SERVER_PORT": str(port),
        "SERVER_PROTOCOL": request.version,
        "wsgi.version": (1, 0),
        "wsgi.url_scheme": request.protocol,
        "wsgi.input": request_body,
        "wsgi.errors": sys.stderr,
        "wsgi.multithread": False,
        "wsgi.multiprocess": True,
        "wsgi.run_once": False,
    }
    if "Content-Type" in request.headers:
        environ["CONTENT_TYPE"] = request.headers.pop("Content-Type")
    if "Content-Length" in request.headers:
        environ["CONTENT_LENGTH"] = request.headers.pop("Content-Length")
    for key, value in request.headers.items():
        environ["HTTP_" + key.replace("-", "_").upper()] = value
    return environ


# Traceback (AttributeError)
2016-10-04 17:22:29,805 - octoprint - ERROR - Exception on /api/files/local [POST]
Traceback (most recent call last):
File "/home/pi/oprint/local/lib/python2.7/site-packages/Flask-0.10.1-py2.7.egg/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/home/pi/oprint/local/lib/python2.7/site-packages/Flask-0.10.1-py2.7.egg/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/home/pi/oprint/local/lib/python2.7/site-packages/Flask-0.10.1-py2.7.egg/flask/app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/home/pi/oprint/local/lib/python2.7/site-packages/Flask-0.10.1-py2.7.egg/flask/app.py", line 1473, in full_dispatch_request
rv = self.preprocess_request()
File "/home/pi/oprint/local/lib/python2.7/site-packages/Flask-0.10.1-py2.7.egg/flask/app.py", line 1666, in preprocess_request
rv = func()
File "/home/pi/oprint/local/lib/python2.7/site-packages/OctoPrint-1.2.16-py2.7.egg/octoprint/server/__init__.py", line 675, in before_request
g.locale = self._get_locale()
File "/home/pi/oprint/local/lib/python2.7/site-packages/OctoPrint-1.2.16-py2.7.egg/octoprint/server/__init__.py", line 543, in _get_locale
if "l10n" in request.values:
File "/home/pi/oprint/local/lib/python2.7/site-packages/Werkzeug-0.8.3-py2.7.egg/werkzeug/local.py", line 336, in __getattr__
return getattr(self._get_current_object(), name)
File "/home/pi/oprint/local/lib/python2.7/site-packages/Werkzeug-0.8.3-py2.7.egg/werkzeug/utils.py", line 77, in __get__
value = self.func(obj)
File "/home/pi/oprint/local/lib/python2.7/site-packages/Werkzeug-0.8.3-py2.7.egg/werkzeug/wrappers.py", line 390, in values
for d in self.args, self.form:
File "/home/pi/oprint/local/lib/python2.7/site-packages/Werkzeug-0.8.3-py2.7.egg/werkzeug/utils.py", line 77, in __get__
value = self.func(obj)
File "/home/pi/oprint/local/lib/python2.7/site-packages/Werkzeug-0.8.3-py2.7.egg/werkzeug/wrappers.py", line 383, in form
self._load_form_data()
File "/home/pi/oprint/local/lib/python2.7/site-packages/Flask-0.10.1-py2.7.egg/flask/wrappers.py", line 165, in _load_form_data
RequestBase._load_form_data(self)
File "/home/pi/oprint/local/lib/python2.7/site-packages/Werkzeug-0.8.3-py2.7.egg/werkzeug/wrappers.py", line 318, in _load_form_data
data = parser.parse_from_environ(self.environ)
File "/home/pi/oprint/local/lib/python2.7/site-packages/Werkzeug-0.8.3-py2.7.egg/werkzeug/formparser.py", line 164, in parse_from_environ
return self.parse(stream, mimetype, content_length, options)
File "/home/pi/oprint/local/lib/python2.7/site-packages/Werkzeug-0.8.3-py2.7.egg/werkzeug/formparser.py", line 182, in parse
input_stream = LimitedStream(stream, content_length)
File "/home/pi/oprint/local/lib/python2.7/site-packages/Werkzeug-0.8.3-py2.7.egg/werkzeug/wsgi.py", line 736, in __init__
self._read = stream.read
AttributeError: 'unicode' object has no attribute 'read'
2016-10-04 17:34:06,720 - octoprint.server.util.sockjs - INFO - Client connection closed: 172.20.10.3

# Fixed code
def environ(request, body=None):
    """
    Converts a ``tornado.httputil.HTTPServerRequest`` to a WSGI environment.

    An optional ``body`` to be used for populating ``wsgi.input`` can be supplied (either a string or a stream). If not
    supplied, ``request.body`` will be wrapped into a ``io.BytesIO`` stream and used instead.

    :param request: the ``tornado.httpserver.HTTPServerRequest`` to derive the WSGI environment from
    :param body: an optional body  to use as ``wsgi.input`` instead of ``request.body``, can be a string or a stream
    """
    from tornado.wsgi import to_wsgi_str
    import sys
    import io

    # determine the request_body to supply as wsgi.input
    if body is not None:
        if isinstance(body, (bytes, str, unicode)):
            request_body = io.BytesIO(tornado.escape.utf8(body))
        else:
            request_body = body
    else:
        request_body = io.BytesIO(tornado.escape.utf8(request.body))

    hostport = request.host.split(":")
    if len(hostport) == 2:
        host = hostport[0]
        port = int(hostport[1])
    else:
        host = request.host
        port = 443 if request.protocol == "https" else 80
    environ = {
        "REQUEST_METHOD": request.method,
        "SCRIPT_NAME": "",
        "PATH_INFO": to_wsgi_str(
            tornado.escape.url_unescape(request.path, encoding=None, plus=False)
        ),
        "QUERY_STRING": request.query,
        "REMOTE_ADDR": request.remote_ip,
        "SERVER_NAME": host,
        "SERVER_PORT": str(port),
        "SERVER_PROTOCOL": request.version,
        "wsgi.version": (1, 0),
        "wsgi.url_scheme": request.protocol,
        "wsgi.input": request_body,
        "wsgi.errors": sys.stderr,
        "wsgi.multithread": False,
        "wsgi.multiprocess": True,
        "wsgi.run_once": False,
    }
    if "Content-Type" in request.headers:
        environ["CONTENT_TYPE"] = request.headers.pop("Content-Type")
    if "Content-Length" in request.headers:
        environ["CONTENT_LENGTH"] = request.headers.pop("Content-Length")
    for key, value in request.headers.items():
        environ["HTTP_" + key.replace("-", "_").upper()] = value
    return environ