|
@@ -3,6 +3,7 @@ package container
|
|
import (
|
|
import (
|
|
"encoding/json"
|
|
"encoding/json"
|
|
"fmt"
|
|
"fmt"
|
|
|
|
+ "io"
|
|
"net/http"
|
|
"net/http"
|
|
"strconv"
|
|
"strconv"
|
|
"strings"
|
|
"strings"
|
|
@@ -14,6 +15,7 @@ import (
|
|
"github.com/docker/docker/api/server/httputils"
|
|
"github.com/docker/docker/api/server/httputils"
|
|
"github.com/docker/docker/api/types/backend"
|
|
"github.com/docker/docker/api/types/backend"
|
|
derr "github.com/docker/docker/errors"
|
|
derr "github.com/docker/docker/errors"
|
|
|
|
+ "github.com/docker/docker/pkg/ioutils"
|
|
"github.com/docker/docker/pkg/signal"
|
|
"github.com/docker/docker/pkg/signal"
|
|
"github.com/docker/docker/pkg/term"
|
|
"github.com/docker/docker/pkg/term"
|
|
"github.com/docker/docker/runconfig"
|
|
"github.com/docker/docker/runconfig"
|
|
@@ -425,18 +427,45 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- attachWithLogsConfig := &backend.ContainerAttachWithLogsConfig{
|
|
|
|
- Hijacker: w.(http.Hijacker),
|
|
|
|
- Upgrade: upgrade,
|
|
|
|
|
|
+ hijacker, ok := w.(http.Hijacker)
|
|
|
|
+ if !ok {
|
|
|
|
+ return derr.ErrorCodeNoHijackConnection.WithArgs(containerName)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
|
|
|
|
+ conn, _, err := hijacker.Hijack()
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, nil, nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // set raw mode
|
|
|
|
+ conn.Write([]byte{})
|
|
|
|
+
|
|
|
|
+ if upgrade {
|
|
|
|
+ fmt.Fprintf(conn, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
|
|
|
|
+ } else {
|
|
|
|
+ fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ closer := func() error {
|
|
|
|
+ httputils.CloseStreams(conn)
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ return ioutils.NewReadCloserWrapper(conn, closer), conn, conn, nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ attachConfig := &backend.ContainerAttachConfig{
|
|
|
|
+ GetStreams: setupStreams,
|
|
UseStdin: httputils.BoolValue(r, "stdin"),
|
|
UseStdin: httputils.BoolValue(r, "stdin"),
|
|
UseStdout: httputils.BoolValue(r, "stdout"),
|
|
UseStdout: httputils.BoolValue(r, "stdout"),
|
|
UseStderr: httputils.BoolValue(r, "stderr"),
|
|
UseStderr: httputils.BoolValue(r, "stderr"),
|
|
Logs: httputils.BoolValue(r, "logs"),
|
|
Logs: httputils.BoolValue(r, "logs"),
|
|
Stream: httputils.BoolValue(r, "stream"),
|
|
Stream: httputils.BoolValue(r, "stream"),
|
|
DetachKeys: keys,
|
|
DetachKeys: keys,
|
|
|
|
+ MuxStreams: true,
|
|
}
|
|
}
|
|
|
|
|
|
- return s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig)
|
|
|
|
|
|
+ return s.backend.ContainerAttach(containerName, attachConfig)
|
|
}
|
|
}
|
|
|
|
|
|
func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
@@ -455,24 +484,44 @@ func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.Respons
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- h := websocket.Handler(func(ws *websocket.Conn) {
|
|
|
|
- defer ws.Close()
|
|
|
|
|
|
+ done := make(chan struct{})
|
|
|
|
+ started := make(chan struct{})
|
|
|
|
|
|
- wsAttachWithLogsConfig := &backend.ContainerWsAttachWithLogsConfig{
|
|
|
|
- InStream: ws,
|
|
|
|
- OutStream: ws,
|
|
|
|
- ErrStream: ws,
|
|
|
|
- Logs: httputils.BoolValue(r, "logs"),
|
|
|
|
- Stream: httputils.BoolValue(r, "stream"),
|
|
|
|
- DetachKeys: keys,
|
|
|
|
|
|
+ setupStreams := func() (io.ReadCloser, io.Writer, io.Writer, error) {
|
|
|
|
+ wsChan := make(chan *websocket.Conn)
|
|
|
|
+ h := func(conn *websocket.Conn) {
|
|
|
|
+ wsChan <- conn
|
|
|
|
+ <-done
|
|
}
|
|
}
|
|
|
|
|
|
- if err := s.backend.ContainerWsAttachWithLogs(containerName, wsAttachWithLogsConfig); err != nil {
|
|
|
|
- logrus.Errorf("Error attaching websocket: %s", utils.GetErrorMessage(err))
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
- ws := websocket.Server{Handler: h, Handshake: nil}
|
|
|
|
- ws.ServeHTTP(w, r)
|
|
|
|
|
|
+ srv := websocket.Server{Handler: h, Handshake: nil}
|
|
|
|
+ go func() {
|
|
|
|
+ close(started)
|
|
|
|
+ srv.ServeHTTP(w, r)
|
|
|
|
+ }()
|
|
|
|
|
|
- return nil
|
|
|
|
|
|
+ conn := <-wsChan
|
|
|
|
+ return conn, conn, conn, nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ attachConfig := &backend.ContainerAttachConfig{
|
|
|
|
+ GetStreams: setupStreams,
|
|
|
|
+ Logs: httputils.BoolValue(r, "logs"),
|
|
|
|
+ Stream: httputils.BoolValue(r, "stream"),
|
|
|
|
+ DetachKeys: keys,
|
|
|
|
+ UseStdin: true,
|
|
|
|
+ UseStdout: true,
|
|
|
|
+ UseStderr: true,
|
|
|
|
+ MuxStreams: false, // TODO: this should be true since it's a single stream for both stdout and stderr
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = s.backend.ContainerAttach(containerName, attachConfig)
|
|
|
|
+ close(done)
|
|
|
|
+ select {
|
|
|
|
+ case <-started:
|
|
|
|
+ logrus.Errorf("Error attaching websocket: %s", err)
|
|
|
|
+ return nil
|
|
|
|
+ default:
|
|
|
|
+ }
|
|
|
|
+ return err
|
|
}
|
|
}
|