Ejemplo n.º 1
0
 def encval(name, val):
     if val is None: pass
     elif isinstance(val, bool): enc(name, (val and "true") or "false")
     elif isinstance(val, int): encval(name, "%d" % val)
     elif isinstance(val, float): encval(name, "%g" % val)
     elif isinstance(val, list) or isinstance(val, tuple):
         for elt in val: encval(name, elt)
     elif isinstance(val, native_str):
         enc(name, val)
     elif isinstance(val, (old_str, unicode)):
         enc(name, to_native_string(val))
     else:
         enc(name, to_native_string(str(val)))
Ejemplo n.º 2
0
 def encval(name, val):
     if val is None: pass
     elif isinstance(val, bool): enc(name, (val and "true") or "false")
     elif isinstance(val, int): encval(name, "%d" % val)
     elif isinstance(val, float): encval(name, "%g" % val)
     elif isinstance(val, list) or isinstance(val, tuple):
         for elt in val: encval(name, elt)
     elif isinstance(val, native_str):
         enc(name, val)
     elif isinstance(val, (old_str, unicode)):
         enc(name, to_native_string(val))
     else:
         enc(name, to_native_string(str(val)))
Ejemplo n.º 3
0
def normalize_headers(headers):
    """
    Create a list of headers from:
       * A list of curl-style headers (return a copy)
       * None
       * a dictionary.

    The result is a list of curl-style headers.

    :param headers: List or dict of header (may also be None).
    :rtype headers: list[string] | dict[string, string] | None
    :return: A list of headers suitable for pycurl.
    :rtype: list[string]
    """
    if headers is None:
        result = []
    elif isinstance(headers, dict):
        result = ['%s: %s' % entry for entry in iteritems(headers)]
    else:
        result = headers[:]
    return [to_native_string(h) for h in result]
Ejemplo n.º 4
0
def normalize_headers(headers):
    """
    Create a list of headers from:
       * A list of curl-style headers (return a copy)
       * None
       * a dictionary.

    The result is a list of curl-style headers.

    :param headers: List or dict of header (may also be None).
    :rtype headers: list[string] | dict[string, string] | None
    :return: A list of headers suitable for pycurl.
    :rtype: list[string]
    """
    if headers is None:
        result = []
    elif isinstance(headers, dict):
        result = ['%s: %s' % entry for entry in iteritems(headers)]
    else:
        result = headers[:]
    return [to_native_string(h) for h in result]
Ejemplo n.º 5
0
 def enc(name, val):
     if buf.tell():
         buf.write('&')
     buf.write(quote(to_native_string(name)))
     buf.write("=")
     buf.write(quote(to_native_string(val)))
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
 def enc(name, val):
     if buf.tell():
         buf.write('&')
     buf.write(quote(to_native_string(name)))
     buf.write("=")
     buf.write(quote(to_native_string(val)))
