Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

port/addr is local dir #51

Merged
merged 2 commits into from Jul 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Expand Up @@ -3,7 +3,11 @@ All notable changes to this project will be documented in this file.

This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased](https://github.com/alexdlaird/pyngrok/compare/4.1.3...HEAD)
## [Unreleased](https://github.com/alexdlaird/pyngrok/compare/4.1.4...HEAD)

## [4.1.4](https://github.com/alexdlaird/pyngrok/compare/4.1.3...4.1.4) - 2020-07-05
### Fixed
- Inconsistent support for a local directory (ex. `file:///`) being passed as `ngrok.connect()`'s `port`. This is valid, and `ngrok` will use its built-in fileserver for the tunnel.

## [4.1.3](https://github.com/alexdlaird/pyngrok/compare/4.1.2...4.1.3) - 2020-06-21
### Fixed
Expand Down
10 changes: 10 additions & 0 deletions docs/index.rst
Expand Up @@ -182,6 +182,16 @@ can be accomplished by using :code:`pyngrok` to open a :code:`tcp` tunnel to the
ngrok.connect(3306, "tcp", options={"remote_addr": "1.tcp.ngrok.io:12345"})


We can also serve up local directories via `ngrok's built-in fileserver <https://ngrok.com/docs#http-file-urls>`_.

.. code-block:: python

from pyngrok import ngrok

# Open a tunnel to a local file server
ngrok.connect("file:///")


Configuration
-------------

Expand Down
20 changes: 14 additions & 6 deletions pyngrok/ngrok.py
Expand Up @@ -17,6 +17,11 @@
from urllib.parse import urlencode
from urllib.request import urlopen, Request, HTTPError, URLError

try:
from urllib.parse import quote_plus
except ImportError: # pragma: no cover
from urllib import quote_plus

try:
from http import HTTPStatus as StatusCodes
except ImportError: # pragma: no cover
Expand All @@ -27,7 +32,7 @@

__author__ = "Alex Laird"
__copyright__ = "Copyright 2020, Alex Laird"
__version__ = "4.1.3"
__version__ = "4.1.4"

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -129,7 +134,7 @@ def get_ngrok_process(pyngrok_config=None):
return process.get_process(pyngrok_config)


def connect(port=80, proto="http", name=None, options=None, pyngrok_config=None):
def connect(port="80", proto="http", name=None, options=None, pyngrok_config=None):
"""
Establish a new :code:`ngrok` tunnel to the given port and protocol, returning the connected
public URL that tunnels to the local port.
Expand All @@ -140,8 +145,9 @@ def connect(port=80, proto="http", name=None, options=None, pyngrok_config=None)
If :code:`ngrok` is not running, calling this method will first start a process with
:class:`~pyngrok.conf.PyngrokConfig`.

:param port: The local port to which to tunnel, defaults to 80.
:type port: int, optional
:param port: The local port to which to tunnel, defaults to 80. Can also be
a `local directory <https://ngrok.com/docs#http-file-urls>`_.
:type port: str, optional
:param proto: The protocol to tunnel, defaults to "http".
:type proto: str, optional
:param name: A friendly name for the tunnel.
Expand All @@ -159,9 +165,11 @@ def connect(port=80, proto="http", name=None, options=None, pyngrok_config=None)
if pyngrok_config is None:
pyngrok_config = PyngrokConfig()

port = str(port)

config = {
"name": name if name else "{}-{}-{}".format(proto, port, uuid.uuid4()),
"addr": str(port),
"name": name if name else "{}-{}-{}".format(proto, quote_plus(port), uuid.uuid4()),
"addr": port,
"proto": proto
}
options.update(config)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -2,7 +2,7 @@

__author__ = "Alex Laird"
__copyright__ = "Copyright 2020, Alex Laird"
__version__ = "4.1.3"
__version__ = "4.1.4"

with open("README.md", "r") as f:
long_description = f.read()
Expand Down
36 changes: 35 additions & 1 deletion tests/test_ngrok.py
Expand Up @@ -22,7 +22,7 @@

__author__ = "Alex Laird"
__copyright__ = "Copyright 2020, Alex Laird"
__version__ = "4.0.1"
__version__ = "4.1.4"


class TestNgrok(NgrokTestCase):
Expand Down Expand Up @@ -259,3 +259,37 @@ def test_web_addr_false_not_allowed(self):
# WHEN
with self.assertRaises(PyngrokError):
ngrok.connect(pyngrok_config=self.pyngrok_config)

def test_connect_file(self):
if "NGROK_AUTHTOKEN" not in os.environ:
self.skipTest("NGROK_AUTHTOKEN environment variable not set")

# GIVEN
self.assertEqual(len(process._current_processes.keys()), 0)
pyngrok_config = PyngrokConfig(config_path=conf.DEFAULT_NGROK_CONFIG_PATH,
auth_token=os.environ["NGROK_AUTHTOKEN"])

# WHEN
url = ngrok.connect("file:///", pyngrok_config=pyngrok_config)
current_process = ngrok.get_ngrok_process()
time.sleep(1)
tunnels = ngrok.get_tunnels()

# THEN
self.assertEqual(len(tunnels), 2)
self.assertIsNotNone(current_process)
self.assertIsNone(current_process.proc.poll())
self.assertTrue(current_process._monitor_thread.is_alive())
self.assertIsNotNone(url)
self.assertIsNotNone(process.get_process(self.pyngrok_config))
self.assertIn('http://', url)
self.assertEqual(len(process._current_processes.keys()), 1)

# WHEN
ngrok.disconnect(url)
time.sleep(1)
tunnels = ngrok.get_tunnels()

# THEN
# There is still one tunnel left, as we only disconnected the http tunnel
self.assertEqual(len(tunnels), 1)