api/types: move HijackedResponse into client

Signed-off-by: Cory Snider <csnider@mirantis.com>
This commit is contained in:
Cory Snider
2025-07-24 15:56:41 -04:00
committed by Sebastiaan van Stijn
parent 7dc46c6e0c
commit 44ae4cd2b7
13 changed files with 109 additions and 122 deletions

View File

@@ -1,51 +1,9 @@
package types
import (
"bufio"
"context"
"net"
)
// NewHijackedResponse initializes a [HijackedResponse] type.
func NewHijackedResponse(conn net.Conn, mediaType string) HijackedResponse {
return HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn), mediaType: mediaType}
}
// HijackedResponse holds connection information for a hijacked request.
type HijackedResponse struct {
mediaType string
Conn net.Conn
Reader *bufio.Reader
}
// Close closes the hijacked connection and reader.
func (h *HijackedResponse) Close() {
h.Conn.Close()
}
// MediaType let client know if HijackedResponse hold a raw or multiplexed stream.
// returns false if HTTP Content-Type is not relevant, and container must be inspected
func (h *HijackedResponse) MediaType() (string, bool) {
if h.mediaType == "" {
return "", false
}
return h.mediaType, true
}
// CloseWriter is an interface that implements structs
// that close input streams to prevent from writing.
type CloseWriter interface {
CloseWrite() error
}
// CloseWrite closes a readWriter for writing.
func (h *HijackedResponse) CloseWrite() error {
if conn, ok := h.Conn.(CloseWriter); ok {
return conn.CloseWrite()
}
return nil
}
// PluginRemoveOptions holds parameters to remove plugins.
type PluginRemoveOptions struct {
Force bool

View File

@@ -64,11 +64,11 @@ type HijackDialer interface {
// ContainerAPIClient defines API client methods for the containers
type ContainerAPIClient interface {
ContainerAttach(ctx context.Context, container string, options container.AttachOptions) (types.HijackedResponse, error)
ContainerAttach(ctx context.Context, container string, options container.AttachOptions) (HijackedResponse, error)
ContainerCommit(ctx context.Context, container string, options container.CommitOptions) (container.CommitResponse, error)
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error)
ContainerDiff(ctx context.Context, container string) ([]container.FilesystemChange, error)
ContainerExecAttach(ctx context.Context, execID string, options container.ExecAttachOptions) (types.HijackedResponse, error)
ContainerExecAttach(ctx context.Context, execID string, options container.ExecAttachOptions) (HijackedResponse, error)
ContainerExecCreate(ctx context.Context, container string, options container.ExecOptions) (container.ExecCreateResponse, error)
ContainerExecInspect(ctx context.Context, execID string) (container.ExecInspect, error)
ContainerExecResize(ctx context.Context, execID string, options container.ResizeOptions) error

View File

@@ -5,7 +5,6 @@ import (
"net/http"
"net/url"
"github.com/moby/moby/api/types"
"github.com/moby/moby/api/types/container"
)
@@ -33,10 +32,10 @@ import (
//
// You can use github.com/moby/moby/api/stdcopy.StdCopy to demultiplex this
// stream.
func (cli *Client) ContainerAttach(ctx context.Context, containerID string, options container.AttachOptions) (types.HijackedResponse, error) {
func (cli *Client) ContainerAttach(ctx context.Context, containerID string, options container.AttachOptions) (HijackedResponse, error) {
containerID, err := trimID("container", containerID)
if err != nil {
return types.HijackedResponse{}, err
return HijackedResponse{}, err
}
query := url.Values{}

View File

@@ -5,7 +5,6 @@ import (
"encoding/json"
"net/http"
"github.com/moby/moby/api/types"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/versions"
)
@@ -80,7 +79,7 @@ func (cli *Client) ContainerExecStart(ctx context.Context, execID string, config
// You can use [github.com/moby/moby/api/stdcopy.StdCopy] to demultiplex this
// stream. Refer to [Client.ContainerAttach] for details about the multiplexed
// stream.
func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, config container.ExecAttachOptions) (types.HijackedResponse, error) {
func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, config container.ExecAttachOptions) (HijackedResponse, error) {
if versions.LessThan(cli.ClientVersion(), "1.42") {
config.ConsoleSize = nil
}

View File

@@ -9,25 +9,24 @@ import (
"net/url"
"time"
"github.com/moby/moby/api/types"
"github.com/moby/moby/api/types/versions"
"github.com/pkg/errors"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
// postHijacked sends a POST request and hijacks the connection.
func (cli *Client) postHijacked(ctx context.Context, path string, query url.Values, body interface{}, headers map[string][]string) (types.HijackedResponse, error) {
func (cli *Client) postHijacked(ctx context.Context, path string, query url.Values, body interface{}, headers map[string][]string) (HijackedResponse, error) {
jsonBody, err := jsonEncode(body)
if err != nil {
return types.HijackedResponse{}, err
return HijackedResponse{}, err
}
req, err := cli.buildRequest(ctx, http.MethodPost, cli.getAPIPath(ctx, path, query), jsonBody, headers)
if err != nil {
return types.HijackedResponse{}, err
return HijackedResponse{}, err
}
conn, mediaType, err := setupHijackConn(cli.dialer(), req, "tcp")
if err != nil {
return types.HijackedResponse{}, err
return HijackedResponse{}, err
}
if versions.LessThan(cli.ClientVersion(), "1.42") {
@@ -35,7 +34,7 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
mediaType = ""
}
return types.NewHijackedResponse(conn, mediaType), nil
return NewHijackedResponse(conn, mediaType), nil
}
// DialHijack returns a hijacked connection with negotiated protocol proto.
@@ -91,7 +90,7 @@ func setupHijackConn(dialer func(context.Context) (net.Conn, error), req *http.R
// If there is buffered content, wrap the connection. We return an
// object that implements CloseWrite if the underlying connection
// implements it.
if _, ok := hc.Conn.(types.CloseWriter); ok {
if _, ok := hc.Conn.(CloseWriter); ok {
conn = &hijackedConnCloseWriter{hc}
} else {
conn = hc
@@ -131,9 +130,49 @@ type hijackedConnCloseWriter struct {
*hijackedConn
}
var _ types.CloseWriter = &hijackedConnCloseWriter{}
var _ CloseWriter = &hijackedConnCloseWriter{}
func (c *hijackedConnCloseWriter) CloseWrite() error {
conn := c.Conn.(types.CloseWriter)
conn := c.Conn.(CloseWriter)
return conn.CloseWrite()
}
// NewHijackedResponse initializes a [HijackedResponse] type.
func NewHijackedResponse(conn net.Conn, mediaType string) HijackedResponse {
return HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn), mediaType: mediaType}
}
// HijackedResponse holds connection information for a hijacked request.
type HijackedResponse struct {
mediaType string
Conn net.Conn
Reader *bufio.Reader
}
// Close closes the hijacked connection and reader.
func (h *HijackedResponse) Close() {
h.Conn.Close()
}
// MediaType let client know if HijackedResponse hold a raw or multiplexed stream.
// returns false if HTTP Content-Type is not relevant, and container must be inspected
func (h *HijackedResponse) MediaType() (string, bool) {
if h.mediaType == "" {
return "", false
}
return h.mediaType, true
}
// CloseWriter is an interface that implements structs
// that close input streams to prevent from writing.
type CloseWriter interface {
CloseWrite() error
}
// CloseWrite closes a readWriter for writing.
func (h *HijackedResponse) CloseWrite() error {
if conn, ok := h.Conn.(CloseWriter); ok {
return conn.CloseWrite()
}
return nil
}

View File

@@ -12,7 +12,6 @@ import (
"testing"
"time"
"github.com/moby/moby/api/types"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
@@ -94,7 +93,7 @@ func TestTLSCloseWriter(t *testing.T) {
assert.NilError(t, err)
defer resp.Close()
_, ok := resp.Conn.(types.CloseWriter)
_, ok := resp.Conn.(CloseWriter)
assert.Check(t, ok, "tls conn did not implement the CloseWrite interface")
_, err = resp.Conn.Write([]byte("hello"))

View File

@@ -9,7 +9,6 @@ import (
"testing"
"github.com/moby/moby/api/stdcopy"
"github.com/moby/moby/api/types"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/client"
@@ -129,7 +128,7 @@ type streams struct {
// demultiplexStreams starts a goroutine to demultiplex stdout and stderr from the types.HijackedResponse resp and
// waits until either multiplexed stream reaches EOF or the context expires. It unconditionally closes resp and waits
// until the demultiplexing goroutine has finished its work before returning.
func demultiplexStreams(ctx context.Context, resp types.HijackedResponse) (streams, error) {
func demultiplexStreams(ctx context.Context, resp client.HijackedResponse) (streams, error) {
var s streams
outputDone := make(chan error, 1)

View File

@@ -8,7 +8,6 @@ import (
"github.com/docker/docker/testutil/daemon"
"github.com/docker/docker/testutil/environment"
"github.com/moby/moby/api/types"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/filters"
swarmtypes "github.com/moby/moby/api/types/swarm"
@@ -212,7 +211,7 @@ func GetRunningTasks(ctx context.Context, t *testing.T, c client.ServiceAPIClien
}
// ExecTask runs the passed in exec config on the given task
func ExecTask(ctx context.Context, t *testing.T, d *daemon.Daemon, task swarmtypes.Task, options container.ExecOptions) types.HijackedResponse {
func ExecTask(ctx context.Context, t *testing.T, d *daemon.Daemon, task swarmtypes.Task, options container.ExecOptions) client.HijackedResponse {
t.Helper()
apiClient := d.NewClientT(t)
defer apiClient.Close()

View File

@@ -1,51 +1,9 @@
package types
import (
"bufio"
"context"
"net"
)
// NewHijackedResponse initializes a [HijackedResponse] type.
func NewHijackedResponse(conn net.Conn, mediaType string) HijackedResponse {
return HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn), mediaType: mediaType}
}
// HijackedResponse holds connection information for a hijacked request.
type HijackedResponse struct {
mediaType string
Conn net.Conn
Reader *bufio.Reader
}
// Close closes the hijacked connection and reader.
func (h *HijackedResponse) Close() {
h.Conn.Close()
}
// MediaType let client know if HijackedResponse hold a raw or multiplexed stream.
// returns false if HTTP Content-Type is not relevant, and container must be inspected
func (h *HijackedResponse) MediaType() (string, bool) {
if h.mediaType == "" {
return "", false
}
return h.mediaType, true
}
// CloseWriter is an interface that implements structs
// that close input streams to prevent from writing.
type CloseWriter interface {
CloseWrite() error
}
// CloseWrite closes a readWriter for writing.
func (h *HijackedResponse) CloseWrite() error {
if conn, ok := h.Conn.(CloseWriter); ok {
return conn.CloseWrite()
}
return nil
}
// PluginRemoveOptions holds parameters to remove plugins.
type PluginRemoveOptions struct {
Force bool

View File

@@ -64,11 +64,11 @@ type HijackDialer interface {
// ContainerAPIClient defines API client methods for the containers
type ContainerAPIClient interface {
ContainerAttach(ctx context.Context, container string, options container.AttachOptions) (types.HijackedResponse, error)
ContainerAttach(ctx context.Context, container string, options container.AttachOptions) (HijackedResponse, error)
ContainerCommit(ctx context.Context, container string, options container.CommitOptions) (container.CommitResponse, error)
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error)
ContainerDiff(ctx context.Context, container string) ([]container.FilesystemChange, error)
ContainerExecAttach(ctx context.Context, execID string, options container.ExecAttachOptions) (types.HijackedResponse, error)
ContainerExecAttach(ctx context.Context, execID string, options container.ExecAttachOptions) (HijackedResponse, error)
ContainerExecCreate(ctx context.Context, container string, options container.ExecOptions) (container.ExecCreateResponse, error)
ContainerExecInspect(ctx context.Context, execID string) (container.ExecInspect, error)
ContainerExecResize(ctx context.Context, execID string, options container.ResizeOptions) error

View File

@@ -5,7 +5,6 @@ import (
"net/http"
"net/url"
"github.com/moby/moby/api/types"
"github.com/moby/moby/api/types/container"
)
@@ -33,10 +32,10 @@ import (
//
// You can use github.com/moby/moby/api/stdcopy.StdCopy to demultiplex this
// stream.
func (cli *Client) ContainerAttach(ctx context.Context, containerID string, options container.AttachOptions) (types.HijackedResponse, error) {
func (cli *Client) ContainerAttach(ctx context.Context, containerID string, options container.AttachOptions) (HijackedResponse, error) {
containerID, err := trimID("container", containerID)
if err != nil {
return types.HijackedResponse{}, err
return HijackedResponse{}, err
}
query := url.Values{}

View File

@@ -5,7 +5,6 @@ import (
"encoding/json"
"net/http"
"github.com/moby/moby/api/types"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/versions"
)
@@ -80,7 +79,7 @@ func (cli *Client) ContainerExecStart(ctx context.Context, execID string, config
// You can use [github.com/moby/moby/api/stdcopy.StdCopy] to demultiplex this
// stream. Refer to [Client.ContainerAttach] for details about the multiplexed
// stream.
func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, config container.ExecAttachOptions) (types.HijackedResponse, error) {
func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, config container.ExecAttachOptions) (HijackedResponse, error) {
if versions.LessThan(cli.ClientVersion(), "1.42") {
config.ConsoleSize = nil
}

View File

@@ -9,25 +9,24 @@ import (
"net/url"
"time"
"github.com/moby/moby/api/types"
"github.com/moby/moby/api/types/versions"
"github.com/pkg/errors"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
// postHijacked sends a POST request and hijacks the connection.
func (cli *Client) postHijacked(ctx context.Context, path string, query url.Values, body interface{}, headers map[string][]string) (types.HijackedResponse, error) {
func (cli *Client) postHijacked(ctx context.Context, path string, query url.Values, body interface{}, headers map[string][]string) (HijackedResponse, error) {
jsonBody, err := jsonEncode(body)
if err != nil {
return types.HijackedResponse{}, err
return HijackedResponse{}, err
}
req, err := cli.buildRequest(ctx, http.MethodPost, cli.getAPIPath(ctx, path, query), jsonBody, headers)
if err != nil {
return types.HijackedResponse{}, err
return HijackedResponse{}, err
}
conn, mediaType, err := setupHijackConn(cli.dialer(), req, "tcp")
if err != nil {
return types.HijackedResponse{}, err
return HijackedResponse{}, err
}
if versions.LessThan(cli.ClientVersion(), "1.42") {
@@ -35,7 +34,7 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu
mediaType = ""
}
return types.NewHijackedResponse(conn, mediaType), nil
return NewHijackedResponse(conn, mediaType), nil
}
// DialHijack returns a hijacked connection with negotiated protocol proto.
@@ -91,7 +90,7 @@ func setupHijackConn(dialer func(context.Context) (net.Conn, error), req *http.R
// If there is buffered content, wrap the connection. We return an
// object that implements CloseWrite if the underlying connection
// implements it.
if _, ok := hc.Conn.(types.CloseWriter); ok {
if _, ok := hc.Conn.(CloseWriter); ok {
conn = &hijackedConnCloseWriter{hc}
} else {
conn = hc
@@ -131,9 +130,49 @@ type hijackedConnCloseWriter struct {
*hijackedConn
}
var _ types.CloseWriter = &hijackedConnCloseWriter{}
var _ CloseWriter = &hijackedConnCloseWriter{}
func (c *hijackedConnCloseWriter) CloseWrite() error {
conn := c.Conn.(types.CloseWriter)
conn := c.Conn.(CloseWriter)
return conn.CloseWrite()
}
// NewHijackedResponse initializes a [HijackedResponse] type.
func NewHijackedResponse(conn net.Conn, mediaType string) HijackedResponse {
return HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn), mediaType: mediaType}
}
// HijackedResponse holds connection information for a hijacked request.
type HijackedResponse struct {
mediaType string
Conn net.Conn
Reader *bufio.Reader
}
// Close closes the hijacked connection and reader.
func (h *HijackedResponse) Close() {
h.Conn.Close()
}
// MediaType let client know if HijackedResponse hold a raw or multiplexed stream.
// returns false if HTTP Content-Type is not relevant, and container must be inspected
func (h *HijackedResponse) MediaType() (string, bool) {
if h.mediaType == "" {
return "", false
}
return h.mediaType, true
}
// CloseWriter is an interface that implements structs
// that close input streams to prevent from writing.
type CloseWriter interface {
CloseWrite() error
}
// CloseWrite closes a readWriter for writing.
func (h *HijackedResponse) CloseWrite() error {
if conn, ok := h.Conn.(CloseWriter); ok {
return conn.CloseWrite()
}
return nil
}