如何从CGI脚本发送响应头和状态

时间:2014-09-12 03:44:07

标签: python cgi

我正在使用CGIHTTPServer.py来创建简单的CGI服务器。如果某些操作出错,我希望我的CGI脚本能够处理响应代码。我怎么能这样做?

我的CGI脚本中的代码段。

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'})

来自请求处理程序的一些代码段

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('+', ' ')

2 个答案:

答案 0 :(得分:2)

使用标准库HTTP服务器,您无法执行此操作。来自library documentation

  

注意CGIHTTPRequestHandler类运行的CGI脚本无法执行重定向(HTTP代码302),因为在执行CGI脚本之前会发送代码200(后面的脚本输出)。这会抢占状态代码。

这意味着服务器不支持脚本中的Status: <status-code> <reason>标头。您正确识别了代码中显示此支持不存在的部分:服务器甚至在运行脚本之前发送状态代码200。您无法在脚本中更改此内容。

在Python bugtracker中有几个与此相关的票证,其中一些带有补丁,请参阅例如issue13893。因此,您可以选择修补标准库以添加此功能。

但是,我强烈建议您切换到另一种技术而不是CGI(或运行真正的Web服务器)。

答案 1 :(得分:2)

可以实现对覆盖HTTP状态行的Status: code message标头的支持(HTTP响应的第一行,例如HTTP/1.0 200 OK)。这需要:

  1. CGIHTTPRequestHandler进行子类化,以欺骗它将CGI脚本的输出写入StringIO对象,而不是直接写入套接字。
  2. 然后,一旦CGI脚本完成,请使用Status:标题中提供的值更新HTTP状态行。
  3. 这是一个黑客,但它并不太糟糕,并且不需要修补标准库代码。

    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()
    

    毋庸置疑,这可能不是最好的方法,你应该看一下非CGIHTTPServer解决方案,例如:一个微框架,如bottle,一个合适的网络服务器(从内存,CGIHTTPServer不推荐用于生产用途),fastcgi或WSGI - 只是举几个选项。