The "tunneling" workflow (ngrok, ssh -R, etc.) is essential for exposing a local server to the internet.
Dozens if not hundreds of tools exist to perform this networking setup, which of course inspired me to make my own. In this post I will share the trick that that makes my 500-LOC h2tunnel a compelling entry into the tunneling hall of fame.
Problem: shared connection
When talking about tunnels, we often hear about encryption, but this is trivially solved by TLS.
However, once you have a secure TLS-encrypted tunnel, you face another problem.
Imagine two browser tabs simultaneously downloading two files through your tunnel. When the tunnel server receives a chunk of data, how does it know which browser tab to send it to? Typically parallel streams are identified by TCP connection, but in a tunneling scenario the connection is singular.
Solution: multiplexing
Multiplexing is a technique that uses a single connection to carry data for multiple simultaneous connections. It is achieved by letting simultaneous connections take turns sending chunks of data on the shared connection, each chunk wrapped in an envelope.
TCP over HTTP/2
Turns out that the HTTP/2 protocol provides multiplexing for free and Node.js provides HTTP/2 for free as part of its standard library. Can we combine TLS and HTTP/2 to create a functional tunnel?
The answer is yes, simply send an HTTP/2 POST request through the newly established tunnel. When received by the other side, an HTTP/2 session is established, which provides a multiplexing API called HTTP/2 streams.
Mapping TCP connections to HTTP/2 streams requires a lot of care, but with extensive testing I made sure it works correctly and even supports TCP edge cases like half-opened / half-closed connections.
Can you think of other uses for this technique?