git-lfs/lfsapi/lfsapi.go

224 lines
4.6 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 config.Environment
osEnv config.Environment
uc *config.URLConfig
}
type Context interface {
OSEnv() config.Environment
GitEnv() config.Environment
}
func NewClient(ctx Context) (*Client, error) {
if ctx == nil {
return newClient(nil, nil)
2016-12-20 17:29:26 +00:00
}
return newClient(ctx.OSEnv(), ctx.GitEnv())
}
2016-12-20 17:29:26 +00:00
func newClient(osEnv, gitEnv config.Environment) (*Client, error) {
if osEnv == nil {
osEnv = make(testEnv)
}
2016-12-20 17:29:26 +00:00
if gitEnv == nil {
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() 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 {
osEnv config.Environment
gitEnv config.Environment
}
func (c *testContext) OSEnv() config.Environment {
return c.osEnv
}
func (c *testContext) GitEnv() config.Environment {
return c.gitEnv
}
func NewContext(osEnv, gitEnv map[string]string) Context {
c := &testContext{}
if osEnv != nil {
c.osEnv = testEnv(osEnv)
}
if gitEnv != nil {
c.gitEnv = testEnv(gitEnv)
}
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) (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 testEnv) 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 testEnv) All() map[string][]string {
m := make(map[string][]string)
for k, _ := range e {
m[k] = e.GetAll(k)
}
return m
}