| 246 | | |
|---|
| 247 | | class HTTPRequestSocketWrapper(object): |
|---|
| 248 | | """ |
|---|
| 249 | | A file like wrapper for HTTP on non-blocking sockets. |
|---|
| 250 | | |
|---|
| 251 | | IOW this class provides as much as it can of the file like |
|---|
| 252 | | interface for sockets over which HTTP requests are made. |
|---|
| 253 | | """ |
|---|
| 254 | | def __init__(self, sock): |
|---|
| 255 | | self.sock = sock |
|---|
| 256 | | self.incomplete_line_buffer = [] |
|---|
| 257 | | self.lines_buffer = [] |
|---|
| 258 | | |
|---|
| 259 | | def read(self, size=None): |
|---|
| 260 | | raise NotImplementedError |
|---|
| 261 | | |
|---|
| 262 | | def readline(self): |
|---|
| 263 | | # This doesn't raise the appropriate exceptions |
|---|
| 264 | | # as the result may result in an Index Error? |
|---|
| 265 | | |
|---|
| 266 | | # if we can't return their data right away, let's try to read (blocking) |
|---|
| 267 | | if not self.lines_buffer: |
|---|
| 268 | | self._fill_lines_buffer() |
|---|
| 269 | | |
|---|
| 270 | | if not self.lines_buffer: |
|---|
| 271 | | return "" |
|---|
| 272 | | |
|---|
| 273 | | return self.lines_buffer.pop(0) |
|---|
| 274 | | |
|---|
| 275 | | |
|---|
| 276 | | def _fill_lines_buffer(self): |
|---|
| 277 | | while True: |
|---|
| 278 | | data = self.sock.recv(256) |
|---|
| 279 | | |
|---|
| 280 | | lines = data.split("\n") |
|---|
| 281 | | # We remove the last piece of the split to ensure |
|---|
| 282 | | # that subsequent processing happens only on data |
|---|
| 283 | | # Representing complete lines. |
|---|
| 284 | | new_incomplete_line = lines.pop() |
|---|
| 285 | | |
|---|
| 286 | | # If we still have data in the lines list that means |
|---|
| 287 | | # we have received some data that forms a complete line |
|---|
| 288 | | if lines: |
|---|
| 289 | | # Ensure that we take the data left over from previous reads |
|---|
| 290 | | # and join it to our current reads before appending the latest |
|---|
| 291 | | # line seen |
|---|
| 292 | | self.incomplete_line_buffer.append(lines.pop(0)) |
|---|
| 293 | | self.lines_buffer.append("".join(self.incomplete_line_buffer)) |
|---|
| 294 | | |
|---|
| 295 | | self.incomplete_line_buffer = [] |
|---|
| 296 | | |
|---|
| 297 | | # remember all other complete lines seen in this read |
|---|
| 298 | | self.lines_buffer.extend(lines) |
|---|
| 299 | | |
|---|
| 300 | | # Record the latest new incomplete line |
|---|
| 301 | | self.incomplete_line_buffer.append(new_incomplete_line) |
|---|
| 302 | | |
|---|
| 303 | | # If they didn't specify a size and we have a line to send them |
|---|
| 304 | | # stop reading |
|---|
| 305 | | if self.lines_buffer: |
|---|
| 306 | | return |
|---|
| 307 | | |
|---|
| 308 | | def readlines(self, sizehint=0): |
|---|
| 309 | | raise NotImplementedError |
|---|
| 310 | | |
|---|
| 311 | | def close(self): |
|---|
| 312 | | self.sock.close() |
|---|
| 313 | | |
|---|
| 314 | | def __iter__(self): |
|---|
| 315 | | return self |
|---|
| 316 | | |
|---|
| 317 | | def next(self): |
|---|
| 318 | | data = self.sock.next() |
|---|
| 319 | | self.bytes_read += len(data) |
|---|
| 320 | | return data |
|---|
| 321 | | |
|---|
| 322 | | def send(self, *args, **kwargs): |
|---|
| 323 | | return self.sock.send(*args, **kwargs) |
|---|
| 324 | | |
|---|
| 325 | | def readline(self, size=None): |
|---|
| 326 | | raise NotImplementedError |
|---|
| 784 | | def _ssl_wrap_method(method, is_reader=False): |
|---|
| 785 | | """Wrap the given method with SSL error-trapping. |
|---|
| 786 | | |
|---|
| 787 | | is_reader: if False (the default), EOF errors will be raised. |
|---|
| 788 | | If True, EOF errors will return "" (to emulate normal sockets). |
|---|
| 789 | | """ |
|---|
| 790 | | def ssl_method_wrapper(self, *args, **kwargs): |
|---|
| 791 | | ## print (id(self), method, args, kwargs) |
|---|
| | 693 | class SSL_fileobject(socket._fileobject): |
|---|
| | 694 | """Faux file object attached to a socket object.""" |
|---|
| | 695 | |
|---|
| | 696 | ssl_timeout = 3 |
|---|
| | 697 | ssl_retry = .01 |
|---|
| | 698 | |
|---|
| | 699 | def _safe_call(self, is_reader, call, *args, **kwargs): |
|---|
| | 700 | """Wrap the given call with SSL error-trapping. |
|---|
| | 701 | |
|---|
| | 702 | is_reader: if False EOF errors will be raised. If True, EOF errors |
|---|
| | 703 | will return "" (to emulate normal sockets). |
|---|
| | 704 | """ |
|---|
| 830 | | return ssl_method_wrapper |
|---|
| 831 | | |
|---|
| 832 | | class SSL_fileobject(socket._fileobject): |
|---|
| 833 | | """Faux file object attached to a socket object.""" |
|---|
| 834 | | |
|---|
| 835 | | ssl_timeout = 3 |
|---|
| 836 | | ssl_retry = .01 |
|---|
| 837 | | |
|---|
| 838 | | close = _ssl_wrap_method(socket._fileobject.close) |
|---|
| 839 | | flush = _ssl_wrap_method(socket._fileobject.flush) |
|---|
| 840 | | write = _ssl_wrap_method(socket._fileobject.write) |
|---|
| 841 | | writelines = _ssl_wrap_method(socket._fileobject.writelines) |
|---|
| 842 | | read = _ssl_wrap_method(socket._fileobject.read, is_reader=True) |
|---|
| 843 | | readline = _ssl_wrap_method(socket._fileobject.readline, is_reader=True) |
|---|
| 844 | | readlines = _ssl_wrap_method(socket._fileobject.readlines, is_reader=True) |
|---|
| | 743 | |
|---|
| | 744 | def flush(self): |
|---|
| | 745 | if self._wbuf: |
|---|
| | 746 | buffer = "".join(self._wbuf) |
|---|
| | 747 | self._wbuf = [] |
|---|
| | 748 | self._safe_call(False, self._sock.sendall, buffer) |
|---|
| | 749 | |
|---|
| | 750 | def read(self, size=-1): |
|---|
| | 751 | data = self._rbuf |
|---|
| | 752 | if size < 0: |
|---|
| | 753 | # Read until EOF |
|---|
| | 754 | buffers = [] |
|---|
| | 755 | if data: |
|---|
| | 756 | buffers.append(data) |
|---|
| | 757 | self._rbuf = "" |
|---|
| | 758 | if self._rbufsize <= 1: |
|---|
| | 759 | recv_size = self.default_bufsize |
|---|
| | 760 | else: |
|---|
| | 761 | recv_size = self._rbufsize |
|---|
| | 762 | |
|---|
| | 763 | while True: |
|---|
| | 764 | data = self._safe_call(True, self._sock.recv, recv_size) |
|---|
| | 765 | if not data: |
|---|
| | 766 | break |
|---|
| | 767 | buffers.append(data) |
|---|
| | 768 | return "".join(buffers) |
|---|
| | 769 | else: |
|---|
| | 770 | # Read until size bytes or EOF seen, whichever comes first |
|---|
| | 771 | buf_len = len(data) |
|---|
| | 772 | if buf_len >= size: |
|---|
| | 773 | self._rbuf = data[size:] |
|---|
| | 774 | return data[:size] |
|---|
| | 775 | buffers = [] |
|---|
| | 776 | if data: |
|---|
| | 777 | buffers.append(data) |
|---|
| | 778 | self._rbuf = "" |
|---|
| | 779 | while True: |
|---|
| | 780 | left = size - buf_len |
|---|
| | 781 | recv_size = max(self._rbufsize, left) |
|---|
| | 782 | data = self._safe_call(True, self._sock.recv, recv_size) |
|---|
| | 783 | if not data: |
|---|
| | 784 | break |
|---|
| | 785 | buffers.append(data) |
|---|
| | 786 | n = len(data) |
|---|
| | 787 | if n >= left: |
|---|
| | 788 | self._rbuf = data[left:] |
|---|
| | 789 | buffers[-1] = data[:left] |
|---|
| | 790 | break |
|---|
| | 791 | buf_len += n |
|---|
| | 792 | return "".join(buffers) |
|---|
| | 793 | |
|---|
| | 794 | def readline(self, size=-1): |
|---|
| | 795 | data = self._rbuf |
|---|
| | 796 | if size < 0: |
|---|
| | 797 | # Read until \n or EOF, whichever comes first |
|---|
| | 798 | if self._rbufsize <= 1: |
|---|
| | 799 | # Speed up unbuffered case |
|---|
| | 800 | assert data == "" |
|---|
| | 801 | buffers = [] |
|---|
| | 802 | while data != "\n": |
|---|
| | 803 | data = self._safe_call(True, self._sock.recv, 1) |
|---|
| | 804 | if not data: |
|---|
| | 805 | break |
|---|
| | 806 | buffers.append(data) |
|---|
| | 807 | return "".join(buffers) |
|---|
| | 808 | nl = data.find('\n') |
|---|
| | 809 | if nl >= 0: |
|---|
| | 810 | nl += 1 |
|---|
| | 811 | self._rbuf = data[nl:] |
|---|
| | 812 | return data[:nl] |
|---|
| | 813 | buffers = [] |
|---|
| | 814 | if data: |
|---|
| | 815 | buffers.append(data) |
|---|
| | 816 | self._rbuf = "" |
|---|
| | 817 | while True: |
|---|
| | 818 | data = self._safe_call(True, self._sock.recv, self._rbufsize) |
|---|
| | 819 | if not data: |
|---|
| | 820 | break |
|---|
| | 821 | buffers.append(data) |
|---|
| | 822 | nl = data.find('\n') |
|---|
| | 823 | if nl >= 0: |
|---|
| | 824 | nl += 1 |
|---|
| | 825 | self._rbuf = data[nl:] |
|---|
| | 826 | buffers[-1] = data[:nl] |
|---|
| | 827 | break |
|---|
| | 828 | return "".join(buffers) |
|---|
| | 829 | else: |
|---|
| | 830 | # Read until size bytes or \n or EOF seen, whichever comes first |
|---|
| | 831 | nl = data.find('\n', 0, size) |
|---|
| | 832 | if nl >= 0: |
|---|
| | 833 | nl += 1 |
|---|
| | 834 | self._rbuf = data[nl:] |
|---|
| | 835 | return data[:nl] |
|---|
| | 836 | buf_len = len(data) |
|---|
| | 837 | if buf_len >= size: |
|---|
| | 838 | self._rbuf = data[size:] |
|---|
| | 839 | return data[:size] |
|---|
| | 840 | buffers = [] |
|---|
| | 841 | if data: |
|---|
| | 842 | buffers.append(data) |
|---|
| | 843 | self._rbuf = "" |
|---|
| | 844 | while True: |
|---|
| | 845 | data = self._safe_call(True, self._sock.recv, self._rbufsize) |
|---|
| | 846 | if not data: |
|---|
| | 847 | break |
|---|
| | 848 | buffers.append(data) |
|---|
| | 849 | left = size - buf_len |
|---|
| | 850 | nl = data.find('\n', 0, left) |
|---|
| | 851 | if nl >= 0: |
|---|
| | 852 | nl += 1 |
|---|
| | 853 | self._rbuf = data[nl:] |
|---|
| | 854 | buffers[-1] = data[:nl] |
|---|
| | 855 | break |
|---|
| | 856 | n = len(data) |
|---|
| | 857 | if n >= left: |
|---|
| | 858 | self._rbuf = data[left:] |
|---|
| | 859 | buffers[-1] = data[:left] |
|---|
| | 860 | break |
|---|
| | 861 | buf_len += n |
|---|
| | 862 | return "".join(buffers) |
|---|