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
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
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
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
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)
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)