Ejemplo n.º 8
0
def makeRequest(obj,
                method,
                url,
                body=None,
                accept=None,
                contentType=None,
                callback=None,
                errCallback=None,
                headers=None):
    """
    Send a request to the server.

    See :func:`franz.miniclient.backends.requests.makeRequest` for documentation.
    """
    curl = Pool.instance().get()

    # TODO: Just make this an option
    # Uncomment these 5 lines to see pycurl debug output
    ## def report(debug_type, debug_msg):
    ##     if debug_type != 3:
    ##         print "debug(%d): %s" % (debug_type, debug_msg)
    ##curl.setopt(pycurl.VERBOSE, 1)
    ##curl.setopt(pycurl.DEBUGFUNCTION, report)

    #curl.setopt(pycurl.TIMEOUT, 45)

    if accept is None:
        accept = "*/*"

    # Proxy support
    if obj.proxy is not None:
        curl.setopt(pycurl.PROXY, obj.proxy_host)
        curl.setopt(pycurl.PROXYPORT, obj.proxy_port)
        curl.setopt(pycurl.PROXYTYPE, _proxy_types[obj.proxy_type])
    else:
        # Unsetopt doesn't work. As usual.
        curl.setopt(pycurl.PROXY, '')

    if obj.user is not None and obj.password is not None:
        curl.setopt(
            pycurl.USERPWD, "%s:%s" %
            (to_native_string(obj.user), to_native_string(obj.password)))
        curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
    else:
        curl.unsetopt(pycurl.USERPWD)
    if obj.cainfo is not None:
        curl.setopt(pycurl.CAINFO, copt(obj.cainfo))
    if obj.sslcert is not None:
        curl.setopt(pycurl.SSLCERT, copt(obj.sslcert))
    if obj.verifyhost is not None:
        curl.setopt(pycurl.SSL_VERIFYHOST, obj.verifyhost)
    if obj.verifypeer is not None:
        curl.setopt(pycurl.SSL_VERIFYPEER, obj.verifypeer)

    url = to_native_string(url)
    if not url.startswith("http:") and not url.startswith("https:"):
        url = to_native_string(obj.url) + to_native_string(
            to_native_string(url))

    method = to_native_string(method)

    # Usually POST and UPLOAD correspond to POST and PUT requests respectively.
    # In our case we will override the method using CUSTOMREQUEST and the
    # settings here will tell us if we're uploading using a READFUNCTION
    # or posting a string with POSTFIELDS.
    curl.setopt(pycurl.POST, 0)
    curl.setopt(pycurl.UPLOAD, 0)
    if body:
        if method in ("POST", "PUT"):
            if isinstance(body, basestring):
                # String
                body = to_bytes(body)
                curl.setopt(pycurl.POSTFIELDS, body)
                curl.setopt(pycurl.POST, 1)
            else:
                # File - can be passed as READDATA, but not as POSTFIELDS.
                curl.setopt(pycurl.READDATA, body)
                curl.setopt(pycurl.UPLOAD, 1)
        else:
            contentType = None
            url = url + "?" + to_native_string(body)
    else:
        contentType = None

    curl.setopt(pycurl.CUSTOMREQUEST, method)
    curl.setopt(pycurl.URL, url)

    # The "Expect:" is there to suppress "Expect: 100-continue"
    # behavior that is the default in libcurl when posting large
    # bodies.
    headers = normalize_headers(headers)
    if headers is None:
        headers = []
    headers.extend([
        "Connection: keep-alive", "Accept: " + to_native_string(accept),
        "Expect:"
    ])
    if callback: headers.append("Connection: close")
    if contentType:
        headers.append("Content-Type: " + to_native_string(contentType))
    if obj.runAsName:
        headers.append("x-masquerade-as-user: "******"")  # which means 'any encoding that curl supports'

    if callback:
        status = [None]
        error = []

        # Called by curl for each header line.
        # The first line will contain the status code and that is
        # the only part we're interested in.
        def headerfunc(string):
            if status[0] is None:
                # Parse the status code if this is the first line.
                status[0] = int(unicode(string, 'utf-8').split(" ")[1])
            # return input length to indicate "no errors".
            return len(string)

        # Called by curl for each block of data received.
        # The argument will be a bytestring.
        def writefunc(string):
            if status[0] == 200: callback(string)
            else: error.append(unicode(string, 'utf-8'))

        curl.setopt(pycurl.WRITEFUNCTION, writefunc)
        curl.setopt(pycurl.HEADERFUNCTION, headerfunc)
        retrying_perform(curl)
        if status[0] != 200:
            errCallback(curl.getinfo(pycurl.RESPONSE_CODE), "".join(error))
    else:
        buf = io.BytesIO()
        curl.setopt(pycurl.WRITEFUNCTION, buf.write)
        retrying_perform(curl)
        response = to_native_string(buf.getvalue())
        buf.close()
        result = (curl.getinfo(pycurl.RESPONSE_CODE), response)
        Pool.instance().put(curl)
        return result
