Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send response headers and status from CGI scripts

Tags:

python

cgi

I am using CGIHTTPServer.py for creating simple CGI server. I want my CGI script to take care of response code if some operation goes wrong . How can I do that?

Code snippet from my CGI script.

if authmxn.authenticate():
     stats = Stats()
     print "Content-Type: application/json"
     print 'Status: 200 OK'
     print

     print json.dumps(stats.getStats())
 else:
     print 'Content-Type: application/json'
     print 'Status: 403 Forbidden'
     print
     print json.dumps({'msg': 'request is not authenticated'})

Some of the snippet from request handler,

def run_cgi(self):
'''
    rest of code
'''
    if not os.path.exists(scriptfile):
        self.send_error(404, "No such CGI script (%s)" % `scriptname`)
        return
    if not os.path.isfile(scriptfile):
        self.send_error(403, "CGI script is not a plain file (%s)" %
                        `scriptname`)
        return
    ispy = self.is_python(scriptname)
    if not ispy:
        if not (self.have_fork or self.have_popen2):
            self.send_error(403, "CGI script is not a Python script (%s)" %
                            `scriptname`)
            return
        if not self.is_executable(scriptfile):
            self.send_error(403, "CGI script is not executable (%s)" %
                            `scriptname`)
            return

    if not self.have_fork:
        # Since we're setting the env in the parent, provide empty
        # values to override previously set values
        for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH',
                  'HTTP_USER_AGENT', 'HTTP_COOKIE'):
            env.setdefault(k, "")

    self.send_response(200, "Script output follows") # overrides the headers

    decoded_query = query.replace('+', ' ')
like image 960
Deepak Ingole Avatar asked Sep 13 '25 08:09

Deepak Ingole


1 Answers

It is possible to implement support for a Status: code message header that overrides the HTTP status line (first line of HTTP response, e.g. HTTP/1.0 200 OK). This requires:

  1. sub-classing CGIHTTPRequestHandler in order to trick it into writing the CGI script's output into a StringIO object instead of directly to a socket.
  2. Then, once the CGI script is complete, update the HTTP status line with the value provided in the Status: header.

It's a hack, but it's not too bad and no standard library code needs to be patched.

import BaseHTTPServer
import SimpleHTTPServer

from CGIHTTPServer import CGIHTTPRequestHandler
from cStringIO import StringIO

class BufferedCGIHTTPRequestHandler(CGIHTTPRequestHandler):
    def setup(self):
        """
        Arrange for CGI response to be buffered in a StringIO rather than
        sent directly to the client.
        """
        CGIHTTPRequestHandler.setup(self)
        self.original_wfile = self.wfile
        self.wfile = StringIO()
        # prevent use of os.dup(self.wfile...) forces use of subprocess instead
        self.have_fork = False

    def run_cgi(self):
        """
        Post-process CGI script response before sending to client.
        Override HTTP status line with value of "Status:" header, if set.
        """
        CGIHTTPRequestHandler.run_cgi(self)
        self.wfile.seek(0)
        headers = []
        for line in self.wfile:
            headers.append(line)    # includes new line character
            if line.strip() == '':  # blank line signals end of headers
                body = self.wfile.read()
                break
            elif line.startswith('Status:'):
                # Use status header to override premature HTTP status line.
                # Header format is: "Status: code message"
                status = line.split(':')[1].strip()
                headers[0] = '%s %s' % (self.protocol_version, status)

        self.original_wfile.write(''.join(headers))
        self.original_wfile.write(body)


def test(HandlerClass = BufferedCGIHTTPRequestHandler,
         ServerClass = BaseHTTPServer.HTTPServer):
    SimpleHTTPServer.test(HandlerClass, ServerClass)

if __name__ == '__main__':
    test()

Needless to say, this is probably not the best way to go and you should look at a non-CGIHTTPServer solution, e.g. a micro-framework such as bottle, a proper web-server (from memory, CGIHTTPServer is not recommended for production use), fastcgi, or WSGI - just to name a few options.

like image 139
mhawke Avatar answered Sep 15 '25 21:09

mhawke