Move connection hijacking logic to the daemon.
Signed-off-by: David Calavera <david.calavera@gmail.com>
| ... | ... |
@@ -44,7 +44,6 @@ type stateBackend interface {
|
| 44 | 44 |
ContainerUnpause(name string) error |
| 45 | 45 |
ContainerWait(name string, timeout time.Duration) (int, error) |
| 46 | 46 |
Exists(id string) bool |
| 47 |
- IsPaused(id string) bool |
|
| 48 | 47 |
} |
| 49 | 48 |
|
| 50 | 49 |
// monitorBackend includes functions to implement to provide containers monitoring functionality. |
| ... | ... |
@@ -400,29 +400,11 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo |
| 400 | 400 |
} |
| 401 | 401 |
containerName := vars["name"] |
| 402 | 402 |
|
| 403 |
- if !s.backend.Exists(containerName) {
|
|
| 404 |
- return derr.ErrorCodeNoSuchContainer.WithArgs(containerName) |
|
| 405 |
- } |
|
| 406 |
- |
|
| 407 |
- if s.backend.IsPaused(containerName) {
|
|
| 408 |
- return derr.ErrorCodePausedContainer.WithArgs(containerName) |
|
| 409 |
- } |
|
| 410 |
- |
|
| 411 |
- inStream, outStream, err := httputils.HijackConnection(w) |
|
| 412 |
- if err != nil {
|
|
| 413 |
- return err |
|
| 414 |
- } |
|
| 415 |
- defer httputils.CloseStreams(inStream, outStream) |
|
| 416 |
- |
|
| 417 |
- if _, ok := r.Header["Upgrade"]; ok {
|
|
| 418 |
- fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n") |
|
| 419 |
- } else {
|
|
| 420 |
- fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") |
|
| 421 |
- } |
|
| 403 |
+ _, upgrade := r.Header["Upgrade"] |
|
| 422 | 404 |
|
| 423 | 405 |
attachWithLogsConfig := &daemon.ContainerAttachWithLogsConfig{
|
| 424 |
- InStream: inStream, |
|
| 425 |
- OutStream: outStream, |
|
| 406 |
+ Hijacker: w.(http.Hijacker), |
|
| 407 |
+ Upgrade: upgrade, |
|
| 426 | 408 |
UseStdin: httputils.BoolValue(r, "stdin"), |
| 427 | 409 |
UseStdout: httputils.BoolValue(r, "stdout"), |
| 428 | 410 |
UseStderr: httputils.BoolValue(r, "stderr"), |
| ... | ... |
@@ -430,11 +412,7 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo |
| 430 | 430 |
Stream: httputils.BoolValue(r, "stream"), |
| 431 | 431 |
} |
| 432 | 432 |
|
| 433 |
- if err := s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig); err != nil {
|
|
| 434 |
- fmt.Fprintf(outStream, "Error attaching: %s\n", err) |
|
| 435 |
- } |
|
| 436 |
- |
|
| 437 |
- return nil |
|
| 433 |
+ return s.backend.ContainerAttachWithLogs(containerName, attachWithLogsConfig) |
|
| 438 | 434 |
} |
| 439 | 435 |
|
| 440 | 436 |
func (s *containerRouter) wsContainersAttach(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| ... | ... |
@@ -1,53 +1,84 @@ |
| 1 | 1 |
package daemon |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "fmt" |
|
| 4 | 5 |
"io" |
| 6 |
+ "net/http" |
|
| 5 | 7 |
"time" |
| 6 | 8 |
|
| 7 | 9 |
"github.com/Sirupsen/logrus" |
| 8 | 10 |
"github.com/docker/docker/container" |
| 9 | 11 |
"github.com/docker/docker/daemon/logger" |
| 12 |
+ derr "github.com/docker/docker/errors" |
|
| 10 | 13 |
"github.com/docker/docker/pkg/stdcopy" |
| 11 | 14 |
) |
| 12 | 15 |
|
| 13 | 16 |
// ContainerAttachWithLogsConfig holds the streams to use when connecting to a container to view logs. |
| 14 | 17 |
type ContainerAttachWithLogsConfig struct {
|
| 15 |
- InStream io.ReadCloser |
|
| 16 |
- OutStream io.Writer |
|
| 17 |
- UseStdin, UseStdout, UseStderr bool |
|
| 18 |
- Logs, Stream bool |
|
| 18 |
+ Hijacker http.Hijacker |
|
| 19 |
+ Upgrade bool |
|
| 20 |
+ UseStdin bool |
|
| 21 |
+ UseStdout bool |
|
| 22 |
+ UseStderr bool |
|
| 23 |
+ Logs bool |
|
| 24 |
+ Stream bool |
|
| 19 | 25 |
} |
| 20 | 26 |
|
| 21 | 27 |
// ContainerAttachWithLogs attaches to logs according to the config passed in. See ContainerAttachWithLogsConfig. |
| 22 | 28 |
func (daemon *Daemon) ContainerAttachWithLogs(prefixOrName string, c *ContainerAttachWithLogsConfig) error {
|
| 29 |
+ if c.Hijacker == nil {
|
|
| 30 |
+ return derr.ErrorCodeNoHijackConnection.WithArgs(prefixOrName) |
|
| 31 |
+ } |
|
| 23 | 32 |
container, err := daemon.GetContainer(prefixOrName) |
| 24 | 33 |
if err != nil {
|
| 34 |
+ return derr.ErrorCodeNoSuchContainer.WithArgs(prefixOrName) |
|
| 35 |
+ } |
|
| 36 |
+ if container.IsPaused() {
|
|
| 37 |
+ return derr.ErrorCodePausedContainer.WithArgs(prefixOrName) |
|
| 38 |
+ } |
|
| 39 |
+ |
|
| 40 |
+ conn, _, err := c.Hijacker.Hijack() |
|
| 41 |
+ if err != nil {
|
|
| 25 | 42 |
return err |
| 26 | 43 |
} |
| 44 |
+ defer conn.Close() |
|
| 45 |
+ // Flush the options to make sure the client sets the raw mode |
|
| 46 |
+ conn.Write([]byte{})
|
|
| 47 |
+ inStream := conn.(io.ReadCloser) |
|
| 48 |
+ outStream := conn.(io.Writer) |
|
| 49 |
+ |
|
| 50 |
+ if c.Upgrade {
|
|
| 51 |
+ fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n") |
|
| 52 |
+ } else {
|
|
| 53 |
+ fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n") |
|
| 54 |
+ } |
|
| 27 | 55 |
|
| 28 | 56 |
var errStream io.Writer |
| 29 | 57 |
|
| 30 | 58 |
if !container.Config.Tty {
|
| 31 |
- errStream = stdcopy.NewStdWriter(c.OutStream, stdcopy.Stderr) |
|
| 32 |
- c.OutStream = stdcopy.NewStdWriter(c.OutStream, stdcopy.Stdout) |
|
| 59 |
+ errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr) |
|
| 60 |
+ outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) |
|
| 33 | 61 |
} else {
|
| 34 |
- errStream = c.OutStream |
|
| 62 |
+ errStream = outStream |
|
| 35 | 63 |
} |
| 36 | 64 |
|
| 37 | 65 |
var stdin io.ReadCloser |
| 38 | 66 |
var stdout, stderr io.Writer |
| 39 | 67 |
|
| 40 | 68 |
if c.UseStdin {
|
| 41 |
- stdin = c.InStream |
|
| 69 |
+ stdin = inStream |
|
| 42 | 70 |
} |
| 43 | 71 |
if c.UseStdout {
|
| 44 |
- stdout = c.OutStream |
|
| 72 |
+ stdout = outStream |
|
| 45 | 73 |
} |
| 46 | 74 |
if c.UseStderr {
|
| 47 | 75 |
stderr = errStream |
| 48 | 76 |
} |
| 49 | 77 |
|
| 50 |
- return daemon.attachWithLogs(container, stdin, stdout, stderr, c.Logs, c.Stream) |
|
| 78 |
+ if err := daemon.attachWithLogs(container, stdin, stdout, stderr, c.Logs, c.Stream); err != nil {
|
|
| 79 |
+ fmt.Fprintf(outStream, "Error attaching: %s\n", err) |
|
| 80 |
+ } |
|
| 81 |
+ return nil |
|
| 51 | 82 |
} |
| 52 | 83 |
|
| 53 | 84 |
// ContainerWsAttachWithLogsConfig attach with websockets, since all |
| ... | ... |
@@ -33,4 +33,13 @@ var ( |
| 33 | 33 |
Description: "Docker's networking stack is disabled for this platform", |
| 34 | 34 |
HTTPStatusCode: http.StatusNotFound, |
| 35 | 35 |
}) |
| 36 |
+ |
|
| 37 |
+ // ErrorCodeNoHijackConnection is generated when a request tries to attach to a container |
|
| 38 |
+ // but the connection to hijack is not provided. |
|
| 39 |
+ ErrorCodeNoHijackConnection = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
|
| 40 |
+ Value: "HIJACK_CONNECTION_MISSING", |
|
| 41 |
+ Message: "error attaching to container %s, hijack connection missing", |
|
| 42 |
+ Description: "The caller didn't provide a connection to hijack", |
|
| 43 |
+ HTTPStatusCode: http.StatusBadRequest, |
|
| 44 |
+ }) |
|
| 36 | 45 |
) |