image server down!——解决 CGI 脚本中 urllib2.urlopen 引发的“连接被拒绝”错误

52 阅读2分钟

有一段 Python CGI 脚本,用于检查一个进程是否正在运行,如果该进程未找到,则启动它。该进程本身是一个基于 web.py 的 Web 服务器。确保该进程正在运行后,尝试向其发出 URL 请求。这个想法是从 CGI 脚本的请求者重定向此请求的结果,基本上是想将查询重定向到监听不同端口的本地 Web 服务器。

这段代码如果在 shell 中先启动服务器,然后从非 CGI 请求中调用,可以正常工作。但是,如果尝试通过下面的 CGI 脚本启动进程,在调用 urllib2.urlopen 时就会抛出异常,表明连接被拒绝。

2、解决方案

问题在于 CGI 脚本和 Web 服务器进程运行在不同的用户帐户下。CGI 脚本通常由 Web 服务器用户运行,而 Web 服务器进程通常由另一个用户(例如 www-data)运行。因此,由 CGI 脚本启动的 Web 服务器进程无法访问 CGI 脚本尝试连接的端口。

要解决这个问题,需要确保 CGI 脚本和 Web 服务器进程都运行在同一个用户帐户下。这可以通过使用 suexec 配置 Apache 来实现。

1、在 Apache 配置文件中添加以下行:

SuexecUserGroup www-data www-data

2、将 CGI 脚本的权限更改为由 www-data 用户拥有:

chown www-data /path/to/cgi-script

3、重新启动 Apache。

现在,CGI 脚本和 Web 服务器进程都将运行在同一个用户帐户下,因此 CGI 脚本可以成功连接到由 Web 服务器进程监听的端口。

以下是经过修改后的代码,可以正常工作:

#!/usr/bin/python
import cgi, cgitb
cgitb.enable()
import os, re
import sys
import subprocess

import urllib2
urlbase = "http://localhost:8080/getimage"
imgserver = "/home/martin/public_html/cgi-bin/stlimgservermirror.py" # this is based on web.py

def findImgServerProcess():
    r = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0]
    return re.match(".*%s" % os.path.split(imgserver)[-1], r, re.DOTALL)

def ensureImgServerProcess():
    if findImgServerProcess():
        return True

    os.environ['LD_LIBRARY_PATH'] = '/home/martin/lib'
    fout = open("/dev/null", "w")
    ferr = fout
    subprocess.Popen([sys.executable, imgserver], stdout=fout, stderr=subprocess.STDOUT)

    # use 'sleep' to ensure that the process will run after 'ensureImgServerProcess()' return
    import time
    time.sleep(1)
    
    # now try and find the process
    return findImgServerProcess() != None

def main():
    if not ensureImgServerProcess():
        print "Content-type: text/plain\n"
        print "image server down!"
        return

    form = cgi.FieldStorage()
    if form.has_key("debug"):
        print "Content-type: text/plain\n"
        print os.environ['QUERY_STRING']
    else:
        try:
            img = urllib2.urlopen("%s?%s" % (urlbase, os.environ['QUERY_STRING'])).read()
        except Exception, e:
            print "Content-type: text/plain\n"
            print e
            sys.exit()
        print "Content-type: image/png\n"
        print img

if __name__ == "__main__":
    main()