1.1 --- a/WebStack/BaseHTTPRequestHandler.py Sat Apr 24 17:01:26 2004 +0000
1.2 +++ b/WebStack/BaseHTTPRequestHandler.py Sat Apr 24 20:33:31 2004 +0000
1.3 @@ -70,7 +70,8 @@
1.4 def get_headers(self):
1.5
1.6 """
1.7 - A framework-specific method which returns all request headers.
1.8 + A framework-specific method which returns all request headers as a
1.9 + dictionary-like object mapping header names to values.
1.10 NOTE: If duplicate header names are permitted, then this interface will
1.11 NOTE: need to change.
1.12 """
1.13 @@ -103,7 +104,7 @@
1.14 Returns the character set preferences.
1.15 """
1.16
1.17 - return self.parse_content_preferences(self.trans.headers["Accept-Charset"])
1.18 + return self.parse_content_preferences(self.trans.headers.get("Accept-Charset"))
1.19
1.20 def get_content_languages(self):
1.21
1.22 @@ -112,7 +113,7 @@
1.23 the transaction.
1.24 """
1.25
1.26 - return self.parse_content_preferences(self.trans.headers["Accept-Language"])
1.27 + return self.parse_content_preferences(self.trans.headers.get("Accept-Language"))
1.28
1.29 def get_path(self):
1.30
1.31 @@ -195,6 +196,8 @@
1.32 """
1.33 A framework-specific method which extracts user information from the
1.34 transaction.
1.35 +
1.36 + Returns a username as a string or None if no user is defined.
1.37 """
1.38
1.39 auth_header = self.get_headers().get("Authorization")
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/WebStack/CGI.py Sat Apr 24 20:33:31 2004 +0000
2.3 @@ -0,0 +1,343 @@
2.4 +#!/usr/bin/env python
2.5 +
2.6 +"""
2.7 +CGI classes.
2.8 +"""
2.9 +
2.10 +import Generic
2.11 +import os, sys
2.12 +from Helpers.Request import MessageBodyStream
2.13 +from Helpers.Auth import UserInfo
2.14 +from Helpers import Environment
2.15 +from cgi import parse_qs, FieldStorage
2.16 +import Cookie
2.17 +from StringIO import StringIO
2.18 +
2.19 +class Transaction(Generic.Transaction):
2.20 +
2.21 + """
2.22 + CGI transaction interface.
2.23 + """
2.24 +
2.25 + def __init__(self, input=None, output=None, env=None):
2.26 +
2.27 + """
2.28 + Initialise the transaction using the CGI 'input' and 'output' streams.
2.29 + These streams are optional and default to standard input and standard
2.30 + output respectively.
2.31 + """
2.32 +
2.33 + self.input = input or sys.stdin
2.34 + self.output = output or sys.stdout
2.35 + self.env = env or os.environ
2.36 +
2.37 + # Other attributes of interest in instances of this class.
2.38 +
2.39 + self.content_type = None
2.40 + self.response_code = 200
2.41 + self.content = StringIO()
2.42 + self.headers_out = {}
2.43 + self.cookies_out = Cookie.SimpleCookie()
2.44 +
2.45 + # Define the incoming cookies.
2.46 +
2.47 + self.cookies_in = Cookie.SimpleCookie(self.env.get("HTTP_COOKIE"))
2.48 +
2.49 + def commit(self):
2.50 +
2.51 + """
2.52 + A special method, synchronising the transaction with framework-specific
2.53 + objects.
2.54 +
2.55 + See draft-coar-cgi-v11-03, section 7.
2.56 + """
2.57 +
2.58 + # NOTE: Provide sensible messages.
2.59 +
2.60 + self.output.write("Status: %s %s\n" % (self.response_code, "WebStack status"))
2.61 + if self.content_type is not None:
2.62 + self.output.write("Content-type: %s\n" % self.format_content_type(self.content_type))
2.63 + for header, value in self.headers_out.items():
2.64 + self.output.write("%s: %s\n" %
2.65 + (self.format_header_value(header), self.format_header_value(value))
2.66 + )
2.67 + self.output.write(str(self.cookies_out))
2.68 + self.output.write("\n")
2.69 +
2.70 + self.content.seek(0)
2.71 + self.output.write(self.content.read())
2.72 +
2.73 + # Request-related methods.
2.74 +
2.75 + def get_request_stream(self):
2.76 +
2.77 + """
2.78 + A framework-specific method which returns the request stream for
2.79 + the transaction.
2.80 + """
2.81 +
2.82 + return self.input
2.83 +
2.84 + def get_request_method(self):
2.85 +
2.86 + """
2.87 + A framework-specific method which gets the request method.
2.88 + """
2.89 +
2.90 + return self.env.get("REQUEST_METHOD")
2.91 +
2.92 + def get_headers(self):
2.93 +
2.94 + """
2.95 + A framework-specific method which returns all request headers as a
2.96 + dictionary-like object mapping header names to values.
2.97 + """
2.98 +
2.99 + return Environment.get_headers(self.env)
2.100 +
2.101 + def get_header_values(self, key):
2.102 +
2.103 + """
2.104 + A framework-specific method which returns a list of all request header
2.105 + values associated with the given 'key'. Note that according to RFC 2616,
2.106 + 'key' is treated as a case-insensitive string.
2.107 + """
2.108 +
2.109 + return self.convert_to_list(self.get_headers().get(key))
2.110 +
2.111 + def get_content_type(self):
2.112 +
2.113 + """
2.114 + A framework-specific method which gets the content type specified on the
2.115 + request, along with the charset employed.
2.116 + """
2.117 +
2.118 + return self.parse_content_type(self.env.get("CONTENT_TYPE"))
2.119 +
2.120 + def get_content_charsets(self):
2.121 +
2.122 + """
2.123 + Returns the character set preferences.
2.124 + """
2.125 +
2.126 + return self.parse_content_preferences(None)
2.127 +
2.128 + def get_content_languages(self):
2.129 +
2.130 + """
2.131 + A framework-specific method which extracts language information from
2.132 + the transaction.
2.133 + """
2.134 +
2.135 + return self.parse_content_preferences(None)
2.136 +
2.137 + def get_path(self):
2.138 +
2.139 + """
2.140 + A framework-specific method which gets the entire path from the request.
2.141 + """
2.142 +
2.143 + path = self.env.get("SCRIPT_NAME") or ""
2.144 + if self.env.has_key("PATH_INFO"):
2.145 + path += self.env["PATH_INFO"]
2.146 + qs = self.env.get("QUERY_STRING")
2.147 + if qs:
2.148 + path += "?"
2.149 + path += qs
2.150 + return path
2.151 +
2.152 + def get_path_info(self):
2.153 +
2.154 + """
2.155 + A framework-specific method which gets the "path info" (the part of the
2.156 + URL after the resource name handling the current request) from the
2.157 + request.
2.158 + """
2.159 +
2.160 + return self.env.get("PATH_INFO") or ""
2.161 +
2.162 + def get_query_string(self):
2.163 +
2.164 + """
2.165 + A framework-specific method which gets the query string from the path in
2.166 + the request.
2.167 + """
2.168 +
2.169 + return self.env.get("QUERY_STRING") or ""
2.170 +
2.171 + # Higher level request-related methods.
2.172 +
2.173 + def get_fields_from_path(self):
2.174 +
2.175 + """
2.176 + A framework-specific method which extracts the form fields from the
2.177 + path specified in the transaction. The underlying framework may refuse
2.178 + to supply fields from the path if handling a POST transaction.
2.179 +
2.180 + Returns a dictionary mapping field names to lists of values (even if a
2.181 + single value is associated with any given field name).
2.182 + """
2.183 +
2.184 + return parse_qs(self.get_query_string(), keep_blank_values=1)
2.185 +
2.186 + def get_fields_from_body(self):
2.187 +
2.188 + """
2.189 + A framework-specific method which extracts the form fields from the
2.190 + message body in the transaction.
2.191 +
2.192 + Returns a dictionary mapping field names to lists of values (even if a
2.193 + single value is associated with any given field name).
2.194 + """
2.195 +
2.196 + storage = FieldStorage(fp=self.get_request_stream(), keep_blank_values=1)
2.197 +
2.198 + # Avoid strange design issues with FieldStorage by checking the internal
2.199 + # field list directly.
2.200 +
2.201 + fields = {}
2.202 + if storage.list is not None:
2.203 +
2.204 + # Traverse the storage, finding each field value.
2.205 +
2.206 + for field_name in storage.keys():
2.207 + fields[field_name] = storage.getlist(field_name)
2.208 + return fields
2.209 +
2.210 + def get_user(self):
2.211 +
2.212 + """
2.213 + A framework-specific method which extracts user information from the
2.214 + transaction.
2.215 +
2.216 + Returns a username as a string or None if no user is defined.
2.217 + """
2.218 +
2.219 + return self.env.get("REMOTE_USER")
2.220 +
2.221 + def get_cookies(self):
2.222 +
2.223 + """
2.224 + A framework-specific method which obtains cookie information from the
2.225 + request.
2.226 +
2.227 + Returns a dictionary mapping cookie names to cookie objects.
2.228 + """
2.229 +
2.230 + return self.cookies_in
2.231 +
2.232 + def get_cookie(self, cookie_name):
2.233 +
2.234 + """
2.235 + A framework-specific method which obtains cookie information from the
2.236 + request.
2.237 +
2.238 + Returns a cookie object for the given 'cookie_name' or None if no such
2.239 + cookie exists.
2.240 + """
2.241 +
2.242 + return self.cookies_in.get(cookie_name)
2.243 +
2.244 + # Response-related methods.
2.245 +
2.246 + def get_response_stream(self):
2.247 +
2.248 + """
2.249 + A framework-specific method which returns the response stream for
2.250 + the transaction.
2.251 + """
2.252 +
2.253 + # Return a stream which is later emptied into the real stream.
2.254 +
2.255 + return self.content
2.256 +
2.257 + def get_response_code(self):
2.258 +
2.259 + """
2.260 + Get the response code associated with the transaction. If no response
2.261 + code is defined, None is returned.
2.262 + """
2.263 +
2.264 + return self.response_code
2.265 +
2.266 + def set_response_code(self, response_code):
2.267 +
2.268 + """
2.269 + Set the 'response_code' using a numeric constant defined in the HTTP
2.270 + specification.
2.271 + """
2.272 +
2.273 + self.response_code = response_code
2.274 +
2.275 + def set_header_value(self, header, value):
2.276 +
2.277 + """
2.278 + Set the HTTP 'header' with the given 'value'.
2.279 + """
2.280 +
2.281 + # The header is not written out immediately due to the buffering in use.
2.282 +
2.283 + self.headers_out[header] = value
2.284 +
2.285 + def set_content_type(self, content_type):
2.286 +
2.287 + """
2.288 + A framework-specific method which sets the 'content_type' for the
2.289 + response.
2.290 + """
2.291 +
2.292 + # The content type has to be written as a header, before actual content,
2.293 + # but after the response line. This means that some kind of buffering is
2.294 + # required. Hence, we don't write the header out immediately.
2.295 +
2.296 + self.content_type = content_type
2.297 +
2.298 + # Higher level response-related methods.
2.299 +
2.300 + def set_cookie(self, cookie):
2.301 +
2.302 + """
2.303 + A framework-specific method which stores the given 'cookie' object in
2.304 + the response.
2.305 + """
2.306 +
2.307 + # NOTE: If multiple cookies of the same name could be specified, this
2.308 + # NOTE: could need changing.
2.309 +
2.310 + self.cookies_out[cookie.name] = cookie.value
2.311 +
2.312 + def set_cookie_value(self, name, value, path=None, expires=None):
2.313 +
2.314 + """
2.315 + A framework-specific method which stores a cookie with the given 'name'
2.316 + and 'value' in the response.
2.317 +
2.318 + The optional 'path' is a string which specifies the scope of the cookie,
2.319 + and the optional 'expires' parameter is a value compatible with the
2.320 + time.time function, and indicates the expiry date/time of the cookie.
2.321 + """
2.322 +
2.323 + self.cookies_out[name] = value
2.324 + if path is not None:
2.325 + self.cookies_out[name]["path"] = path
2.326 + if expires is not None:
2.327 + self.cookies_out[name]["expires"] = expires
2.328 +
2.329 + def delete_cookie(self, cookie_name):
2.330 +
2.331 + """
2.332 + A framework-specific method which adds to the response a request that
2.333 + the cookie with the given 'cookie_name' be deleted/discarded by the
2.334 + client.
2.335 + """
2.336 +
2.337 + # Create a special cookie, given that we do not know whether the browser
2.338 + # has been sent the cookie or not.
2.339 + # NOTE: Magic discovered in Webware.
2.340 +
2.341 + self.cookies_out[cookie_name] = ""
2.342 + self.cookies_out[cookie_name]["path"] = "/"
2.343 + self.cookies_out[cookie_name]["expires"] = 0
2.344 + self.cookies_out[cookie_name]["max-age"] = 0
2.345 +
2.346 +# vim: tabstop=4 expandtab shiftwidth=4
3.1 --- a/WebStack/Generic.py Sat Apr 24 17:01:26 2004 +0000
3.2 +++ b/WebStack/Generic.py Sat Apr 24 20:33:31 2004 +0000
3.3 @@ -94,6 +94,9 @@
3.4 preferences are appropriate.
3.5 """
3.6
3.7 + if accept_preference is None:
3.8 + return []
3.9 +
3.10 accept_defs = accept_preference.split(",")
3.11 accept_prefs = []
3.12 for accept_def in accept_defs:
4.1 --- a/WebStack/JavaServlet.py Sat Apr 24 17:01:26 2004 +0000
4.2 +++ b/WebStack/JavaServlet.py Sat Apr 24 20:33:31 2004 +0000
4.3 @@ -85,7 +85,8 @@
4.4 def get_headers(self):
4.5
4.6 """
4.7 - A framework-specific method which returns all request headers.
4.8 + A framework-specific method which returns all request headers as a
4.9 + dictionary-like object mapping header names to values.
4.10 NOTE: If duplicate header names are permitted, then this interface will
4.11 NOTE: need to change.
4.12 """
4.13 @@ -218,6 +219,8 @@
4.14 """
4.15 A framework-specific method which extracts user information from the
4.16 transaction.
4.17 +
4.18 + Returns a username as a string or None if no user is defined.
4.19 """
4.20
4.21 return self.request.getRemoteUser()
5.1 --- a/WebStack/ModPython.py Sat Apr 24 17:01:26 2004 +0000
5.2 +++ b/WebStack/ModPython.py Sat Apr 24 20:33:31 2004 +0000
5.3 @@ -48,7 +48,8 @@
5.4 def get_headers(self):
5.5
5.6 """
5.7 - A framework-specific method which returns all request headers.
5.8 + A framework-specific method which returns all request headers as a
5.9 + dictionary-like object mapping header names to values.
5.10 NOTE: If duplicate header names are permitted, then this interface will
5.11 NOTE: need to change.
5.12 """
5.13 @@ -80,7 +81,7 @@
5.14 Returns the character set preferences.
5.15 """
5.16
5.17 - return self.parse_content_preferences(self.trans.headers_in["Accept-Charset"])
5.18 + return self.parse_content_preferences(self.trans.headers_in.get("Accept-Charset"))
5.19
5.20 def get_content_languages(self):
5.21
5.22 @@ -89,7 +90,7 @@
5.23 the transaction.
5.24 """
5.25
5.26 - return self.parse_content_preferences(self.trans.headers_in["Accept-Language"])
5.27 + return self.parse_content_preferences(self.trans.headers_in.get("Accept-Language"))
5.28
5.29 def get_path(self):
5.30
5.31 @@ -162,6 +163,8 @@
5.32 """
5.33 A framework-specific method which extracts user information from the
5.34 transaction.
5.35 +
5.36 + Returns a username as a string or None if no user is defined.
5.37 """
5.38
5.39 return self.trans.user
6.1 --- a/WebStack/Twisted.py Sat Apr 24 17:01:26 2004 +0000
6.2 +++ b/WebStack/Twisted.py Sat Apr 24 20:33:31 2004 +0000
6.3 @@ -43,7 +43,8 @@
6.4 def get_headers(self):
6.5
6.6 """
6.7 - A framework-specific method which returns all request headers.
6.8 + A framework-specific method which returns all request headers as a
6.9 + dictionary-like object mapping header names to values.
6.10 NOTE: If duplicate header names are permitted, then this interface will
6.11 NOTE: need to change.
6.12 """
6.13 @@ -155,6 +156,8 @@
6.14 """
6.15 A framework-specific method which extracts user information from the
6.16 transaction.
6.17 +
6.18 + Returns a username as a string or None if no user is defined.
6.19 """
6.20
6.21 # NOTE: Twisted makes headers lower case, for some reason.
6.22 @@ -276,10 +279,12 @@
6.23 A framework-specific method which adds to the response a request that
6.24 the cookie with the given 'cookie_name' be deleted/discarded by the
6.25 client.
6.26 -
6.27 - NOTE: Not supported yet.
6.28 """
6.29
6.30 - pass
6.31 + # Create a special cookie, given that we do not know whether the browser
6.32 + # has been sent the cookie or not.
6.33 + # NOTE: Magic discovered in Webware.
6.34 +
6.35 + self.trans.addCookie(cookie_name, "", expires=0, path="/", max_age=0)
6.36
6.37 # vim: tabstop=4 expandtab shiftwidth=4
7.1 --- a/WebStack/Webware.py Sat Apr 24 17:01:26 2004 +0000
7.2 +++ b/WebStack/Webware.py Sat Apr 24 20:33:31 2004 +0000
7.3 @@ -7,6 +7,7 @@
7.4 import Generic
7.5 from cgi import parse_qs
7.6 import StringIO
7.7 +from Helpers import Environment
7.8
7.9 class Transaction(Generic.Transaction):
7.10
7.11 @@ -54,7 +55,8 @@
7.12 def get_headers(self):
7.13
7.14 """
7.15 - A framework-specific method which returns all request headers.
7.16 + A framework-specific method which returns all request headers as a
7.17 + dictionary-like object mapping header names to values.
7.18 NOTE: If duplicate header names are permitted, then this interface will
7.19 NOTE: need to change.
7.20 """
7.21 @@ -63,13 +65,7 @@
7.22 # NOTE: Using lower case for the header names.
7.23
7.24 env = self.trans.request().environ()
7.25 - headers = {}
7.26 - for cgi_key, value in env.items():
7.27 - if cgi_key.startswith("HTTP_"):
7.28 - header_name = cgi_key[len("HTTP_"):].replace("_", "-").lower()
7.29 - headers[header_name] = value
7.30 -
7.31 - return headers
7.32 + return Environment.get_headers(env)
7.33
7.34 def get_header_values(self, key):
7.35
7.36 @@ -183,6 +179,8 @@
7.37 """
7.38 A framework-specific method which extracts user information from the
7.39 transaction.
7.40 +
7.41 + Returns a username as a string or None if no user is defined.
7.42 """
7.43
7.44 # NOTE: Webware relies entirely on a CGI-style environment where the