Ejemplo n.º 1
0
def _download_file(url, timeout, retries=0):
    try:
        if not url.lower().startswith('http'):
            raise PyngrokSecurityError(
                "URL must start with 'http': {}".format(url))

        logger.debug("Download ngrok from {} ...".format(url))

        local_filename = url.split("/")[-1]
        response = urlopen(url, timeout=timeout)

        status_code = response.getcode()
        logger.debug("Response status code: {}".format(status_code))

        if status_code != StatusCodes.OK:
            return None

        download_path = os.path.join(tempfile.gettempdir(), local_filename)

        with open(download_path, "wb") as f:
            shutil.copyfileobj(response, f)

        return download_path
    except socket.timeout as e:
        if retries < DEFAULT_RETRY_COUNT:
            time.sleep(0.5)

            return _download_file(url, timeout, retries + 1)
        else:
            raise e
Ejemplo n.º 2
0
    def healthy(self):
        if self.api_url is None or \
                not self._tunnel_started or not self._client_connected:
            return False

        if not self.api_url.lower().startswith('http'):
            raise PyngrokSecurityError("URL must start with 'http': {}".format(
                self.api_url))

        # Ensure the process is available for requests before registering it as healthy
        request = Request("{}/api/tunnels".format(self.api_url))
        response = urlopen(request)
        if response.getcode() != StatusCodes.OK:
            return False

        return self.proc.poll() is None and \
               self.startup_error is None
Ejemplo n.º 3
0
    def healthy(self):
        """
        Check whether the :code:`ngrok` process has finished starting up and is in a running, healthy state.

        :return: :code:`True` if the :code:`ngrok` process is started, running, and healthy, :code:`False` otherwise.
        :rtype: bool
        """
        if self.api_url is None or \
                not self._tunnel_started or not self._client_connected:
            return False

        if not self.api_url.lower().startswith("http"):
            raise PyngrokSecurityError("URL must start with \"http\": {}".format(self.api_url))

        # Ensure the process is available for requests before registering it as healthy
        request = Request("{}/api/tunnels".format(self.api_url))
        response = urlopen(request)
        if response.getcode() != StatusCodes.OK:
            return False

        return self.proc.poll() is None and \
               self.startup_error is None
