Skip to content

Commit

Permalink
port/addr is local dir (#51)
Browse files Browse the repository at this point in the history
- Address issue #50, where addr (pyngrok's "port" var in connect, which perhaps should be renamed in a future breaking release to "addr", since it doesn't HAVE to be a port) can actually be a local directory (ex. "file:///") and ngrok will serve up a fileserver. Added test.
- Preparing for 4.1.4 release.
- Updating documentation
  • Loading branch information
alexdlaird committed Jul 6, 2020
1 parent f6eafc9 commit af78c5d
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 9 deletions.
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)

0 comments on commit af78c5d

Please sign in to comment.