Reputation: 15772
I have python server serving cgi scripts,
I want to add status code to my response. I did,
try:
cgi = CGI()
output = cgi.fire()
print 'Content-Type text/json'
print 'Status:200 success'
print
print json.dumps(output)
except:
print 'Content-Type: text/json'
print 'Status: 403 Forbidden'
print
print json.dumps({'msg':'error'})
But when I request the this script via dojo xhr request, I get 200 request status. Why is so?
Request URL:http://192.168.2.72:8080/cgi-bin/cgi.py
Request Method:POST
Status Code:200 Script output follows
Request Headersview source
Accept:*/*
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Content-Length:125
Content-Type:application/x-www-form-urlencoded
Host:192.168.2.72:7999
Origin:http://192.168.2.72:7999
Pragma:no-cache
Referer:http://192.168.2.72:7999/home.html
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko) Ubuntu Chromium/25.0.1364.160 Chrome/25.0.1364.160 Safari/537.22
X-Requested-With:XMLHttpRequest
Form Dataview sourceview URL encoded
Response Headersview source
Content-Type:text/json
Date:Fri, 08 Aug 2014 05:16:29 GMT
Server:SimpleHTTP/0.6 Python/2.7.3
Status:403 Forbidden
Any inputs?
what I already have tried:
result.ioArgs.xhr.getAllResponseHeaders() // returns string
ioargs.xhr.status // returns the request status.
Upvotes: 3
Views: 4485
Reputation: 5515
I see some comments confusing.
Unless I am missing something it is my understanding that if you return an HTTP 500 from the CGI script the server shall honour that HTTP response code and forward it to the client. Instead, http.server seems to report HTTP 200.
I prepare this CGI shell test script to mimic a CGI returning 500:
$ cat ERROR500.cgi
#!/bin/sh
# Set the HTTP return code to 500 Internal Server Error
echo "Status: 500 Internal Server Error"
# Print the Content-Type header
echo "Content-Type: text/html; charset=UTF-8"
# Print a blank line to separate headers from the body
echo
# Print the HTML body
echo "<html>"
echo "<head><title>Internal Server Error</title></head>"
echo "<body>"
echo "<h1>500 - Internal Server Error</h1>"
echo "<p>Something went wrong on the server. Please try again later.</p>"
echo "</body>"
echo "</html>"
I invoke it via CGI using $ python3.9 -m http.server --cgi
and then accessing http://localhost:8000/cgi-bin/ERROR500.cgi
and still I get a 200 Ok from the server.
Is this the expected behaviour according to the CGI RFC?
Note: In case it helps someone and although a bit less convenient, a single-file zero dependencies web server that handles CGIs as expected in this case is althttpd, written by the author of SQlite.
There is a single-file repository here: https://github.com/jesrui/althttpd which I found easier to compile.
Upvotes: 0
Reputation: 21
For anyone coming across this in future, the reason for this is the python http.server which was being used to serve the content. For some reason this is designed to spit out a Status 200: script output follows
header before the cgi script starts running. This means that you can't change the status code returned within your script (see the documentation for CGIHTTPRequestHandler on this page)
This actually makes it a real pain to use when developing as the errors don't propagate in the same way they would in production.
Upvotes: 2
Reputation: 2095
just do json.dumps() to a string before outputting your headers and you should be fine no?
that will protect you from setting headers and then getting an exception as exception in print is unlikely
Upvotes: 0
Reputation: 11235
To complement what Jason S says I reproduced exactly in his answer I reproduced the exactly same failure with a non json serializable object (in this example a md5 hash) and have the same behaviour than original poster a 200 return code
#!/usr/bin/env python
import json
import traceback
class CGI:
def fire(self):
import md5
return md5.md5()
try:
cgi = CGI()
output = cgi.fire()
print 'Content-Type text/json'
print 'Status:200 success'
print
print json.dumps(output)
except:
traceback.print_exc()
print 'Content-Type: text/json'
print 'Status: 403 Forbidden'
print
print json.dumps({'msg':'error'})
interacting with the server
$ socat - TCP4:localhost:8000
input
GET /cgi-bin/test.py HTTP/1.0
output
HTTP/1.0 200 Script output follows
Server: SimpleHTTP/0.6 Python/3.4.0
Date: Sun, 17 Aug 2014 16:16:19 GMT
Content-Type text/json
Status:200 success
Content-Type: text/json
Status: 403 Forbidden
{"msg": "error"}
traceback:
127.0.0.1 - - [17/Aug/2014 18:16:19] "GET /cgi-bin/test.py HTTP/1.0" 200 -
Traceback (most recent call last):
File "/home/xcombelle/dev/test/cgi-bin/test.py", line 16, in <module>
print json.dumps(output)
File "/usr/lib/python2.7/json/__init__.py", line 231, in dumps
return _default_encoder.encode(obj)
File "/usr/lib/python2.7/json/encoder.py", line 200, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python2.7/json/encoder.py", line 263, in iterencode
return _iterencode(o, 0)
File "/usr/lib/python2.7/json/encoder.py", line 177, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: <md5 HASH object @ 0x7fcb090d0a30> is not JSON serializable
Upvotes: 1
Reputation: 13799
If json.dumps(output)
raises an exception, you will have already printed your headers including status code (generally would be spelled as Status: 200 OK
) and a blank line to end the header section of the HTTP response.
Then, the except
block will print a second set of headers, but those are actually considered part of the body of the response at that point because printing an empty line ended the headers. See the HTTP message spec.
The solution is to wait until you know what your output is going to be to print any headers.
-more-
json.dumps
can raise exceptions if you give it input that is not serializable. And given that cgi.fire()
appears to be a method of some custom CGI object (builtin cgi
module doesn't have that method) it could be returning anything.
To debug you need to log what exception is being raised, preferably with traceback. The bare except:
block you have will catch all errors and then do nothing with them, so you don't know what's going on, nor does anyone looking at the question. You might also need to log the value of output
.
Upvotes: 5
Reputation: 3196
It looks to me like you are setting a Status:
header field but you want to set Status-Code:
.
Does your script really write Status Code:200 Script output follows
as a header field?
Upvotes: -3