github.com/noxiouz/docker@v0.7.3-0.20160629055221-3d231c78e8c5/pkg/proxy/tcp_proxy.go (about) 1 package proxy 2 3 import ( 4 "io" 5 "net" 6 "sync" 7 "syscall" 8 9 "github.com/Sirupsen/logrus" 10 ) 11 12 // TCPProxy is a proxy for TCP connections. It implements the Proxy interface to 13 // handle TCP traffic forwarding between the frontend and backend addresses. 14 type TCPProxy struct { 15 listener *net.TCPListener 16 frontendAddr *net.TCPAddr 17 backendAddr *net.TCPAddr 18 } 19 20 // NewTCPProxy creates a new TCPProxy. 21 func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) { 22 listener, err := net.ListenTCP("tcp", frontendAddr) 23 if err != nil { 24 return nil, err 25 } 26 // If the port in frontendAddr was 0 then ListenTCP will have a picked 27 // a port to listen on, hence the call to Addr to get that actual port: 28 return &TCPProxy{ 29 listener: listener, 30 frontendAddr: listener.Addr().(*net.TCPAddr), 31 backendAddr: backendAddr, 32 }, nil 33 } 34 35 func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) { 36 backend, err := net.DialTCP("tcp", nil, proxy.backendAddr) 37 if err != nil { 38 logrus.Printf("Can't forward traffic to backend tcp/%v: %s\n", proxy.backendAddr, err) 39 client.Close() 40 return 41 } 42 43 var wg sync.WaitGroup 44 var broker = func(to, from *net.TCPConn) { 45 if _, err := io.Copy(to, from); err != nil { 46 // If the socket we are writing to is shutdown with 47 // SHUT_WR, forward it to the other end of the pipe: 48 if err, ok := err.(*net.OpError); ok && err.Err == syscall.EPIPE { 49 from.CloseWrite() 50 } 51 } 52 to.CloseRead() 53 wg.Done() 54 } 55 56 wg.Add(2) 57 go broker(client, backend) 58 go broker(backend, client) 59 60 finish := make(chan struct{}) 61 go func() { 62 wg.Wait() 63 close(finish) 64 }() 65 66 select { 67 case <-quit: 68 case <-finish: 69 } 70 client.Close() 71 backend.Close() 72 <-finish 73 } 74 75 // Run starts forwarding the traffic using TCP. 76 func (proxy *TCPProxy) Run() { 77 quit := make(chan bool) 78 defer close(quit) 79 for { 80 client, err := proxy.listener.Accept() 81 if err != nil { 82 logrus.Printf("Stopping proxy on tcp/%v for tcp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err) 83 return 84 } 85 go proxy.clientLoop(client.(*net.TCPConn), quit) 86 } 87 } 88 89 // Close stops forwarding the traffic. 90 func (proxy *TCPProxy) Close() { proxy.listener.Close() } 91 92 // FrontendAddr returns the TCP address on which the proxy is listening. 93 func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr } 94 95 // BackendAddr returns the TCP proxied address. 96 func (proxy *TCPProxy) BackendAddr() net.Addr { return proxy.backendAddr }