Как запустить скрипт CGI из обработчика WSGI? - PullRequest
3 голосов
/ 05 марта 2012

Я пытаюсь создать небольшой тестовый сервер для Bugzilla, чтобы можно было проверить изменения, которые я внесу, прежде чем они будут развернуты на главном сервере на основе Apache.Я наиболее знаком с Python, и я хотел, чтобы встроенный в Python HTTP-сервер запускал CGI-программы Bugzilla.

К сожалению, Bugzilla имеет гораздо больше, чем CGI-приложения.Он имеет кучу CSS и других данных, которые обслуживаются напрямую.Это означает, что обработчик должен иметь с ними дело.Я хотел бы настроить обработчик WSGI, который просматривает URL-адрес запроса и соответствующим образом направляет запрос либо в один из сценариев Bugzilla CGI, либо извлекает данные непосредственно из файловой системы.

Есть ли лучший способ выполнитьЧто я хочу сделать?Если нет, то есть ли уже приложение WSGI, которое настроит среду CGI и вызовет приложение CGI через модуль subprocess Python?

Ответы [ 2 ]

5 голосов
/ 09 марта 2012

Вот решение, которое работает, хотя оно довольно уродливое и немного медленное.Для каждого скрипта CGI, который вы хотите запустить, требуется отдельный CGIApplication объект.Поэтому, если у вас есть полный каталог, вам нужно будет создать отдельный объект CGIApplication для каждого из них.Когда вы создаете его, конечно, решать вам.Вы можете создавать новые экземпляры для каждого запроса, но, скорее всего, сэкономите немного времени, если избежите этого.

import os
import os.path as _osp
import re
import subprocess
import io
import email.parser

env_forward = re.compile('^[A-Z][A-Z0-9_]*$')
header_match = re.compile(b'^(.*?\\n[ \\t\\r]*\\n)(.*)$', re.M | re.S)
env_whitelist = frozenset(('AUTH_TYPE', 'CONTENT_LENGTH', 'CONTENT_TYPE',
                           'DOCUMENT_ROOT', 'QUERY_STRING', 'PATH_INFO',
                           'PATH_TRANSLATED', 'REMOTE_ADDR', 'REMOTE_PORT',
                           'REMOTE_IDENT', 'REMOTE_USER', 'REQUEST_METHOD',
                           'REQUEST_URI', 'SCRIPT_NAME',
                           'SERVER_ADDR', 'SERVER_ADMIN', 'SERVER_NAME',
                           'SERVER_PORT', 'SERVER_PROTOCOL',
                           'SERVER_SIGNATURE', 'SERVER_SOFTWARE'))

class CGIApplication(object):
    def __init__(self, appfname):
        self._appfname = _osp.abspath(appfname)

    def __call__(self, environ, start_respose):
        appenv = {item[0]: item[1] \
                      for item in environ.items() \
                      if ((item[0] in env_whitelist) or
                          item[0].startswith('HTTP_'))}
        appenv['GATEWAY_INTERFACE'] = 'CGI/1.1'
        appenv['PATH'] = '/usr/local/bin:/usr/bin:/bin'
        appenv['SCRIPT_FILENAME'] = self._appfname
        nbytes_for_cgi = appenv.get('CONTENT_LENGTH', '')
        nbytes_for_cgi = (int(nbytes_for_cgi) if nbytes_for_cgi != '' else 0)

        args = [self._appfname]
        query = environ.get('QUERY_STRING', None)
        query = query.replace('+', ' ')
        if '=' not in query:
            args.append(query)
        proc = subprocess.Popen(args,
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                env = appenv,
                                cwd = _osp.dirname(self._appfname))
        bytes_read = 0
        data_for_cgi = io.BytesIO()
        while bytes_read < nbytes_for_cgi:
            data = environ['wsgi.input'].read(nbytes_for_cgi - bytes_read)
            bytes_read += len(data)
            data_for_cgi.write(data)
            data = None
        data_for_cgi = data_for_cgi.getvalue()
        output, errdata = proc.communicate(data_for_cgi)
        data_for_cgi = None
        proc.stdin.close()
        proc.stdout.close()
        proc.stderr.close()
        try:
            errdata = errdata.decode('utf-8')
        except UnicodeDecodeError:
            errdata = errdata.decode('iso8859-1')
        environ['wsgi.errors'].write(errdata)
        errdata = None
        if proc.returncode != 0:
            start_respose('500 Internal Server Error',
                          [('Content-Type', 'text/plain')])
            return (b"CGI application died with non-zero return code.\n",)
        else:
            output_hdr = header_match.match(output)
            output_hdr, output = output_hdr.groups()
            parser = email.parser.HeaderParser()
            headers = parser.parsestr(output_hdr.decode('iso8859-1'))
            status = headers.get_all('Status', ['200 OK'])[-1]
            del headers['Status']
            start_respose(status, list(headers.items()))
            return (output,)
1 голос
/ 09 марта 2012

Вы используете pybugz?http://www.liquidx.net/pybugz/

http://code.google.com/p/pybugz/

Кроме того, настроить WSGI через Django и AppEngine действительно легко.Довольно быстро устанавливается и может быть полезным в качестве обходного пути.Это даст вам тестовый сервер, который сможет работать с cgi, css и т. Д. Bugzilla

Удачи

...