forked from TrueCloudLab/distribution
Add transport package to support CancelRequest
Signed-off-by: Tibor Vass <tibor@docker.com>
This commit is contained in:
parent
9e6affc364
commit
808c87ce27
9 changed files with 136 additions and 118 deletions
106
docs/registry.go
106
docs/registry.go
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/docker/docker/autogen/dockerversion"
|
||||
"github.com/docker/docker/pkg/parsers/kernel"
|
||||
"github.com/docker/docker/pkg/timeoutconn"
|
||||
"github.com/docker/docker/pkg/transport"
|
||||
"github.com/docker/docker/pkg/useragent"
|
||||
)
|
||||
|
||||
|
@ -36,17 +37,32 @@ const (
|
|||
ConnectTimeout
|
||||
)
|
||||
|
||||
type httpsTransport struct {
|
||||
*http.Transport
|
||||
// dockerUserAgent is the User-Agent the Docker client uses to identify itself.
|
||||
// It is populated on init(), comprising version information of different components.
|
||||
var dockerUserAgent string
|
||||
|
||||
func init() {
|
||||
httpVersion := make([]useragent.VersionInfo, 0, 6)
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{"docker", dockerversion.VERSION})
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{"go", runtime.Version()})
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{"git-commit", dockerversion.GITCOMMIT})
|
||||
if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{"kernel", kernelVersion.String()})
|
||||
}
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{"os", runtime.GOOS})
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{"arch", runtime.GOARCH})
|
||||
|
||||
dockerUserAgent = useragent.AppendVersions("", httpVersion...)
|
||||
}
|
||||
|
||||
type httpsRequestModifier struct{ tlsConfig *tls.Config }
|
||||
|
||||
// DRAGONS(tiborvass): If someone wonders why do we set tlsconfig in a roundtrip,
|
||||
// it's because it's so as to match the current behavior in master: we generate the
|
||||
// certpool on every-goddam-request. It's not great, but it allows people to just put
|
||||
// the certs in /etc/docker/certs.d/.../ and let docker "pick it up" immediately. Would
|
||||
// prefer an fsnotify implementation, but that was out of scope of my refactoring.
|
||||
// TODO: improve things
|
||||
func (tr *httpsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
func (m *httpsRequestModifier) ModifyRequest(req *http.Request) error {
|
||||
var (
|
||||
roots *x509.CertPool
|
||||
certs []tls.Certificate
|
||||
|
@ -66,7 +82,7 @@ func (tr *httpsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||
logrus.Debugf("hostDir: %s", hostDir)
|
||||
fs, err := ioutil.ReadDir(hostDir)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, f := range fs {
|
||||
|
@ -77,7 +93,7 @@ func (tr *httpsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||
logrus.Debugf("crt: %s", hostDir+"/"+f.Name())
|
||||
data, err := ioutil.ReadFile(path.Join(hostDir, f.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
roots.AppendCertsFromPEM(data)
|
||||
}
|
||||
|
@ -86,11 +102,11 @@ func (tr *httpsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||
keyName := certName[:len(certName)-5] + ".key"
|
||||
logrus.Debugf("cert: %s", hostDir+"/"+f.Name())
|
||||
if !hasFile(fs, keyName) {
|
||||
return nil, fmt.Errorf("Missing key %s for certificate %s", keyName, certName)
|
||||
return fmt.Errorf("Missing key %s for certificate %s", keyName, certName)
|
||||
}
|
||||
cert, err := tls.LoadX509KeyPair(path.Join(hostDir, certName), path.Join(hostDir, keyName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
certs = append(certs, cert)
|
||||
}
|
||||
|
@ -99,38 +115,32 @@ func (tr *httpsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||
certName := keyName[:len(keyName)-4] + ".cert"
|
||||
logrus.Debugf("key: %s", hostDir+"/"+f.Name())
|
||||
if !hasFile(fs, certName) {
|
||||
return nil, fmt.Errorf("Missing certificate %s for key %s", certName, keyName)
|
||||
return fmt.Errorf("Missing certificate %s for key %s", certName, keyName)
|
||||
}
|
||||
}
|
||||
}
|
||||
if tr.Transport.TLSClientConfig == nil {
|
||||
tr.Transport.TLSClientConfig = &tls.Config{
|
||||
// Avoid fallback to SSL protocols < TLS1.0
|
||||
MinVersion: tls.VersionTLS10,
|
||||
}
|
||||
}
|
||||
tr.Transport.TLSClientConfig.RootCAs = roots
|
||||
tr.Transport.TLSClientConfig.Certificates = certs
|
||||
m.tlsConfig.RootCAs = roots
|
||||
m.tlsConfig.Certificates = certs
|
||||
}
|
||||
return tr.Transport.RoundTrip(req)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewTransport(timeout TimeoutType, secure bool) http.RoundTripper {
|
||||
tlsConfig := tls.Config{
|
||||
tlsConfig := &tls.Config{
|
||||
// Avoid fallback to SSL protocols < TLS1.0
|
||||
MinVersion: tls.VersionTLS10,
|
||||
InsecureSkipVerify: !secure,
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
tr := &http.Transport{
|
||||
DisableKeepAlives: true,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
TLSClientConfig: &tlsConfig,
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
|
||||
switch timeout {
|
||||
case ConnectTimeout:
|
||||
transport.Dial = func(proto string, addr string) (net.Conn, error) {
|
||||
tr.Dial = func(proto string, addr string) (net.Conn, error) {
|
||||
// Set the connect timeout to 30 seconds to allow for slower connection
|
||||
// times...
|
||||
d := net.Dialer{Timeout: 30 * time.Second, DualStack: true}
|
||||
|
@ -144,7 +154,7 @@ func NewTransport(timeout TimeoutType, secure bool) http.RoundTripper {
|
|||
return conn, nil
|
||||
}
|
||||
case ReceiveTimeout:
|
||||
transport.Dial = func(proto string, addr string) (net.Conn, error) {
|
||||
tr.Dial = func(proto string, addr string) (net.Conn, error) {
|
||||
d := net.Dialer{DualStack: true}
|
||||
|
||||
conn, err := d.Dial(proto, addr)
|
||||
|
@ -159,51 +169,23 @@ func NewTransport(timeout TimeoutType, secure bool) http.RoundTripper {
|
|||
if secure {
|
||||
// note: httpsTransport also handles http transport
|
||||
// but for HTTPS, it sets up the certs
|
||||
return &httpsTransport{transport}
|
||||
return transport.NewTransport(tr, &httpsRequestModifier{tlsConfig})
|
||||
}
|
||||
|
||||
return transport
|
||||
return tr
|
||||
}
|
||||
|
||||
type DockerHeaders struct {
|
||||
http.RoundTripper
|
||||
Headers http.Header
|
||||
}
|
||||
|
||||
// cloneRequest returns a clone of the provided *http.Request.
|
||||
// The clone is a shallow copy of the struct and its Header map
|
||||
func cloneRequest(r *http.Request) *http.Request {
|
||||
// shallow copy of the struct
|
||||
r2 := new(http.Request)
|
||||
*r2 = *r
|
||||
// deep copy of the Header
|
||||
r2.Header = make(http.Header, len(r.Header))
|
||||
for k, s := range r.Header {
|
||||
r2.Header[k] = append([]string(nil), s...)
|
||||
// DockerHeaders returns request modifiers that ensure requests have
|
||||
// the User-Agent header set to dockerUserAgent and that metaHeaders
|
||||
// are added.
|
||||
func DockerHeaders(metaHeaders http.Header) []transport.RequestModifier {
|
||||
modifiers := []transport.RequestModifier{
|
||||
transport.NewHeaderRequestModifier(http.Header{"User-Agent": []string{dockerUserAgent}}),
|
||||
}
|
||||
return r2
|
||||
}
|
||||
|
||||
func (tr *DockerHeaders) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
req = cloneRequest(req)
|
||||
httpVersion := make([]useragent.VersionInfo, 0, 4)
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{"docker", dockerversion.VERSION})
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{"go", runtime.Version()})
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{"git-commit", dockerversion.GITCOMMIT})
|
||||
if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{"kernel", kernelVersion.String()})
|
||||
if metaHeaders != nil {
|
||||
modifiers = append(modifiers, transport.NewHeaderRequestModifier(metaHeaders))
|
||||
}
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{"os", runtime.GOOS})
|
||||
httpVersion = append(httpVersion, useragent.VersionInfo{"arch", runtime.GOARCH})
|
||||
|
||||
userAgent := useragent.AppendVersions(req.UserAgent(), httpVersion...)
|
||||
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
|
||||
for k, v := range tr.Headers {
|
||||
req.Header[k] = v
|
||||
}
|
||||
return tr.RoundTripper.RoundTrip(req)
|
||||
return modifiers
|
||||
}
|
||||
|
||||
type debugTransport struct{ http.RoundTripper }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue