Merge pull request #50612 from thaJeztah/stdcopy_clean

apk/pkg/stdcopy: remove use of `iota`, improve docs, and add example
This commit is contained in:
Sebastiaan van Stijn
2025-08-04 19:44:22 +02:00
committed by GitHub
3 changed files with 120 additions and 30 deletions

View File

@@ -14,16 +14,13 @@ import (
type StdType byte
const (
// Stdin represents standard input stream type.
Stdin StdType = iota
// Stdout represents standard output stream type.
Stdout
// Stderr represents standard error steam type.
Stderr
// Systemerr represents errors originating from the system that make it
// into the multiplexed stream.
Systemerr
Stdin StdType = 0 // Stdin represents standard input stream. It is present for completeness and should NOT be used. When reading the stream with [StdCopy] it is output on [Stdout].
Stdout StdType = 1 // Stdout represents standard output stream.
Stderr StdType = 2 // Stderr represents standard error steam.
Systemerr StdType = 3 // Systemerr represents errors originating from the system. When reading the stream with [StdCopy] it is returned as an error.
)
const (
stdWriterPrefixLen = 8
stdWriterFdIndex = 0
stdWriterSizeIndex = 4
@@ -75,9 +72,20 @@ func (w *stdWriter) Write(p []byte) (int, error) {
// stream "w".
//
// Writers created through NewStdWriter allow for multiple write streams
// (e.g. stdout ([Stdout]) and stderr ([Stderr]) to be multiplexed into a
// (e.g., stdout ([Stdout]) and stderr ([Stderr]) to be multiplexed into a
// single connection. "streamType" indicates the type of stream to encapsulate,
// and can be [Stdin], [Stdout], pr [Stderr].
// commonly, [Stdout] or [Stderr]. The [Systemerr] stream can be used to
// include server-side errors in the stream. Information on this stream
// is returned as an error by [StdCopy] and terminates processing the
// stream.
//
// The [Stdin] stream is present for completeness and should generally
// NOT be used. It is output on [Stdout] when reading the stream with
// [StdCopy].
//
// All streams must share the same underlying [io.Writer] to ensure proper
// multiplexing. Each call to NewStdWriter wraps that shared writer with
// a header indicating the target stream.
func NewStdWriter(w io.Writer, streamType StdType) io.Writer {
return &stdWriter{
Writer: w,
@@ -94,11 +102,15 @@ func NewStdWriter(w io.Writer, streamType StdType) io.Writer {
// [NewStdWriter].
//
// As it reads from "multiplexedSource", StdCopy writes [Stdout] messages
// to "destOut", and [Stderr] message to "destErr].
// to "destOut", and [Stderr] message to "destErr]. For backward-compatibility,
// [Stdin] messages are output to "destOut". The [Systemerr] stream provides
// errors produced by the daemon. It is returned as an error, and terminates
// processing the stream.
//
// StdCopy it reads until it hits [io.EOF] on "multiplexedSource", after
// which it returns a nil error. In other words: any error returned indicates
// a real underlying error.
// a real underlying error, which may be when an unknown [StdType] stream
// is received.
//
// The "written" return holds the total number of bytes written to "destOut"
// and "destErr" combined.
@@ -129,8 +141,8 @@ func StdCopy(destOut, destErr io.Writer, multiplexedSource io.Reader) (written i
}
}
stream := StdType(buf[stdWriterFdIndex])
// Check the first byte to know where to write
stream := StdType(buf[stdWriterFdIndex])
switch stream {
case Stdin:
fallthrough
@@ -146,7 +158,7 @@ func StdCopy(destOut, destErr io.Writer, multiplexedSource io.Reader) (written i
// to outstream if Systemerr is the stream
out = nil
default:
return 0, fmt.Errorf("unrecognized input header: %d", buf[stdWriterFdIndex])
return 0, fmt.Errorf("unrecognized stream: %d", stream)
}
// Retrieve the size of the frame

View File

@@ -0,0 +1,66 @@
package stdcopy_test
import (
"errors"
"fmt"
"io"
"os"
"time"
"github.com/moby/moby/api/pkg/stdcopy"
)
func ExampleNewStdWriter() {
muxReader, muxStream := io.Pipe()
defer func() { _ = muxStream.Close() }()
// Start demuxing before the daemon starts writing.
done := make(chan error, 1)
go func() {
// using os.Stdout for both, otherwise output doesn't show up in the example.
osStdout := os.Stdout
osStderr := os.Stdout
_, err := stdcopy.StdCopy(osStdout, osStderr, muxReader)
done <- err
}()
// daemon writing to stdout, stderr, and systemErr.
stdout := stdcopy.NewStdWriter(muxStream, stdcopy.Stdout)
stderr := stdcopy.NewStdWriter(muxStream, stdcopy.Stderr)
systemErr := stdcopy.NewStdWriter(muxStream, stdcopy.Systemerr)
for range 10 {
_, _ = fmt.Fprintln(stdout, "hello from stdout")
_, _ = fmt.Fprintln(stderr, "hello from stderr")
time.Sleep(50 * time.Millisecond)
}
_, _ = fmt.Fprintln(systemErr, errors.New("something went wrong"))
// Wait for the demuxer to finish.
if err := <-done; err != nil {
fmt.Println(err)
}
// Output:
// hello from stdout
// hello from stderr
// hello from stdout
// hello from stderr
// hello from stdout
// hello from stderr
// hello from stdout
// hello from stderr
// hello from stdout
// hello from stderr
// hello from stdout
// hello from stderr
// hello from stdout
// hello from stderr
// hello from stdout
// hello from stderr
// hello from stdout
// hello from stderr
// hello from stdout
// hello from stderr
// error from daemon in stream: something went wrong
}

View File

@@ -14,16 +14,13 @@ import (
type StdType byte
const (
// Stdin represents standard input stream type.
Stdin StdType = iota
// Stdout represents standard output stream type.
Stdout
// Stderr represents standard error steam type.
Stderr
// Systemerr represents errors originating from the system that make it
// into the multiplexed stream.
Systemerr
Stdin StdType = 0 // Stdin represents standard input stream. It is present for completeness and should NOT be used. When reading the stream with [StdCopy] it is output on [Stdout].
Stdout StdType = 1 // Stdout represents standard output stream.
Stderr StdType = 2 // Stderr represents standard error steam.
Systemerr StdType = 3 // Systemerr represents errors originating from the system. When reading the stream with [StdCopy] it is returned as an error.
)
const (
stdWriterPrefixLen = 8
stdWriterFdIndex = 0
stdWriterSizeIndex = 4
@@ -75,9 +72,20 @@ func (w *stdWriter) Write(p []byte) (int, error) {
// stream "w".
//
// Writers created through NewStdWriter allow for multiple write streams
// (e.g. stdout ([Stdout]) and stderr ([Stderr]) to be multiplexed into a
// (e.g., stdout ([Stdout]) and stderr ([Stderr]) to be multiplexed into a
// single connection. "streamType" indicates the type of stream to encapsulate,
// and can be [Stdin], [Stdout], pr [Stderr].
// commonly, [Stdout] or [Stderr]. The [Systemerr] stream can be used to
// include server-side errors in the stream. Information on this stream
// is returned as an error by [StdCopy] and terminates processing the
// stream.
//
// The [Stdin] stream is present for completeness and should generally
// NOT be used. It is output on [Stdout] when reading the stream with
// [StdCopy].
//
// All streams must share the same underlying [io.Writer] to ensure proper
// multiplexing. Each call to NewStdWriter wraps that shared writer with
// a header indicating the target stream.
func NewStdWriter(w io.Writer, streamType StdType) io.Writer {
return &stdWriter{
Writer: w,
@@ -94,11 +102,15 @@ func NewStdWriter(w io.Writer, streamType StdType) io.Writer {
// [NewStdWriter].
//
// As it reads from "multiplexedSource", StdCopy writes [Stdout] messages
// to "destOut", and [Stderr] message to "destErr].
// to "destOut", and [Stderr] message to "destErr]. For backward-compatibility,
// [Stdin] messages are output to "destOut". The [Systemerr] stream provides
// errors produced by the daemon. It is returned as an error, and terminates
// processing the stream.
//
// StdCopy it reads until it hits [io.EOF] on "multiplexedSource", after
// which it returns a nil error. In other words: any error returned indicates
// a real underlying error.
// a real underlying error, which may be when an unknown [StdType] stream
// is received.
//
// The "written" return holds the total number of bytes written to "destOut"
// and "destErr" combined.
@@ -129,8 +141,8 @@ func StdCopy(destOut, destErr io.Writer, multiplexedSource io.Reader) (written i
}
}
stream := StdType(buf[stdWriterFdIndex])
// Check the first byte to know where to write
stream := StdType(buf[stdWriterFdIndex])
switch stream {
case Stdin:
fallthrough
@@ -146,7 +158,7 @@ func StdCopy(destOut, destErr io.Writer, multiplexedSource io.Reader) (written i
// to outstream if Systemerr is the stream
out = nil
default:
return 0, fmt.Errorf("unrecognized input header: %d", buf[stdWriterFdIndex])
return 0, fmt.Errorf("unrecognized stream: %d", stream)
}
// Retrieve the size of the frame