mirror of
https://github.com/containers/podman.git
synced 2025-10-17 19:24:04 +08:00
API attach: return vnd.docker.multiplexed-stream header
The attach API used to always return the Content-Type `vnd.docker.raw-stream`, however docker api v1.42 added the `vnd.docker.multiplexed-stream` type when no tty was used. Follow suit and return the same header for docker api v1.42 and libpod v4.7.0. This technically allows clients to make a small optimization as they no longer need to inspect the container to see if they get a raw or multiplexed stream. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
@ -614,7 +614,7 @@ func (r *ConmonOCIRuntime) HTTPAttach(ctr *Container, req *http.Request, w http.
|
|||||||
|
|
||||||
hijackDone <- true
|
hijackDone <- true
|
||||||
|
|
||||||
writeHijackHeader(req, httpBuf)
|
writeHijackHeader(req, httpBuf, isTerminal)
|
||||||
|
|
||||||
// Force a flush after the header is written.
|
// Force a flush after the header is written.
|
||||||
if err := httpBuf.Flush(); err != nil {
|
if err := httpBuf.Flush(); err != nil {
|
||||||
|
@ -569,7 +569,7 @@ func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.Resp
|
|||||||
hijackDone <- true
|
hijackDone <- true
|
||||||
|
|
||||||
// Write a header to let the client know what happened
|
// Write a header to let the client know what happened
|
||||||
writeHijackHeader(r, httpBuf)
|
writeHijackHeader(r, httpBuf, isTerminal)
|
||||||
|
|
||||||
// Force a flush after the header is written.
|
// Force a flush after the header is written.
|
||||||
if err := httpBuf.Flush(); err != nil {
|
if err := httpBuf.Flush(); err != nil {
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/containers/common/libnetwork/types"
|
"github.com/containers/common/libnetwork/types"
|
||||||
"github.com/containers/common/pkg/config"
|
"github.com/containers/common/pkg/config"
|
||||||
"github.com/containers/podman/v4/libpod/define"
|
"github.com/containers/podman/v4/libpod/define"
|
||||||
|
"github.com/containers/podman/v4/pkg/api/handlers/utils/apiutil"
|
||||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/opencontainers/selinux/go-selinux/label"
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -182,22 +183,36 @@ func makeHTTPAttachHeader(stream byte, length uint32) []byte {
|
|||||||
|
|
||||||
// writeHijackHeader writes a header appropriate for the type of HTTP Hijack
|
// writeHijackHeader writes a header appropriate for the type of HTTP Hijack
|
||||||
// that occurred in a hijacked HTTP connection used for attach.
|
// that occurred in a hijacked HTTP connection used for attach.
|
||||||
func writeHijackHeader(r *http.Request, conn io.Writer) {
|
func writeHijackHeader(r *http.Request, conn io.Writer, tty bool) {
|
||||||
// AttachHeader is the literal header sent for upgraded/hijacked connections for
|
// AttachHeader is the literal header sent for upgraded/hijacked connections for
|
||||||
// attach, sourced from Docker at:
|
// attach, sourced from Docker at:
|
||||||
// https://raw.githubusercontent.com/moby/moby/b95fad8e51bd064be4f4e58a996924f343846c85/api/server/router/container/container_routes.go
|
// https://raw.githubusercontent.com/moby/moby/b95fad8e51bd064be4f4e58a996924f343846c85/api/server/router/container/container_routes.go
|
||||||
// Using literally to ensure compatibility with existing clients.
|
// Using literally to ensure compatibility with existing clients.
|
||||||
|
|
||||||
|
// New docker API uses a different header for the non tty case.
|
||||||
|
// Lets do the same for libpod. Only do this for the new api versions to not break older clients.
|
||||||
|
header := "application/vnd.docker.raw-stream"
|
||||||
|
if !tty {
|
||||||
|
version := "4.7.0"
|
||||||
|
if !apiutil.IsLibpodRequest(r) {
|
||||||
|
version = "1.42.0" // docker only used two digest "1.42" but our semver lib needs the extra .0 to work
|
||||||
|
}
|
||||||
|
if _, err := apiutil.SupportedVersion(r, ">= "+version); err == nil {
|
||||||
|
header = "application/vnd.docker.multiplexed-stream"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
c := r.Header.Get("Connection")
|
c := r.Header.Get("Connection")
|
||||||
proto := r.Header.Get("Upgrade")
|
proto := r.Header.Get("Upgrade")
|
||||||
if len(proto) == 0 || !strings.EqualFold(c, "Upgrade") {
|
if len(proto) == 0 || !strings.EqualFold(c, "Upgrade") {
|
||||||
// OK - can't upgrade if not requested or protocol is not specified
|
// OK - can't upgrade if not requested or protocol is not specified
|
||||||
fmt.Fprintf(conn,
|
fmt.Fprintf(conn,
|
||||||
"HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
|
"HTTP/1.1 200 OK\r\nContent-Type: %s\r\n\r\n", header)
|
||||||
} else {
|
} else {
|
||||||
// Upgraded
|
// Upgraded
|
||||||
fmt.Fprintf(conn,
|
fmt.Fprintf(conn,
|
||||||
"HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: %s\r\n\r\n",
|
"HTTP/1.1 101 UPGRADED\r\nContent-Type: %s\r\nConnection: Upgrade\r\nUpgrade: %s\r\n\r\n",
|
||||||
proto)
|
proto, header)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1376,6 +1376,7 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
|
|||||||
//
|
//
|
||||||
// When the TTY setting is disabled for the container,
|
// When the TTY setting is disabled for the container,
|
||||||
// the HTTP Content-Type header is set to application/vnd.docker.multiplexed-stream
|
// the HTTP Content-Type header is set to application/vnd.docker.multiplexed-stream
|
||||||
|
// (starting with v4.7.0, previously application/vnd.docker.raw-stream was always used)
|
||||||
// and the stream over the hijacked connected is multiplexed to separate out
|
// and the stream over the hijacked connected is multiplexed to separate out
|
||||||
// `stdout` and `stderr`. The stream consists of a series of frames, each
|
// `stdout` and `stderr`. The stream consists of a series of frames, each
|
||||||
// containing a header and a payload.
|
// containing a header and a payload.
|
||||||
|
@ -30,6 +30,21 @@ podman run --rm -d --replace --name foo $IMAGE sh -c "echo $mytext;sleep 42"
|
|||||||
# Looks like it is missing the required 0 bytes from the message, why?
|
# Looks like it is missing the required 0 bytes from the message, why?
|
||||||
t POST "containers/foo/attach?logs=true&stream=false" 200 \
|
t POST "containers/foo/attach?logs=true&stream=false" 200 \
|
||||||
$'\001\031'$mytext
|
$'\001\031'$mytext
|
||||||
|
|
||||||
|
# check old docker header
|
||||||
|
response_headers=$(cat "$WORKDIR/curl.headers.out")
|
||||||
|
like "$response_headers" ".*Content-Type: application/vnd\.docker\.raw-stream.*" "vnd.docker.raw-stream docker v1.40"
|
||||||
|
# check new vnd.docker.multiplexed-stream header
|
||||||
|
t POST "/v1.42/containers/foo/attach?logs=true&stream=false" 200
|
||||||
|
response_headers=$(cat "$WORKDIR/curl.headers.out")
|
||||||
|
like "$response_headers" ".*Content-Type: application/vnd\.docker\.multiplexed-stream.*" "vnd.docker.multiplexed-stream docker v1.42"
|
||||||
|
t POST "/v4.6.0/libpod/containers/foo/attach?logs=true&stream=false" 200
|
||||||
|
response_headers=$(cat "$WORKDIR/curl.headers.out")
|
||||||
|
like "$response_headers" ".*Content-Type: application/vnd\.docker\.raw-stream.*" "vnd.docker.raw-stream libpod v4.6.0"
|
||||||
|
t POST "/v4.7.0/libpod/containers/foo/attach?logs=true&stream=false" 200
|
||||||
|
response_headers=$(cat "$WORKDIR/curl.headers.out")
|
||||||
|
like "$response_headers" ".*Content-Type: application/vnd\.docker\.multiplexed-stream.*" "vnd.docker.multiplexed-stream libpod v4.7.0"
|
||||||
|
|
||||||
t POST "containers/foo/kill" 204
|
t POST "containers/foo/kill" 204
|
||||||
|
|
||||||
podman run --replace --name=foo -v /tmp:/tmp $IMAGE true
|
podman run --replace --name=foo -v /tmp:/tmp $IMAGE true
|
||||||
|
Reference in New Issue
Block a user