5.4 KiB
Specification
Naming conventions
server
is listening to public connection and is responsible of routing public HTTP requests to clients.client
is a long running process, connected to a server and running on a local machine.virtualHost
is a virtual domain that maps a domain to a single client. i.e:arslan.koding.io
is a virtualhost which is mapped to myclient
running on my local machine.identifier
is a secret token, which is not meant to be shared with others. An identifier is responsible of mapping a virtualhost to a client.session
is a single TCP connection which uses the libraryyamux
. A session can be created either viayamux.Server()
oryamux.Client
stream
is anet.Conn
compatiblevirtual
connection that is multiplexed over thesession
. A session can have hundreds of thousands streamscontrol connection
is a singlestream
which is used to communicate and handle messaging between server and client. It uses a custom protocol which is JSON encoded.tunnel connection
is a singlestream
which is used to proxy public HTTP requests from theserver
to theclient
and vice versa. A singletunnel
connection is created for every single HTTP requests.public connection
is a connection from a remote machine to theserver
ControlHandler
is a http.Handler which listens to requests coming to/_controlPath_/
. It's used to setup the initialsession
connection fromclient
toserver
. And creates thecontrol connection
from this session. server and client, and also for all additional new tunnel. It literally captures the incoming HTTP request and hijacks it and converts it into RAW TCP, which then is used as the foundation for all yamuxsessions.
Server
- Server is created with
NewServer()
which returns*Server
, ahttp.Handler
compatible type. Plug into any HTTP server you want. The root path"/"
is recommended to listen and proxy any tunnels. It also listens to any request coming toControlHandler
- Tunneling is based on virtual hosts. A virtual hosts is identified with an unique identifier. This identifier is the only piece that both client and server needs to known ahead. Think of it as a secret token.
- To add a virtual host, call
server.AddHost(virtualHost, identifier)
. This step needs to be done from the server itself. This can be could manually or via custom auth based HTTP handlers, such as "/addhost", which adds virtualhosts and returns theidentifier
to the requester (in our caseclient
) - A DNS record and it's subdomains needs to point to a
server
, so it can handle virtual hosts, i.e:*.example.com
is routed to a server, which can handlefoo.example.com
,bar.example.com
, etc..
Client
- Client is created with
NewClient(serverAddr, localAddr)
which returns a*Client
. HereserverAddr
is the TCP address to the server.localAddr
is the server in which all public requests are forwarded to. It's optional if you want it to be done dynamically - Once a client is created, it starts with
client.Start(identifier)
. Hereidentifier
is needed upfront. This method creates the initial TCP connection to the server. It sends the identifier back to the server. This TCP connection is used as the foundation foryamux.Client()
. Once a yamux session is established, we are able to use this single connection to have multiple streams, which are multiplexed over this one connection. Acontrol connection
is created and client starts to listen it.client.Start
is blocking.
Control Handshake
- Client sends a
handshakeRequest
over thecontrol connection
stream - The server sends back a
handshakeResponse
to the client over thecontrol connection
stream - Once the client receives the
handshakeResponse
from the server, it starts to listen from thecontrol connection
stream. - A
control connection
is json.Encoder/Decoder both for server and client
Tunnel creation
-
When the server receives a public connection, it checks the HTTP host headers and retrieves the corresponding identifier from the given host.
-
The server retrieves the
control connection
which was associated with thisidentifier
and sends aControlMsg
message with the actionRequestClientSession
. This message is in the form of:type ControlMsg struct { Action Action `json:"action"` Protocol TransportProtocol `json:"transportProtocol"` LocalPort string `json:"localPort"` }
Here the
LocalPort
is read from the HTTP Host header. If absent a zero port is sent and client maps it to the local server running at port 80, unless thelocalAddr
is specified inclient.Start()
method.Protocol
is reserved for future features. -
The server immediately starts to listen(accept) to a new
stream
. This is blocking and it waits there. -
When the client receives the
RequestClientSession
message, it opens a newvirtual
TCP connection, astream
to the server. -
The server which was waiting for a new stream in step 3, establish the stream.
-
The server copies the request over the stream to the client.
-
The client copies the request coming from the server to the local server and copies back the result to the server
-
The server reads the response coming from the client and returns back it to the public connection requester