1package httputils // import "github.com/docker/docker/api/server/httputils"
2
3import (
4	"context"
5	"fmt"
6	"io"
7	"net/url"
8	"sort"
9
10	"github.com/docker/docker/api/types"
11	"github.com/docker/docker/api/types/backend"
12	"github.com/docker/docker/pkg/ioutils"
13	"github.com/docker/docker/pkg/jsonmessage"
14	"github.com/docker/docker/pkg/stdcopy"
15)
16
17// WriteLogStream writes an encoded byte stream of log messages from the
18// messages channel, multiplexing them with a stdcopy.Writer if mux is true
19func WriteLogStream(_ context.Context, w io.Writer, msgs <-chan *backend.LogMessage, config *types.ContainerLogsOptions, mux bool) {
20	wf := ioutils.NewWriteFlusher(w)
21	defer wf.Close()
22
23	wf.Flush()
24
25	outStream := io.Writer(wf)
26	errStream := outStream
27	sysErrStream := errStream
28	if mux {
29		sysErrStream = stdcopy.NewStdWriter(outStream, stdcopy.Systemerr)
30		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
31		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
32	}
33
34	for {
35		msg, ok := <-msgs
36		if !ok {
37			return
38		}
39		// check if the message contains an error. if so, write that error
40		// and exit
41		if msg.Err != nil {
42			fmt.Fprintf(sysErrStream, "Error grabbing logs: %v\n", msg.Err)
43			continue
44		}
45		logLine := msg.Line
46		if config.Details {
47			logLine = append(attrsByteSlice(msg.Attrs), ' ')
48			logLine = append(logLine, msg.Line...)
49		}
50		if config.Timestamps {
51			logLine = append([]byte(msg.Timestamp.Format(jsonmessage.RFC3339NanoFixed)+" "), logLine...)
52		}
53		if msg.Source == "stdout" && config.ShowStdout {
54			_, _ = outStream.Write(logLine)
55		}
56		if msg.Source == "stderr" && config.ShowStderr {
57			_, _ = errStream.Write(logLine)
58		}
59	}
60}
61
62type byKey []backend.LogAttr
63
64func (b byKey) Len() int           { return len(b) }
65func (b byKey) Less(i, j int) bool { return b[i].Key < b[j].Key }
66func (b byKey) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
67
68func attrsByteSlice(a []backend.LogAttr) []byte {
69	// Note this sorts "a" in-place. That is fine here - nothing else is
70	// going to use Attrs or care about the order.
71	sort.Sort(byKey(a))
72
73	var ret []byte
74	for i, pair := range a {
75		k, v := url.QueryEscape(pair.Key), url.QueryEscape(pair.Value)
76		ret = append(ret, []byte(k)...)
77		ret = append(ret, '=')
78		ret = append(ret, []byte(v)...)
79		if i != len(a)-1 {
80			ret = append(ret, ',')
81		}
82	}
83	return ret
84}
85