Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested Bottle app with Waitress #76

Closed
PlaidPhantom opened this issue Nov 17, 2014 · 5 comments · Fixed by #82
Closed

Nested Bottle app with Waitress #76

PlaidPhantom opened this issue Nov 17, 2014 · 5 comments · Fixed by #82
Labels

Comments

@PlaidPhantom
Copy link

I am building a web application using nested Bottle apps and I'm having problems returning a static file from the nested app when running the site through the Waitress web server. It works fine when run with WSGIRefServer, which suggests a problem with Waitress.

Sample app that replicates the issue below (browse to /test/). If I run the app using WSGIRefServer (commented run command) then the javascript file downloads fine. If i use server="waitress" then /test/js returns a blank response (no errors).

from bottle import Bottle, run, static_file

baseapp = Bottle()
app = Bottle()
baseapp.mount("/test/", app)

@app.get("/")
def index():
    return """<!DOCTYPE html>
<html><body>
<p>Result: <span id="asdf">Failed :(</span></p>
<script src="js"></script>
</body></html>
"""

@app.get("/js")
def js():
    return static_file("test.js", "./")

run(app = baseapp, server="waitress", url="0.0.0.0", port=8080)
#run(app = baseapp, url="0.0.0.0", port=8080)

Contents of test.js:

document.getElementById('asdf').innerHTML = 'Worked! :)';

I'm running Python 3.4.2 with Bottle 0.12.7 and Waitress 0.8.9. (downloaded via pip).

@frankdarcy
Copy link

I have the same issue,
with Python 2.7.5, Bottle v0.13-dev and Waitress 0.8.10dev (cloned about a month ago)
In my case I see a warning on the server side.
With your sample code and I see the same problem.

WARNING:waitress:application returned too few bytes (0) for specified Content-Length (58) via app_iter

Seems to be to do with how waitress implements 'wsgi.file_wrapper'

In the bottle app, if I remove 'wsgi.file_wrapper' from request.environ then it all works fine.
Like so ..

@app.get("/js")
def js():
    if 'wsgi.file_wrapper' in request.environ:
        del request.environ['wsgi.file_wrapper']
    return static_file("test.js", "./")

This causes an iterable file wrapper to be created, which avoids the problem,
but its only a workaround, a fix would be great.

@frankdarcy
Copy link

Have looked into this a little deeper and I'm not convinced this is a waitress bug,
could be a bottle bug, would love to hear opinions from others with more WSGI knowledge and experience. Here's what I've found ...

waitress implements 'wsgi.file_wrapper' using the class ReadOnlyFileBasedBuffer
which inherits from FileBasedBuffer which implements:

def __nonzero__(self):
    return self.remain > 0

So this means that until self.remain is set to something greater than zero,
the iterator returned by 'wsgi.file_wrapper' evaluates to False
self.remain does not get set until prepare() is called.

This becomes a problem if used in a nested bottle app
because bottle wraps such apps in its mount method with the mountpoint_wrapper() function
which checks the response from the nested bottle app as follows:

body = app(request.environ, start_response)
if body and rs.body: body = itertools.chain(rs.body, body)
rs.body = body or rs.body
return rs

In our case, the above wrapper is called before ReadOnlyFileBasedBuffer.prepare() is called,
so body (which contains the ReadOnlyFileBasedBuffer object) evaluates to False and so it is not returned in rs.body

I have found that adding self.remain = 1 to ReadOnlyFileBasedBuffer.__init__ avoids the bug in this case, but ...

Why should bottle check whether or not body is True or False? Should it simple check it is an iterable object?

@frankdarcy
Copy link

From defnull in Bottle Issue 678

A non-empty iterator should not evaluate to False just because it does not know if it is empty or not.

If waitress' maintainers agree then can we fix this?

@djamelfel
Copy link

I have got the same issue when I try to mount a plugin on my web application:
WARNING:waitress:application returned too few bytes (0) for specified Content-Length (1142) via app_iter

Do you know how I can work around this issue until this is fixed upstream?

@frankdarcy
Copy link

Workaround ...
In the bottle app, if I remove 'wsgi.file_wrapper' from request.environ then it all works fine.
Like so ..

@app.get("/js")
def js():
    if 'wsgi.file_wrapper' in request.environ:
        del request.environ['wsgi.file_wrapper']
    return static_file("test.js", "./")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants