stdcopy.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package stdcopy
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "sync"
  9. )
  10. // StdType is the type of standard stream
  11. // a writer can multiplex to.
  12. type StdType byte
  13. const (
  14. // Stdin represents standard input stream type.
  15. Stdin StdType = iota
  16. // Stdout represents standard output stream type.
  17. Stdout
  18. // Stderr represents standard error steam type.
  19. Stderr
  20. stdWriterPrefixLen = 8
  21. stdWriterFdIndex = 0
  22. stdWriterSizeIndex = 4
  23. startingBufLen = 32*1024 + stdWriterPrefixLen + 1
  24. )
  25. var bufPool = &sync.Pool{New: func() interface{} { return bytes.NewBuffer(nil) }}
  26. // stdWriter is wrapper of io.Writer with extra customized info.
  27. type stdWriter struct {
  28. io.Writer
  29. prefix byte
  30. }
  31. // Write sends the buffer to the underneath writer.
  32. // It inserts the prefix header before the buffer,
  33. // so stdcopy.StdCopy knows where to multiplex the output.
  34. // It makes stdWriter to implement io.Writer.
  35. func (w *stdWriter) Write(p []byte) (n int, err error) {
  36. if w == nil || w.Writer == nil {
  37. return 0, errors.New("Writer not instantiated")
  38. }
  39. if p == nil {
  40. return 0, nil
  41. }
  42. header := [stdWriterPrefixLen]byte{stdWriterFdIndex: w.prefix}
  43. binary.BigEndian.PutUint32(header[stdWriterSizeIndex:], uint32(len(p)))
  44. buf := bufPool.Get().(*bytes.Buffer)
  45. buf.Write(header[:])
  46. buf.Write(p)
  47. n, err = w.Writer.Write(buf.Bytes())
  48. n -= stdWriterPrefixLen
  49. if n < 0 {
  50. n = 0
  51. }
  52. buf.Reset()
  53. bufPool.Put(buf)
  54. return
  55. }
  56. // NewStdWriter instantiates a new Writer.
  57. // Everything written to it will be encapsulated using a custom format,
  58. // and written to the underlying `w` stream.
  59. // This allows multiple write streams (e.g. stdout and stderr) to be muxed into a single connection.
  60. // `t` indicates the id of the stream to encapsulate.
  61. // It can be stdcopy.Stdin, stdcopy.Stdout, stdcopy.Stderr.
  62. func NewStdWriter(w io.Writer, t StdType) io.Writer {
  63. return &stdWriter{
  64. Writer: w,
  65. prefix: byte(t),
  66. }
  67. }
  68. // StdCopy is a modified version of io.Copy.
  69. //
  70. // StdCopy will demultiplex `src`, assuming that it contains two streams,
  71. // previously multiplexed together using a StdWriter instance.
  72. // As it reads from `src`, StdCopy will write to `dstout` and `dsterr`.
  73. //
  74. // StdCopy will read until it hits EOF on `src`. It will then return a nil error.
  75. // In other words: if `err` is non nil, it indicates a real underlying error.
  76. //
  77. // `written` will hold the total number of bytes written to `dstout` and `dsterr`.
  78. func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error) {
  79. var (
  80. buf = make([]byte, startingBufLen)
  81. bufLen = len(buf)
  82. nr, nw int
  83. er, ew error
  84. out io.Writer
  85. frameSize int
  86. )
  87. for {
  88. // Make sure we have at least a full header
  89. for nr < stdWriterPrefixLen {
  90. var nr2 int
  91. nr2, er = src.Read(buf[nr:])
  92. nr += nr2
  93. if er == io.EOF {
  94. if nr < stdWriterPrefixLen {
  95. return written, nil
  96. }
  97. break
  98. }
  99. if er != nil {
  100. return 0, er
  101. }
  102. }
  103. // Check the first byte to know where to write
  104. switch StdType(buf[stdWriterFdIndex]) {
  105. case Stdin:
  106. fallthrough
  107. case Stdout:
  108. // Write on stdout
  109. out = dstout
  110. case Stderr:
  111. // Write on stderr
  112. out = dsterr
  113. default:
  114. return 0, fmt.Errorf("Unrecognized input header: %d", buf[stdWriterFdIndex])
  115. }
  116. // Retrieve the size of the frame
  117. frameSize = int(binary.BigEndian.Uint32(buf[stdWriterSizeIndex : stdWriterSizeIndex+4]))
  118. // Check if the buffer is big enough to read the frame.
  119. // Extend it if necessary.
  120. if frameSize+stdWriterPrefixLen > bufLen {
  121. buf = append(buf, make([]byte, frameSize+stdWriterPrefixLen-bufLen+1)...)
  122. bufLen = len(buf)
  123. }
  124. // While the amount of bytes read is less than the size of the frame + header, we keep reading
  125. for nr < frameSize+stdWriterPrefixLen {
  126. var nr2 int
  127. nr2, er = src.Read(buf[nr:])
  128. nr += nr2
  129. if er == io.EOF {
  130. if nr < frameSize+stdWriterPrefixLen {
  131. return written, nil
  132. }
  133. break
  134. }
  135. if er != nil {
  136. return 0, er
  137. }
  138. }
  139. // Write the retrieved frame (without header)
  140. nw, ew = out.Write(buf[stdWriterPrefixLen : frameSize+stdWriterPrefixLen])
  141. if ew != nil {
  142. return 0, ew
  143. }
  144. // If the frame has not been fully written: error
  145. if nw != frameSize {
  146. return 0, io.ErrShortWrite
  147. }
  148. written += int64(nw)
  149. // Move the rest of the buffer to the beginning
  150. copy(buf, buf[frameSize+stdWriterPrefixLen:])
  151. // Move the index
  152. nr -= frameSize + stdWriterPrefixLen
  153. }
  154. }