def get_repl_header(conn, ex, mocker): req = mocker.spy(franz.miniclient.request, 'makeRequest') conn.addTriple(ex.s, ex.p, ex.o) # not implemented in Python 2... # req.assert_called() for _args, kwargs in req.call_args_list: headers = normalize_headers(kwargs.get('headers', {})) or {} return normalize_repl_header(headers.get('x-repl-settings'))
def test_commit_settings(conn, ex, mocker): req = mocker.spy(franz.miniclient.request, 'makeRequest') conn.addTriple(ex.s, ex.p, ex.o) conn.commit(durability='quorum') # Not available in Python 2 # req.assert_called() args, kwargs = req.call_args headers = normalize_headers(kwargs.get('headers', {})) or {} header = normalize_repl_header(headers.get('x-repl-settings')) assert header == 'durability=quorum'
def makeRequest(obj, method, url, body=None, accept=None, contentType=None, callback=None, errCallback=None, headers=None): """ Send an HTTP request to given URL. :param obj: A service object containing auth and config information. :type obj: franz.miniclient.repository.Service :param method: Request method ("GET", "POST", ...). :type method: string :param url: Target address :type url: string :param body: Request body (for PUT/POST requests) or query string, optional. :type body: basestring|file :param accept: Value of the accept header (default: */*) :type accept: string :param contentType: MIME type of the request body, optional. :type contentType: string :param callback: Function that will receive the response data. It will be called multiple times per request. The return value should be either None or the number of bytes received, anything else will cause the request to be aborted. :type callback: (bytestring) -> int :param errCallback: Invoked if the server returned an error. Used only if `callback` is not `None`. The arguments are the response code and the message returned by the server. Unlike normal callback, this is invoked at most once and receives the complete response body. :type errCallback: (int, string) -> None :param headers: Either a dictionary mapping headers to values or a list of strings that will be included in the request's headers. :type headers: Iterable[string] | dict[string, string] | None :return: Status code and response body, unless callback is specified (in that case None is returned). :rtype: (int, string) | None """ if accept is None: accept = "*/*" # We create a session object lazily, so we do not have any requests-specific stuff # in the implementation of the Service class. if obj.session is None: obj.session = create_session(obj) # Unfortunately our current API does not seem to have a good place # to close that explicitly. atexit.register(obj.session.close) # Encode data as utf-8 if required - requests tries to use ascii now. if isinstance(body, unicode_type): body = body.encode('utf-8') method = method.upper() if method in ('PUT', 'POST'): data = body params = None else: data = None params = body # Get the full url url = to_native_string(url) if not url.startswith("http:") and not url.startswith("https:"): url = to_native_string(obj.url) + to_native_string(url) # Note that this will create a copy if necessary, so we're not changing the argument headers = normalize_headers(headers) headers['accept'] = accept if contentType: headers['content-type'] = contentType if obj.runAsName: headers['x-masquerade-as-user'] = obj.runAsName response = obj.session.request(method, url, params=params, data=data, headers=headers, stream=True) with contextlib.closing(response): if callback is not None: if 200 <= response.status_code < 300: for chunk in response.iter_content(BUFFER_SIZE): callback_result = callback(chunk) # Simulate curl's behavior if callback_result is not None and callback_result != len( chunk): break else: if errCallback is None: response.raise_for_status() else: errCallback( response.status_code, to_native_string( response.raw.read(decode_content=True))) else: # Note: no error callback in this case return response.status_code, to_native_string(response.content)