Example #1
0
def _create_request_handle(request, cookies, timeout):
    """
    Returns Curl object (easy handle) which is set up witc specified parameters.

    Request request -- request specification
    dict cookies -- cookies to add to request
    int timeot -- request timeout
    """
    # it is not possible to take this callback out of this function, because of
    # curl API
    def __debug_callback(data_type, debug_data):
        prefixes = {
            pycurl.DEBUG_TEXT: b"* ",
            pycurl.DEBUG_HEADER_IN: b"< ",
            pycurl.DEBUG_HEADER_OUT: b"> ",
            pycurl.DEBUG_DATA_IN: b"<< ",
            pycurl.DEBUG_DATA_OUT: b">> ",
        }
        if data_type in prefixes:
            debug_output.write(prefixes[data_type])
            debug_output.write(debug_data)
            if not debug_data.endswith(b"\n"):
                debug_output.write(b"\n")

    output = io.BytesIO()
    debug_output = io.BytesIO()
    cookies.update(request.cookies)
    handle = pycurl.Curl()
    handle.setopt(pycurl.PROTOCOLS, pycurl.PROTO_HTTPS)
    handle.setopt(pycurl.TIMEOUT, timeout)
    handle.setopt(pycurl.URL, request.url.encode("utf-8"))
    handle.setopt(pycurl.WRITEFUNCTION, output.write)
    handle.setopt(pycurl.VERBOSE, 1)
    handle.setopt(pycurl.DEBUGFUNCTION, __debug_callback)
    handle.setopt(pycurl.SSL_VERIFYHOST, 0)
    handle.setopt(pycurl.SSL_VERIFYPEER, 0)
    handle.setopt(pycurl.NOSIGNAL, 1) # required for multi-threading
    handle.setopt(pycurl.HTTPHEADER, ["Expect: "])
    if cookies:
        handle.setopt(
            pycurl.COOKIE, _dict_to_cookies(cookies).encode("utf-8")
        )
    if request.data:
        handle.setopt(
            pycurl.COPYPOSTFIELDS, request.data.encode("utf-8")
        )
    # add reference for request object and output bufers to handle, so later
    # we don't need to match these objects when they are returned from
    # pycurl after they've been processed
    # similar usage is in pycurl example:
    # https://github.com/pycurl/pycurl/blob/REL_7_19_0_3/examples/retriever-multi.py
    handle.request_obj = request
    handle.output_buffer = output
    handle.debug_buffer = debug_output
    return handle
Example #2
0
    def call_host(self, host, request, data, request_timeout=None):
        """
        Send a request to a host
        host host address
        request command to be run on the host
        data command parameters, encoded by format_data_* method
        request timeout float timeout for request, if not set object property
            will be used
        """
        def __debug_callback(data_type, debug_data):
            prefixes = {
                pycurl.DEBUG_TEXT: b"* ",
                pycurl.DEBUG_HEADER_IN: b"< ",
                pycurl.DEBUG_HEADER_OUT: b"> ",
                pycurl.DEBUG_DATA_IN: b"<< ",
                pycurl.DEBUG_DATA_OUT: b">> ",
            }
            if data_type in prefixes:
                debug_output.write(prefixes[data_type])
                debug_output.write(debug_data)
                if not debug_data.endswith(b"\n"):
                    debug_output.write(b"\n")

        output = io.BytesIO()
        debug_output = io.BytesIO()
        cookies = self.__prepare_cookies(host)
        timeout = (request_timeout
                   if request_timeout is not None else self.request_timeout)
        url = "https://{host}:2224/{request}".format(
            host=("[{0}]".format(host) if ":" in host else host),
            request=request)

        handler = pycurl.Curl()
        handler.setopt(pycurl.PROTOCOLS, pycurl.PROTO_HTTPS)
        handler.setopt(pycurl.TIMEOUT_MS, int(timeout * 1000))
        handler.setopt(pycurl.URL, url.encode("utf-8"))
        handler.setopt(pycurl.WRITEFUNCTION, output.write)
        handler.setopt(pycurl.VERBOSE, 1)
        handler.setopt(pycurl.DEBUGFUNCTION, __debug_callback)
        handler.setopt(pycurl.SSL_VERIFYHOST, 0)
        handler.setopt(pycurl.SSL_VERIFYPEER, 0)
        handler.setopt(pycurl.NOSIGNAL, 1)  # required for multi-threading
        handler.setopt(pycurl.HTTPHEADER, ["Expect: "])
        if cookies:
            handler.setopt(pycurl.COOKIE, ";".join(cookies).encode("utf-8"))
        if data:
            handler.setopt(pycurl.COPYPOSTFIELDS, data.encode("utf-8"))

        msg = "Sending HTTP Request to: {url}"
        if data:
            msg += "\n--Debug Input Start--\n{data}\n--Debug Input End--"
        self._logger.debug(msg.format(url=url, data=data))
        self._reporter.process(reports.node_communication_started(url, data))
        result_msg = (
            "Finished calling: {url}\nResponse Code: {code}" +
            "\n--Debug Response Start--\n{response}\n--Debug Response End--")

        try:
            handler.perform()
            response_data = output.getvalue().decode("utf-8")
            response_code = handler.getinfo(pycurl.RESPONSE_CODE)
            self._logger.debug(
                result_msg.format(url=url,
                                  code=response_code,
                                  response=response_data))
            self._reporter.process(
                reports.node_communication_finished(url, response_code,
                                                    response_data))
            if response_code == 400:
                # old pcsd protocol: error messages are commonly passed in plain
                # text in response body with HTTP code 400
                # we need to be backward compatible with that
                raise NodeCommandUnsuccessfulException(host, request,
                                                       response_data.rstrip())
            elif response_code == 401:
                raise NodeAuthenticationException(
                    host, request, "HTTP error: {0}".format(response_code))
            elif response_code == 403:
                raise NodePermissionDeniedException(
                    host, request, "HTTP error: {0}".format(response_code))
            elif response_code == 404:
                raise NodeUnsupportedCommandException(
                    host, request, "HTTP error: {0}".format(response_code))
            elif response_code >= 400:
                raise NodeCommunicationException(
                    host, request, "HTTP error: {0}".format(response_code))
            return response_data
        except pycurl.error as e:
            # In pycurl versions lower then 7.19.3 it is not possible to set
            # NOPROXY option. Therefore for the proper support of proxy settings
            # we have to use environment variables.
            if is_proxy_set(os.environ):
                self._logger.warning("Proxy is set")
                self._reporter.process(
                    reports.node_communication_proxy_is_set())
            errno, reason = e.args
            msg = "Unable to connect to {node} ({reason})"
            self._logger.debug(msg.format(node=host, reason=reason))
            self._reporter.process(
                reports.node_communication_not_connected(host, reason))
            if errno == pycurl.E_OPERATION_TIMEDOUT:
                raise NodeConnectionTimedOutException(host, request, reason)
            else:
                raise NodeConnectionException(host, request, reason)
        finally:
            debug_data = debug_output.getvalue().decode("utf-8", "ignore")
            self._logger.debug(
                ("Communication debug info for calling: {url}\n"
                 "--Debug Communication Info Start--\n"
                 "{data}\n"
                 "--Debug Communication Info End--").format(url=url,
                                                            data=debug_data))
            self._reporter.process(
                reports.node_communication_debug_info(url, debug_data))