ngrok / ngrok-python

Embed ngrok secure ingress into your Python apps with a single line of code.
https://ngrok.com
Apache License 2.0
103 stars 19 forks source link

Can't find a way to use static domain by Python #49

Closed BabakAmini closed 9 months ago

BabakAmini commented 9 months ago

Contrary to what has been said here, there is no ‍domain parameter in the ngrok.connect() method. The function declaration seems to be changed, so how can I use my static domain obtained from https://dashboard.ngrok.com/cloud-edge/domains?

russorat commented 9 months ago

hi @BabakAmini , thanks for the issue. I'm able to use the domain parameter in the example found here: https://github.com/ngrok/ngrok-python/blob/main/examples/ngrok-connect-full.py

do you mind pasting your code and the specific error you are seeing so that we can reproduce?

BabakAmini commented 9 months ago

Hi @russorat,

Thanks for the reply. I've seen the domain parameter is commented inside the source code, so I thought it was removed. Now I just added it to the connect method of ngrok inside a configuration method like the following:

def configure():
    from pyngrok import ngrok, conf
    import sys

    pyngrok_config = conf.PyngrokConfig(auth_token=config('NGROK_AUTH_TOKEN'))
    conf.set_default(pyngrok_config)
    public_url = ngrok.connect(port=8000, domain='xxx.ngrok-free.app').public_url
    logger.info(f'ngrok public url is: {public_url}')

if __name__ == '__main__':
    configure()
    uvicorn.run(api, port=8000, host='127.0.0.1')

After running the code everything is fine, ngrok is configured as expected and Uvicorn runs on the local host without any error. Then when I open my static domain in the browser for the first time I reach a page like the following:

01

And after clicking on the visit site button I get an internal server error. Here is the terminal output:

20:21:47.327 pyngrok.process.ngrok:process:108 t=2023-09-12T20:21:47+0330 lvl=warn msg="ngrok config file found at legacy location, move to XDG location" xdg_path=/root/.config/ngrok/ngrok.yml legacy_path=/root/.ngrok2/ngrok.yml
20:21:47.901 main:72 ngrok public url is: https://xxx.ngrok-free.app
INFO:     Started server process [480020]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     xxxx:xxxx:1:15c::2:0 - "GET / HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/home/babak/my_env/lib/python3.11/site-packages/starlette/middleware/base.py", line 78, in call_next
    message = await recv_stream.receive()
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/babak/my_env/lib/python3.11/site-packages/anyio/streams/memory.py", line 98, in receive
    return self.receive_nowait()
           ^^^^^^^^^^^^^^^^^^^^^
  File "/home/babak/my_env/lib/python3.11/site-packages/anyio/streams/memory.py", line 91, in receive_nowait
    raise EndOfStream
anyio.EndOfStream

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/babak/my_env/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 426, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/babak/my_env/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/babak/my_env/lib/python3.11/site-packages/fastapi/applications.py", line 289, in __call__
    await super().__call__(scope, receive, send)
  File "/home/babak/my_env/lib/python3.11/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/babak/my_env/lib/python3.11/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/home/babak/my_env/lib/python3.11/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/home/babak/my_env/lib/python3.11/site-packages/starlette/middleware/base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/babak/web/main.py", line 56, in add_process_time_header
    response = await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/babak/my_env/lib/python3.11/site-packages/starlette/middleware/base.py", line 84, in call_next
    raise app_exc
  File "/home/babak/my_env/lib/python3.11/site-packages/starlette/middleware/base.py", line 70, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/home/babak/my_env/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/home/babak/my_env/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/home/babak/my_env/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 20, in __call__
    raise e
  File "/home/babak/my_env/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 17, in __call__
    await self.app(scope, receive, send)
  File "/home/babak/my_env/lib/python3.11/site-packages/starlette/routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "/home/babak/my_env/lib/python3.11/site-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/home/babak/my_env/lib/python3.11/site-packages/starlette/routing.py", line 66, in app
    response = await func(request)
               ^^^^^^^^^^^^^^^^^^^
  File "/home/babak/my_env/lib/python3.11/site-packages/fastapi/routing.py", line 273, in app
    raw_response = await run_endpoint_function(
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/babak/my_env/lib/python3.11/site-packages/fastapi/routing.py", line 190, in run_endpoint_function
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The exception is raised from a middleware function like the following:

@api.middleware("http")
async def add_process_time_header(request: Request, call_next):
    response = await call_next(request)

I'm also wondering about that warning displayed in the first line of the above output.

bobzilladev commented 9 months ago

Hello @BabakAmini, it looks like the code here is using the community pyngrok library, not the one from this repo. For instance, this line is importing from pyngrok:

from pyngrok import ngrok, conf

If you want to use the library from this repo you can python -m pip install ngrok to install it, and then import ngrok to start using it in the code. This library has a similar connect call which can be made, but does so without running a separate agent process.

As far as the error, does it occur when hitting the service directly (not going through ngrok)? That looks like it may be unrelated to the ngrok connectivity.

BabakAmini commented 9 months ago

Hello @bobzilladev, sorry for the late reply. You are right, and I was using the wrong package. I've changed it to your suggested one, and now I have a new error:

failed to start tunnel', "Only Personal, Pro and Enterprise plans may use custom hostnames.\nFailed to bind the custom hostname 'xxx.ngrok-free.app' for an unauthenticated client.

Authentication is functioning correctly when the domain name is not used. However, the error mentioned above occurred when I included the only available domain in my free subscription account.

bobzilladev commented 9 months ago

Hello! That error with the text for an unauthenticated client makes it look like the authtoken is not being loaded, I suspect when the domain is not set it is running as unauthenticated rather than as a free authenticated account.

If using the connect function check that authtoken_from_env=True is set, and the authtoken from the ngrok dashboard is in the NGROK_AUTHTOKEN environment variable. Or alternatively you can set the authtoken directly with authtoken="<token>". We can look into adding some more logging around whether an authtoken was used or not for session creation.

BabakAmini commented 9 months ago

I'm already using authtoken="<token>" to connect:

import ngrok
from decouple import config

ngrok.connect(port=8000, auth_token=config('NGROK_AUTH_TOKEN'), domain='xxx.ngrok-free.app')
bobzilladev commented 9 months ago

Ah, the authtoken option doesn't have an underscore in the middle, so that property isn't being used there. The full list of available options can be referenced here: https://github.com/ngrok/ngrok-python#full-configuration

BabakAmini commented 9 months ago

Sorry for the misunderstanding. I changed it to authtoken and it's working now. Thank you for your assistance.

bobzilladev commented 9 months ago

Thank you for bringing this up! We'll update the logging here to make it more apparent what is going on. This feedback is really helpful, thanks again.

bobzilladev commented 9 months ago

I'll close this ticket out, feel free to open a new one if you run into any other issues!