Package restkit :: Package http :: Module message
[hide private]
[frames] | no frames]

Source Code for Module restkit.http.message

  1  # -*- coding: utf-8 - 
  2  # 
  3  # This file is part of restkit released under the MIT license.  
  4  # See the NOTICE for more information. 
  5   
  6  import re 
  7  import urlparse 
  8   
  9  try: 
 10      from cStringIO import StringIO 
 11  except ImportError: 
 12      from StringIO import StringIO 
 13   
 14  from restkit.http.body import ChunkedReader, LengthReader, EOFReader, Body, \ 
 15  GzipBody, DeflateBody 
 16  from restkit.errors import InvalidHeader, InvalidHeaderName, NoMoreData, \ 
 17  InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, InvalidHTTPStatus 
 18   
19 -class Message(object):
20 - def __init__(self, unreader, **kwargs):
21 self.unreader = unreader 22 self.version = None 23 self.headers = [] 24 self.trailers = [] 25 self.body = None 26 self.encoding = None 27 28 self.hdrre = re.compile("[\x00-\x1F\x7F()<>@,;:\[\]={} \t\\\\\"]") 29 30 unused = self.parse(self.unreader) 31 self.unreader.unread(unused) 32 self.set_body_reader()
33
34 - def get_data(self, unreader, buf, stop=False):
35 data = unreader.read() 36 if not data: 37 if stop: 38 raise StopIteration() 39 raise NoMoreData(buf.getvalue()) 40 buf.write(data)
41
42 - def parse(self, unreader):
43 buf = StringIO() 44 45 self.get_data(unreader, buf, stop=True) 46 47 # Request line 48 idx = buf.getvalue().find("\r\n") 49 while idx < 0: 50 self.get_data(unreader, buf) 51 idx = buf.getvalue().find("\r\n") 52 self.parse_first_line(buf.getvalue()[:idx]) 53 rest = buf.getvalue()[idx+2:] # Skip \r\n 54 buf.truncate(0) 55 buf.write(rest) 56 57 # Headers 58 idx = buf.getvalue().find("\r\n\r\n") 59 done = buf.getvalue()[:2] == "\r\n" 60 while idx < 0 and not done: 61 self.get_data(unreader, buf) 62 idx = buf.getvalue().find("\r\n\r\n") 63 done = buf.getvalue()[:2] == "\r\n" 64 if done: 65 self.unreader.unread(buf.getvalue()[2:]) 66 return "" 67 68 self.headers = self.parse_headers(buf.getvalue()[:idx]) 69 70 ret = buf.getvalue()[idx+4:] 71 buf.truncate(0) 72 return ret
73
74 - def parse_first_line(self):
75 raise NotImplementedError()
76
77 - def parse_headers(self, data):
78 headers = [] 79 80 # Split lines on \r\n keeping the \r\n on each line 81 lines = [] 82 lines = [line + "\r\n" for line in data.split("\r\n")] 83 84 # Parse headers into key/value pairs paying attention 85 # to continuation lines. 86 while len(lines): 87 # Parse initial header name : value pair. 88 curr = lines.pop(0) 89 if curr.find(":") < 0: 90 raise InvalidHeader(curr.strip()) 91 name, value = curr.split(":", 1) 92 name = name.rstrip(" \t") 93 if self.hdrre.search(name.upper()): 94 raise InvalidHeaderName(name) 95 name, value = name.strip(), [value.lstrip()] 96 97 # Consume value continuation lines 98 while len(lines) and lines[0].startswith((" ", "\t")): 99 value.append(lines.pop(0)) 100 value = ''.join(value).rstrip() 101 102 headers.append((name, value)) 103 return headers
104
105 - def set_body_reader(self):
106 chunked = False 107 clength = None 108 for (name, value) in self.headers: 109 if name.upper() == "CONTENT-LENGTH": 110 try: 111 clength = int(value) 112 except ValueError: 113 clength = None 114 elif name.upper() == "TRANSFER-ENCODING": 115 chunked = value.lower() == "chunked" 116 elif name.upper() == "CONTENT-ENCODING": 117 self.encoding = value.lower() 118 119 if chunked: 120 self.body = Body(ChunkedReader(self, self.unreader)) 121 elif clength is not None: 122 self.body = Body(LengthReader(self, self.unreader, clength)) 123 else: 124 self.body = Body(EOFReader(self, self.unreader))
125
126 - def should_close(self):
127 for (h, v) in self.headers: 128 if h.lower() == "connection": 129 if v.lower().strip() == "close": 130 return True 131 elif v.lower().strip() == "keep-alive": 132 return False 133 return self.version <= (1, 0)
134 135
136 -class Request(Message):
137 - def __init__(self, unreader):
138 self.methre = re.compile("[A-Z0-9$-_.]{3,20}") 139 self.versre = re.compile("HTTP/(\d+).(\d+)") 140 141 self.method = None 142 self.uri = None 143 self.scheme = None 144 self.host = None 145 self.port = 80 146 self.path = None 147 self.query = None 148 self.fragment = None 149 150 super(Request, self).__init__(unreader)
151
152 - def parse_first_line(self, line):
153 bits = line.split(None, 2) 154 if len(bits) != 3: 155 raise InvalidRequestLine(line) 156 157 # Method 158 if not self.methre.match(bits[0]): 159 raise InvalidRequestMethod(bits[0]) 160 self.method = bits[0].upper() 161 162 # URI 163 self.uri = bits[1] 164 parts = urlparse.urlparse(bits[1]) 165 self.scheme = parts.scheme or None 166 self.host = parts.netloc or None 167 if parts.port is None: 168 self.port = 80 169 else: 170 self.host = self.host.rsplit(":", 1)[0] 171 self.port = parts.port 172 self.path = parts.path or None 173 self.query = parts.query or None 174 self.fragment = parts.fragment or None 175 176 # Version 177 match = self.versre.match(bits[2]) 178 if match is None: 179 raise InvalidHTTPVersion(bits[2]) 180 self.version = (int(match.group(1)), int(match.group(2)))
181
182 - def set_body_reader(self):
183 super(Request, self).set_body_reader() 184 if isinstance(self.body.reader, EOFReader): 185 self.body = Body(LengthReader(self, self.unreader, 0))
186
187 -class Response(Message):
188
189 - def __init__(self, unreader, decompress=True):
190 self.versre = re.compile("HTTP/(\d+).(\d+)") 191 self.stare = re.compile("(\d{3})\s*(\w*)") 192 self.status = None 193 self.reason = None 194 self.status_int = None 195 self.decompress = decompress 196 super(Response, self).__init__(unreader)
197
198 - def parse_first_line(self, line):
199 bits = line.split(None, 1) 200 if len(bits) != 2: 201 raise InvalidRequestLine(line) 202 203 # version 204 matchv = self.versre.match(bits[0]) 205 if matchv is None: 206 raise InvalidHTTPVersion(bits[0]) 207 self.version = (int(matchv.group(1)), int(matchv.group(2))) 208 209 # status 210 matchs = self.stare.match(bits[1]) 211 if matchs is None: 212 raise InvalidHTTPStatus(bits[1]) 213 214 self.status = bits[1] 215 self.status_int = int(matchs.group(1)) 216 self.reason = matchs.group(2)
217
218 - def set_body_reader(self):
219 super(Response, self).set_body_reader() 220 221 if self.decompress: 222 if self.encoding == "gzip": 223 self.body = GzipBody(self.body.reader) 224 elif self.encoding == "deflate": 225 self.body = DeflateBody(self.body.reader)
226