Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions cli/command/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...Initialize
}

if cli.client == nil {
cli.client, err = newAPIClientFromEndpoint(cli.dockerEndpoint, cli.configFile)
cli.client, err = newAPIClientFromEndpoint(opts.Common, cli.dockerEndpoint, cli.configFile)
if err != nil {
return err
}
Expand All @@ -255,18 +255,26 @@ func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.
if err != nil {
return nil, errors.Wrap(err, "unable to resolve docker endpoint")
}
return newAPIClientFromEndpoint(endpoint, configFile)
return newAPIClientFromEndpoint(opts, endpoint, configFile)
}

func newAPIClientFromEndpoint(ep docker.Endpoint, configFile *configfile.ConfigFile) (client.APIClient, error) {
func newAPIClientFromEndpoint(opts *cliflags.CommonOptions, ep docker.Endpoint, configFile *configfile.ConfigFile) (client.APIClient, error) {
clientOpts, err := ep.ClientOpts()
if err != nil {
return nil, err
}
customHeaders := make(map[string]string, len(configFile.HTTPHeaders))
customHeaders := make(map[string]string, len(configFile.HTTPHeaders)+len(opts.HttpHeaders))
for k, v := range configFile.HTTPHeaders {
customHeaders[k] = v
}

for i := 0; i < len(opts.HttpHeaders); i++ {
split := strings.Split(opts.HttpHeaders[i], ":")

@thaJeztah thaJeztah May 30, 2022

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have yet to have a closer look, but some quick thoughts;

  • we should probably use = as separator (--http-header My-Header=myvalue)
  • use strings.SplitN(.., ..., 2) (as we expect only a single separator); this prevents issues if the value would contain :, which could be if (just thinking out loud) for example, an IPv6 address is passed (Foo-Header=[::]:123)
  • We probably need to do this in some other places as well (existing locations that is; outside of scope for this PR), but perhaps we should also use http. CanonicalHeaderKey() (after stripping whitespace), to prevent duplicate headers, and to prevent User-Agent from being overridden (which we don't want); it looks like the client already does normalising (I just notice it doesn't trim whitespace, and probably should though), but it doesn't hurt to prevent possible duplicates here

Overall, I still want to look if this function (newAPIClientFromEndpoint()) is the best place to set this; I'm considering if perhaps we should merge the config-file and flag options earlier, perhaps as part of DockerCLI.Initialize() ? (I'll have to have a look if that would work)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should probably use = as separator (--http-header My-Header=myvalue)

I'm trying to keep consistent with curl since it has been widely accepted. And I'm not sure if there are traps if we use =

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm considering if perhaps we should merge the config-file and flag options earlier, perhaps as part of DockerCLI.Initialize()

Agree. This will make the whole configing procedure more clear

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use strings.SplitN(.., ..., 2)

Addressed

k := strings.TrimSpace(split[0])
v := strings.TrimSpace(split[1])
customHeaders[k] = v
}

customHeaders["User-Agent"] = UserAgent()
clientOpts = append(clientOpts, client.WithHTTPHeaders(customHeaders))
return client.NewClientWithOpts(clientOpts...)
Expand Down
17 changes: 10 additions & 7 deletions cli/flags/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ var (

// CommonOptions are options common to both the client and the daemon.
type CommonOptions struct {
Debug bool
Hosts []string
LogLevel string
TLS bool
TLSVerify bool
TLSOptions *tlsconfig.Options
Context string
Debug bool
Hosts []string
LogLevel string
TLS bool
TLSVerify bool
TLSOptions *tlsconfig.Options
Context string
HttpHeaders []string

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w.r.t. the above (parsing) in mind I'm thinking if perhaps the value parsing should be part of a custom type (so that this would be an (e.g.) opts.HttpHeadersOpt with its own validation function, similar fo opts.ListOpts and opts. NamedListOpts

Thinking of which (sorry, bit hazy on that one), but I think the NamedListOpts and NamedOpts types were used for command-line flags that also have an equivalent in ~/.docker/config.json. Which (in this case) could be considered the case (the --http-headers being an equivalent for the HttpHeaders option in the config file)

(Hope this helps, but happy to do some further digging in this)

@mapleeit mapleeit May 31, 2022

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, you want to use opts.NamedMapOpts and write according validators for it, right?

}

// NewCommonOptions returns a new CommonOptions
Expand Down Expand Up @@ -87,6 +88,8 @@ func (commonOpts *CommonOptions) InstallFlags(flags *pflag.FlagSet) {
flags.VarP(hostOpt, "host", "H", "Daemon socket(s) to connect to")
flags.StringVarP(&commonOpts.Context, "context", "c", "",
`Name of the context to use to connect to the daemon (overrides `+client.EnvOverrideHost+` env var and default context set with "docker context use")`)
httpHeaderOpt := opts.NewNamedListOptsRef("http-headers", &commonOpts.HttpHeaders, nil)
flags.VarP(httpHeaderOpt, "http-header", "", "Custom HTTP headers")
}

// SetDefaultOptions sets default values for options after flag parsing is
Expand Down