Truncated output using Python bottle 0.12.8 as a C

2019-08-24 02:15发布

问题:

This is the application:

#!/home2/friendv0/Python-2.7.9/bin/python

from bottle import Bottle

app = Bottle()

@app.get('/')
def hello():
    return """<!DOCTYPE html>
<html lang="en">
<head>
<title>bottle Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
</head>
<body>
Hello!
</body>
</html>
"""

app.run(server='cgi')

The resulting output is:

<!DOCTYPE html>
<html lang="en">
<head>
<title>bottle Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
</head>
<body>
Hello!
</body>

Note that the closing </html> tag is missing. This only occurs when the applications is run as a CGI script under Windows 7 (or on Windows 8) -- not when it is installed as a native WSGI application. I have tried this with both Apache 2.2 and Apache 2.4. Note that the same CGI script runs without truncation when installed on a Linux system running Apache 2.2. What is puzzling is that I have successfully run other WSGI applications as CGI scripts under Windows without experiencing truncation using the same technique that bottle uses, namely:

from wsgiref.handlers import CGIHandler
CGIHandler().run(application)

Has anyone else experienced the same problem? As a side note: The reason why I am interested in running bottle as a CGI script is because my anticipated volume is very low so performance will not be an issue. But on the Linux server (where, fortunately, CGI is working), I do not have the ability to restart the server and if I have to make emergency changes to the source code, I need the changes to go into effect immediately.

回答1:

Well, I have figured out the problem. The string literal length is 201 characters long (which is the content-length in the header). Each line is terminated by a single LF (linefeed) character (even though on Windows the actual text is terminated with CRLF). Yet, when the text is being sent out to the browser, each line ending is now a CR-LF pair, making the actual output longer than 201 characters, but because of the content-length being set in the header, there is truncation. I went back to my other, working WSGi-CGI application and now remember that because there were instances where I was sending out images, that I had set the stdout stream to binary mode (not necessary on Unix/Linux). This clearly had the side effect of preventing extra carriage return characters from being inserted in text streams if I had templates without them to begin with. So, now I have the following code:

import os
if os.name == 'nt':
    import msvcrt
    msvcrt.setmode(0, os.O_BINARY ) # 0 = sysin
    msvcrt.setmode(1, os.O_BINARY ) # 0 = stdout

app.run(server='cgi')


回答2:

Yes, I had the same problem in my Flask application: Python 2.7.13 CGIHandler (mod_cgi), Apache 2.2 on Windows platform.
The solution is confirmed! This is a test script:

#!C:/Python27/python.exe
# -*- coding: utf-8 -*-

import os

if os.name == 'nt':
    import msvcrt
    msvcrt.setmode( sys.stdin.fileno(), os.O_BINARY )
    msvcrt.setmode( sys.stdout.fileno(), os.O_BINARY )
    msvcrt.setmode( sys.stderr.fileno(), os.O_BINARY )

from wsgiref.handlers import CGIHandler

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return ['Hello World!\nXMX\nYMY\n']

if __name__ == '__main__':
    CGIHandler().run( application )

Without the msvcrt.setmode the output will be truncated.
The same application with WSGI (mod_wsgi) works correctly.
No CGI problems on POSIX systems.
Another solution is to put Python in 'unbuffered mode' adding -u parameter to the interpreter directive:

#!C:/Python27/python.exe -u
# -*- coding: utf-8 -*-

from wsgiref.handlers import CGIHandler

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return ['Hello World!\nXMX\nYMY\n']

if __name__ == '__main__':
    CGIHandler().run( application )

Python documentation reference says:

-u Force stdin, stdout and stderr to be totally unbuffered. On systems where it matters, also put stdin, stdout and stderr in binary mode.