changeset 82:189e7ca3fbe2

Initial work on porting AJP server to Python 3.0. Still unsure of environ handling.
author Allan Saddi <allan@saddi.com>
date Thu, 04 Dec 2008 13:37:09 -0800
parents ed98ce70981e
children 32cc618c009a
files flup/server/ajp.py flup/server/ajp_base.py
diffstat 2 files changed, 117 insertions(+), 117 deletions(-) [+]
line wrap: on
line diff
     1.1 --- a/flup/server/ajp.py	Thu Dec 04 09:55:21 2008 -0800
     1.2 +++ b/flup/server/ajp.py	Thu Dec 04 13:37:09 2008 -0800
     1.3 @@ -100,7 +100,7 @@
     1.4      Of course you will need an AJP1.3 connector for your webserver (e.g.
     1.5      mod_jk) - see <http://jakarta.apache.org/tomcat/connectors-doc/>.
     1.6      """
     1.7 -    def __init__(self, application, scriptName='', environ=None,
     1.8 +    def __init__(self, application, scriptName=b'', environ=None,
     1.9                   multithreaded=True, multiprocess=False,
    1.10                   bindAddress=('localhost', 8009), allowedServers=None,
    1.11                   loggingLevel=logging.INFO, debug=True, **kw):
     2.1 --- a/flup/server/ajp_base.py	Thu Dec 04 09:55:21 2008 -0800
     2.2 +++ b/flup/server/ajp_base.py	Thu Dec 04 13:37:09 2008 -0800
     2.3 @@ -47,100 +47,100 @@
     2.4      pass
     2.5  
     2.6  # Packet header prefixes.
     2.7 -SERVER_PREFIX = '\x12\x34'
     2.8 -CONTAINER_PREFIX = 'AB'
     2.9 +SERVER_PREFIX = b'\x12\x34'
    2.10 +CONTAINER_PREFIX = b'AB'
    2.11  
    2.12  # Server packet types.
    2.13 -PKTTYPE_FWD_REQ = '\x02'
    2.14 -PKTTYPE_SHUTDOWN = '\x07'
    2.15 -PKTTYPE_PING = '\x08'
    2.16 -PKTTYPE_CPING = '\x0a'
    2.17 +PKTTYPE_FWD_REQ = 0x02
    2.18 +PKTTYPE_SHUTDOWN = 0x07
    2.19 +PKTTYPE_PING = 0x08
    2.20 +PKTTYPE_CPING = 0x0a
    2.21  
    2.22  # Container packet types.
    2.23 -PKTTYPE_SEND_BODY = '\x03'
    2.24 -PKTTYPE_SEND_HEADERS = '\x04'
    2.25 -PKTTYPE_END_RESPONSE = '\x05'
    2.26 -PKTTYPE_GET_BODY = '\x06'
    2.27 -PKTTYPE_CPONG = '\x09'
    2.28 +PKTTYPE_SEND_BODY = b'\x03'
    2.29 +PKTTYPE_SEND_HEADERS = b'\x04'
    2.30 +PKTTYPE_END_RESPONSE = b'\x05'
    2.31 +PKTTYPE_GET_BODY = b'\x06'
    2.32 +PKTTYPE_CPONG = b'\x09'
    2.33  
    2.34  # Code tables for methods/headers/attributes.
    2.35  methodTable = [
    2.36      None,
    2.37 -    'OPTIONS',
    2.38 -    'GET',
    2.39 -    'HEAD',
    2.40 -    'POST',
    2.41 -    'PUT',
    2.42 -    'DELETE',
    2.43 -    'TRACE',
    2.44 -    'PROPFIND',
    2.45 -    'PROPPATCH',
    2.46 -    'MKCOL',
    2.47 -    'COPY',
    2.48 -    'MOVE',
    2.49 -    'LOCK',
    2.50 -    'UNLOCK',
    2.51 -    'ACL',
    2.52 -    'REPORT',
    2.53 -    'VERSION-CONTROL',
    2.54 -    'CHECKIN',
    2.55 -    'CHECKOUT',
    2.56 -    'UNCHECKOUT',
    2.57 -    'SEARCH',
    2.58 -    'MKWORKSPACE',
    2.59 -    'UPDATE',
    2.60 -    'LABEL',
    2.61 -    'MERGE',
    2.62 -    'BASELINE_CONTROL',
    2.63 -    'MKACTIVITY'
    2.64 +    b'OPTIONS',
    2.65 +    b'GET',
    2.66 +    b'HEAD',
    2.67 +    b'POST',
    2.68 +    b'PUT',
    2.69 +    b'DELETE',
    2.70 +    b'TRACE',
    2.71 +    b'PROPFIND',
    2.72 +    b'PROPPATCH',
    2.73 +    b'MKCOL',
    2.74 +    b'COPY',
    2.75 +    b'MOVE',
    2.76 +    b'LOCK',
    2.77 +    b'UNLOCK',
    2.78 +    b'ACL',
    2.79 +    b'REPORT',
    2.80 +    b'VERSION-CONTROL',
    2.81 +    b'CHECKIN',
    2.82 +    b'CHECKOUT',
    2.83 +    b'UNCHECKOUT',
    2.84 +    b'SEARCH',
    2.85 +    b'MKWORKSPACE',
    2.86 +    b'UPDATE',
    2.87 +    b'LABEL',
    2.88 +    b'MERGE',
    2.89 +    b'BASELINE_CONTROL',
    2.90 +    b'MKACTIVITY'
    2.91      ]
    2.92  
    2.93  requestHeaderTable = [
    2.94      None,
    2.95 -    'Accept',
    2.96 -    'Accept-Charset',
    2.97 -    'Accept-Encoding',
    2.98 -    'Accept-Language',
    2.99 -    'Authorization',
   2.100 -    'Connection',
   2.101 -    'Content-Type',
   2.102 -    'Content-Length',
   2.103 -    'Cookie',
   2.104 -    'Cookie2',
   2.105 -    'Host',
   2.106 -    'Pragma',
   2.107 -    'Referer',
   2.108 -    'User-Agent'
   2.109 +    b'Accept',
   2.110 +    b'Accept-Charset',
   2.111 +    b'Accept-Encoding',
   2.112 +    b'Accept-Language',
   2.113 +    b'Authorization',
   2.114 +    b'Connection',
   2.115 +    b'Content-Type',
   2.116 +    b'Content-Length',
   2.117 +    b'Cookie',
   2.118 +    b'Cookie2',
   2.119 +    b'Host',
   2.120 +    b'Pragma',
   2.121 +    b'Referer',
   2.122 +    b'User-Agent'
   2.123      ]
   2.124  
   2.125  attributeTable = [
   2.126      None,
   2.127 -    'CONTEXT',
   2.128 -    'SERVLET_PATH',
   2.129 -    'REMOTE_USER',
   2.130 -    'AUTH_TYPE',
   2.131 -    'QUERY_STRING',
   2.132 -    'JVM_ROUTE',
   2.133 -    'SSL_CERT',
   2.134 -    'SSL_CIPHER',
   2.135 -    'SSL_SESSION',
   2.136 +    b'CONTEXT',
   2.137 +    b'SERVLET_PATH',
   2.138 +    b'REMOTE_USER',
   2.139 +    b'AUTH_TYPE',
   2.140 +    b'QUERY_STRING',
   2.141 +    b'JVM_ROUTE',
   2.142 +    b'SSL_CERT',
   2.143 +    b'SSL_CIPHER',
   2.144 +    b'SSL_SESSION',
   2.145      None, # name follows
   2.146 -    'SSL_KEY_SIZE'
   2.147 +    b'SSL_KEY_SIZE'
   2.148      ]
   2.149  
   2.150  responseHeaderTable = [
   2.151      None,
   2.152 -    'content-type',
   2.153 -    'content-language',
   2.154 -    'content-length',
   2.155 -    'date',
   2.156 -    'last-modified',
   2.157 -    'location',
   2.158 -    'set-cookie',
   2.159 -    'set-cookie2',
   2.160 -    'servlet-engine',
   2.161 -    'status',
   2.162 -    'www-authenticate'
   2.163 +    b'content-type',
   2.164 +    b'content-language',
   2.165 +    b'content-length',
   2.166 +    b'date',
   2.167 +    b'last-modified',
   2.168 +    b'location',
   2.169 +    b'set-cookie',
   2.170 +    b'set-cookie2',
   2.171 +    b'servlet-engine',
   2.172 +    b'status',
   2.173 +    b'www-authenticate'
   2.174      ]
   2.175  
   2.176  # The main classes use this name for logging.
   2.177 @@ -167,7 +167,7 @@
   2.178          length = struct.unpack('>H', data[pos:pos+2])[0]
   2.179          pos += 2
   2.180          if length == 0xffff: # This was undocumented!
   2.181 -            return '', pos
   2.182 +            return b'', pos
   2.183          s = data[pos:pos+length]
   2.184          return s, pos+length+1 # Don't forget NUL
   2.185      except Exception as e:
   2.186 @@ -176,9 +176,9 @@
   2.187  def decodeRequestHeader(data, pos=0):
   2.188      """Decode a request header/value pair."""
   2.189      try:
   2.190 -        if data[pos] == '\xa0':
   2.191 +        if data[pos] == 0xa0:
   2.192              # Use table
   2.193 -            i = ord(data[pos+1])
   2.194 +            i = data[pos+1]
   2.195              name = requestHeaderTable[i]
   2.196              if name is None:
   2.197                  raise ValueError('bad request header code')
   2.198 @@ -193,7 +193,7 @@
   2.199  def decodeAttribute(data, pos=0):
   2.200      """Decode a request attribute."""
   2.201      try:
   2.202 -        i = ord(data[pos])
   2.203 +        i = data[pos]
   2.204          pos += 1
   2.205          if i == 0xff:
   2.206              # end
   2.207 @@ -218,7 +218,7 @@
   2.208  
   2.209  def encodeString(s):
   2.210      """Encode a string."""
   2.211 -    return struct.pack('>H', len(s)) + s + '\x00'
   2.212 +    return struct.pack('>H', len(s)) + s + b'\x00'
   2.213  
   2.214  def encodeResponseHeader(name, value):
   2.215      """Encode a response header/value pair."""
   2.216 @@ -226,7 +226,7 @@
   2.217      if lname in responseHeaderTable:
   2.218          # Use table
   2.219          i = responseHeaderTable.index(lname)
   2.220 -        out = '\xa0' + chr(i)
   2.221 +        out = b'\xa0' + bytes([i])
   2.222      else:
   2.223          out = encodeString(name)
   2.224      out += encodeString(value)
   2.225 @@ -235,7 +235,7 @@
   2.226  class Packet(object):
   2.227      """An AJP message packet."""
   2.228      def __init__(self):
   2.229 -        self.data = ''
   2.230 +        self.data = b''
   2.231          # Don't set this on write, it will be calculated automatically.
   2.232          self.length = 0
   2.233  
   2.234 @@ -261,7 +261,7 @@
   2.235              dataLen = len(data)
   2.236              recvLen += dataLen
   2.237              length -= dataLen
   2.238 -        return ''.join(dataList), recvLen
   2.239 +        return b''.join(dataList), recvLen
   2.240      _recvall = staticmethod(_recvall)
   2.241  
   2.242      def read(self, sock):
   2.243 @@ -325,7 +325,7 @@
   2.244          # See WSGIServer.
   2.245          self._shrinkThreshold = conn.server.inputStreamShrinkThreshold
   2.246  
   2.247 -        self._buf = ''
   2.248 +        self._buf = b''
   2.249          self._bufList = []
   2.250          self._pos = 0 # Current read position.
   2.251          self._avail = 0 # Number of bytes currently available.
   2.252 @@ -357,7 +357,7 @@
   2.253  
   2.254      def read(self, n=-1):
   2.255          if self._pos == self._length:
   2.256 -            return ''
   2.257 +            return b''
   2.258          while True:
   2.259              if n < 0 or (self._avail - self._pos) < n:
   2.260                  # Not enough data available.
   2.261 @@ -374,7 +374,7 @@
   2.262                  break
   2.263          # Merge buffer list, if necessary.
   2.264          if self._bufList:
   2.265 -            self._buf += ''.join(self._bufList)
   2.266 +            self._buf += b''.join(self._bufList)
   2.267              self._bufList = []
   2.268          r = self._buf[self._pos:newPos]
   2.269          self._pos = newPos
   2.270 @@ -383,11 +383,11 @@
   2.271  
   2.272      def readline(self, length=None):
   2.273          if self._pos == self._length:
   2.274 -            return ''
   2.275 +            return b''
   2.276          while True:
   2.277              # Unfortunately, we need to merge the buffer list early.
   2.278              if self._bufList:
   2.279 -                self._buf += ''.join(self._bufList)
   2.280 +                self._buf += b''.join(self._bufList)
   2.281                  self._bufList = []
   2.282              # Find newline.
   2.283              i = self._buf.find('\n', self._pos)
   2.284 @@ -494,7 +494,7 @@
   2.285  
   2.286          # Notify server of end of response (reuse flag is set to true).
   2.287          pkt = Packet()
   2.288 -        pkt.data = PKTTYPE_END_RESPONSE + '\x01'
   2.289 +        pkt.data = PKTTYPE_END_RESPONSE + b'\x01'
   2.290          self._conn.writePacket(pkt)
   2.291  
   2.292          handlerTime = end - start
   2.293 @@ -526,21 +526,21 @@
   2.294          self.environ['SERVER_NAME'] = value
   2.295  
   2.296      def setServerPort(self, value):
   2.297 -        self.environ['SERVER_PORT'] = str(value)
   2.298 +        self.environ['SERVER_PORT'] = str(value).encode('latin-1')
   2.299  
   2.300      def setIsSSL(self, value):
   2.301          if value:
   2.302              self.environ['HTTPS'] = 'on'
   2.303  
   2.304      def addHeader(self, name, value):
   2.305 -        name = name.replace('-', '_').upper()
   2.306 -        if name in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
   2.307 -            self.environ[name] = value
   2.308 -            if name == 'CONTENT_LENGTH':
   2.309 +        name = name.replace(b'-', b'_').upper()
   2.310 +        if name in (b'CONTENT_TYPE', b'CONTENT_LENGTH'):
   2.311 +            self.environ[name.decode('latin-1')] = value
   2.312 +            if name == b'CONTENT_LENGTH':
   2.313                  length = int(value)
   2.314                  self.input.setDataLength(length)
   2.315          else:
   2.316 -            self.environ['HTTP_'+name] = value
   2.317 +            self.environ[(b'HTTP_'+name).decode('latin-1')] = value
   2.318  
   2.319      def addAttribute(self, name, value):
   2.320          self.environ[name] = value
   2.321 @@ -564,7 +564,7 @@
   2.322                     struct.pack('>H', statusCode) + \
   2.323                     encodeString(statusMsg) + \
   2.324                     struct.pack('>H', len(headers)) + \
   2.325 -                   ''.join([encodeResponseHeader(name, value)
   2.326 +                   b''.join([encodeResponseHeader(name, value)
   2.327                              for name,value in headers])
   2.328  
   2.329          self._conn.writePacket(pkt)
   2.330 @@ -586,7 +586,7 @@
   2.331              pkt = Packet()
   2.332              pkt.data = PKTTYPE_SEND_BODY + \
   2.333                         struct.pack('>H', toWrite) + \
   2.334 -                       data[:toWrite] + '\x00' # Undocumented
   2.335 +                       data[:toWrite] + b'\x00' # Undocumented
   2.336              self._conn.writePacket(pkt)
   2.337  
   2.338              data = data[toWrite:]
   2.339 @@ -663,7 +663,7 @@
   2.340          assert self._request is None
   2.341  
   2.342          req = self.server.requestClass(self)
   2.343 -        i = ord(pkt.data[1])
   2.344 +        i = pkt.data[1]
   2.345          method = methodTable[i]
   2.346          if method is None:
   2.347              raise ValueError('bad method field')
   2.348 @@ -680,7 +680,7 @@
   2.349          req.setServerName(value)
   2.350          value = struct.unpack('>H', pkt.data[pos:pos+2])[0]
   2.351          req.setServerPort(value)
   2.352 -        i = ord(pkt.data[pos+2])
   2.353 +        i = pkt.data[pos+2]
   2.354          req.setIsSSL(i != 0)
   2.355  
   2.356          # Request headers.
   2.357 @@ -733,7 +733,7 @@
   2.358              self._request.input.addData(pkt.data[2:2+length])
   2.359          else:
   2.360              # Shouldn't really ever get here.
   2.361 -            self._request.input.addData('')
   2.362 +            self._request.input.addData(b'')
   2.363  
   2.364      def writePacket(self, pkt):
   2.365          """Sends a Packet to the server."""
   2.366 @@ -749,7 +749,7 @@
   2.367      # it is the maximum size of new data added per chunk.)
   2.368      inputStreamShrinkThreshold = 102400 - 8192
   2.369  
   2.370 -    def __init__(self, application, scriptName='', environ=None,
   2.371 +    def __init__(self, application, scriptName=b'', environ=None,
   2.372                   multithreaded=True, multiprocess=False,
   2.373                   bindAddress=('localhost', 8009), allowedServers=NoDefault,
   2.374                   loggingLevel=logging.INFO, debug=True):
   2.375 @@ -834,9 +834,9 @@
   2.376          environ['wsgi.run_once'] = False
   2.377  
   2.378          if environ.get('HTTPS', 'off') in ('on', '1'):
   2.379 -            environ['wsgi.url_scheme'] = 'https'
   2.380 +            environ['wsgi.url_scheme'] = b'https'
   2.381          else:
   2.382 -            environ['wsgi.url_scheme'] = 'http'
   2.383 +            environ['wsgi.url_scheme'] = b'http'
   2.384  
   2.385          self._sanitizeEnv(environ)
   2.386  
   2.387 @@ -845,7 +845,7 @@
   2.388          result = None
   2.389  
   2.390          def write(data):
   2.391 -            assert type(data) is str, 'write() argument must be string'
   2.392 +            assert type(data) is bytes, 'write() argument must be string'
   2.393              assert headers_set, 'write() before start_response()'
   2.394  
   2.395              if not headers_sent:
   2.396 @@ -854,14 +854,14 @@
   2.397                  statusMsg = status[4:]
   2.398                  found = False
   2.399                  for header,value in responseHeaders:
   2.400 -                    if header.lower() == 'content-length':
   2.401 +                    if header.lower() == b'content-length':
   2.402                          found = True
   2.403                          break
   2.404                  if not found and result is not None:
   2.405                      try:
   2.406                          if len(result) == 1:
   2.407 -                            responseHeaders.append(('Content-Length',
   2.408 -                                                    str(len(data))))
   2.409 +                            responseHeaders.append((b'Content-Length',
   2.410 +                                                    str(len(data)).encode('latin-1')))
   2.411                      except:
   2.412                          pass
   2.413                  request.startResponse(statusCode, statusMsg, responseHeaders)
   2.414 @@ -879,15 +879,15 @@
   2.415              else:
   2.416                  assert not headers_set, 'Headers already set!'
   2.417  
   2.418 -            assert type(status) is str, 'Status must be a string'
   2.419 +            assert type(status) is bytes, 'Status must be a string'
   2.420              assert len(status) >= 4, 'Status must be at least 4 characters'
   2.421              assert int(status[:3]), 'Status must begin with 3-digit code'
   2.422 -            assert status[3] == ' ', 'Status must have a space after code'
   2.423 +            assert status[3] == 0x20, 'Status must have a space after code'
   2.424              assert type(response_headers) is list, 'Headers must be a list'
   2.425              if __debug__:
   2.426                  for name,val in response_headers:
   2.427 -                    assert type(name) is str, 'Header name "%s" must be a string' % name
   2.428 -                    assert type(val) is str, 'Value of header "%s" must be a string' % name
   2.429 +                    assert type(name) is bytes, 'Header name "%s" must be a string' % name
   2.430 +                    assert type(val) is bytes, 'Value of header "%s" must be a string' % name
   2.431  
   2.432              headers_set[:] = [status, response_headers]
   2.433              return write
   2.434 @@ -902,7 +902,7 @@
   2.435                          if data:
   2.436                              write(data)
   2.437                      if not headers_sent:
   2.438 -                        write('') # in case body was empty
   2.439 +                        write(b'') # in case body was empty
   2.440                  finally:
   2.441                      if hasattr(result, 'close'):
   2.442                          result.close()
   2.443 @@ -926,13 +926,13 @@
   2.444  
   2.445          reqUri = None
   2.446          if 'REQUEST_URI' in environ:
   2.447 -            reqUri = environ['REQUEST_URI'].split('?', 1)
   2.448 +            reqUri = environ['REQUEST_URI'].split(b'?', 1)
   2.449  
   2.450          if 'QUERY_STRING' not in environ or not environ['QUERY_STRING']:
   2.451              if reqUri is not None and len(reqUri) > 1:
   2.452                  environ['QUERY_STRING'] = reqUri[1]
   2.453              else:
   2.454 -                environ['QUERY_STRING'] = ''
   2.455 +                environ['QUERY_STRING'] = b''
   2.456  
   2.457      def error(self, request):
   2.458          """
   2.459 @@ -940,11 +940,11 @@
   2.460          all errors should be caught at the application level.
   2.461          """
   2.462          if self.debug:
   2.463 -            request.startResponse(200, 'OK', [('Content-Type', 'text/html')])
   2.464 +            request.startResponse(200, b'OK', [(b'Content-Type', b'text/html')])
   2.465              import cgitb
   2.466 -            request.write(cgitb.html(sys.exc_info()))
   2.467 +            request.write(cgitb.html(sys.exc_info()).encode('latin-1'))
   2.468          else:
   2.469 -            errorpage = """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
   2.470 +            errorpage = b"""<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
   2.471  <html><head>
   2.472  <title>Unhandled Exception</title>
   2.473  </head><body>
   2.474 @@ -952,5 +952,5 @@
   2.475  <p>An unhandled exception was thrown by the application.</p>
   2.476  </body></html>
   2.477  """
   2.478 -            request.startResponse(200, 'OK', [('Content-Type', 'text/html')])
   2.479 +            request.startResponse(200, b'OK', [(b'Content-Type', b'text/html')])
   2.480              request.write(errorpage)