package proxy import ( "encoding/json" "io" "net" "github.com/hashicorp/yamux" log "github.com/sirupsen/logrus" ) type HelloClient struct { Version string Data string } type HelloServer struct { Version string SessionID string PublicURL string Data string } type proxyConnection struct { muxSession *yamux.Session backConnAddress string SessionID string PublicURL string } func NewProxyConnection(backConnAddrr, proxyAddr string) (*proxyConnection, error) { conn, err := net.Dial("tcp", proxyAddr) if err != nil { return nil, err } // C -> S: HelloCLient // S -> C: HelloServer (sesionID) je := json.NewEncoder(conn) helloC := HelloClient{ Version: "1", Data: "-", } err = je.Encode(helloC) if err != nil { return nil, err } jd := json.NewDecoder(conn) var helloS HelloServer err = jd.Decode(&helloS) if err != nil { return nil, err } log.Debugf("Got from the ReverseProxy: version=%s, sessionID=%s", helloS.Version, helloS.SessionID) session, err := yamux.Server(conn, nil) return &proxyConnection{ muxSession: session, backConnAddress: backConnAddrr, SessionID: helloS.SessionID, PublicURL: helloS.PublicURL, }, nil } func (p *proxyConnection) RunProxy() { for { conn, err := p.muxSession.Accept() if err != nil { log.Errorf("Client: Accept returned %s. Done with this client.\n", err.Error()) return } go func() { dst, err := net.Dial("tcp", p.backConnAddress) defer dst.Close() defer conn.Close() if err != nil { log.Errorf("Client: Can't connect to the target HTTP server: %s\n", err.Error()) } glueConnAndWait(dst, conn) }() } } func (p *proxyConnection) Stop() { p.muxSession.Close() } func errToString(err error) string { if err != nil { return err.Error() } return "nil" } func glueConnAndWait(conn1, conn2 net.Conn) error { errChan := make(chan error, 2) log.Debugf("Starting the glue of the two conn %s %s", conn1.LocalAddr().String(), conn2.LocalAddr().String()) copyAndNotify := func(dst, src net.Conn) { n, err := io.Copy(dst, src) log.Debugf("Wrote %d bytes, %s -> %s\n", n, src.LocalAddr().String(), dst.LocalAddr().String()) if err != nil { log.Debugf(" -- ended with error: %s\n", err.Error()) } errChan <- err } go copyAndNotify(conn1, conn2) go copyAndNotify(conn2, conn1) err1 := <-errChan err2 := <-errChan log.Debugf("Finished the glued connections with: %s and %s", errToString(err1), errToString(err2)) return err1 }