1 #!/usr/bin/env python 2 3 """ 4 Java Servlet classes. 5 """ 6 7 import Generic 8 from StringIO import StringIO 9 from Helpers.Request import Cookie 10 import javax.servlet.http.Cookie 11 12 class Stream: 13 14 """ 15 Wrapper around java.io.BufferedReader. 16 """ 17 18 def __init__(self, stream): 19 20 "Initialise the stream with the given underlying 'stream'." 21 22 self.stream = stream 23 24 def read(self): 25 26 "Read the entire message, returning it as a string." 27 28 characters = StringIO() 29 while 1: 30 c = self.stream.read() 31 if c == -1: 32 return characters.getvalue() 33 else: 34 characters.write(chr(c)) 35 36 def readline(self): 37 38 "Read a line from the stream, returning it as a string." 39 40 return self.stream.readLine() 41 42 class Transaction(Generic.Transaction): 43 44 """ 45 Java Servlet transaction interface. 46 """ 47 48 def __init__(self, request, response): 49 50 """ 51 Initialise the transaction using the Java Servlet HTTP 'request' and 52 'response'. 53 """ 54 55 self.request = request 56 self.response = response 57 self.status = None 58 self.user = None 59 60 # Remember the cookies received in the request. 61 # NOTE: Discarding much of the information received. 62 63 self.cookies_in = {} 64 for cookie in self.request.getCookies() or []: 65 cookie_name = cookie.getName() 66 self.cookies_in[cookie_name] = Cookie(cookie_name, cookie.getValue()) 67 68 def commit(self): 69 70 """ 71 A special method, synchronising the transaction with framework-specific 72 objects. 73 """ 74 75 self.get_response_stream().close() 76 77 # Request-related methods. 78 79 def get_request_stream(self): 80 81 """ 82 A framework-specific method which returns the request stream for 83 the transaction. 84 """ 85 86 return Stream(self.request.getReader()) 87 88 def get_request_method(self): 89 90 """ 91 A framework-specific method which gets the request method. 92 """ 93 94 return self.request.getMethod() 95 96 def get_headers(self): 97 98 """ 99 A framework-specific method which returns all request headers as a 100 dictionary-like object mapping header names to values. 101 NOTE: If duplicate header names are permitted, then this interface will 102 NOTE: need to change. 103 """ 104 105 headers = {} 106 header_names_enum = self.request.getHeaderNames() 107 while header_names_enum.hasMoreElements(): 108 109 # NOTE: Retrieve only a single value (not using getHeaders). 110 111 header_name = header_names_enum.nextElement() 112 headers[header_name] = self.request.getHeader(header_name) 113 114 return headers 115 116 def get_header_values(self, key): 117 118 """ 119 A framework-specific method which returns a list of all request header 120 values associated with the given 'key'. Note that according to RFC 2616, 121 'key' is treated as a case-insensitive string. 122 """ 123 124 values = [] 125 headers_enum = self.request.getHeaders(key) 126 while headers_enum.hasMoreElements(): 127 values.append(headers_enum.nextElement()) 128 return values 129 130 def get_content_type(self): 131 132 """ 133 A framework-specific method which gets the content type specified on the 134 request, along with the charset employed. 135 """ 136 137 content_types = self.get_header_values("Content-Type") or [] 138 if len(content_types) >= 1: 139 return self.parse_content_type(content_types[0]) 140 else: 141 return None 142 143 def get_content_charsets(self): 144 145 """ 146 Returns the character set preferences. 147 """ 148 149 accept_charsets = self.get_header_values("Accept-Charset") or [] 150 if len(accept_charsets) >= 1: 151 return self.parse_content_preferences(accept_charsets[0]) 152 else: 153 return None 154 155 def get_content_languages(self): 156 157 """ 158 A framework-specific method which extracts language information from 159 the transaction. 160 """ 161 162 accept_languages = self.get_header_values("Accept-Language") or [] 163 if len(accept_languages) >= 1: 164 return self.parse_content_preferences(accept_languages[0]) 165 else: 166 return None 167 168 def get_path(self): 169 170 """ 171 A framework-specific method which gets the entire path from the request. 172 """ 173 174 # NOTE: To be verified. 175 176 path = self.get_path_without_query() 177 qs = self.get_query_string() 178 if qs: 179 path += "?" 180 path += qs 181 return path 182 183 def get_path_without_query(self): 184 185 """ 186 A framework-specific method which gets the entire path from the request 187 minus the query string. 188 """ 189 190 return self.request.getServletPath() 191 192 def get_path_info(self): 193 194 """ 195 A framework-specific method which gets the "path info" (the part of the 196 URL after the resource name handling the current request) from the 197 request. 198 """ 199 200 return self.request.getPathInfo() 201 202 def get_query_string(self): 203 204 """ 205 A framework-specific method which gets the query string from the path in 206 the request. 207 """ 208 209 return self.request.getQueryString() 210 211 # Higher level request-related methods. 212 213 def get_fields_from_path(self): 214 215 """ 216 A framework-specific method which extracts the form fields from the 217 path specified in the transaction. The underlying framework may refuse 218 to supply fields from the path if handling a POST transaction. 219 220 Returns a dictionary mapping field names to lists of values (even if a 221 single value is associated with any given field name). 222 223 NOTE: There may not be a reliable means of extracting only the fields 224 NOTE: from the path. 225 """ 226 227 return self.get_fields_from_body() 228 229 def get_fields_from_body(self, encoding=None): 230 231 """ 232 A framework-specific method which extracts the form fields from the 233 message body in the transaction. The optional 'encoding' parameter 234 specifies the character encoding of the message body for cases where no 235 such information is available, but where the default encoding is to be 236 overridden. 237 238 Returns a dictionary mapping field names to lists of values (even if a 239 single value is associated with any given field name). 240 241 NOTE: There may not be a reliable means of extracting only the fields 242 NOTE: from the message body. Moreover, the encoding of the fields may 243 NOTE: not be pertinent. 244 """ 245 246 parameter_map = self.request.getParameterMap() 247 fields = {} 248 if parameter_map: 249 for key in parameter_map.keySet(): 250 fields[key] = parameter_map[key] 251 return fields 252 253 def get_user(self): 254 255 """ 256 A framework-specific method which extracts user information from the 257 transaction. 258 259 Returns a username as a string or None if no user is defined. 260 """ 261 262 if self.user is not None: 263 return self.user 264 else: 265 return self.request.getRemoteUser() 266 267 def get_cookies(self): 268 269 """ 270 A framework-specific method which obtains cookie information from the 271 request. 272 273 Returns a dictionary mapping cookie names to cookie objects. 274 """ 275 276 return self.cookies_in 277 278 def get_cookie(self, cookie_name): 279 280 """ 281 A framework-specific method which obtains cookie information from the 282 request. 283 284 Returns a cookie object for the given 'cookie_name' or None if no such 285 cookie exists. 286 """ 287 288 return self.cookies_in.get(cookie_name) 289 290 # Response-related methods. 291 292 def get_response_stream(self): 293 294 """ 295 A framework-specific method which returns the response stream for 296 the transaction. 297 """ 298 299 return self.response.getOutputStream() 300 301 def get_response_code(self): 302 303 """ 304 Get the response code associated with the transaction. If no response 305 code is defined, None is returned. 306 """ 307 308 return self.status 309 310 def set_response_code(self, response_code): 311 312 """ 313 Set the 'response_code' using a numeric constant defined in the HTTP 314 specification. 315 """ 316 317 self.status = response_code 318 self.response.setStatus(self.status) 319 320 def set_header_value(self, header, value): 321 322 """ 323 Set the HTTP 'header' with the given 'value'. 324 """ 325 326 self.response.setHeader(self.format_header_value(header), self.format_header_value(value)) 327 328 def set_content_type(self, content_type): 329 330 """ 331 A framework-specific method which sets the 'content_type' for the 332 response. 333 """ 334 335 return self.response.setHeader("Content-Type", self.format_content_type(content_type)) 336 337 # Higher level response-related methods. 338 339 def set_cookie(self, cookie): 340 341 """ 342 A framework-specific method which stores the given 'cookie' object in 343 the response. 344 """ 345 346 new_cookie = javax.servlet.http.Cookie(cookie.name, cookie.value) 347 self.response.addCookie(new_cookie) 348 349 def set_cookie_value(self, name, value, path=None, expires=None): 350 351 """ 352 A framework-specific method which stores a cookie with the given 'name' 353 and 'value' in the response. 354 355 The optional 'path' is a string which specifies the scope of the cookie, 356 and the optional 'expires' parameter is a value compatible with the 357 time.time function, and indicates the expiry date/time of the cookie. 358 """ 359 360 cookie = javax.servlet.http.Cookie(name, value) 361 if path is not None: 362 cookie.setPath(path) 363 364 # NOTE: The expires parameter seems not to be supported. 365 366 self.response.addCookie(cookie) 367 368 def delete_cookie(self, cookie_name): 369 370 """ 371 A framework-specific method which adds to the response a request that 372 the cookie with the given 'cookie_name' be deleted/discarded by the 373 client. 374 """ 375 376 # Create a special cookie, given that we do not know whether the browser 377 # has been sent the cookie or not. 378 # NOTE: Magic discovered in Webware. 379 380 cookie = javax.servlet.http.Cookie(cookie_name, "") 381 cookie.setPath("/") 382 cookie.setMaxAge(0) 383 self.response.addCookie(cookie) 384 385 # Application-specific methods. 386 387 def set_user(self, username): 388 389 """ 390 An application-specific method which sets the user information with 391 'username' in the transaction. This affects subsequent calls to 392 'get_user'. 393 """ 394 395 self.user = username 396 397 # vim: tabstop=4 expandtab shiftwidth=4