Package caldavclientlibrary :: Package protocol :: Package http :: Module requestresponse
[hide private]
[frames] | no frames]

Source Code for Module caldavclientlibrary.protocol.http.requestresponse

  1  ## 
  2  # Copyright (c) 2006-2016 Apple Inc. All rights reserved. 
  3  # 
  4  # Licensed under the Apache License, Version 2.0 (the "License"); 
  5  # you may not use this file except in compliance with the License. 
  6  # You may obtain a copy of the License at 
  7  # 
  8  # http://www.apache.org/licenses/LICENSE-2.0 
  9  # 
 10  # Unless required by applicable law or agreed to in writing, software 
 11  # distributed under the License is distributed on an "AS IS" BASIS, 
 12  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 13  # See the License for the specific language governing permissions and 
 14  # limitations under the License. 
 15  ## 
 16   
 17  from StringIO import StringIO 
 18  from caldavclientlibrary.protocol.http.definitions import headers 
 19  from caldavclientlibrary.protocol.http.definitions import methods 
 20  from caldavclientlibrary.protocol.http.definitions import statuscodes 
 21   
22 -class ResponseError(Exception):
23 pass
24
25 -class RequestResponse(object):
26
27 - def __init__(self, session, method, url, etag=None, etag_match=False):
28 self._initResponse() 29 self.session = session 30 self.method = method 31 self.url = url 32 self.etag = etag 33 self.etag_match = etag_match
34
35 - def _initResponse(self):
36 self.session = None 37 self.request_data = None 38 self.response_data = None 39 self.method = methods.GET 40 self.url = None 41 self.etag = None 42 self.etag_match = False 43 self.status_code = statuscodes.Unknown 44 self.status_reason = None 45 self.headers = {} 46 self.connection_close = False 47 self.content_length = 0 48 self.chunked = False 49 self.completed = False
50
51 - def setSession(self, session):
53 - def getSession(self):
54 return self.session
55
56 - def getMethod(self):
57 return self.method
58
59 - def setURL(self, ruri):
60 self.url = ruri
61 - def getURL(self):
62 return self.url
63
64 - def setETag(self, etag, etag_match):
65 self.etag = etag 66 self.etag_match = etag_match
67
68 - def queuedForSending(self, session):
70
71 - def setData(self, request_data, response_data):
72 self.request_data = request_data 73 self.response_data = response_data
74
75 - def hasRequestData(self):
76 return self.request_data != None
77
78 - def hasResponseData(self):
79 return self.response_data != None
80
81 - def getRequestData(self):
82 if self.request_data: 83 return self.request_data 84 else: 85 return None
86
87 - def getResponseData(self):
88 if self.response_data: 89 return self.response_data 90 else: 91 return None
92
93 - def getRequestStartLine(self):
94 return "%s %s HTTP/1.1" % (self.method, self.url,)
95
96 - def getRequestHeaders(self):
97 # This will be overridden by sub-classes that add headers - those classes should 98 # call this class's implementation to write out the basic set of headers 99 result = [] 100 self.addHeaders(result) 101 return tuple(result)
102
103 - def generateRequestHeader(self):
104 os = StringIO() 105 os.write("%s\r\n" % (self.getRequestStartLine(),)) 106 for header, value in self.getRequestHeaders(): 107 os.write("%s: %s\r\n" % (header, value,)) 108 os.write("\r\n") 109 return os.getvalue()
110
111 - def addHeaders(self, hdrs):
112 113 # Write host 114 hdrs.append((headers.Host, "%s:%s" % (self.session.server, self.session.port,))) 115 116 # Do ETag matching 117 if self.etag: 118 if self.etag_match: 119 hdrs.append((headers.IfMatch, self.etag)) 120 else: 121 hdrs.append((headers.IfNoneMatch, self.etag)) 122 123 # Do session global headers 124 self.session.addHeaders(hdrs, self) 125 126 # Check for content 127 self.addContentHeaders(hdrs)
128
129 - def addContentHeaders(self, hdrs):
130 # Check for content 131 if self.hasRequestData(): 132 hdrs.append((headers.ContentLength, str(self.request_data.getContentLength()))) 133 hdrs.append((headers.ContentType, self.request_data.getContentType()))
134
135 - def setResponseStatus(self, version, status, reason):
136 self.status_code = status 137 self.status_reason = reason
138
139 - def setResponseHeaders(self, hdrs):
140 for header in hdrs: 141 splits = header.split(":") 142 self.headers.setdefault(splits[0].strip().lower(), []).append(splits[1].strip()) 143 144 # Now cache some useful header values 145 self.cacheHeaders()
146
147 - def clearResponse(self):
148 self.etag_match = False 149 self.status_code = statuscodes.Unknown 150 self.status_reason = None 151 self.headers = {} 152 self.connection_close = False 153 self.content_length = 0 154 self.chunked = False 155 self.completed = False 156 157 if self.response_data: 158 self.response_data.clear()
159
160 - def getStatusCode(self):
161 return self.status_code
162 - def getStatusReason(self):
163 return self.status_reason
164
165 - def getConnectionClose(self):
166 return self.connection_close
167
168 - def getContentLength(self):
169 return self.content_length
170 - def getChunked(self):
171 return self.chunked
172
173 - def setComplete(self):
174 self.completed = True
175 - def getCompleted(self):
176 return self.completed
177
178 - def hasResponseHeader(self, hdr):
179 return self.headers.has_key(hdr.lower())
180
181 - def getResponseHeader(self, hdr):
182 if self.headers.has_key(hdr.lower()): 183 return self.headers[hdr.lower()][0] 184 else: 185 return ""
186
187 - def getResponseHeaders(self, hdr=None):
188 if hdr: 189 if self.headers.has_key(hdr.lower()): 190 return self.headers[hdr.lower()] 191 else: 192 return () 193 else: 194 return self.headers
195
196 - def isRedirect(self):
197 # Only these are allowed 198 return self.status_code in (statuscodes.MovedPermanently, statuscodes.Found, statuscodes.TemporaryRedirect)
199
200 - def parseStatusLine(self, line):
201 202 # Must have 'HTTP/' version at start 203 if line[0:5] != "HTTP/": 204 raise ResponseError("status line incorrect at start") 205 206 # Must have version '1.1 ' 207 if line[5:9] != "1.1 ": 208 raise ResponseError("incorrect http version in status line") 209 210 # Must have three digits followed by nothing or one space 211 if not line[9:12].isdigit() or (len(line) > 12 and line[12] != " "): 212 raise ResponseError("invalid status response code syntax") 213 214 # Read in the status code 215 self.status_code = int(line[9:12]) 216 217 # Remainder is reason 218 if len(line) > 13: 219 self.status_reason = line[13:]
220
221 - def readFoldedLine(self, instream, line1, line2, log):
222 # If line2 already has data, transfer that into line1 223 if line2 or line1: 224 line1 = line2 225 else: 226 # Fill first line 227 line1 = instream.readline() 228 if not line1: 229 return False, line1, line2 230 line1 = line1.rstrip("\r\n") 231 232 if log: 233 log.write("%s\n" % (line1,)) 234 235 # Terminate on blank line which is end of headers 236 if not line1: 237 return True, line1, line2 238 239 # Now loop looking ahead at the next line to see if it is folded 240 while True: 241 # Get next line 242 line2 = instream.readline() 243 if not line2: 244 return True, line1, line2 245 line2 = line2.rstrip("\r\n") 246 247 if log: 248 log.write("%s\n" % (line2,)) 249 250 # Does it start with a space => folded 251 if line2 and line2[0].isspace(): 252 # Copy folded line (without space) to current line and cycle for more 253 line1 += line2[1:] 254 else: 255 # Not folded - just exit loop 256 break 257 258 return True, line1, line2
259
260 - def cacheHeaders(self):
261 # Connection 262 if self.headers.has_key(headers.Connection): 263 value = self.headers[headers.Connection][0] 264 self.connection_close = (value.lower() == headers.ConnectionClose) 265 266 # Content-Length 267 if self.headers.has_key(headers.ContentLength): 268 value = self.headers[headers.ContentLength][0] 269 self.content_length = int(value) 270 271 # Transfer encoding 272 if self.headers.has_key(headers.TransferEncoding): 273 value = self.headers[headers.TransferEncoding][0] 274 self.chunked = (value == headers.TransferEncodingChunked)
275