« Does your bender have a Lucy Liu?Newsradio DVD just shipped!!! »

WSGI gateway for ASP (Microsoft IIS)

05/26/05

Permalink 03:31:02 pm, by fumanchu Email , 542 words   English (US)
Categories: Python, WSGI

WSGI gateway for ASP (Microsoft IIS)

Update: I forgot to address buffering.

As I mentioned, I threw together an WSGI wrapper (gateway) for ASP. Here it is. Feedback welcome.

It blanks out SCRIPT_NAME to behave more like Apache. It also handles URL-rewriting, since that's pretty much the only sane way to use ASP with WSGI (or any framework, for that matter).


"""
WSGI wrapper for ASP.


Example Global.asa for a CherryPy app called "mcontrol":

<script language=Python runat=Server> 
def Application_OnStart():
    Application.Contents("multiprocess") = False
    Application.Contents("multithread") = True
    from mcontrol import chpy
</script>


Example handler.asp:

<%@Language=Python%>
<%
from wsgiref.asp_gateway import handler
from cherrypy.wsgiapp import wsgiApp

handler(Application, Request, Response).run(wsgiApp)
%>

"""

import sys
from wsgiref.handlers import BaseCGIHandler


class ASPInputWrapper(object):
    
    def __init__(self, Request):
        self.stream = Request.BinaryRead
        size = Request.ServerVariables('CONTENT_LENGTH')
        self.remainder = self.size = int(size)
    
    def read(self, size=-1):
        if size lt; 0:
            size = self.remainder
        content, size = self.stream(size)
        self.remainder -= size
        return content
    
    def readline(self):
        output = []
        while True:
            # Use an internal buffer instead? Still have to check for \n
            char = self.read(1)
            if not char:
                break
            output.append(char)
            if char in ('\n', '\r'):
                break
        return ''.join(output)
    
    def readlines(self, hint=-1):
        lines = []
        while True:
            line = self.readline()
            if not line:
                break
            lines.append(line)
        return lines
    
    def __iter__(self):
        line = self.readline()
        while line:
            yield line
            # Notice this won't prefetch the next line; it only
            # gets called if the generator is resumed.
            line = self.readline()


class handler(BaseCGIHandler):
    
    def __init__(self, Application, Request, Response, buffering=True):
        # If you set buffering to False, you must not "Enable Buffering" in
        # the current Virtual Directory, NOR in any of its parent containers
        # (directory, site, or server). IIS 5 and 6 buffer by default.
        # See http://support.microsoft.com/default.aspx?scid=kb;en-us;Q306805
        # and http://www.aspfaq.com/show.asp?id=2262
        Response.Buffer = buffering
        
        env = {}
        for name in Request.ServerVariables:
            try:
                # names and values are both probably unicode. coerce them.
                env[str(name)] = str(Request.ServerVariables(name))
            except UnicodeEncodeError, x:
                # There's a potential problem lurking here, since some ASP
                # server var's which are required by WSGI may be high ASCII.
                x.args += ((u"Server Variable '%s'" % name),)
                raise x
        
        multiprocess = str(Application.Contents("multiprocess"))
        multithread = str(Application.Contents("multithread"))
        
        # You will probably need *some* form of rewriter to use ASP
        # with WSGI, since ASP requires one physical .asp file
        # per requestable-URL; so far, we support one:
        
        # Handle URL rewriting done by ISAPI_Rewrite Lite.
        # http://www.isapirewrite.com/
        # Note that PATH_TRANSLATED is also rewritten, but we
        # don't make any provision for unmunging that.
        old_path = env.get("HTTP_X_REWRITE_URL", None)
        if old_path:
            # Tear off any params.
            env["PATH_INFO"] = old_path.split("?")[0]
        
        # ASP puts the same values in SCRIPT_NAME and PATH_INFO,
        # for some odd reason. Empty one of them.
        env["SCRIPT_NAME"] = ""
        
        BaseCGIHandler.__init__(self,
                                stdin=ASPInputWrapper(Request),
                                stdout=None,
                                stderr=sys.stderr,
                                environ=env,
                                multiprocess=multiprocess,
                                multithread=multithread
                                )
        
        self.Response = Response
        self._write = Response.Write
    
    def _flush(self):
        self.Response.Flush()
    
    def send_headers(self):
        self.cleanup_headers()
        self.headers_sent = True
        for key, val in self.headers.items():
            self.Response.AddHeader(key, val)

7 comments

Comment from: Andy Todd [Visitor] · http://www.halfcooked.com/

I'm not up on the technicalities, but does this module work with Mark Ree's ISAPI-WSGI toolkit?

http://isapi-wsgi.python-hosting.com/

05/28/05 @ 00:13
Comment from: fumanchu [Member] Email

Hi, Andy! I've been reading your blog for quite a while.

As I mentioned earlier, I don't think Mark's ISAPI is ready for my needs yet (no threads yet). The non-WSGI ISAPI that Phil Frantz put into pywin32 (build 203+) seems a bit further along right now--could be helpful to finish Mark's.

Of course, the ASP wrapper (above) doesn't really have anything to do with any ISAPI implementation, except that they compete in roughly the same space.

05/28/05 @ 09:28
Comment from: Mark Rees [Visitor]

Yes, isapi_wsgi doesn't have thread support yet. Have been working on the next release to use IO Completion ports but have a few problems to sort out. Also a lack of spare time is slowing down development :-)

05/29/05 @ 06:15
Comment from: Sean Fulmer [Visitor]

How does global.asa find mcontrol? Where does the cherrypy app live?

06/30/05 @ 14:30
Comment from: fumanchu [Member] Email

Sean,

global.asa finds "mcontrol" in the same manner that any other Python script finds a module. In my particular case, I have a package named "mcontrol" in my site-packages directory; "mcontrol" is the name of my cherrypy application, and it contains a module named "chpy" which initializes the CherryPy config and server.

07/02/05 @ 12:11
Comment from: Jason R. Coombs [Visitor] · http://www.jaraco.com

I'm revisiting using this for running cherrypy behind IIS. It's mostly working at this point, after running into a swath of small problems. The last problem I encountered is that in CherryPy, it expects the result of a cgiFieldStorage read to return a string (or at least an object with a .strip method). The object returned at line 43, however, returns a buffer object which does not meet this interface.

This causes an error 'buffer object has no attribute strip' in cgi.py when a POST request is sent through CherryPy.

I corrected the problem with a quick fix by explicitly transforming the result to a string:

def read(self, size=-1):
if size < 0:
size = self.remainder
content, size = self.stream(size)
self.remainder -= size
return str( content )

03/02/06 @ 20:23
Comment from: fumanchu [Member] Email

Thanks, Jason! I've incorporated your change at the permanent home of asp_gateway: http://projects.amor.org/misc/browser/asp_gateway.py

03/02/06 @ 23:20

Leave a comment


Your email address will not be revealed on this site.

Your URL will be displayed.

Please enter the phrase "I am a real human." in the textbox above.
(Line breaks become <br />)
(Name, email & website)
(Allow users to contact you through a message form (your email will not be revealed.)
July 2009
Sun Mon Tue Wed Thu Fri Sat
 << <   > >>
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

Search

The requested Blog doesn't exist any more!

XML Feeds

blogging software