Cause JupyterHub to redirect to users Notebook server NOT proxy to it

I exploring a set up using third parties managed Jupyter Notebook service. I’m keen to still present this to our users through JupyterHub.

The API on the Notebook server means I can easily create a spawner that can start, stop and poll the Notebook service. However, once the Notebook server has started I want to redirect to a URL NOT proxy to it.

I understand this isn’t how Jupyter Hub was designed (I don’t think) but any ideas how to do this? The only idea I have so far is to create a service that the Hub can proxy to that just then redirects the user to this new url.

I cant proxy to the Notebook services as it manages it’s own auth, https, etc.

Incase it helps anyone the current solution we’ve implemented is below. It’s a class that runs a simple server with one action, take a requests and send a 302 redirect to the new URL.

At the end of the start method on our custom spawner we have

            self.redirect_server = redirector.RedirectServer(url)
            return self.redirect_server.route

The redirect server looks like this:

import time
import http.server
import socketserver
import multiprocessing

def redirect_handler_factory(url):
    Returns a request handler class that redirects to supplied `url`
    class RedirectHandler(http.server.SimpleHTTPRequestHandler):
        def do_GET(self):
            self.send_header('Location', url)

    return RedirectHandler

def _create_server(url, port):
    port = port
    with socketserver.TCPServer(("", port), redirect_handler_factory(url)) as httpd:
        print("serving at port", port)

class RedirectServer:
    _start_port = 9001
    _redirects = {}

    def get_existing_redirect(cls, url):
        port = cls._redirects.get('port')
        return port if ("", port) else None

    def _get_free_port(cls):
        port = cls._start_port
        taken = list(cls._redirects.values())
        while port in taken:
            port += 1
        return port

    def __init__(self, redirect_to_url):
        self.url = redirect_to_url

    def start(self):

        self.port = RedirectServer._get_free_port()
        self.server_process = multiprocessing.Process(target=_create_server, args=[self.url, self.port], daemon=True)
        RedirectServer._redirects[self.url] = self.port

    def stop(self):
            del RedirectServer._redirects[self.url]

        except Exception as e:
            raise e

    def route(self):
        return ("", self.port)

@yuvipanda Hi - any chance you could wade in with an opinion? We are stuck.

Hi @tam203, @pierocor and @nbarlowATI!

I’m still not so understanding what have caused this properly, but I think it may be resolved by this PR: