1 // Copyright 2018 Google LLC All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package transport 16 17 import ( 18 "context" 19 "net/http" 20 "strings" 21 22 "github.com/google/go-containerregistry/pkg/authn" 23 "github.com/google/go-containerregistry/pkg/name" 24 ) 25 26 // New returns a new RoundTripper based on the provided RoundTripper that has been 27 // setup to authenticate with the remote registry "reg", in the capacity 28 // laid out by the specified scopes. 29 // 30 // Deprecated: Use NewWithContext. 31 func New(reg name.Registry, auth authn.Authenticator, t http.RoundTripper, scopes []string) (http.RoundTripper, error) { 32 return NewWithContext(context.Background(), reg, auth, t, scopes) 33 } 34 35 // NewWithContext returns a new RoundTripper based on the provided RoundTripper that has been 36 // set up to authenticate with the remote registry "reg", in the capacity 37 // laid out by the specified scopes. 38 // In case the RoundTripper is already of the type Wrapper it assumes 39 // authentication was already done prior to this call, so it just returns 40 // the provided RoundTripper without further action 41 func NewWithContext(ctx context.Context, reg name.Registry, auth authn.Authenticator, t http.RoundTripper, scopes []string) (http.RoundTripper, error) { 42 // When the transport provided is of the type Wrapper this function assumes that the caller already 43 // executed the necessary login and check. 44 switch t.(type) { 45 case *Wrapper: 46 return t, nil 47 } 48 // The handshake: 49 // 1. Use "t" to ping() the registry for the authentication challenge. 50 // 51 // 2a. If we get back a 200, then simply use "t". 52 // 53 // 2b. If we get back a 401 with a Basic challenge, then use a transport 54 // that just attachs auth each roundtrip. 55 // 56 // 2c. If we get back a 401 with a Bearer challenge, then use a transport 57 // that attaches a bearer token to each request, and refreshes is on 401s. 58 // Perform an initial refresh to seed the bearer token. 59 60 // First we ping the registry to determine the parameters of the authentication handshake 61 // (if one is even necessary). 62 pr, err := Ping(ctx, reg, t) 63 if err != nil { 64 return nil, err 65 } 66 67 // Wrap t with a useragent transport unless we already have one. 68 if _, ok := t.(*userAgentTransport); !ok { 69 t = NewUserAgent(t, "") 70 } 71 72 scheme := "https" 73 if pr.Insecure { 74 scheme = "http" 75 } 76 77 // Wrap t in a transport that selects the appropriate scheme based on the ping response. 78 t = &schemeTransport{ 79 scheme: scheme, 80 registry: reg, 81 inner: t, 82 } 83 84 if strings.ToLower(pr.Scheme) != "bearer" { 85 return &Wrapper{&basicTransport{inner: t, auth: auth, target: reg.RegistryStr()}}, nil 86 } 87 88 bt, err := fromChallenge(reg, auth, t, pr) 89 if err != nil { 90 return nil, err 91 } 92 bt.scopes = scopes 93 94 if err := bt.refresh(ctx); err != nil { 95 return nil, err 96 } 97 return &Wrapper{bt}, nil 98 } 99 100 // Wrapper results in *not* wrapping supplied transport with additional logic such as retries, useragent and debug logging 101 // Consumers are opt-ing into providing their own transport without any additional wrapping. 102 type Wrapper struct { 103 inner http.RoundTripper 104 } 105 106 // RoundTrip delegates to the inner RoundTripper 107 func (w *Wrapper) RoundTrip(in *http.Request) (*http.Response, error) { 108 return w.inner.RoundTrip(in) 109 } 110