Known issues in this document
I just changed my mind, it's probably better to make all 4 connections POSTs, and not have any GETs.
Forwarding arbitrary TCP ports is similar to PTTH, but with more moving parts. The client must run a PTTH proxy to translate between HTTP and TCP.
These programs are party to a connection:
- A TCP client (agnostic of PTTH)
- The PTTH client-side proxy
- The PTTH relay server
- The PTTH server-side proxy (integrated into ptth_server)
- A TCP server (agnostic of PTTH)
To establish a connection:
- The TCP server must be bound to a port.
- ptth_server GETs out to the relay.
- The user starts the PTTH proxy and it binds to a port.
- The user tells the TCP client to connect to the PTTH proxy.
- The PTTH proxy generates a connection ID and GET+POSTs out to the relay.
- The relay holds open that POST and returns ptth_server's GET.
- ptth_server connects to the TCP server.
- ptth_server GET+POSTs out to the relay.
Under any error condition, either the client-side proxy or ptth_server will notify the relay, and the relay will close the upstream and downstream. When the client proxy sees its streams close, it closes its connection to the TCP client. When ptth_server sees its streams close, it closes its connection to the TCP server.
The relay is also allowed to time out idle connections.
Client terminator's POV
- Bind to a TCP port on localhost
- Listen for a TCP connection
- Request to
- If it gives us an ID, take it and request to
- Accept the TCP connection
- Stream down and up until any of the 3 connections closes
Server terminator's POV
- Request to
- If it gives us an ID, connect to a TCP port on localhost
- If that succeeds, take the ID
upstream --> accept connect <-- downstream
When a client opens a connection, the relay will see these endpoints request / respond in this order:
- listen request
- connect request
- listen response
- accept request
- connect response (client downstream)
- accept response (server upstream)
- upstream request (client upstream)
- downstream request (server downstream)
The order is relaxed for steps 5 - 8. If any connection closes, the other 3 will close, but the specific order doesn't matter much.
All calls are authenticated by the server's key, so the relay always knows who the server is.
GET here to wait for clients. The relay will long-poll until a client connects, or until a timeout.
server_port is an opaque ID that somehow maps to a TCP port number. Only the server knows what it means.
- An opaque connection ID shared between you and the relay
POST here to accept an incoming connection. On success, the relay responds with 200 OK, and the response body is your half of the upstream.
POST here with your downstream as the request body.
All clients are authenticated by the client's key, so the relay always knows who the client is.
connect (server_name, opaque_server_port)
POST here to try connecting to a server. On success, the relay responds with 200 OK, and the response stream is your downstream. If the connection fails, the relay drops any bytes you've sent, and responds with an error code.
The response will include an opaque connection ID as a header. Pass this to 'upstream'.
POST here with your upstream as the request body. The server can only respond by closing the stream when the stream fails for any reason.