1 #!/usr/bin/env python 2 3 """ 4 Request helper classes. 5 """ 6 7 class MessageBodyStream: 8 9 """ 10 A naive stream class, providing a non-blocking stream for transactions when 11 reading the message body. According to the HTTP standard, the following 12 things decide how long the message is: 13 14 * Use of the Content-Length header field (see 4.4 Message Length). 15 * Use of the Transfer-Coding header field (see 3.6 Transfer Codings), 16 particularly when the "chunked" coding is used. 17 18 NOTE: For now, we don't support the Transfer-Coding business. 19 """ 20 21 def __init__(self, stream, headers): 22 23 """ 24 Initialise the object with the given underlying 'stream'. The supplied 25 'headers' in a dictionary-style object are used to examine the nature of 26 the request. 27 """ 28 29 self.stream = stream 30 self.headers = headers 31 self.length = int(headers.get("Content-Length") or 0) 32 33 def read(self, limit=None): 34 35 "Reads all remaining data from the message body." 36 37 if limit is not None: 38 limit = min(limit, self.length) 39 else: 40 limit = self.length 41 data = self.stream.read(limit) 42 self.length = self.length - len(data) 43 return data 44 45 def readline(self): 46 47 "Reads a single line of data from the message body." 48 49 data = [] 50 while self.length > 0: 51 data.append(self.read(1)) 52 if data[-1] == "\n": 53 break 54 return "".join(data) 55 56 def readlines(self): 57 58 """ 59 Reads all remaining data from the message body, splitting it into lines 60 and returning the data as a list of lines. 61 """ 62 63 lines = self.read().split("\n") 64 for i in range(0, len(lines) - 1): 65 lines[i] = lines[i] + "\n" 66 return lines 67 68 def close(self): 69 70 "Closes the stream." 71 72 self.stream.close() 73 74 class Cookie: 75 76 """ 77 A simple cookie class for frameworks which do not return cookies in 78 structured form. 79 """ 80 81 def __init__(self, name, value): 82 self.name = name 83 self.value = value 84 85 def get_storage_items(storage_body): 86 87 """ 88 Return the items (2-tuples of the form key, values) from the 'storage_body'. 89 This is used in conjunction with FieldStorage objects. 90 """ 91 92 items = [] 93 for key in storage_body.keys(): 94 items.append((key, storage_body[key])) 95 return items 96 97 def get_body_fields(field_items, encoding): 98 99 """ 100 Returns a dictionary mapping field names to lists of field values for all 101 entries in the given 'field_items' (2-tuples of the form key, values) using 102 the given 'encoding'. 103 This is used in conjunction with FieldStorage objects. 104 """ 105 106 fields = {} 107 108 for field_name, field_values in field_items: 109 if type(field_values) == type([]): 110 fields[field_name] = [] 111 for field_value in field_values: 112 fields[field_name].append(get_body_field(field_value.value, encoding)) 113 else: 114 fields[field_name] = [get_body_field(field_values.value, encoding)] 115 116 return fields 117 118 def get_body_field(field_str, encoding): 119 120 """ 121 Returns the appropriate value for the given 'field_str' string using the 122 given 'encoding'. 123 """ 124 125 # Detect stray FieldStorage objects (eg. with Webware) or stray FileUpload 126 # objects (eg. with Zope). 127 128 if hasattr(field_str, "value"): 129 return get_body_field(field_str.value, encoding) 130 elif hasattr(field_str, "read"): 131 return field_str.read() 132 elif encoding is not None: 133 try: 134 return unicode(field_str, encoding) 135 except UnicodeError: 136 # NOTE: Hacks to permit graceful failure. 137 try: 138 return unicode(field_str, "iso-8859-1") 139 except UnicodeError: 140 return u"" 141 else: 142 return field_str 143 144 def get_fields_from_query_string(query_string, decoder): 145 146 """ 147 Returns a dictionary mapping field names to lists of values for the data 148 encoded in the given 'query_string'. Use the given 'decoder' function or 149 method to process the URL-encoded values. 150 """ 151 152 fields = {} 153 154 for pair in query_string.split("&"): 155 t = pair.split("=") 156 name = decoder(t[0]) 157 158 if len(t) == 2: 159 value = decoder(t[1]) 160 else: 161 value = "" 162 163 # NOTE: Remove empty names. 164 165 if name: 166 if not fields.has_key(name): 167 fields[name] = [] 168 fields[name].append(value) 169 170 return fields 171 172 def filter_fields(all_fields, fields_from_path): 173 174 """ 175 Taking items from the 'all_fields' dictionary, produce a new dictionary 176 which does not contain items from the 'fields_from_path' dictionary. 177 Return a new dictionary. 178 """ 179 180 fields = {} 181 for field_name, field_values in all_fields.items(): 182 183 # Find the path values for this field (for filtering below). 184 185 if fields_from_path.has_key(field_name): 186 field_from_path_values = fields_from_path[field_name] 187 if type(field_from_path_values) != type([]): 188 field_from_path_values = [field_from_path_values] 189 else: 190 field_from_path_values = [] 191 192 fields[field_name] = [] 193 for field_value in field_values: 194 195 # Filter path values. 196 197 if field_value not in field_from_path_values: 198 fields[field_name].append(field_value) 199 200 # Remove filtered fields. 201 202 if fields[field_name] == []: 203 del fields[field_name] 204 205 return fields 206 207 # vim: tabstop=4 expandtab shiftwidth=4