github.com/pachyderm/pachyderm@v1.13.4/src/server/pkg/cert/logging_conn.go (about) 1 // logging_conn.go defines LoggingListener, an implementation of net.Listener 2 // that allows tests to read the text transported over a client-server 3 // connection, and confirm, in the case of a TLS connection, that nothing 4 // sensitive was transported in plain text. 5 6 package cert 7 8 import ( 9 "bytes" 10 "context" 11 "io" 12 "net" 13 "sync" 14 "time" 15 16 "github.com/pachyderm/pachyderm/src/client/pkg/errors" 17 ) 18 19 type loggingPipeAddr struct{} 20 21 func (o loggingPipeAddr) Network() string { 22 return "logging-pipe" 23 } 24 25 func (o loggingPipeAddr) String() string { 26 return "logging-pipe" 27 } 28 29 // loggingPipe is a struct containing two buffers, which log all of the traffic 30 // passing through LoggingPipe in each direction 31 type loggingPipe struct { 32 ClientToServerBuf, ServerToClientBuf bytes.Buffer 33 34 clientReader, serverReader io.Reader 35 clientWriter, serverWriter io.WriteCloser 36 } 37 38 // newLoggingPipe initializes a loggingPipe 39 func newLoggingPipe() *loggingPipe { 40 p := &loggingPipe{} 41 p.clientReader, p.clientWriter = io.Pipe() 42 p.clientReader = io.TeeReader(p.clientReader, &p.ServerToClientBuf) 43 p.serverReader, p.serverWriter = io.Pipe() 44 p.serverReader = io.TeeReader(p.serverReader, &p.ClientToServerBuf) 45 return p 46 } 47 48 // Close closes 'l' (no more reading/writing will be possible) 49 func (p *loggingPipe) Close() error { 50 p.clientWriter.Close() 51 p.serverWriter.Close() 52 return nil 53 } 54 55 // clientConn returns a loggingConn at the oposite end of the loggingPipe as 56 // serverConn. There is no fundamental difference between the clientConn and 57 // the serverConn, as communication is full duplex, but distinguishing the two 58 // ends of the pipe as client and server, rather than e.g. left and right, 59 // hopefully makes the calling code easier to read 60 func (p *loggingPipe) clientConn() *loggingConn { 61 return &loggingConn{ 62 pipe: p, 63 r: p.clientReader, 64 w: p.serverWriter, 65 } 66 } 67 68 // serverConn returns a loggingConn at the opposite end of the loggingPipe of 69 // clientConn (see the clientConn description for more information) 70 func (p *loggingPipe) serverConn() *loggingConn { 71 return &loggingConn{ 72 pipe: p, 73 r: p.serverReader, 74 w: p.clientWriter, 75 } 76 } 77 78 // loggingConn is an implementation of net.Conn that communicates with another 79 // party over a loggingPipe. 80 type loggingConn struct { 81 // pipe is the loggingPipe over which this connection is communicating 82 pipe *loggingPipe 83 r io.Reader 84 w io.WriteCloser 85 } 86 87 // Read implements the corresponding method of net.Conn 88 func (l *loggingConn) Read(b []byte) (n int, err error) { 89 return l.r.Read(b) 90 } 91 92 // Write implements the corresponding method of net.Conn 93 func (l *loggingConn) Write(b []byte) (n int, err error) { 94 return l.w.Write(b) 95 } 96 97 // Close implements the corresponding method of net.Conn 98 func (l *loggingConn) Close() error { 99 return l.pipe.Close() 100 } 101 102 // LocalAddr implements the corresponding method of net.Conn 103 func (l *loggingConn) LocalAddr() net.Addr { 104 return loggingPipeAddr{} 105 } 106 107 // RemoteAddr implements the corresponding method of net.Conn 108 func (l *loggingConn) RemoteAddr() net.Addr { 109 return loggingPipeAddr{} 110 } 111 112 // SetDeadline implements the corresponding method of net.Conn 113 func (l *loggingConn) SetDeadline(t time.Time) error { 114 return errors.New("not implemented") 115 } 116 117 // SetReadDeadline implements the corresponding method of net.Conn 118 func (l *loggingConn) SetReadDeadline(t time.Time) error { 119 return errors.New("not implemented") 120 } 121 122 // SetWriteDeadline implements the corresponding method of net.Conn 123 func (l *loggingConn) SetWriteDeadline(t time.Time) error { 124 return errors.New("not implemented") 125 } 126 127 // TestListener implements the net.Listener interface, returning loggingConns 128 type TestListener struct { 129 // conn is the first (and last) non-nil connection returned from a call to 130 // Accept() 131 conn *loggingConn 132 connMu sync.Mutex 133 134 // connCh provides connections (or nil) to Accept() 135 connCh chan net.Conn 136 } 137 138 // NewTestListener initializes and returns a new TestListener. To create 139 // a new connection that that this Listener will serve on, call Dial(). To see 140 // the logged communication over that connection's pipe, see ClientToServerLog 141 // and ServerToClientLog 142 func NewTestListener() *TestListener { 143 return &TestListener{ 144 connCh: make(chan net.Conn), 145 } 146 } 147 148 // Dial initializes a new connection and releases a blocked call to Accept() 149 func (l *TestListener) Dial(context.Context, string, string) (net.Conn, error) { 150 l.connMu.Lock() 151 defer l.connMu.Unlock() 152 if l.conn != nil { 153 return nil, errors.New("Dial() has already been called on this TestListener") 154 } 155 156 // Initialize logging pipe 157 p := newLoggingPipe() 158 l.conn = p.serverConn() 159 160 // send serverConn to Accept() and close l.connCh (so future callers to 161 // Accept() get nothing) 162 l.connCh <- p.serverConn() 163 close(l.connCh) 164 return p.clientConn(), nil 165 } 166 167 // ClientToServerLog returns the log of client -> server communication over the 168 // first (and only) connection spawned by this listener 169 func (l *TestListener) ClientToServerLog() []byte { 170 return l.conn.pipe.ClientToServerBuf.Bytes() 171 } 172 173 // ServerToClientLog the log of server -> client communication over the first 174 // (and only) connection spawned by this listener 175 func (l *TestListener) ServerToClientLog() []byte { 176 return l.conn.pipe.ServerToClientBuf.Bytes() 177 } 178 179 // Accept implements the corresponding method of net.Listener for 180 // TestListener 181 func (l *TestListener) Accept() (net.Conn, error) { 182 conn := <-l.connCh 183 if conn == nil { 184 return nil, errors.New("Accept() has already been called on this TestListener") 185 } 186 return conn, nil 187 } 188 189 // Close implements the corresponding method of net.Listener for 190 // TestListener. Any blocked Accept operations will be unblocked and return 191 // errors. 192 func (l *TestListener) Close() error { 193 l.connMu.Lock() 194 defer l.connMu.Unlock() 195 c := <-l.connCh 196 if c != nil { 197 close(l.connCh) 198 } 199 return nil 200 } 201 202 // Addr implements the corresponding method of net.Listener for 203 // TestListener 204 func (l *TestListener) Addr() net.Addr { 205 return loggingPipeAddr{} 206 }