8 "github.com/fsouza/go-dockerclient/external/github.com/Sirupsen/logrus"
12 stdWriterPrefixLen = 8
14 stdWriterSizeIndex = 4
16 startingBufLen = 32*1024 + stdWriterPrefixLen + 1
19 // StdType prefixes type and length to standard stream.
20 type StdType [stdWriterPrefixLen]byte
23 // Stdin represents standard input stream type.
25 // Stdout represents standard output stream type.
26 Stdout = StdType{0: 1}
27 // Stderr represents standard error steam type.
28 Stderr = StdType{0: 2}
31 // StdWriter is wrapper of io.Writer with extra customized info.
32 type StdWriter struct {
38 func (w *StdWriter) Write(buf []byte) (n int, err error) {
40 if w == nil || w.Writer == nil {
41 return 0, errors.New("Writer not instantiated")
43 binary.BigEndian.PutUint32(w.prefix[4:], uint32(len(buf)))
44 n1, err = w.Writer.Write(w.prefix[:])
46 n = n1 - stdWriterPrefixLen
48 n2, err = w.Writer.Write(buf)
49 n = n1 + n2 - stdWriterPrefixLen
57 // NewStdWriter instantiates a new Writer.
58 // Everything written to it will be encapsulated using a custom format,
59 // and written to the underlying `w` stream.
60 // This allows multiple write streams (e.g. stdout and stderr) to be muxed into a single connection.
61 // `t` indicates the id of the stream to encapsulate.
62 // It can be stdcopy.Stdin, stdcopy.Stdout, stdcopy.Stderr.
63 func NewStdWriter(w io.Writer, t StdType) *StdWriter {
67 sizeBuf: make([]byte, 4),
71 var errInvalidStdHeader = errors.New("Unrecognized input header")
73 // StdCopy is a modified version of io.Copy.
75 // StdCopy will demultiplex `src`, assuming that it contains two streams,
76 // previously multiplexed together using a StdWriter instance.
77 // As it reads from `src`, StdCopy will write to `dstout` and `dsterr`.
79 // StdCopy will read until it hits EOF on `src`. It will then return a nil error.
80 // In other words: if `err` is non nil, it indicates a real underlying error.
82 // `written` will hold the total number of bytes written to `dstout` and `dsterr`.
83 func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error) {
85 buf = make([]byte, startingBufLen)
94 // Make sure we have at least a full header
95 for nr < stdWriterPrefixLen {
97 nr2, er = src.Read(buf[nr:])
100 if nr < stdWriterPrefixLen {
101 logrus.Debugf("Corrupted prefix: %v", buf[:nr])
107 logrus.Debugf("Error reading header: %s", er)
112 // Check the first byte to know where to write
113 switch buf[stdWriterFdIndex] {
123 logrus.Debugf("Error selecting output fd: (%d)", buf[stdWriterFdIndex])
124 return 0, errInvalidStdHeader
127 // Retrieve the size of the frame
128 frameSize = int(binary.BigEndian.Uint32(buf[stdWriterSizeIndex : stdWriterSizeIndex+4]))
129 logrus.Debugf("framesize: %d", frameSize)
131 // Check if the buffer is big enough to read the frame.
132 // Extend it if necessary.
133 if frameSize+stdWriterPrefixLen > bufLen {
134 logrus.Debugf("Extending buffer cap by %d (was %d)", frameSize+stdWriterPrefixLen-bufLen+1, len(buf))
135 buf = append(buf, make([]byte, frameSize+stdWriterPrefixLen-bufLen+1)...)
139 // While the amount of bytes read is less than the size of the frame + header, we keep reading
140 for nr < frameSize+stdWriterPrefixLen {
142 nr2, er = src.Read(buf[nr:])
145 if nr < frameSize+stdWriterPrefixLen {
146 logrus.Debugf("Corrupted frame: %v", buf[stdWriterPrefixLen:nr])
152 logrus.Debugf("Error reading frame: %s", er)
157 // Write the retrieved frame (without header)
158 nw, ew = out.Write(buf[stdWriterPrefixLen : frameSize+stdWriterPrefixLen])
160 logrus.Debugf("Error writing frame: %s", ew)
163 // If the frame has not been fully written: error
165 logrus.Debugf("Error Short Write: (%d on %d)", nw, frameSize)
166 return 0, io.ErrShortWrite
170 // Move the rest of the buffer to the beginning
171 copy(buf, buf[frameSize+stdWriterPrefixLen:])
173 nr -= frameSize + stdWriterPrefixLen