git-lfs/lfsapi/lfsapi.go

229 lines
5.0 KiB
Go
Raw Normal View History

package lfsapi
2016-12-16 22:43:05 +00:00
import (
"encoding/json"
2016-12-22 22:31:48 +00:00
"fmt"
2016-12-22 18:01:51 +00:00
"io"
2016-12-16 22:43:05 +00:00
"net/http"
"regexp"
2016-12-20 17:02:25 +00:00
"sync"
2016-12-16 22:43:05 +00:00
"github.com/ThomsonReutersEikon/go-ntlm/ntlm"
"github.com/git-lfs/git-lfs/config"
2016-12-16 22:47:02 +00:00
"github.com/git-lfs/git-lfs/errors"
"github.com/git-lfs/git-lfs/git"
2016-12-16 22:43:05 +00:00
)
var (
lfsMediaTypeRE = regexp.MustCompile(`\Aapplication/vnd\.git\-lfs\+json(;|\z)`)
jsonMediaTypeRE = regexp.MustCompile(`\Aapplication/json(;|\z)`)
)
type Client struct {
2016-12-19 21:38:06 +00:00
Endpoints EndpointFinder
Credentials CredentialHelper
SSH SSHResolver
2016-12-19 21:53:18 +00:00
Netrc NetrcFinder
2016-12-20 17:02:25 +00:00
DialTimeout int
KeepaliveTimeout int
TLSTimeout int
ConcurrentTransfers int
2016-12-20 18:22:20 +00:00
SkipSSLVerify bool
2016-12-20 17:29:26 +00:00
Verbose bool
DebuggingVerbose bool
2016-12-22 18:01:51 +00:00
VerboseOut io.Writer
2016-12-20 23:02:10 +00:00
2016-12-20 17:02:25 +00:00
hostClients map[string]*http.Client
clientMu sync.Mutex
2016-12-20 18:22:20 +00:00
ntlmSessions map[string]ntlm.ClientSession
ntlmMu sync.Mutex
httpLogger *syncLogger
2016-12-21 18:22:22 +00:00
LoggingStats bool // DEPRECATED
commandCredHelper *commandCredentialHelper
askpassCredHelper *AskPassCredentialHelper
cachingCredHelper *credentialCacher
gitEnv config.Environment
osEnv config.Environment
uc *config.URLConfig
sshTries int
}
type Context interface {
GitConfig() *git.Configuration
OSEnv() config.Environment
GitEnv() config.Environment
}
func NewClient(ctx Context) (*Client, error) {
if ctx == nil {
ctx = NewContext(nil, nil, nil)
2016-12-20 17:29:26 +00:00
}
gitEnv := ctx.GitEnv()
osEnv := ctx.OSEnv()
2017-09-29 17:45:52 +00:00
netrc, netrcfile, err := ParseNetrc(osEnv)
if err != nil {
2017-09-29 17:45:52 +00:00
return nil, errors.Wrap(err, fmt.Sprintf("bad netrc file %s", netrcfile))
}
cacheCreds := gitEnv.Bool("lfs.cachecredentials", true)
var sshResolver SSHResolver = &sshAuthClient{os: osEnv, git: gitEnv}
if cacheCreds {
sshResolver = withSSHCache(sshResolver)
}
c := &Client{
Endpoints: NewEndpointFinder(ctx),
SSH: sshResolver,
Netrc: netrc,
DialTimeout: gitEnv.Int("lfs.dialtimeout", 0),
KeepaliveTimeout: gitEnv.Int("lfs.keepalive", 0),
TLSTimeout: gitEnv.Int("lfs.tlstimeout", 0),
ConcurrentTransfers: gitEnv.Int("lfs.concurrenttransfers", 3),
2016-12-20 18:22:20 +00:00
SkipSSLVerify: !gitEnv.Bool("http.sslverify", true) || osEnv.Bool("GIT_SSL_NO_VERIFY", false),
Verbose: osEnv.Bool("GIT_CURL_VERBOSE", false),
DebuggingVerbose: osEnv.Bool("LFS_DEBUG_HTTP", false),
commandCredHelper: &commandCredentialHelper{
SkipPrompt: osEnv.Bool("GIT_TERMINAL_PROMPT", false),
},
gitEnv: gitEnv,
osEnv: osEnv,
uc: config.NewURLConfig(gitEnv),
sshTries: gitEnv.Int("lfs.ssh.retries", 5),
}
askpass, ok := osEnv.Get("GIT_ASKPASS")
if !ok {
askpass, ok = gitEnv.Get("core.askpass")
}
if !ok {
askpass, _ = osEnv.Get("SSH_ASKPASS")
}
if len(askpass) > 0 {
c.askpassCredHelper = &AskPassCredentialHelper{
Program: askpass,
}
}
if cacheCreds {
c.cachingCredHelper = newCredentialCacher()
}
return c, nil
}
func (c *Client) GitEnv() config.Environment {
return c.gitEnv
}
func (c *Client) OSEnv() config.Environment {
return c.osEnv
}
2016-12-22 22:31:48 +00:00
func IsDecodeTypeError(err error) bool {
_, ok := err.(*decodeTypeError)
return ok
}
type decodeTypeError struct {
Type string
}
func (e *decodeTypeError) TypeError() {}
func (e *decodeTypeError) Error() string {
return fmt.Sprintf("Expected json type, got: %q", e.Type)
}
func DecodeJSON(res *http.Response, obj interface{}) error {
2016-12-16 22:43:05 +00:00
ctype := res.Header.Get("Content-Type")
if !(lfsMediaTypeRE.MatchString(ctype) || jsonMediaTypeRE.MatchString(ctype)) {
2016-12-22 22:31:48 +00:00
return &decodeTypeError{Type: ctype}
2016-12-16 22:43:05 +00:00
}
err := json.NewDecoder(res.Body).Decode(obj)
res.Body.Close()
if err != nil {
return errors.Wrapf(err, "Unable to parse HTTP response for %s %s", res.Request.Method, res.Request.URL)
}
return nil
}
2016-12-20 17:29:26 +00:00
type testContext struct {
gitConfig *git.Configuration
osEnv config.Environment
gitEnv config.Environment
}
func (c *testContext) GitConfig() *git.Configuration {
return c.gitConfig
}
func (c *testContext) OSEnv() config.Environment {
return c.osEnv
}
func (c *testContext) GitEnv() config.Environment {
return c.gitEnv
}
func NewContext(gitConf *git.Configuration, osEnv, gitEnv map[string]string) Context {
c := &testContext{gitConfig: gitConf}
if c.gitConfig == nil {
c.gitConfig = git.NewConfig("", "")
}
if osEnv != nil {
c.osEnv = testEnv(osEnv)
} else {
c.osEnv = make(testEnv)
}
if gitEnv != nil {
c.gitEnv = testEnv(gitEnv)
} else {
c.gitEnv = make(testEnv)
}
return c
}
type testEnv map[string]string
func (e testEnv) Get(key string) (v string, ok bool) {
v, ok = e[key]
return
}
func (e testEnv) GetAll(key string) []string {
if v, ok := e.Get(key); ok {
return []string{v}
}
return make([]string, 0)
}
func (e testEnv) Int(key string, def int) int {
s, _ := e.Get(key)
return config.Int(s, def)
}
func (e testEnv) Bool(key string, def bool) bool {
s, _ := e.Get(key)
return config.Bool(s, def)
}
func (e testEnv) All() map[string][]string {
m := make(map[string][]string)
for k, _ := range e {
m[k] = e.GetAll(k)
}
return m
}