| | 236 | |
|---|
| | 237 | class HTTPRequestSocketWrapper(object): |
|---|
| | 238 | """ |
|---|
| | 239 | A file like wrapper for HTTP on non-blocking sockets. |
|---|
| | 240 | |
|---|
| | 241 | IOW this class provides as much as it can of the file like |
|---|
| | 242 | interface for sockets over which HTTP requests are made. |
|---|
| | 243 | """ |
|---|
| | 244 | def __init__(self, sock): |
|---|
| | 245 | self.sock = sock |
|---|
| | 246 | self.incomplete_line_size = 0 |
|---|
| | 247 | self.incomplete_line_buffer = [] |
|---|
| | 248 | self.lines_buffer = [] |
|---|
| | 249 | |
|---|
| | 250 | def read(self, size=None): |
|---|
| | 251 | raise NotImplementedError |
|---|
| | 252 | |
|---|
| | 253 | def readline(self, size=None): |
|---|
| | 254 | # This doesn't raise the appropriate exceptions |
|---|
| | 255 | # as the result may result in an Index Error? |
|---|
| | 256 | |
|---|
| | 257 | # if we can't return their data right away, let's try to read (blocking) |
|---|
| | 258 | if not (self.lines_buffer or (size and self.incomplete_line_size >= size)): |
|---|
| | 259 | self._fill_lines_buffer() |
|---|
| | 260 | |
|---|
| | 261 | # if we have a complete line to send, use it. |
|---|
| | 262 | if self.lines_buffer: |
|---|
| | 263 | line = self.lines_buffer[0] |
|---|
| | 264 | if size and len(line) > size: |
|---|
| | 265 | self.lines_buffer[0] = line[size:] |
|---|
| | 266 | line = line[:size] |
|---|
| | 267 | return line |
|---|
| | 268 | else: |
|---|
| | 269 | return self.lines_buffer.pop(0) |
|---|
| | 270 | |
|---|
| | 271 | # If we have enough of a non-complete line to |
|---|
| | 272 | # satisfy the size requirement return that. |
|---|
| | 273 | elif size and self.incomplete_line_size >= size: |
|---|
| | 274 | line = ''.join(self.incomplete_line_buffer) |
|---|
| | 275 | new_incomplete_line = line[:size] |
|---|
| | 276 | self.incomplete_line_size = len(new_incomplete_line) |
|---|
| | 277 | self.incomplete_line_buffer = [new_incomplete_line] |
|---|
| | 278 | else: |
|---|
| | 279 | assert ("We should never get here, should we?") |
|---|
| | 280 | |
|---|
| | 281 | def _fill_lines_buffer(self, size=None): |
|---|
| | 282 | bytes_seen = 0 |
|---|
| | 283 | while True: |
|---|
| | 284 | data = self.sock.recv(256) |
|---|
| | 285 | bytes_seen += len(data) |
|---|
| | 286 | |
|---|
| | 287 | lines = data.split("\n") |
|---|
| | 288 | # We remove the last piece of the split to ensure |
|---|
| | 289 | # that subsequent processing happens only on data |
|---|
| | 290 | # Representing complete lines. |
|---|
| | 291 | new_incomplete_line = lines.pop() |
|---|
| | 292 | |
|---|
| | 293 | # If we still have data in the lines list that means |
|---|
| | 294 | # we have received some data that forms a complete line |
|---|
| | 295 | if lines: |
|---|
| | 296 | # Ensure that we take the data left over from previous reads |
|---|
| | 297 | # and join it to our current reads before appending the latest |
|---|
| | 298 | # line seen |
|---|
| | 299 | self.incomplete_line_buffer.append(lines.pop(0)) |
|---|
| | 300 | self.lines_buffer.append("".join(self.incomplete_line_buffer)) |
|---|
| | 301 | self.incomplete_line_size = 0 |
|---|
| | 302 | self.incomplete_line_buffer = [] |
|---|
| | 303 | |
|---|
| | 304 | # remember all other complete lines seen in this read |
|---|
| | 305 | self.lines_buffer.extend(lines) |
|---|
| | 306 | |
|---|
| | 307 | # Record the latest new incomplete line |
|---|
| | 308 | if new_incomplete_line: |
|---|
| | 309 | self.incomplete_line_size += len(new_incomplete_line) |
|---|
| | 310 | self.incomplete_line_buffer.append(new_incomplete_line) |
|---|
| | 311 | |
|---|
| | 312 | # If they didn't specify a size and we have a line to send them |
|---|
| | 313 | # stop reading |
|---|
| | 314 | if not size and self.lines_buffer: |
|---|
| | 315 | return |
|---|
| | 316 | |
|---|
| | 317 | # if we've read over the size that they wanted, then stop reading |
|---|
| | 318 | if size and bytes_seen > size: |
|---|
| | 319 | return |
|---|
| | 320 | |
|---|
| | 321 | def readlines(self, sizehint=0): |
|---|
| | 322 | raise NotImplementedError |
|---|
| | 323 | |
|---|
| | 324 | def close(self): |
|---|
| | 325 | self.sock.close() |
|---|
| | 326 | |
|---|
| | 327 | def __iter__(self): |
|---|
| | 328 | return self |
|---|
| | 329 | |
|---|
| | 330 | def next(self): |
|---|
| | 331 | data = self.sock.next() |
|---|
| | 332 | self.bytes_read += len(data) |
|---|
| | 333 | return data |
|---|
| | 334 | |
|---|
| | 335 | def send(self, *args, **kwargs): |
|---|
| | 336 | return self.sock.send(*args, **kwargs) |
|---|
| | 337 | |
|---|
| | 338 | def readline(self, size=None): |
|---|
| | 339 | raise NotImplementedError |
|---|