git-lfs/lfsapi/lfsapi.go

192 lines
4.1 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"
"strconv"
2016-12-20 17:29:26 +00:00
"strings"
2016-12-20 17:02:25 +00:00
"sync"
2016-12-16 22:43:05 +00:00
"github.com/ThomsonReutersEikon/go-ntlm/ntlm"
2016-12-16 22:47:02 +00:00
"github.com/git-lfs/git-lfs/errors"
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
HTTPSProxy string
HTTPProxy string
NoProxy string
2016-12-20 18:22:20 +00:00
SkipSSLVerify bool
2016-12-20 17:29:26 +00:00
Verbose bool
DebuggingVerbose bool
LoggingStats 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
2016-12-21 18:22:22 +00:00
transferBuckets map[string][]*http.Response
transferBucketMu sync.Mutex
transfers map[*http.Response]*httpTransfer
transferMu sync.Mutex
2016-12-20 18:22:20 +00:00
// only used for per-host ssl certs
gitEnv Env
osEnv Env
}
func NewClient(osEnv Env, gitEnv Env) (*Client, error) {
2016-12-20 17:29:26 +00:00
if osEnv == nil {
2017-01-06 18:34:43 +00:00
osEnv = make(TestEnv)
2016-12-20 17:29:26 +00:00
}
if gitEnv == nil {
2017-01-06 18:34:43 +00:00
gitEnv = make(TestEnv)
2016-12-20 17:29:26 +00:00
}
netrc, err := ParseNetrc(osEnv)
if err != nil {
return nil, err
}
2016-12-20 17:29:26 +00:00
httpsProxy, httpProxy, noProxy := getProxyServers(osEnv, gitEnv)
2017-03-24 17:27:52 +00:00
creds := withCredentialCache(&commandCredentialHelper{
SkipPrompt: !osEnv.Bool("GIT_TERMINAL_PROMPT", true),
2017-03-24 17:27:52 +00:00
})
2017-03-23 19:58:15 +00:00
sshResolver := withSSHCache(&sshAuthClient{os: osEnv})
c := &Client{
Endpoints: NewEndpointFinder(gitEnv),
Credentials: creds,
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", 0),
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),
LoggingStats: osEnv.Bool("GIT_LOG_STATS", false),
HTTPSProxy: httpsProxy,
HTTPProxy: httpProxy,
NoProxy: noProxy,
2016-12-20 18:22:20 +00:00
gitEnv: gitEnv,
osEnv: osEnv,
}
return c, nil
}
func (c *Client) GitEnv() Env {
return c.gitEnv
}
func (c *Client) OSEnv() Env {
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
// Env is an interface for the config.Environment methods that this package
// relies on.
type Env interface {
Get(string) (string, bool)
Int(string, int) int
Bool(string, bool) bool
All() map[string]string
}
2017-01-06 18:34:43 +00:00
// TestEnv is a basic config.Environment implementation. Only used in tests, or
// as a zero value to NewClient().
type TestEnv map[string]string
2016-12-20 17:29:26 +00:00
2017-01-06 18:34:43 +00:00
func (e TestEnv) Get(key string) (string, bool) {
2016-12-20 17:29:26 +00:00
v, ok := e[key]
return v, ok
}
2017-01-06 18:34:43 +00:00
func (e TestEnv) Int(key string, def int) (val int) {
s, _ := e.Get(key)
if len(s) == 0 {
return def
}
i, err := strconv.Atoi(s)
if err != nil {
return def
}
return i
}
2017-01-06 18:34:43 +00:00
func (e TestEnv) Bool(key string, def bool) (val bool) {
2016-12-20 17:29:26 +00:00
s, _ := e.Get(key)
if len(s) == 0 {
return def
}
switch strings.ToLower(s) {
case "true", "1", "on", "yes", "t":
return true
case "false", "0", "off", "no", "f":
return false
default:
return false
}
}
2017-01-06 18:34:43 +00:00
func (e TestEnv) All() map[string]string {
2016-12-20 17:29:26 +00:00
return e
}