*wfastcgi.py for IIS and web2py* воскресенье, 3 апреля 2016 г., 7:56:03 UTC+3 пользователь Dmitri Ermolaev написал: > > I do it! Its work: > http://web2py.com/books/default/chapter/29/13/deployment-recipes#IIS > > IIS much quicker than Apache - Apache load a processor at 100% for AJAX > requests (( > > thanx > > > вторник, 6 сентября 2011 г., 13:03:42 UTC+3 пользователь Hassan Alnatour > написал: >> >> how can i deploy web2py on IIS ?? > >
-- Resources: - http://web2py.com - http://web2py.com/book (Documentation) - http://github.com/web2py/web2py (Source code) - https://code.google.com/p/web2py/issues/list (Report Issues) --- You received this message because you are subscribed to the Google Groups "web2py-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to web2py+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
# ############################################################################ # # Copyright (c) Microsoft Corporation. # # This source code is subject to terms and conditions of the Apache License, Version 2.0. A # copy of the license can be found in the License.html file at the root of this distribution. If # you cannot locate the Apache License, Version 2.0, please send an email to # vspyt...@microsoft.com. By using this source code in any fashion, you are agreeing to be bound # by the terms of the Apache License, Version 2.0. # # You must not remove this notice, or any other, from this software. # # ########################################################################### from __future__ import absolute_import, print_function, with_statement import ctypes import datetime import os import re import struct import sys import traceback from xml.dom import minidom try: from cStringIO import StringIO BytesIO = StringIO except ImportError: from io import StringIO, BytesIO try: from thread import start_new_thread except ImportError: from _thread import start_new_thread __version__ = '3.0.0' if sys.version_info[0] == 3: def to_str(value): return value.decode(sys.getfilesystemencoding()) else: def to_str(value): return value.encode(sys.getfilesystemencoding()) # http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S3 FCGI_VERSION_1 = 1 FCGI_HEADER_LEN = 8 FCGI_BEGIN_REQUEST = 1 FCGI_ABORT_REQUEST = 2 FCGI_END_REQUEST = 3 FCGI_PARAMS = 4 FCGI_STDIN = 5 FCGI_STDOUT = 6 FCGI_STDERR = 7 FCGI_DATA = 8 FCGI_GET_VALUES = 9 FCGI_GET_VALUES_RESULT = 10 FCGI_UNKNOWN_TYPE = 11 FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE FCGI_NULL_REQUEST_ID = 0 FCGI_KEEP_CONN = 1 FCGI_RESPONDER = 1 FCGI_AUTHORIZER = 2 FCGI_FILTER = 3 FCGI_REQUEST_COMPLETE = 0 FCGI_CANT_MPX_CONN = 1 FCGI_OVERLOADED = 2 FCGI_UNKNOWN_ROLE = 3 FCGI_MAX_CONNS = "FCGI_MAX_CONNS" FCGI_MAX_REQS = "FCGI_MAX_REQS" FCGI_MPXS_CONNS = "FCGI_MPXS_CONNS" class FastCgiRecord(object): """Represents a FastCgiRecord. Encapulates the type, role, flags. Holds onto the params which we will receive and update later.""" def __init__(self, type, req_id, role, flags): self.type = type self.req_id = req_id self.role = role self.flags = flags self.params = {} def __repr__(self): return '<FastCgiRecord(%d, %d, %d, %d)>' % (self.type, self.req_id, self.role, self.flags) #typedef struct { # unsigned char version; # unsigned char type; # unsigned char requestIdB1; # unsigned char requestIdB0; # unsigned char contentLengthB1; # unsigned char contentLengthB0; # unsigned char paddingLength; # unsigned char reserved; # unsigned char contentData[contentLength]; # unsigned char paddingData[paddingLength]; #} FCGI_Record; class _ExitException(Exception): pass if sys.version_info[0] >= 3: # indexing into byte strings gives us an int, so # ord is unnecessary on Python 3 def ord(x): return x def chr(x): return bytes((x, )) def wsgi_decode(x): return x.decode('iso-8859-1') def wsgi_encode(x): return x.encode('iso-8859-1') def fs_encode(x): return x def exception_with_traceback(exc_value, exc_tb): return exc_value.with_traceback(exc_tb) zero_bytes = bytes else: # Replace the builtin open with one that supports an encoding parameter from codecs import open def wsgi_decode(x): return x def wsgi_encode(x): return x def fs_encode(x): return x if isinstance(x, str) else x.encode(sys.getfilesystemencoding()) def exception_with_traceback(exc_value, exc_tb): # x.with_traceback() is not supported on 2.x return exc_value bytes = str def zero_bytes(length): return '\x00' * length def read_fastcgi_record(stream): """reads the main fast cgi record""" data = stream.read(8) # read record if not data: # no more data, our other process must have died... raise _ExitException() fcgi_ver, reqtype, req_id, content_size, padding_len, _ = struct.unpack('>BBHHBB', data) content = stream.read(content_size) # read content stream.read(padding_len) if fcgi_ver != FCGI_VERSION_1: raise Exception('Unknown fastcgi version %s' % fcgi_ver) processor = REQUEST_PROCESSORS.get(reqtype) if processor is not None: return processor(stream, req_id, content) # unknown type requested, send response log('Unknown request type %s' % reqtype) send_response(stream, req_id, FCGI_UNKNOWN_TYPE, chr(reqtype) + zero_bytes(7)) return None def read_fastcgi_begin_request(stream, req_id, content): """reads the begin request body and updates our _REQUESTS table to include the new request""" # typedef struct { # unsigned char roleB1; # unsigned char roleB0; # unsigned char flags; # unsigned char reserved[5]; # } FCGI_BeginRequestBody; # TODO: Ignore request if it exists res = FastCgiRecord( FCGI_BEGIN_REQUEST, req_id, (ord(content[0]) << 8) | ord(content[1]), # role ord(content[2]), # flags ) _REQUESTS[req_id] = res def read_encoded_int(content, offset): i = struct.unpack_from('>B', content, offset)[0] if i < 0x80: return offset + 1, i return offset + 4, struct.unpack_from('>I', content, offset)[0] & ~0x80000000 def read_fastcgi_keyvalue_pairs(content, offset): """Reads a FastCGI key/value pair stream""" offset, name_len = read_encoded_int(content, offset) offset, value_len = read_encoded_int(content, offset) name = content[offset:(offset + name_len)] offset += name_len value = content[offset:(offset + value_len)] offset += value_len return offset, name, value def get_encoded_int(i): """Writes the length of a single name for a key or value in a key/value stream""" if i <= 0x7f: return struct.pack('>B', i) elif i < 0x80000000: return struct.pack('>I', i | 0x80000000) else: raise ValueError('cannot encode value %s (%x) because it is too large' % (i, i)) def write_fastcgi_keyvalue_pairs(pairs): """Creates a FastCGI key/value stream and returns it as a byte string""" parts = [] for raw_key, raw_value in pairs.items(): key = wsgi_encode(raw_key) value = wsgi_encode(raw_value) parts.append(get_encoded_int(len(key))) parts.append(get_encoded_int(len(value))) parts.append(key) parts.append(value) return bytes().join(parts) # Keys in this set will be stored in the record without modification but with a # 'wsgi.' prefix. The original key will have the decoded version. # (Following mod_wsgi from http://wsgi.readthedocs.org/en/latest/python3.html) RAW_VALUE_NAMES = { 'SCRIPT_NAME' : 'wsgi.script_name', 'PATH_INFO' : 'wsgi.path_info', 'QUERY_STRING' : 'wsgi.query_string', 'HTTP_X_ORIGINAL_URL' : 'wfastcgi.http_x_original_url', } def read_fastcgi_params(stream, req_id, content): if not content: return None offset = 0 res = _REQUESTS[req_id].params while offset < len(content): offset, name, value = read_fastcgi_keyvalue_pairs(content, offset) name = wsgi_decode(name) raw_name = RAW_VALUE_NAMES.get(name) if raw_name: res[raw_name] = value res[name] = wsgi_decode(value) def read_fastcgi_input(stream, req_id, content): """reads FastCGI std-in and stores it in wsgi.input passed in the wsgi environment array""" res = _REQUESTS[req_id].params if 'wsgi.input' not in res: res['wsgi.input'] = content else: res['wsgi.input'] += content if not content: # we've hit the end of the input stream, time to process input... return _REQUESTS[req_id] def read_fastcgi_data(stream, req_id, content): """reads FastCGI data stream and publishes it as wsgi.data""" res = _REQUESTS[req_id].params if 'wsgi.data' not in res: res['wsgi.data'] = content else: res['wsgi.data'] += content def read_fastcgi_abort_request(stream, req_id, content): """reads the wsgi abort request, which we ignore, we'll send the finish execution request anyway...""" pass def read_fastcgi_get_values(stream, req_id, content): """reads the fastcgi request to get parameter values, and immediately responds""" offset = 0 request = {} while offset < len(content): offset, name, value = read_fastcgi_keyvalue_pairs(content, offset) request[name] = value response = {} if FCGI_MAX_CONNS in request: response[FCGI_MAX_CONNS] = '1' if FCGI_MAX_REQS in request: response[FCGI_MAX_REQS] = '1' if FCGI_MPXS_CONNS in request: response[FCGI_MPXS_CONNS] = '0' send_response( stream, req_id, FCGI_GET_VALUES_RESULT, write_fastcgi_keyvalue_pairs(response) ) # Our request processors for different FastCGI protocol requests. Only those # requests that we receive are defined here. REQUEST_PROCESSORS = { FCGI_BEGIN_REQUEST : read_fastcgi_begin_request, FCGI_ABORT_REQUEST : read_fastcgi_abort_request, FCGI_PARAMS : read_fastcgi_params, FCGI_STDIN : read_fastcgi_input, FCGI_DATA : read_fastcgi_data, FCGI_GET_VALUES : read_fastcgi_get_values } def log(txt): """Logs messages to a log file if WSGI_LOG env var is defined.""" log_file = os.environ.get('WSGI_LOG') if log_file: with open(log_file, 'a+', encoding='utf-8') as f: txt = txt.replace('\r\n', '\n') f.write('%s: %s%s' % (datetime.datetime.now(), txt, '' if txt.endswith('\n') else '\n')) def maybe_log(txt): """Logs messages to a log file if WSGI_LOG env var is defined, and does not raise exceptions if logging fails.""" try: log(txt) except: pass def send_response(stream, req_id, resp_type, content, streaming=True): """sends a response w/ the given id, type, and content to the server. If the content is streaming then an empty record is sent at the end to terminate the stream""" if not isinstance(content, bytes): raise TypeError("content must be encoded before sending: %r" % content) offset = 0 while True: len_remaining = max(min(len(content) - offset, 0xFFFF), 0) data = struct.pack( '>BBHHBB', FCGI_VERSION_1, # version resp_type, # type req_id, # requestIdB1:B0 len_remaining, # contentLengthB1:B0 0, # paddingLength 0, # reserved ) + content[offset:(offset + len_remaining)] offset += len_remaining os.write(stream.fileno(), data) if len_remaining == 0 or not streaming: break stream.flush() def get_environment(dir): web_config = os.path.join(dir, 'Web.config') if not os.path.exists(web_config): return {} d = {} doc = minidom.parse(web_config) config = doc.getElementsByTagName('configuration') for configSection in config: appSettings = configSection.getElementsByTagName('appSettings') for appSettingsSection in appSettings: values = appSettingsSection.getElementsByTagName('add') for curAdd in values: key = curAdd.getAttribute('key') value = curAdd.getAttribute('value') if key and value is not None: d[key.strip()] = value return d ReadDirectoryChangesW = ctypes.windll.kernel32.ReadDirectoryChangesW ReadDirectoryChangesW.restype = ctypes.c_uint32 ReadDirectoryChangesW.argtypes = [ ctypes.c_void_p, # HANDLE hDirectory ctypes.c_void_p, # LPVOID lpBuffer ctypes.c_uint32, # DWORD nBufferLength ctypes.c_uint32, # BOOL bWatchSubtree ctypes.c_uint32, # DWORD dwNotifyFilter ctypes.POINTER(ctypes.c_uint32), # LPDWORD lpBytesReturned ctypes.c_void_p, # LPOVERLAPPED lpOverlapped ctypes.c_void_p # LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine ] try: from _winapi import (CreateFile, CloseHandle, GetLastError, ExitProcess, WaitForSingleObject, INFINITE, OPEN_EXISTING) except ImportError: CreateFile = ctypes.windll.kernel32.CreateFileW CreateFile.restype = ctypes.c_void_p CreateFile.argtypes = [ ctypes.c_wchar_p, # lpFilename ctypes.c_uint32, # dwDesiredAccess ctypes.c_uint32, # dwShareMode ctypes.c_void_p, # LPSECURITY_ATTRIBUTES, ctypes.c_uint32, # dwCreationDisposition, ctypes.c_uint32, # dwFlagsAndAttributes, ctypes.c_void_p # hTemplateFile ] CloseHandle = ctypes.windll.kernel32.CloseHandle CloseHandle.argtypes = [ctypes.c_void_p] GetLastError = ctypes.windll.kernel32.GetLastError GetLastError.restype = ctypes.c_uint32 ExitProcess = ctypes.windll.kernel32.ExitProcess ExitProcess.restype = ctypes.c_void_p ExitProcess.argtypes = [ctypes.c_uint32] WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleObject WaitForSingleObject.argtypes = [ctypes.c_void_p, ctypes.c_uint32] WaitForSingleObject.restype = ctypes.c_uint32 OPEN_EXISTING = 3 INFINITE = -1 FILE_LIST_DIRECTORY = 1 FILE_SHARE_READ = 0x00000001 FILE_SHARE_WRITE = 0x00000002 FILE_SHARE_DELETE = 0x00000004 FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 MAX_PATH = 260 FILE_NOTIFY_CHANGE_LAST_WRITE = 0x10 ERROR_NOTIFY_ENUM_DIR = 1022 INVALID_HANDLE_VALUE = 0xFFFFFFFF class FILE_NOTIFY_INFORMATION(ctypes.Structure): _fields_ = [('NextEntryOffset', ctypes.c_uint32), ('Action', ctypes.c_uint32), ('FileNameLength', ctypes.c_uint32), ('Filename', ctypes.c_wchar)] _ON_EXIT_TASKS = None def run_exit_tasks(): global _ON_EXIT_TASKS maybe_log("Running on_exit tasks") while _ON_EXIT_TASKS: tasks, _ON_EXIT_TASKS = _ON_EXIT_TASKS, [] for t in tasks: try: t() except Exception: maybe_log("Error in exit task: " + traceback.format_exc()) def on_exit(task): global _ON_EXIT_TASKS if _ON_EXIT_TASKS is None: _ON_EXIT_TASKS = tasks = [] try: evt = int(os.getenv('_FCGI_SHUTDOWN_EVENT_')) except (TypeError, ValueError): maybe_log("Could not wait on event %s" % os.getenv('_FCGI_SHUTDOWN_EVENT_')) else: def _wait_for_exit(): WaitForSingleObject(evt, INFINITE) run_exit_tasks() ExitProcess(0) start_new_thread(_wait_for_exit, ()) _ON_EXIT_TASKS.append(task) def start_file_watcher(path, restart_regex): if restart_regex is None: restart_regex = ".*((\\.py)|(\\.config))$" elif not restart_regex: # restart regex set to empty string, no restart behavior return def enum_changes(path): """Returns a generator that blocks until a change occurs, then yields the filename of the changed file. Yields an empty string and stops if the buffer overruns, indicating that too many files were changed.""" buffer = ctypes.create_string_buffer(32 * 1024) bytes_ret = ctypes.c_uint32() try: the_dir = CreateFile( path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0, ) except OSError: maybe_log("Unable to create watcher") return if not the_dir or the_dir == INVALID_HANDLE_VALUE: maybe_log("Unable to create watcher") return while True: ret_code = ReadDirectoryChangesW( the_dir, buffer, ctypes.sizeof(buffer), True, FILE_NOTIFY_CHANGE_LAST_WRITE, ctypes.byref(bytes_ret), None, None, ) if ret_code: cur_pointer = ctypes.addressof(buffer) while True: fni = ctypes.cast(cur_pointer, ctypes.POINTER(FILE_NOTIFY_INFORMATION)) # FileName is not null-terminated, so specifying length is mandatory. filename = ctypes.wstring_at(cur_pointer + 12, fni.contents.FileNameLength // 2) yield filename if fni.contents.NextEntryOffset == 0: break cur_pointer = cur_pointer + fni.contents.NextEntryOffset elif GetLastError() == ERROR_NOTIFY_ENUM_DIR: CloseHandle(the_dir) yield '' return else: CloseHandle(the_dir) return log('wfastcgi.py will restart when files in %s are changed: %s' % (path, restart_regex)) def watcher(path, restart): for filename in enum_changes(path): if not filename: log('wfastcgi.py exiting because the buffer was full') run_exit_tasks() ExitProcess(0) elif restart.match(filename): log('wfastcgi.py exiting because %s has changed, matching %s' % (filename, restart_regex)) # we call ExitProcess directly to quickly shutdown the whole process # because sys.exit(0) won't have an effect on the main thread. run_exit_tasks() ExitProcess(0) restart = re.compile(restart_regex) start_new_thread(watcher, (path, restart)) def get_wsgi_handler(handler_name): if not handler_name: raise Exception('WSGI_HANDLER env var must be set') if not isinstance(handler_name, str): handler_name = to_str(handler_name) module_name, _, callable_name = handler_name.rpartition('.') should_call = callable_name.endswith('()') callable_name = callable_name[:-2] if should_call else callable_name name_list = [(callable_name, should_call)] handler = None last_tb = '' while module_name: try: handler = __import__(module_name, fromlist=[name_list[0][0]]) last_tb = '' for name, should_call in name_list: handler = getattr(handler, name) if should_call: handler = handler() break except ImportError: module_name, _, callable_name = module_name.rpartition('.') should_call = callable_name.endswith('()') callable_name = callable_name[:-2] if should_call else callable_name name_list.insert(0, (callable_name, should_call)) handler = None last_tb = ': ' + traceback.format_exc() if handler is None: raise ValueError('"%s" could not be imported%s' % (handler_name, last_tb)) return handler def read_wsgi_handler(physical_path): env = get_environment(physical_path) os.environ.update(env) for path in (v for k, v in env.items() if k.lower() == 'pythonpath'): # Expand environment variables manually. expanded_path = re.sub( '%(\\w+?)%', lambda m: os.getenv(m.group(1), ''), path ) sys.path.extend(fs_encode(p) for p in expanded_path.split(';') if p) handler = get_wsgi_handler(os.getenv('WSGI_HANDLER')) instr_key = env.get("APPINSIGHTS_INSTRUMENTATIONKEY") if instr_key: try: # Attempt the import after updating sys.path- sites must # include applicationinsights themselves. from applicationinsights.requests import WSGIApplication except ImportError: maybe_log("Failed to import applicationinsights: " + traceback.format_exc()) pass else: handler = WSGIApplication(instr_key, handler) # Ensure we will flush any remaining events when we exit on_exit(handler.client.flush) return env, handler class handle_response(object): """A context manager for handling the response. This will ensure that exceptions in the handler are correctly reported, and the FastCGI request is properly terminated. """ def __init__(self, stream, record, get_output, get_errors): self.stream = stream self.record = record self._get_output = get_output self._get_errors = get_errors self.error_message = '' self.fatal_errors = False self.physical_path = '' self.header_bytes = None self.sent_headers = False def __enter__(self): record = self.record record.params['wsgi.input'] = BytesIO(record.params['wsgi.input']) record.params['wsgi.version'] = (1, 0) record.params['wsgi.url_scheme'] = 'https' if record.params.get('HTTPS', '').lower() == 'on' else 'http' record.params['wsgi.multiprocess'] = True record.params['wsgi.multithread'] = False record.params['wsgi.run_once'] = False self.physical_path = record.params.get('APPL_PHYSICAL_PATH', os.path.dirname(__file__)) if 'HTTP_X_ORIGINAL_URL' in record.params: # We've been re-written for shared FastCGI hosting, so send the # original URL as PATH_INFO. record.params['PATH_INFO'] = record.params['HTTP_X_ORIGINAL_URL'] record.params['wsgi.path_info'] = record.params['wfastcgi.http_x_original_url'] # PATH_INFO is not supposed to include the query parameters, so remove them record.params['PATH_INFO'] = record.params['PATH_INFO'].partition('?')[0] record.params['wsgi.path_info'] = record.params['wsgi.path_info'].partition(wsgi_encode('?'))[0] return self def __exit__(self, exc_type, exc_value, exc_tb): # Send any error message on FCGI_STDERR. if exc_type and exc_type is not _ExitException: error_msg = "%s:\n\n%s\n\nStdOut: %s\n\nStdErr: %s" % ( self.error_message or 'Error occurred', ''.join(traceback.format_exception(exc_type, exc_value, exc_tb)), self._get_output(), self._get_errors(), ) if not self.header_bytes or not self.sent_headers: self.header_bytes = wsgi_encode('Status: 500 Internal Server Error\r\n') self.send(FCGI_STDERR, wsgi_encode(error_msg)) # Best effort at writing to the log. It's more important to # finish the response or the user will only see a generic 500 # error. maybe_log(error_msg) # End the request. This has to run in both success and failure cases. self.send(FCGI_END_REQUEST, zero_bytes(8), streaming=False) # Remove the request from our global dict del _REQUESTS[self.record.req_id] # Suppress all exceptions unless requested return not self.fatal_errors @staticmethod def _decode_header(key, value): if not isinstance(key, str): key = wsgi_decode(key) if not isinstance(value, str): value = wsgi_decode(value) return key, value def start(self, status, headers, exc_info=None): """Starts sending the response. The response is ended when the context manager exits.""" if exc_info: try: if self.sent_headers: # We have to re-raise if we've already started sending data. raise exception_with_traceback(exc_info[1], exc_info[2]) finally: exc_info = None elif self.header_bytes: raise Exception('start_response has already been called') if not isinstance(status, str): status = wsgi_decode(status) header_text = 'Status: %s\r\n' % status if headers: header_text += ''.join('%s: %s\r\n' % handle_response._decode_header(*i) for i in headers) self.header_bytes = wsgi_encode(header_text + '\r\n') return lambda content: self.send(FCGI_STDOUT, content) def send(self, resp_type, content, streaming=True): '''Sends part of the response.''' if not self.sent_headers: if not self.header_bytes: raise Exception("start_response has not yet been called") self.sent_headers = True send_response(self.stream, self.record.req_id, FCGI_STDOUT, self.header_bytes) self.header_bytes = None return send_response(self.stream, self.record.req_id, resp_type, content, streaming) _REQUESTS = {} def main(): initialized = False log('wfastcgi.py %s started' % __version__) log('Python version: %s' % sys.version) try: fcgi_stream = sys.stdin.detach() if sys.version_info[0] >= 3 else sys.stdin try: import msvcrt msvcrt.setmode(fcgi_stream.fileno(), os.O_BINARY) except ImportError: pass while True: record = read_fastcgi_record(fcgi_stream) if not record: continue errors = sys.stderr = sys.__stderr__ = record.params['wsgi.errors'] = StringIO() output = sys.stdout = sys.__stdout__ = StringIO() with handle_response(fcgi_stream, record, output.getvalue, errors.getvalue) as response: if not initialized: log('wfastcgi.py %s initializing' % __version__) os.chdir(response.physical_path) sys.path[0] = '.' # Initialization errors should be treated as fatal. response.fatal_errors = True response.error_message = 'Error occurred while reading WSGI handler' env, handler = read_wsgi_handler(response.physical_path) response.error_message = 'Error occurred starting file watcher' start_file_watcher(response.physical_path, env.get('WSGI_RESTART_FILE_REGEX')) response.error_message = '' response.fatal_errors = False log('wfastcgi.py %s initialized' % __version__) initialized = True os.environ.update(env) # SCRIPT_NAME + PATH_INFO is supposed to be the full path # (http://www.python.org/dev/peps/pep-0333/) but by default # (http://msdn.microsoft.com/en-us/library/ms525840(v=vs.90).aspx) # IIS is sending us the full URL in PATH_INFO, so we need to # clear the script name here if 'AllowPathInfoForScriptMappings' not in os.environ: record.params['SCRIPT_NAME'] = '' record.params['wsgi.script_name'] = wsgi_encode('') # Send each part of the response to FCGI_STDOUT. # Exceptions raised in the handler will be logged by the context # manager and we will then wait for the next record. result = handler(record.params, response.start) try: for part in result: if part: response.send(FCGI_STDOUT, part) finally: if hasattr(result, 'close'): result.close() except _ExitException: pass except Exception: maybe_log('Unhandled exception in wfastcgi.py: ' + traceback.format_exc()) except BaseException: maybe_log('Unhandled exception in wfastcgi.py: ' + traceback.format_exc()) raise finally: run_exit_tasks() maybe_log('wfastcgi.py %s closed' % __version__) def _run_appcmd(args): from subprocess import check_call, CalledProcessError if len(sys.argv) > 1 and os.path.isfile(sys.argv[1]): appcmd = sys.argv[1:] else: appcmd = [os.path.join(os.getenv('SystemRoot'), 'system32', 'inetsrv', 'appcmd.exe')] if not os.path.isfile(appcmd[0]): print('IIS configuration tool appcmd.exe was not found at', appcmd, file=sys.stderr) return -1 args = appcmd + args try: return check_call(args) except CalledProcessError as ex: print('''An error occurred running the command: %r Ensure your user has sufficient privileges and try again.''' % args, file=sys.stderr) return ex.returncode def enable(): res = _run_appcmd([ "set", "config", "/section:system.webServer/fastCGI", "/+[fullPath='" + sys.executable + "', arguments='" + __file__ + "', signalBeforeTerminateSeconds='30']" ]) if res == 0: print('"%s|%s" can now be used as a FastCGI script processor' % (sys.executable, __file__)) return res def disable(): res = _run_appcmd([ "set", "config", "/section:system.webServer/fastCGI", "/-[fullPath='" + sys.executable + "', arguments='" + __file__ + "', signalBeforeTerminateSeconds='30']" ]) if res == 0: print('"%s|%s" is no longer registered for use with FastCGI' % (sys.executable, __file__)) return res if __name__ == '__main__': main()