12 // StdType is the type of standard stream
13 // a writer can multiplex to.
17 // Stdin represents standard input stream type.
19 // Stdout represents standard output stream type.
21 // Stderr represents standard error steam type.
23 // Systemerr represents errors originating from the system that make it
24 // into the the multiplexed stream.
27 stdWriterPrefixLen = 8
29 stdWriterSizeIndex = 4
31 startingBufLen = 32*1024 + stdWriterPrefixLen + 1
34 var bufPool = &sync.Pool{New: func() interface{} { return bytes.NewBuffer(nil) }}
36 // stdWriter is wrapper of io.Writer with extra customized info.
37 type stdWriter struct {
42 // Write sends the buffer to the underneath writer.
43 // It inserts the prefix header before the buffer,
44 // so stdcopy.StdCopy knows where to multiplex the output.
45 // It makes stdWriter to implement io.Writer.
46 func (w *stdWriter) Write(p []byte) (n int, err error) {
47 if w == nil || w.Writer == nil {
48 return 0, errors.New("Writer not instantiated")
54 header := [stdWriterPrefixLen]byte{stdWriterFdIndex: w.prefix}
55 binary.BigEndian.PutUint32(header[stdWriterSizeIndex:], uint32(len(p)))
56 buf := bufPool.Get().(*bytes.Buffer)
60 n, err = w.Writer.Write(buf.Bytes())
61 n -= stdWriterPrefixLen
71 // NewStdWriter instantiates a new Writer.
72 // Everything written to it will be encapsulated using a custom format,
73 // and written to the underlying `w` stream.
74 // This allows multiple write streams (e.g. stdout and stderr) to be muxed into a single connection.
75 // `t` indicates the id of the stream to encapsulate.
76 // It can be stdcopy.Stdin, stdcopy.Stdout, stdcopy.Stderr.
77 func NewStdWriter(w io.Writer, t StdType) io.Writer {
84 // StdCopy is a modified version of io.Copy.
86 // StdCopy will demultiplex `src`, assuming that it contains two streams,
87 // previously multiplexed together using a StdWriter instance.
88 // As it reads from `src`, StdCopy will write to `dstout` and `dsterr`.
90 // StdCopy will read until it hits EOF on `src`. It will then return a nil error.
91 // In other words: if `err` is non nil, it indicates a real underlying error.
93 // `written` will hold the total number of bytes written to `dstout` and `dsterr`.
94 func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error) {
96 buf = make([]byte, startingBufLen)
105 // Make sure we have at least a full header
106 for nr < stdWriterPrefixLen {
108 nr2, er = src.Read(buf[nr:])
111 if nr < stdWriterPrefixLen {
121 stream := StdType(buf[stdWriterFdIndex])
122 // Check the first byte to know where to write
133 // If we're on Systemerr, we won't write anywhere.
134 // NB: if this code changes later, make sure you don't try to write
135 // to outstream if Systemerr is the stream
138 return 0, fmt.Errorf("Unrecognized input header: %d", buf[stdWriterFdIndex])
141 // Retrieve the size of the frame
142 frameSize = int(binary.BigEndian.Uint32(buf[stdWriterSizeIndex : stdWriterSizeIndex+4]))
144 // Check if the buffer is big enough to read the frame.
145 // Extend it if necessary.
146 if frameSize+stdWriterPrefixLen > bufLen {
147 buf = append(buf, make([]byte, frameSize+stdWriterPrefixLen-bufLen+1)...)
151 // While the amount of bytes read is less than the size of the frame + header, we keep reading
152 for nr < frameSize+stdWriterPrefixLen {
154 nr2, er = src.Read(buf[nr:])
157 if nr < frameSize+stdWriterPrefixLen {
167 // we might have an error from the source mixed up in our multiplexed
168 // stream. if we do, return it.
169 if stream == Systemerr {
170 return written, fmt.Errorf("error from daemon in stream: %s", string(buf[stdWriterPrefixLen:frameSize+stdWriterPrefixLen]))
173 // Write the retrieved frame (without header)
174 nw, ew = out.Write(buf[stdWriterPrefixLen : frameSize+stdWriterPrefixLen])
179 // If the frame has not been fully written: error
181 return 0, io.ErrShortWrite
185 // Move the rest of the buffer to the beginning
186 copy(buf, buf[frameSize+stdWriterPrefixLen:])
188 nr -= frameSize + stdWriterPrefixLen