Ejemplo n.º 4
0
def _download_file(url, retries=0, **kwargs):
    """
    Download a file to a temporary path and emit a status to stdout (if possible) as the download progresses.

    :param url: The URL to download.
    :type url: str
    :param retries: The number of retries to attempt, if download fails.
    :type retries: int, optional
    :param kwargs: Remaining ``kwargs`` will be passed to :py:func:`urllib.request.urlopen`.
    :type kwargs: dict, optional
    :return: The path to the downloaded temporary file.
    :rtype: str
    """
    kwargs["timeout"] = kwargs.get("timeout", DEFAULT_DOWNLOAD_TIMEOUT)

    if not url.lower().startswith("http"):
        raise PyngrokSecurityError("URL must start with \"http\": {}".format(url))

    try:
        _print_progress("Downloading ngrok ...")

        logger.debug("Download ngrok from {} ...".format(url))

        local_filename = url.split("/")[-1]
        response = urlopen(url, **kwargs, context=ctx)

        status_code = response.getcode()

        if status_code != HTTPStatus.OK:
            logger.debug("Response status code: {}".format(status_code))

            return None

        length = response.getheader("Content-Length")
        if length:
            length = int(length)
            chunk_size = max(4096, length // 100)
        else:
            chunk_size = 64 * 1024

        download_path = os.path.join(tempfile.gettempdir(), local_filename)
        with open(download_path, "wb") as f:
            size = 0
            while True:
                buffer = response.read(chunk_size)

                if not buffer:
                    break

                f.write(buffer)
                size += len(buffer)

                if length:
                    percent_done = int((float(size) / float(length)) * 100)
                    _print_progress("Downloading ngrok: {}%".format(percent_done))

        _clear_progress()

        return download_path
    except socket.timeout as e:
        if retries < DEFAULT_RETRY_COUNT:
            time.sleep(0.5)

            return _download_file(url, retries + 1, **kwargs)
        else:
            raise e
Ejemplo n.º 5
0
def api_request(url, method="GET", data=None, params=None, timeout=4):
    """
    Invoke an API request to the given URI, returning JSON data from the response as a dict.

    :param url: The request URL.
    :type url: str
    :param method: The HTTP method.
    :type method: str, optional
    :param data: The request body.
    :type data: dict, optional
    :param params: The URL parameters.
    :type params: list[str], optional
    :param timeout: The request timeout, in seconds.
    :type timeout: float, optional
    :return: The response from the request.
    :rtype: dict
    """
    if params is None:
        params = []

    if not url.lower().startswith("http"):
        raise PyngrokSecurityError(
            "URL must start with \"http\": {}".format(url))

    data = json.dumps(data).encode("utf-8") if data else None

    if params:
        url += "?{}".format(urlencode([(x, params[x]) for x in params]))

    request = Request(url, method=method.upper())
    request.add_header("Content-Type", "application/json")

    logger.debug("Making {} request to {} with data: {}".format(
        method, url, data))

    try:
        response = urlopen(request, data, timeout)
        response_data = response.read().decode("utf-8")

        status_code = response.getcode()
        logger.debug("Response status code: {}".format(status_code))
        logger.debug("Response: {}".format(response_data))

        if str(status_code)[0] != "2":
            raise PyngrokNgrokHTTPError(
                "ngrok client API returned {}: {}".format(
                    status_code, response_data), url, status_code, None,
                request.headers, response_data)
        elif status_code == StatusCodes.NO_CONTENT:
            return None

        return json.loads(response_data)
    except socket.timeout:
        raise PyngrokNgrokURLError(
            "ngrok client exception, URLError: timed out", "timed out")
    except HTTPError as e:
        response_data = e.read().decode("utf-8")

        status_code = e.getcode()
        logger.debug("Response status code: {}".format(status_code))
        logger.debug("Response: {}".format(response_data))

        raise PyngrokNgrokHTTPError(
            "ngrok client exception, API returned {}: {}".format(
                status_code, response_data), e.url, status_code, e.msg, e.hdrs,
            response_data)
    except URLError as e:
        raise PyngrokNgrokURLError(
            "ngrok client exception, URLError: {}".format(e.reason), e.reason)
Ejemplo n.º 6
0
def api_request(url, method="GET", data=None, params=None, timeout=4):
    """
    Invoke an API request to the given URL, returning JSON data from the response.

    One use for this method is making requests to ``ngrok`` tunnels:

    .. code-block:: python

        from pyngrok import ngrok

        public_url = ngrok.connect()
        response = ngrok.api_request("{}/some-route".format(public_url),
                                     method="POST", data={"foo": "bar"})

    Another is making requests to the ``ngrok`` API itself:

    .. code-block:: python

        from pyngrok import ngrok

        api_url = ngrok.get_ngrok_process().api_url
        response = ngrok.api_request("{}/api/requests/http".format(api_url),
                                     params={"tunnel_name": "foo"})

    :param url: The request URL.
    :type url: str
    :param method: The HTTP method.
    :type method: str, optional
    :param data: The request body.
    :type data: dict, optional
    :param params: The URL parameters.
    :type params: dict, optional
    :param timeout: The request timeout, in seconds.
    :type timeout: float, optional
    :return: The response from the request.
    :rtype: dict
    """
    if params is None:
        params = []

    if not url.lower().startswith("http"):
        raise PyngrokSecurityError(
            "URL must start with \"http\": {}".format(url))

    data = json.dumps(data).encode("utf-8") if data else None

    if params:
        url += "?{}".format(urlencode([(x, params[x]) for x in params]))

    request = Request(url, method=method.upper())
    request.add_header("Content-Type", "application/json")

    logger.debug("Making {} request to {} with data: {}".format(
        method, url, data))

    try:
        response = urlopen(request, data, timeout)
        response_data = response.read().decode("utf-8")

        status_code = response.getcode()
        logger.debug("Response {}: {}".format(status_code,
                                              response_data.strip()))

        if str(status_code)[0] != "2":
            raise PyngrokNgrokHTTPError(
                "ngrok client API returned {}: {}".format(
                    status_code, response_data), url, status_code, None,
                request.headers, response_data)
        elif status_code == HTTPStatus.NO_CONTENT:
            return None

        return json.loads(response_data)
    except socket.timeout:
        raise PyngrokNgrokURLError(
            "ngrok client exception, URLError: timed out", "timed out")
    except HTTPError as e:
        response_data = e.read().decode("utf-8")

        status_code = e.getcode()
        logger.debug("Response {}: {}".format(status_code,
                                              response_data.strip()))

        raise PyngrokNgrokHTTPError(
            "ngrok client exception, API returned {}: {}".format(
                status_code, response_data), e.url, status_code, e.msg, e.hdrs,
            response_data)
    except URLError as e:
        raise PyngrokNgrokURLError(
            "ngrok client exception, URLError: {}".format(e.reason), e.reason)