Ejemplo n.º 9
0
def makeRequest(obj, method, url, body=None, accept=None, contentType=None, callback=None, errCallback=None, headers=None):
    """
    Send a request to the server.

    See :func:`franz.miniclient.backends.requests.makeRequest` for documentation.
    """
    curl = Pool.instance().get()

    # TODO: Just make this an option
    # Uncomment these 5 lines to see pycurl debug output
    ## def report(debug_type, debug_msg):
    ##     if debug_type != 3:
    ##         print "debug(%d): %s" % (debug_type, debug_msg)
    ##curl.setopt(pycurl.VERBOSE, 1)
    ##curl.setopt(pycurl.DEBUGFUNCTION, report)

    #curl.setopt(pycurl.TIMEOUT, 45)

    if accept is None:
        accept = "*/*"

    # Proxy support
    if obj.proxy is not None:
        curl.setopt(pycurl.PROXY, obj.proxy_host)
        curl.setopt(pycurl.PROXYPORT, obj.proxy_port)
        curl.setopt(pycurl.PROXYTYPE, _proxy_types[obj.proxy_type])
    else:
        # Unsetopt doesn't work. As usual.
        curl.setopt(pycurl.PROXY, '')

    if obj.user is not None and obj.password is not None:
        curl.setopt(pycurl.USERPWD, "%s:%s" % (to_native_string(obj.user),
                                               to_native_string(obj.password)))
        curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC)
    else:
        curl.unsetopt(pycurl.USERPWD)
    if obj.cainfo is not None:
        curl.setopt(pycurl.CAINFO, copt(obj.cainfo))
    if obj.sslcert is not None:
        curl.setopt(pycurl.SSLCERT, copt(obj.sslcert))
    if obj.verifyhost is not None:
        curl.setopt(pycurl.SSL_VERIFYHOST, obj.verifyhost)
    if obj.verifypeer is not None:
        curl.setopt(pycurl.SSL_VERIFYPEER, obj.verifypeer)

    url = to_native_string(url)
    if not url.startswith("http:") and not url.startswith("https:"):
        url = to_native_string(obj.url) + to_native_string(to_native_string(url))

    method = to_native_string(method)

    # Usually POST and UPLOAD correspond to POST and PUT requests respectively.
    # In our case we will override the method using CUSTOMREQUEST and the
    # settings here will tell us if we're uploading using a READFUNCTION
    # or posting a string with POSTFIELDS.
    curl.setopt(pycurl.POST, 0)
    curl.setopt(pycurl.UPLOAD, 0)
    if body:
        if method in ("POST", "PUT"):
            if isinstance(body, basestring):
                # String
                body = to_bytes(body)
                curl.setopt(pycurl.POSTFIELDS, body)
                curl.setopt(pycurl.POST, 1)
            else:
                # File - can be passed as READDATA, but not as POSTFIELDS.
                curl.setopt(pycurl.READDATA, body)
                curl.setopt(pycurl.UPLOAD, 1)
        else:
            contentType = None
            url = url + "?" + to_native_string(body)
    else:
        contentType = None

    curl.setopt(pycurl.CUSTOMREQUEST, method)
    curl.setopt(pycurl.URL, url)

    # The "Expect:" is there to suppress "Expect: 100-continue"
    # behavior that is the default in libcurl when posting large
    # bodies.
    headers = normalize_headers(headers)
    if headers is None:
        headers = []
    headers.extend(["Connection: keep-alive", "Accept: " + to_native_string(accept), "Expect:"])
    if callback: headers.append("Connection: close")
    if contentType: headers.append("Content-Type: " + to_native_string(contentType))
    if obj.runAsName: headers.append("x-masquerade-as-user: "******"")  # which means 'any encoding that curl supports'

    if callback:
        status = [None]
        error = []

        # Called by curl for each header line.
        # The first line will contain the status code and that is
        # the only part we're interested in.
        def headerfunc(string):
            if status[0] is None:
                # Parse the status code if this is the first line.
                status[0] = int(unicode(string, 'utf-8').split(" ")[1])
            # return input length to indicate "no errors".
            return len(string)

        # Called by curl for each block of data received.
        # The argument will be a bytestring.
        def writefunc(string):
            if status[0] == 200: callback(string)
            else: error.append(unicode(string, 'utf-8'))

        curl.setopt(pycurl.WRITEFUNCTION, writefunc)
        curl.setopt(pycurl.HEADERFUNCTION, headerfunc)
        retrying_perform(curl)
        if status[0] != 200:
            errCallback(curl.getinfo(pycurl.RESPONSE_CODE), "".join(error))
    else:
        buf = io.BytesIO()
        curl.setopt(pycurl.WRITEFUNCTION, buf.write)
        retrying_perform(curl)
        response = to_native_string(buf.getvalue())
        buf.close()
        result = (curl.getinfo(pycurl.RESPONSE_CODE), response)
        Pool.instance().put(curl)
        return result