2020-12-09 21:46:53 +00:00
|
|
|
package ingress
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"github.com/cloudflare/cloudflared/websocket"
|
|
|
|
gws "github.com/gorilla/websocket"
|
|
|
|
)
|
|
|
|
|
|
|
|
// OriginConnection is a way to stream to a service running on the user's origin.
|
|
|
|
// Different concrete implementations will stream different protocols as long as they are io.ReadWriters.
|
|
|
|
type OriginConnection interface {
|
|
|
|
// Stream should generally be implemented as a bidirectional io.Copy.
|
|
|
|
Stream(tunnelConn io.ReadWriter)
|
|
|
|
Close()
|
|
|
|
}
|
|
|
|
|
2021-01-11 19:59:45 +00:00
|
|
|
type streamHandlerFunc func(originConn io.ReadWriter, remoteConn net.Conn)
|
|
|
|
|
|
|
|
// Stream copies copy data to & from provided io.ReadWriters.
|
|
|
|
func Stream(conn, backendConn io.ReadWriter) {
|
|
|
|
proxyDone := make(chan struct{}, 2)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
io.Copy(conn, backendConn)
|
|
|
|
proxyDone <- struct{}{}
|
|
|
|
}()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
io.Copy(backendConn, conn)
|
|
|
|
proxyDone <- struct{}{}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// If one side is done, we are done.
|
|
|
|
<-proxyDone
|
|
|
|
}
|
|
|
|
|
|
|
|
// DefaultStreamHandler is an implementation of streamHandlerFunc that
|
|
|
|
// performs a two way io.Copy between originConn and remoteConn.
|
|
|
|
func DefaultStreamHandler(originConn io.ReadWriter, remoteConn net.Conn) {
|
|
|
|
Stream(originConn, remoteConn)
|
|
|
|
}
|
|
|
|
|
2020-12-09 21:46:53 +00:00
|
|
|
// tcpConnection is an OriginConnection that directly streams to raw TCP.
|
|
|
|
type tcpConnection struct {
|
|
|
|
conn net.Conn
|
2021-01-11 19:59:45 +00:00
|
|
|
streamHandler streamHandlerFunc
|
2020-12-09 21:46:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (tc *tcpConnection) Stream(tunnelConn io.ReadWriter) {
|
|
|
|
tc.streamHandler(tunnelConn, tc.conn)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tc *tcpConnection) Close() {
|
|
|
|
tc.conn.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// wsConnection is an OriginConnection that streams to TCP packets by encapsulating them in Websockets.
|
|
|
|
// TODO: TUN-3710 Remove wsConnection and have helloworld service reuse tcpConnection like bridgeService does.
|
|
|
|
type wsConnection struct {
|
|
|
|
wsConn *gws.Conn
|
|
|
|
resp *http.Response
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wsc *wsConnection) Stream(tunnelConn io.ReadWriter) {
|
2021-01-11 19:59:45 +00:00
|
|
|
Stream(tunnelConn, wsc.wsConn.UnderlyingConn())
|
2020-12-09 21:46:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (wsc *wsConnection) Close() {
|
|
|
|
wsc.resp.Body.Close()
|
|
|
|
wsc.wsConn.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func newWSConnection(transport *http.Transport, r *http.Request) (OriginConnection, error) {
|
|
|
|
d := &gws.Dialer{
|
|
|
|
TLSClientConfig: transport.TLSClientConfig,
|
|
|
|
}
|
|
|
|
wsConn, resp, err := websocket.ClientConnect(r, d)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &wsConnection{
|
|
|
|
wsConn,
|
|
|
|
resp,
|
|
|
|
}, nil
|
|
|
|
}
|