Fix managed http/https transport failures #858
61
http.go
61
http.go
|
@ -1,6 +1,8 @@
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -190,8 +192,19 @@ func (self *httpSmartSubtransportStream) sendRequest() error {
|
||||||
|
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
var err error
|
var err error
|
||||||
var userName string
|
|
||||||
var password string
|
// Obtain the credentials and use them.
|
||||||
|
cred, err := self.owner.transport.SmartCredentials("", CredentialTypeUserpassPlaintext)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer cred.Free()
|
||||||
|
|
||||||
|
userName, password, err := cred.GetUserpassPlaintext()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
req := &http.Request{
|
req := &http.Request{
|
||||||
Method: self.req.Method,
|
Method: self.req.Method,
|
||||||
|
@ -204,7 +217,32 @@ func (self *httpSmartSubtransportStream) sendRequest() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
req.SetBasicAuth(userName, password)
|
req.SetBasicAuth(userName, password)
|
||||||
resp, err = http.DefaultClient.Do(req)
|
|
||||||
|
c := http.Client{}
|
||||||
|
|
||||||
|
cap := x509.NewCertPool()
|
||||||
|
|
||||||
|
// NOTE: self.req.URL.Host returns only host without port. To be
|
||||||
|
// able to fetch the correct certs from the global certs, parse again
|
||||||
|
// and get host+port with url.Host.
|
||||||
|
u, err := url.Parse(self.req.URL.String())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse URL: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use CA cert if found.
|
||||||
|
if cert, found := globalCACertPool.certPool[u.Host]; found {
|
||||||
|
if ok := cap.AppendCertsFromPEM(cert); !ok {
|
||||||
|
return fmt.Errorf("failed to parse CA cert")
|
||||||
|
}
|
||||||
|
c.Transport = &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
RootCAs: cap,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -213,23 +251,6 @@ func (self *httpSmartSubtransportStream) sendRequest() error {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode == http.StatusUnauthorized {
|
|
||||||
resp.Body.Close()
|
|
||||||
|
|
||||||
cred, err := self.owner.transport.SmartCredentials("", CredentialTypeUserpassPlaintext)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer cred.Free()
|
|
||||||
|
|
||||||
userName, password, err = cred.GetUserpassPlaintext()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any other error we treat as a hard error and punt back to the caller
|
// Any other error we treat as a hard error and punt back to the caller
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
return fmt.Errorf("Unhandled HTTP error %s", resp.Status)
|
return fmt.Errorf("Unhandled HTTP error %s", resp.Status)
|
||||||
|
|
31
transport.go
31
transport.go
|
@ -24,6 +24,7 @@ import "C"
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -39,8 +40,38 @@ var (
|
||||||
}{
|
}{
|
||||||
transports: make(map[string]*RegisteredSmartTransport),
|
transports: make(map[string]*RegisteredSmartTransport),
|
||||||
}
|
}
|
||||||
|
// globalCACertPool is a mapping of global CA certs used by git2go-managed
|
||||||
|
// transports. The map's key is hostname+port and the value is a
|
||||||
|
// corresponding CA cert.
|
||||||
|
// Since the git2go-managed transports aren't public, this can be used to
|
||||||
|
// provide certs to the subtransports that can be looked up for a given
|
||||||
|
// host.
|
||||||
|
globalCACertPool = struct {
|
||||||
|
sync.Mutex
|
||||||
|
certPool map[string][]byte
|
||||||
|
}{
|
||||||
|
certPool: make(map[string][]byte),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RegisterCACerts registers CA cert associated with an address in the
|
||||||
|
// globalCACertPool.
|
||||||
|
func RegisterCACerts(address string, caBundle []byte) error {
|
||||||
|
globalCACertPool.Lock()
|
||||||
|
defer globalCACertPool.Unlock()
|
||||||
|
// Ignore empty CA bundles.
|
||||||
|
if len(caBundle) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
u, err := url.Parse(address)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Store the certificate based on host+port, e.g.: 127.0.0.1:42107.
|
||||||
|
globalCACertPool.certPool[u.Host] = caBundle
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// unregisterManagedTransports unregisters all git2go-managed transports.
|
// unregisterManagedTransports unregisters all git2go-managed transports.
|
||||||
func unregisterManagedTransports() error {
|
func unregisterManagedTransports() error {
|
||||||
globalRegisteredSmartTransports.Lock()
|
globalRegisteredSmartTransports.Lock()
|
||||||
|
|
Loading…
Reference in New Issue