Deepak Ingole
Deepak Ingole

Reputation: 15772

Return response code python HTTP header

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?

Header

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

Answers (6)

M.E.
M.E.

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.

enter image description here

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

Simon Andrews
Simon Andrews

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

user3012759
user3012759

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

Xavier Combelle
Xavier Combelle

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

Jason S
Jason S

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

Andrew Johnson
Andrew Johnson

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

Related Questions