~ubuntu-branches/ubuntu/trusty/flup/trusty-proposed

« back to all changes in this revision

Viewing changes to flup/client/scgi_app.py

  • Committer: Bazaar Package Importer
  • Author(s): Kai Hendry
  • Date: 2006-11-28 18:53:39 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061128185339-sl543vf0gnzrkm7s
Tags: 0.2126-1
* New upstream release
* Started using the subversion revision as the package version with
  upstream's request

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2006 Allan Saddi <allan@saddi.com>
 
2
# All rights reserved.
 
3
#
 
4
# Redistribution and use in source and binary forms, with or without
 
5
# modification, are permitted provided that the following conditions
 
6
# are met:
 
7
# 1. Redistributions of source code must retain the above copyright
 
8
#    notice, this list of conditions and the following disclaimer.
 
9
# 2. Redistributions in binary form must reproduce the above copyright
 
10
#    notice, this list of conditions and the following disclaimer in the
 
11
#    documentation and/or other materials provided with the distribution.
 
12
#
 
13
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 
14
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
15
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
16
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 
17
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 
18
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 
19
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 
20
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 
21
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 
22
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 
23
# SUCH DAMAGE.
 
24
#
 
25
# $Id: scgi_app.py 2111 2006-11-25 02:00:21Z asaddi $
 
26
 
 
27
__author__ = 'Allan Saddi <allan@saddi.com>'
 
28
__version__ = '$Revision: 2111 $'
 
29
 
 
30
import select
 
31
import struct
 
32
import socket
 
33
import errno
 
34
 
 
35
__all__ = ['SCGIApp']
 
36
 
 
37
def encodeNetstring(s):
 
38
    return ''.join([str(len(s)), ':', s, ','])
 
39
 
 
40
class SCGIApp(object):
 
41
    def __init__(self, connect=None, host=None, port=None,
 
42
                 filterEnviron=True):
 
43
        if host is not None:
 
44
            assert port is not None
 
45
            connect=(host, port)
 
46
 
 
47
        assert connect is not None
 
48
        self._connect = connect
 
49
 
 
50
        self._filterEnviron = filterEnviron
 
51
        
 
52
    def __call__(self, environ, start_response):
 
53
        sock = self._getConnection()
 
54
 
 
55
        outfile = sock.makefile('w')
 
56
        infile = sock.makefile('r')
 
57
 
 
58
        sock.close()
 
59
 
 
60
        # Filter WSGI environ and send as request headers
 
61
        if self._filterEnviron:
 
62
            headers = self._defaultFilterEnviron(environ)
 
63
        else:
 
64
            headers = self._lightFilterEnviron(environ)
 
65
        # TODO: Anything not from environ that needs to be sent also?
 
66
 
 
67
        content_length = int(environ.get('CONTENT_LENGTH') or 0)
 
68
        if headers.has_key('CONTENT_LENGTH'):
 
69
            del headers['CONTENT_LENGTH']
 
70
            
 
71
        headers_out = ['CONTENT_LENGTH', str(content_length), 'SCGI', '1']
 
72
        for k,v in headers.items():
 
73
            headers_out.append(k)
 
74
            headers_out.append(v)
 
75
        headers_out.append('') # For trailing NUL
 
76
        outfile.write(encodeNetstring('\x00'.join(headers_out)))
 
77
 
 
78
        # Transfer wsgi.input to outfile
 
79
        while True:
 
80
            chunk_size = min(content_length, 4096)
 
81
            s = environ['wsgi.input'].read(chunk_size)
 
82
            content_length -= len(s)
 
83
            outfile.write(s)
 
84
 
 
85
            if not s: break
 
86
 
 
87
        outfile.close()
 
88
        
 
89
        # Read result from SCGI server
 
90
        result = []
 
91
        while True:
 
92
            buf = infile.read(4096)
 
93
            if not buf: break
 
94
 
 
95
            result.append(buf)
 
96
 
 
97
        infile.close()
 
98
        
 
99
        result = ''.join(result)
 
100
 
 
101
        # Parse response headers
 
102
        status = '200 OK'
 
103
        headers = []
 
104
        pos = 0
 
105
        while True:
 
106
            eolpos = result.find('\n', pos)
 
107
            if eolpos < 0: break
 
108
            line = result[pos:eolpos-1]
 
109
            pos = eolpos + 1
 
110
 
 
111
            # strip in case of CR. NB: This will also strip other
 
112
            # whitespace...
 
113
            line = line.strip()
 
114
            
 
115
            # Empty line signifies end of headers
 
116
            if not line: break
 
117
 
 
118
            # TODO: Better error handling
 
119
            header, value = line.split(':', 1)
 
120
            header = header.strip().lower()
 
121
            value = value.strip()
 
122
 
 
123
            if header == 'status':
 
124
                # Special handling of Status header
 
125
                status = value
 
126
                if status.find(' ') < 0:
 
127
                    # Append a dummy reason phrase if one was not provided
 
128
                    status += ' SCGIApp'
 
129
            else:
 
130
                headers.append((header, value))
 
131
 
 
132
        result = result[pos:]
 
133
 
 
134
        # Set WSGI status, headers, and return result.
 
135
        start_response(status, headers)
 
136
        return [result]
 
137
 
 
138
    def _getConnection(self):
 
139
        if type(self._connect) is str:
 
140
            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
 
141
        else:
 
142
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
143
        sock.connect(self._connect)
 
144
        return sock
 
145
    
 
146
    _environPrefixes = ['SERVER_', 'HTTP_', 'REQUEST_', 'REMOTE_', 'PATH_',
 
147
                        'CONTENT_']
 
148
    _environCopies = ['SCRIPT_NAME', 'QUERY_STRING', 'AUTH_TYPE']
 
149
    _environRenames = {}
 
150
 
 
151
    def _defaultFilterEnviron(self, environ):
 
152
        result = {}
 
153
        for n in environ.keys():
 
154
            for p in self._environPrefixes:
 
155
                if n.startswith(p):
 
156
                    result[n] = environ[n]
 
157
            if n in self._environCopies:
 
158
                result[n] = environ[n]
 
159
            if n in self._environRenames:
 
160
                result[self._environRenames[n]] = environ[n]
 
161
                
 
162
        return result
 
163
 
 
164
    def _lightFilterEnviron(self, environ):
 
165
        result = {}
 
166
        for n in environ.keys():
 
167
            if n.upper() == n:
 
168
                result[n] = environ[n]
 
169
        return result
 
170
 
 
171
if __name__ == '__main__':
 
172
    from flup.server.ajp import WSGIServer
 
173
    app = SCGIApp(connect=('localhost', 4000))
 
174
    #import paste.lint
 
175
    #app = paste.lint.middleware(app)
 
176
    WSGIServer(app).run()