endpoint.go

 1package lfs
 2
 3import (
 4	"fmt"
 5	"net/url"
 6	"strings"
 7)
 8
 9// Endpoint is a Git LFS endpoint.
10type Endpoint = *url.URL
11
12// NewEndpoint returns a new Git LFS endpoint.
13func NewEndpoint(rawurl string) (Endpoint, error) {
14	u, err := url.Parse(rawurl)
15	if err != nil {
16		e, err := endpointFromBareSSH(rawurl)
17		if err != nil {
18			return nil, err
19		}
20		u = e
21	}
22
23	u.Path = strings.TrimSuffix(u.Path, "/")
24
25	switch u.Scheme {
26	case "git":
27		// Use https for git:// URLs and strip the port if it exists.
28		u.Scheme = httpsScheme
29		if u.Port() != "" {
30			u.Host = u.Hostname()
31		}
32		fallthrough
33	case httpScheme, httpsScheme:
34		if strings.HasSuffix(u.Path, ".git") {
35			u.Path += "/info/lfs"
36		} else {
37			u.Path += ".git/info/lfs"
38		}
39	case "ssh", "git+ssh", "ssh+git":
40	default:
41		return nil, fmt.Errorf("unknown url: %s", rawurl)
42	}
43
44	return u, nil
45}
46
47// endpointFromBareSSH creates a new endpoint from a bare ssh repo.
48//
49//	user@host.com:path/to/repo.git or
50//	[user@host.com:port]:path/to/repo.git
51func endpointFromBareSSH(rawurl string) (*url.URL, error) {
52	parts := strings.Split(rawurl, ":")
53	partsLen := len(parts)
54	if partsLen < 2 {
55		return url.Parse(rawurl) //nolint:wrapcheck
56	}
57
58	// Treat presence of ':' as a bare URL
59	var newPath string
60	if len(parts) > 2 { // port included; really should only ever be 3 parts
61		// Correctly handle [host:port]:path URLs
62		parts[0] = strings.TrimPrefix(parts[0], "[")
63		parts[1] = strings.TrimSuffix(parts[1], "]")
64		newPath = fmt.Sprintf("%v:%v", parts[0], strings.Join(parts[1:], "/"))
65	} else {
66		newPath = strings.Join(parts, "/")
67	}
68	newrawurl := fmt.Sprintf("ssh://%v", newPath)
69	return url.Parse(newrawurl) //nolint:wrapcheck
70}