/usr/share/pyshared/zope/server/http/httptask.py is in python-zope.server 3.8.6-0ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | ##############################################################################
#
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.
#
##############################################################################
"""HTTP Task
An HTTP task that can execute an HTTP request with the help of the channel and
the server it belongs to.
"""
import socket
import time
from zope.server.http.http_date import build_http_date
from zope.publisher.interfaces.http import IHeaderOutput
from zope.server.interfaces import ITask
from zope.interface import implements
rename_headers = {
'CONTENT_LENGTH' : 'CONTENT_LENGTH',
'CONTENT_TYPE' : 'CONTENT_TYPE',
'CONNECTION' : 'CONNECTION_TYPE',
}
class HTTPTask(object):
"""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 = ''
cgi_env = None
def __init__(self, channel, request_data):
self.channel = channel
self.request_data = request_data
self.response_headers = {}
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
def service(self):
"""See zope.server.interfaces.ITask"""
try:
try:
self.start()
self.channel.server.executeRequest(self)
self.finish()
except socket.error:
self.close_on_finish = 1
if self.channel.adj.log_socket_errors:
raise
finally:
if self.close_on_finish:
self.channel.close_when_done()
def cancel(self):
"""See zope.server.interfaces.ITask"""
self.channel.close_when_done()
def defer(self):
"""See zope.server.interfaces.ITask"""
pass
def setResponseStatus(self, status, reason):
"""See zope.publisher.interfaces.http.IHeaderOutput"""
self.status = status
self.reason = reason
def setResponseHeaders(self, mapping):
"""See zope.publisher.interfaces.http.IHeaderOutput"""
self.response_headers.update(mapping)
def appendResponseHeaders(self, lst):
"""See zope.publisher.interfaces.http.IHeaderOutput"""
accum = self.accumulated_headers
if accum is None:
self.accumulated_headers = accum = []
accum.extend(lst)
def wroteResponseHeader(self):
"""See zope.publisher.interfaces.http.IHeaderOutput"""
return self.wrote_header
def setAuthUserName(self, name):
"""See zope.publisher.interfaces.http.IHeaderOutput"""
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
accumulated_headers = self.accumulated_headers
if accumulated_headers is None:
accumulated_headers = []
if version == '1.0':
if connection == 'keep-alive':
if not ('Content-Length' in response_headers):
close_it = 1
else:
response_headers['Connection'] = 'Keep-Alive'
else:
close_it = 1
elif version == '1.1':
if 'connection: close' in (header.lower() for header in
accumulated_headers):
close_it = 1
if connection == 'close':
close_it = 1
elif 'Transfer-Encoding' in response_headers:
if not response_headers['Transfer-Encoding'] == 'chunked':
close_it = 1
elif self.status == '304':
# Replying with headers only.
pass
elif not ('Content-Length' in response_headers):
# accumulated_headers is a simple list, we need to cut off
# the value of content-length manually
if 'content-length' not in (header[:14].lower() for header in
accumulated_headers):
close_it = 1
# under HTTP 1.1 keep-alive is default, no need to set the header
else:
# Close if unrecognized HTTP version.
close_it = 1
self.close_on_finish = close_it
if close_it:
self.response_headers['Connection'] = 'close'
# Set the Server and Date field, if not yet specified. This is needed
# if the server is used as a proxy.
if 'server' not in (header[:6].lower() for header in
accumulated_headers):
self.response_headers['Server'] = self.channel.server.SERVER_IDENT
else:
self.response_headers['Via'] = self.channel.server.SERVER_IDENT
if 'date' not in (header[:4].lower() for header in
accumulated_headers):
self.response_headers['Date'] = build_http_date(self.start_time)
def buildResponseHeader(self):
self.prepareResponseHeaders()
first_line = 'HTTP/%s %s %s' % (self.version, self.status, self.reason)
lines = [first_line] + ['%s: %s' % hv
for hv in 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 getCGIEnvironment(self):
"""Returns a CGI-like environment."""
env = self.cgi_env
if env is not None:
# Return the cached copy.
return env
request_data = self.request_data
path = request_data.path
channel = self.channel
server = channel.server
while path and path.startswith('/'):
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 addr in dns_cache:
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
self.cgi_env = env
return env
def start(self):
now = time.time()
self.start_time = now
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:
self.bytes_written += channel.write(data)
def flush(self):
self.channel.flush()
|