diff --git a/daemon/logs.go b/daemon/logs.go index 79d4044bbe..ae3e997311 100644 --- a/daemon/logs.go +++ b/daemon/logs.go @@ -129,6 +129,11 @@ func (daemon *Daemon) ContainerLogs(name string, config *ContainerLogsConfig) er errors := make(chan error, 2) wg := sync.WaitGroup{} + // write an empty chunk of data (this is to ensure that the + // HTTP Response is sent immediatly, even if the container has + // not yet produced any data) + outStream.Write(nil) + if config.UseStdout { wg.Add(1) stdoutPipe := container.StdoutLogPipe() diff --git a/integration-cli/docker_api_logs_test.go b/integration-cli/docker_api_logs_test.go index f9284494d2..d77ddef30a 100644 --- a/integration-cli/docker_api_logs_test.go +++ b/integration-cli/docker_api_logs_test.go @@ -60,3 +60,25 @@ func (s *DockerSuite) TestLogsApiNoStdoutNorStderr(c *check.C) { c.Fatalf("Expected %s, got %s", expected, string(body[:])) } } + +// Regression test for #12704 +func (s *DockerSuite) TestLogsApiFollowEmptyOutput(c *check.C) { + defer deleteAllContainers() + name := "logs_test" + t0 := time.Now() + runCmd := exec.Command(dockerBinary, "run", "-d", "-t", "--name", name, "busybox", "sleep", "10") + if out, _, err := runCommandWithOutput(runCmd); err != nil { + c.Fatal(out, err) + } + + _, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/logs?follow=1&stdout=1&stderr=1&tail=all", name), bytes.NewBuffer(nil), "") + t1 := time.Now() + body.Close() + if err != nil { + c.Fatal(err) + } + elapsed := t1.Sub(t0).Seconds() + if elapsed > 5.0 { + c.Fatalf("HTTP response was not immediate (elapsed %.1fs)", elapsed) + } +}