git-lfs/lfsapi/lfsapi.go

250 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"
"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"
"github.com/git-lfs/git-lfs/config"
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
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
2016-12-20 18:22:20 +00:00
// only used for per-host ssl certs
gitEnv Env
osEnv Env
uc *config.URLConfig
}
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
}
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))
}
creds, err := getCredentialHelper(osEnv, gitEnv)
if err != nil {
return nil, errors.Wrap(err, "cannot find credential helper(s)")
}
var sshResolver SSHResolver = &sshAuthClient{os: osEnv}
if gitEnv.Bool("lfs.cachecredentials", true) {
sshResolver = withSSHCache(sshResolver)
}
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", 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),
2016-12-20 18:22:20 +00:00
gitEnv: gitEnv,
osEnv: osEnv,
uc: config.NewURLConfig(gitEnv),
}
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)
GetAll(string) []string
Int(string, int) int
Bool(string, bool) bool
All() map[string][]string
}
type UniqTestEnv map[string]string
func (e UniqTestEnv) Get(key string) (v string, ok bool) {
v, ok = e[key]
return
}
func (e UniqTestEnv) GetAll(key string) []string {
if v, ok := e.Get(key); ok {
return []string{v}
}
return make([]string, 0)
}
func (e UniqTestEnv) 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
}
func (e UniqTestEnv) Bool(key string, def bool) (val bool) {
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
}
}
func (e UniqTestEnv) All() map[string][]string {
m := make(map[string][]string)
for k, _ := range e {
m[k] = e.GetAll(k)
}
return m
}
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) {
all := e.GetAll(key)
if len(all) == 0 {
return "", false
}
return all[len(all)-1], true
}
func (e TestEnv) GetAll(key string) []string {
return e[key]
2016-12-20 17:29:26 +00:00
}
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
}
}
func (e TestEnv) All() map[string][]string {
2016-12-20 17:29:26 +00:00
return e
}