forked from git-lfs/git-lfs
/
endpoint.go
140 lines (121 loc) · 3.26 KB
/
endpoint.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package lfs
import (
"fmt"
"net/url"
"path"
"regexp"
"strings"
)
const EndpointUrlUnknown = "<unknown>"
// An Endpoint describes how to access a Git LFS server.
type Endpoint struct {
Url string
SshUserAndHost string
SshPath string
SshPort string
}
// NewEndpointFromCloneURL creates an Endpoint from a git clone URL by appending
// "[.git]/info/lfs".
func NewEndpointFromCloneURL(url string) Endpoint {
e := NewEndpoint(url)
if e.Url == EndpointUrlUnknown {
return e
}
// When using main remote URL for HTTP, append info/lfs
if path.Ext(url) == ".git" {
e.Url += "/info/lfs"
} else {
e.Url += ".git/info/lfs"
}
return e
}
// NewEndpoint initializes a new Endpoint for a given URL.
func NewEndpoint(rawurl string) Endpoint {
u, err := url.Parse(rawurl)
if err != nil {
return Endpoint{Url: EndpointUrlUnknown}
}
switch u.Scheme {
case "ssh":
return endpointFromSshUrl(u)
case "http", "https":
return endpointFromHttpUrl(u)
case "":
return endpointFromBareSshUrl(u)
default:
// Just passthrough to preserve
return Endpoint{Url: rawurl}
}
}
// endpointFromBareSshUrl constructs a new endpoint from a bare SSH URL:
//
// user@host.com:path/to/repo.git
//
func endpointFromBareSshUrl(u *url.URL) Endpoint {
parts := strings.Split(u.Path, ":")
partsLen := len(parts)
if partsLen < 2 {
return Endpoint{Url: u.String()}
}
// Treat presence of ':' as a bare URL
var newPath string
if len(parts) > 2 { // port included; really should only ever be 3 parts
newPath = fmt.Sprintf("%v:%v", parts[0], strings.Join(parts[1:], "/"))
} else {
newPath = strings.Join(parts, "/")
}
newrawurl := fmt.Sprintf("ssh://%v", newPath)
newu, err := url.Parse(newrawurl)
if err != nil {
return Endpoint{Url: EndpointUrlUnknown}
}
return endpointFromSshUrl(newu)
}
// endpointFromSshUrl constructs a new endpoint from an ssh:// URL
func endpointFromSshUrl(u *url.URL) Endpoint {
var endpoint Endpoint
// Pull out port now, we need it separately for SSH
regex := regexp.MustCompile(`^([^\:]+)(?:\:(\d+))?$`)
match := regex.FindStringSubmatch(u.Host)
if match == nil || len(match) < 2 {
endpoint.Url = EndpointUrlUnknown
return endpoint
}
host := match[1]
if u.User != nil && u.User.Username() != "" {
endpoint.SshUserAndHost = fmt.Sprintf("%s@%s", u.User.Username(), host)
} else {
endpoint.SshUserAndHost = host
}
if len(match) > 2 {
endpoint.SshPort = match[2]
}
// u.Path includes a preceding '/', strip off manually
// rooted paths in the URL will be '//path/to/blah'
// this is just how Go's URL parsing works
if strings.HasPrefix(u.Path, "/") {
endpoint.SshPath = u.Path[1:]
} else {
endpoint.SshPath = u.Path
}
// Fallback URL for using HTTPS while still using SSH for git
// u.Host includes host & port so can't use SSH port
endpoint.Url = fmt.Sprintf("https://%s%s", host, u.Path)
return endpoint
}
// Construct a new endpoint from a HTTP URL
func endpointFromHttpUrl(u *url.URL) Endpoint {
// just pass this straight through
return Endpoint{Url: u.String()}
}
func ObjectUrl(endpoint Endpoint, oid string) (*url.URL, error) {
u, err := url.Parse(endpoint.Url)
if err != nil {
return nil, err
}
u.Path = path.Join(u.Path, "objects")
if len(oid) > 0 {
u.Path = path.Join(u.Path, oid)
}
return u, nil
}