[Zope-Checkins] CVS: Zope3/lib/python/Zope/Server/HTTP - Chunking.py:1.1.2.1 CommonHitLogger.py:1.1.2.1 HTTPRequestParser.py:1.1.2.1 HTTPServer.py:1.1.2.1 HTTPServerChannel.py:1.1.2.1 HTTPTask.py:1.1.2.1 PublisherHTTPChannel.py:1.1.2.1 PublisherHTTPServer.py:1.1.2.1 PublisherHTTPTask.py:1.1.2.1 __init__.py:1.1.2.1 http_date.py:1.1.2.1
Stephan Richter
srichter@cbu.edu
Tue, 2 Apr 2002 00:08:08 -0500
Update of /cvs-repository/Zope3/lib/python/Zope/Server/HTTP
In directory cvs.zope.org:/tmp/cvs-serv6290/lib/python/Zope/Server/HTTP
Added Files:
Tag: Zope3-Server-Branch
Chunking.py CommonHitLogger.py HTTPRequestParser.py
HTTPServer.py HTTPServerChannel.py HTTPTask.py
PublisherHTTPChannel.py PublisherHTTPServer.py
PublisherHTTPTask.py __init__.py http_date.py
Log Message:
Issue 53: Comment
- Created a bunch of interfaces that let us know what is going on.
- Split, updated and zopefied the Logger code.
- Reorganized dir structure in Zope.Server
- HTTP component split up in files (HTTP server works)
- Inserted Shane's skeleton FTP code (since I like his better than mine)
- Took a first cut at the Virtual File System (VFS) by copying and updating
medusa'a old filesys.py code.
=== Added File Zope3/lib/python/Zope/Server/HTTP/Chunking.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: Chunking.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""
from Zope.Server.Utilities import find_double_newline
from Zope.Server.IStreamConsumer import IStreamConsumer
class ChunkedReceiver:
__implements__ = IStreamConsumer
chunk_remainder = 0
control_line = ''
all_chunks_received = 0
trailer = ''
completed = 0
# max_control_line = 1024
# max_trailer = 65536
def __init__(self, buf):
self.buf = buf
def received(self, s):
# Returns the number of bytes consumed.
if self.completed:
return 0
orig_size = len(s)
while s:
rm = self.chunk_remainder
if rm > 0:
# Receive the remainder of a chunk.
to_write = s[:rm]
self.buf.append(to_write)
written = len(to_write)
s = s[written:]
self.chunk_remainder -= written
elif not self.all_chunks_received:
# Receive a control line.
s = self.control_line + s
pos = s.find('\n')
if pos < 0:
# Control line not finished.
self.control_line = s
s = ''
else:
# Control line finished.
line = s[:pos]
s = s[pos + 1:]
self.control_line = ''
line = line.strip()
if line:
# Begin a new chunk.
semi = line.find(';')
if semi >= 0:
# discard extension info.
line = line[:semi]
sz = int(line.strip(), 16) # hexadecimal
if sz > 0:
# Start a new chunk.
self.chunk_remainder = sz
else:
# Finished chunks.
self.all_chunks_received = 1
# else expect a control line.
else:
# Receive the trailer.
trailer = self.trailer + s
if trailer.startswith('\r\n'):
# No trailer.
self.completed = 1
return orig_size - (len(trailer) - 2)
elif trailer.startswith('\n'):
# No trailer.
self.completed = 1
return orig_size - (len(trailer) - 1)
pos = find_double_newline(trailer)
if pos < 0:
# Trailer not finished.
self.trailer = trailer
s = ''
else:
# Finished the trailer.
self.completed = 1
self.trailer = trailer[:pos]
return orig_size - (len(trailer) - pos)
return orig_size
def getfile(self):
return self.buf.getfile()
=== Added File Zope3/lib/python/Zope/Server/HTTP/CommonHitLogger.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: CommonHitLogger.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""
import time
import sys
from http_date import monthname
from Zope.Server.Logger.FileLogger import FileLogger
from Zope.Server.Logger.ResolvingLogger import ResolvingLogger
from Zope.Server.Logger.UnresolvingLogger import UnresolvingLogger
class CommonHitLogger:
"""Outputs hits in common HTTP log format.
"""
def __init__(self, logger_object=None, resolver=None):
if logger_object is None:
logger_object = FileLogger(sys.stdout)
if resolver is not None:
self.output = ResolvingLogger(resolver, logger_object)
else:
self.output = UnresolvingLogger(logger_object)
def compute_timezone_for_log(self, tz):
if tz > 0:
neg = 1
else:
neg = 0
tz = -tz
h, rem = divmod (tz, 3600)
m, rem = divmod (rem, 60)
if neg:
return '-%02d%02d' % (h, m)
else:
return '+%02d%02d' % (h, m)
tz_for_log = None
tz_for_log_alt = None
def log_date_string(self, when):
logtime = time.localtime(when)
Y, M, D, h, m, s = logtime[:6]
if not time.daylight:
tz = self.tz_for_log
if tz is None:
tz = self.compute_timezone_for_log(time.timezone)
self.tz_for_log = tz
else:
tz = self.tz_for_log_alt
if tz is None:
tz = self.compute_timezone_for_log(time.altzone)
self.tz_for_log_alt = tz
return '%d/%s/%02d:%02d:%02d:%02d %s' % (
Y, monthname[M], D, h, m, s, tz)
def log(self, task):
"""
Receives a completed task and logs it in the
common log format.
"""
now = time.time()
request_data = task.request_data
req_headers = request_data.headers
user_name = task.auth_user_name or 'anonymous'
user_agent = req_headers.get('USER_AGENT', '')
referer = req_headers.get('REFERER', '')
self.output.log(
task.channel.addr[0],
' - %s [%s] "%s" %s %d "%s" "%s"\n' % (
user_name,
self.log_date_string(now),
request_data.first_line,
task.status,
task.bytes_written,
referer,
user_agent
)
)
=== Added File Zope3/lib/python/Zope/Server/HTTP/HTTPRequestParser.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
This server uses asyncore to accept connections and do initial
processing but threads to do work.
$Id: HTTPRequestParser.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""
import re
from urllib import unquote
from Zope.Server.FixedStreamReceiver import FixedStreamReceiver
from Zope.Server.Buffers import OverflowableBuffer
from Zope.Server.Utilities import find_double_newline
from Zope.Server.IStreamConsumer import IStreamConsumer
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
class HTTPRequestParser:
"""A structure that collects the HTTP request.
Once the stream is completed, the instance is passed to
a server task constructor.
"""
__implements__ = IStreamConsumer
completed = 0 # Set once request is completed.
empty = 0 # Set if no request was made.
header_plus = ''
chunked = 0
content_length = 0
body_rcv = None
# Other attributes: first_line, header, headers, command, uri, version,
# path, query, fragment
# headers is a mapping containing keys translated to uppercase
# with dashes turned into underscores.
def __init__(self, adj):
"""
adj is an Adjustments object.
"""
self.headers = {}
self.adj = adj
def received(self, data):
"""
Receives the HTTP stream for one request.
Returns the number of bytes consumed.
Sets the completed flag once both the header and the
body have been received.
"""
if self.completed:
return 0 # Can't consume any more.
datalen = len(data)
br = self.body_rcv
if br is None:
# In header.
s = self.header_plus + data
index = find_double_newline(s)
if index >= 0:
# Header finished.
header_plus = s[:index]
consumed = len(data) - (len(s) - index)
self.in_header = 0
# Remove preceeding blank lines.
header_plus = header_plus.lstrip()
if not header_plus:
self.empty = 1
self.completed = 1
else:
self.parse_header(header_plus)
if self.body_rcv is None:
self.completed = 1
return consumed
else:
# Header not finished yet.
self.header_plus = s
return datalen
else:
# In body.
consumed = br.received(data)
if br.completed:
self.completed = 1
return consumed
def parse_header(self, header_plus):
"""
Parses the header_plus block of text (the headers plus the
first line of the request).
"""
index = header_plus.find('\n')
if index >= 0:
first_line = header_plus[:index].rstrip()
header = header_plus[index + 1:]
else:
first_line = header_plus.rstrip()
header = ''
self.first_line = first_line
self.header = header
lines = self.get_header_lines()
headers = self.headers
for line in lines:
index = line.find(':')
if index > 0:
key = line[:index]
value = line[index + 1:].strip()
key1 = key.upper().replace('-', '_')
headers[key1] = value
# else there's garbage in the headers?
command, uri, version = self.crack_first_line()
self.command = str(command)
if uri and '%' in uri:
uri = unquote(uri)
self.uri = str(uri)
self.version = version
self.split_uri()
if version == '1.1':
te = headers.get('TRANSFER_ENCODING', '')
if te == 'chunked':
from Chunking import ChunkedReceiver
self.chunked = 1
buf = OverflowableBuffer(self.adj.inbuf_overflow)
self.body_rcv = ChunkedReceiver(buf)
if not self.chunked:
cl = int(headers.get('CONTENT_LENGTH', 0))
self.content_length = cl
if cl > 0:
buf = OverflowableBuffer(self.adj.inbuf_overflow)
self.body_rcv = FixedStreamReceiver(cl, buf)
def get_header_lines(self):
"""
Splits the header into lines, putting multi-line headers together.
"""
r = []
lines = self.header.split('\n')
for line in lines:
if line and line[0] in ' \t':
r[-1] = r[-1] + line[1:]
else:
r.append(line)
return r
first_line_re = re.compile (
'([^ ]+) (?:[^ :?#]+://[^ ?#/]*)?([^ ]+)(( HTTP/([0-9.]+))$|$)')
def crack_first_line(self):
r = self.first_line
m = self.first_line_re.match (r)
if m is not None and m.end() == len(r):
if m.group(3):
version = m.group(5)
else:
version = None
return m.group(1).upper(), m.group(2), version
else:
return None, None, None
path_regex = re.compile (
# path query fragment
r'([^?#]*)(\?[^#]*)?(#.*)?'
)
def split_uri(self):
m = self.path_regex.match (self.uri)
if m.end() != len(self.uri):
raise ValueError, "Broken URI"
else:
self.path, query, self.fragment = m.groups()
if query:
query = query[1:]
self.query = query
def getBodyStream(self):
body_rcv = self.body_rcv
if body_rcv is not None:
return body_rcv.getfile()
else:
return StringIO('')
=== Added File Zope3/lib/python/Zope/Server/HTTP/HTTPServer.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
This server uses asyncore to accept connections and do initial
processing but threads to do work.
$Id: HTTPServer.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""
from Zope.Server.ServerBase import ServerBase
from HTTPServerChannel import HTTPServerChannel
class HTTPServer(ServerBase):
"""This is a generic HTTP Server."""
__implements__ = ServerBase.__implements__
channel_class = HTTPServerChannel
SERVER_IDENT = 'Zope.Server.HTTPServer'
if __name__ == '__main__':
from Zope.Server.TaskThreads import ThreadedTaskDispatcher
td = ThreadedTaskDispatcher()
td.setThreadCount(4)
HTTPServer('', 8080, task_dispatcher=td)
try:
while 1:
asyncore.poll(5)
except KeyboardInterrupt:
print 'shutting down...'
td.shutdown()
=== Added File Zope3/lib/python/Zope/Server/HTTP/HTTPServerChannel.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: HTTPServerChannel.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""
from Zope.Server.ServerChannelBase import ServerChannelBase
from HTTPTask import HTTPTask
from HTTPRequestParser import HTTPRequestParser
class HTTPServerChannel(ServerChannelBase):
"""HTTP-specific Server Channel"""
__implements__ = ServerChannelBase.__implements__
task_class = HTTPTask
parser_class = HTTPRequestParser
active_channels = {} # Class-specific channel tracker
next_channel_cleanup = [0] # Class-specific cleanup time
=== Added File Zope3/lib/python/Zope/Server/HTTP/HTTPTask.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
This server uses asyncore to accept connections and do initial
processing but threads to do work.
$Id: HTTPTask.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""
import socket
import time
from http_date import build_http_date
from Zope.Server.IHeaderOutput import IHeaderOutput
from Zope.Server.ITask import ITask
class HTTPTask:
"""An HTTP task accepts a request and writes to a channel.
Subclass this and override the execute() method.
"""
__implements__ = ITask, IHeaderOutput #, IOutputStream
instream = None
close_on_finish = 1
status = '200'
reason = 'Ok'
wrote_header = 0
accumulated_headers = None
bytes_written = 0
auth_user_name = ''
def __init__(self, channel, request_data):
self.channel = channel
self.request_data = request_data
self.response_headers = {
'Server': channel.server.SERVER_IDENT,
}
version = request_data.version
if version not in ('1.0', '1.1'):
# fall back to a version we support.
version = '1.0'
self.version = version
############################################################
# Implementation methods for interface
# Zope.Server.ITask
def service(self):
'See Zope.Server.ITask.ITask'
try:
try:
self.start()
self.execute()
self.finish()
except socket.error:
self.close_on_finish = 1
if self.channel.adj.log_socket_errors:
raise
finally:
self.channel.end_task(self.close_on_finish)
def cancel(self):
'See Zope.Server.ITask.ITask'
self.channel.close_when_done()
def defer(self):
'See Zope.Server.ITask.ITask'
pass
#
############################################################
def setResponseStatus(self, status, reason):
"""See the IHeaderOutput interface."""
self.status = status
self.reason = reason
def setResponseHeaders(self, mapping):
"""See the IHeaderOutput interface."""
self.response_headers.update(mapping)
def appendResponseHeaders(self, lst):
"""See the IHeaderOutput interface."""
accum = self.accumulated_headers
if accum is None:
self.accumulated_headers = accum = []
accum.extend(lst)
def wroteResponseHeader(self):
"""See the IHeaderOutput interface."""
return self.wrote_header
def setAuthUserName(self, name):
"""See the IHeaderOutput interface."""
self.auth_user_name = name
def prepareResponseHeaders(self):
version = self.version
# Figure out whether the connection should be closed.
connection = self.request_data.headers.get('CONNECTION', '').lower()
close_it = 0
response_headers = self.response_headers
if version == '1.0':
if connection == 'keep-alive':
if not response_headers.has_key('Content-Length'):
close_it = 1
else:
response_headers['Connection'] = 'Keep-Alive'
else:
close_it = 1
elif version == '1.1':
if connection == 'close':
close_it = 1
elif response_headers.has_key ('Transfer-Encoding'):
if not response_headers['Transfer-Encoding'] == 'chunked':
close_it = 1
elif self.status == '304':
# Replying with headers only.
pass
elif not response_headers.has_key ('Content-Length'):
close_it = 1
else:
# Close if unrecognized HTTP version.
close_it = 1
self.close_on_finish = close_it
if close_it:
self.response_headers['Connection'] = 'close'
def buildResponseHeader(self):
self.prepareResponseHeaders()
first_line = 'HTTP/%s %s %s' % (self.version, self.status, self.reason)
lines = [first_line] + map(
lambda hv: '%s: %s' % hv, self.response_headers.items())
accum = self.accumulated_headers
if accum is not None:
lines.extend(accum)
res = '%s\r\n\r\n' % '\r\n'.join(lines)
return res
def start(self):
now = time.time()
self.start_time = now
self.response_headers['Date'] = build_http_date (now)
def execute(self):
"""
Override this.
"""
body = "The HTTP server is running!\r\n" * 10
self.response_headers['Content-Type'] = 'text/plain'
self.response_headers['Content-Length'] = str(len(body))
self.write(body)
def finish(self):
if not self.wrote_header:
self.write('')
hit_log = self.channel.server.hit_log
if hit_log is not None:
hit_log.log(self)
def write(self, data):
channel = self.channel
if not self.wrote_header:
rh = self.buildResponseHeader()
channel.write(rh)
self.bytes_written += len(rh)
self.wrote_header = 1
if data:
channel.write(data)
self.bytes_written += len(data)
def flush(self):
self.channel.flush()
=== Added File Zope3/lib/python/Zope/Server/HTTP/PublisherHTTPChannel.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: PublisherHTTPChannel.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""
from HTTPServer import HTTPServerChannel
from PublisherHTTPTask import PublisherHTTPTask
class PublisherHTTPChannel(HTTPServerChannel):
"""Zope Publisher-specif HTTP Channel"""
__implments__ = HTTPServerChannel.__implements__
task_class = PublisherHTTPTask
=== Added File Zope3/lib/python/Zope/Server/HTTP/PublisherHTTPServer.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: PublisherHTTPServer.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""
from HTTPServer import HTTPServer
from PublisherHTTPChannel import PublisherHTTPChannel
class PublisherHTTPServer(HTTPServer):
"""Zope Publisher-specific HTTP Server"""
__implements__ = HTTPServer.__implements__
channel_class = PublisherHTTPChannel
def __init__(self, request_factory, sub_protocol=None, *args, **kw):
self.request_factory = request_factory
# An HTTP server is not limited to server up HTML; it can be
# used for other protocols, like XML-RPC, SOAP and so as well
# Here we just allow the logger to output the sub-protocol type.
if sub_protocol:
self.SERVER_IDENT += ' (%s)' %sub_protocol
HTTPServer.__init__(self, *args, **kw)
=== Added File Zope3/lib/python/Zope/Server/HTTP/PublisherHTTPTask.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: PublisherHTTPTask.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""
from HTTPTask import HTTPTask
from Zope.Publisher.Publish import publish
rename_headers = {
'CONTENT_LENGTH' : 'CONTENT_LENGTH',
'CONTENT_TYPE' : 'CONTENT_TYPE',
'CONNECTION' : 'CONNECTION_TYPE',
}
class PublisherHTTPTask (HTTPTask):
def execute(self):
server = self.channel.server
env = self.create_environment()
instream = self.request_data.getBodyStream()
request = server.request_factory(instream, self, env)
response = request.getResponse()
response.setHeaderOutput(self)
publish(request)
def create_environment(self):
request_data = self.request_data
path = request_data.path
channel = self.channel
server = channel.server
while path and path[0] == '/':
path = path[1:]
env = {}
env['REQUEST_METHOD'] = request_data.command.upper()
env['SERVER_PORT'] = str(server.port)
env['SERVER_NAME'] = server.server_name
env['SERVER_SOFTWARE'] = server.SERVER_IDENT
env['SERVER_PROTOCOL'] = "HTTP/%s" % self.version
env['channel.creation_time'] = channel.creation_time
env['SCRIPT_NAME']=''
env['PATH_INFO']='/' + path
query = request_data.query
if query:
env['QUERY_STRING'] = query
env['GATEWAY_INTERFACE'] = 'CGI/1.1'
addr = channel.addr[0]
env['REMOTE_ADDR'] = addr
# If the server has a resolver, try to get the
# remote host from the resolver's cache.
resolver = getattr(server, 'resolver', None)
if resolver is not None:
dns_cache = resolver.cache
if dns_cache.has_key(addr):
remote_host = dns_cache[addr][2]
if remote_host is not None:
env['REMOTE_HOST'] = remote_host
env_has = env.has_key
for key, value in request_data.headers.items():
value = value.strip()
mykey = rename_headers.get(key, None)
if mykey is None:
mykey = 'HTTP_%s' % key
if not env_has(mykey):
env[mykey] = value
return env
=== Added File Zope3/lib/python/Zope/Server/HTTP/__init__.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: __init__.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""
=== Added File Zope3/lib/python/Zope/Server/HTTP/http_date.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: http_date.py,v 1.1.2.1 2002/04/02 05:08:06 srichter Exp $
"""
import re
import string
import time
def concat (*args):
return ''.join (args)
def join (seq, field=' '):
return field.join (seq)
def group (s):
return '(' + s + ')'
short_days = ['sun','mon','tue','wed','thu','fri','sat']
long_days = ['sunday','monday','tuesday','wednesday',
'thursday','friday','saturday']
short_day_reg = group(join (short_days, '|'))
long_day_reg = group(join (long_days, '|'))
daymap = {}
for i in range(7):
daymap[short_days[i]] = i
daymap[long_days[i]] = i
hms_reg = join (3 * [group('[0-9][0-9]')], ':')
months = ['jan','feb','mar','apr','may','jun','jul',
'aug','sep','oct','nov','dec']
monmap = {}
for i in range(12):
monmap[months[i]] = i+1
months_reg = group (join (months, '|'))
# From draft-ietf-http-v11-spec-07.txt/3.3.1
# Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
# Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
# Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
# rfc822 format
rfc822_date = join (
[concat (short_day_reg,','), # day
group('[0-9][0-9]?'), # date
months_reg, # month
group('[0-9]+'), # year
hms_reg, # hour minute second
'gmt'
],
' '
)
rfc822_reg = re.compile (rfc822_date)
def unpack_rfc822 (m):
g = m.group
a = string.atoi
return (
a(g(4)), # year
monmap[g(3)], # month
a(g(2)), # day
a(g(5)), # hour
a(g(6)), # minute
a(g(7)), # second
0,
0,
0
)
# rfc850 format
rfc850_date = join (
[concat (long_day_reg,','),
join (
[group ('[0-9][0-9]?'),
months_reg,
group ('[0-9]+')
],
'-'
),
hms_reg,
'gmt'
],
' '
)
rfc850_reg = re.compile (rfc850_date)
# they actually unpack the same way
def unpack_rfc850 (m):
g = m.group
a = string.atoi
return (
a(g(4)), # year
monmap[g(3)], # month
a(g(2)), # day
a(g(5)), # hour
a(g(6)), # minute
a(g(7)), # second
0,
0,
0
)
# parsdate.parsedate - ~700/sec.
# parse_http_date - ~1333/sec.
weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
monthname = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
def build_http_date (when):
year, month, day, hh, mm, ss, wd, y, z = time.gmtime(when)
return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
weekdayname[wd],
day, monthname[month], year,
hh, mm, ss)
def parse_http_date (d):
d = string.lower (d)
tz = time.timezone
m = rfc850_reg.match (d)
if m and m.end() == len(d):
retval = int (time.mktime (unpack_rfc850(m)) - tz)
else:
m = rfc822_reg.match (d)
if m and m.end() == len(d):
retval = int (time.mktime (unpack_rfc822(m)) - tz)
else:
return 0
# Thanks to Craig Silverstein <csilvers@google.com> for pointing
# out the DST discrepancy
if time.daylight and time.localtime(retval)[-1] == 1: # DST correction
retval = retval + (tz - time.altzone)
return retval