def handle_response(url, code, msg, data): """Interpret the code & headers and wrap the provided data in a RangeFile. This is a factory method which returns an appropriate RangeFile based on the code & headers it's given. :param url: The url being processed. Mostly for error reporting :param code: The integer HTTP response code :param msg: An HTTPMessage containing the headers for the response :param data: A file-like object that can be read() to get the requested data :return: A file-like object that can seek()+read() the ranges indicated by the headers. """ rfile = RangeFile(url, data) if code == 200: # A whole file size = msg.getheader('content-length', None) if size is None: size = -1 else: size = int(size) rfile.set_range(0, size) elif code == 206: content_type = msg.getheader('content-type', None) if content_type is None: # When there is no content-type header we treat the response as # being of type 'application/octet-stream' as per RFC2616 section # 7.2.1. # Therefore it is obviously not multipart content_type = 'application/octet-stream' is_multipart = False else: is_multipart = (msg.getmaintype() == 'multipart' and msg.getsubtype() == 'byteranges') if is_multipart: # Full fledged multipart response rfile.set_boundary(msg.getparam('boundary')) else: # A response to a range request, but not multipart content_range = msg.getheader('content-range', None) if content_range is None: raise errors.InvalidHttpResponse( url, 'Missing the Content-Range header in a 206 range response') rfile.set_range_from_header(content_range) else: raise errors.InvalidHttpResponse(url, 'Unknown response code %s' % code) return rfile
def _raise_curl_http_error(self, curl, info=None, body=None): """Common curl->bzrlib error translation. Some methods may choose to override this for particular cases. The URL and code are automatically included as appropriate. :param info: Extra information to include in the message. :param body: File-like object from which the body of the page can be read. """ code = curl.getinfo(pycurl.HTTP_CODE) url = curl.getinfo(pycurl.EFFECTIVE_URL) if body is not None: response_body = body.read() plaintext_body = unhtml_roughly(response_body) else: response_body = None plaintext_body = '' if code == 403: raise errors.TransportError( 'Server refuses to fulfill the request (403 Forbidden)' ' for %s: %s' % (url, plaintext_body)) else: if info is None: msg = '' else: msg = ': ' + info raise errors.InvalidHttpResponse( url, 'Unable to handle http code %d%s: %s' % (code, msg, plaintext_body))
def send_http_smart_request(self, bytes): try: # Get back the http_transport hold by the weak reference t = self._http_transport_ref() code, body_filelike = t._post(bytes) if code != 200: raise errors.InvalidHttpResponse( t._remote_path('.bzr/smart'), 'Expected 200 response code, got %r' % (code, )) except (errors.InvalidHttpResponse, errors.ConnectionReset), e: raise errors.SmartProtocolError(str(e))
def read_range_definition(self): """Read a new range definition in a multi parts message. Parse the headers including the empty line following them so that we are ready to read the data itself. """ self._headers = httplib.HTTPMessage(self._file, seekable=0) # Extract the range definition content_range = self._headers.getheader('content-range', None) if content_range is None: raise errors.InvalidHttpResponse( self._path, 'Content-Range header missing in a multi-part response') self.set_range_from_header(content_range)
def _raise_curl_http_error(self, curl, info=None): code = curl.getinfo(pycurl.HTTP_CODE) url = curl.getinfo(pycurl.EFFECTIVE_URL) # Some error codes can be handled the same way for all # requests if code == 403: raise errors.TransportError( 'Server refuses to fulfill the request (403 Forbidden)' ' for %s' % url) else: if info is None: msg = '' else: msg = ': ' + info raise errors.InvalidHttpResponse( url, 'Unable to handle http code %d%s' % (code, msg))
def read_boundary(self): """Read the boundary headers defining a new range""" boundary_line = '\r\n' while boundary_line == '\r\n': # RFC2616 19.2 Additional CRLFs may precede the first boundary # string entity. # To be on the safe side we allow it before any boundary line boundary_line = self._file.readline() if boundary_line != '--' + self._boundary + '\r\n': # rfc822.unquote() incorrectly unquotes strings enclosed in <> # IIS 6 and 7 incorrectly wrap boundary strings in <> # together they make a beautiful bug, which we will be gracious # about here if (self._unquote_boundary(boundary_line) != '--' + self._boundary + '\r\n'): raise errors.InvalidHttpResponse( self._path, "Expected a boundary (%s) line, got '%s'" % (self._boundary, boundary_line))