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)
            self.redirect_server.start()
            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_response(302)
            self.send_header('Location', url)
            self.end_headers()

    return RedirectHandler


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


class RedirectServer:
    _start_port = 9001
    _redirects = {}

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

    @classmethod
    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
        super().__init__()

    def start(self):

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

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

        except Exception as e:
            print(e)
            raise e
        pass

    @property
    def route(self):
        return ("0.0.0.0", 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: