aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/transport/common.go
diff options
context:
space:
mode:
authorSantiago M. Mola <santi@mola.io>2017-04-26 17:04:59 +0200
committerSantiago M. Mola <santi@mola.io>2017-04-27 14:09:41 +0200
commit45bdbcbe6fdab5a8a4ed4f1b16c191f400a0f6b6 (patch)
tree5cd0364d068255d361a657963080a78c7ab735d9 /plumbing/transport/common.go
parent64cd72debb2a94a49de5ffd3c3a6bfd626df7340 (diff)
downloadgo-git-45bdbcbe6fdab5a8a4ed4f1b16c191f400a0f6b6.tar.gz
transport: make Endpoint an interface, fixes #362
* add internal *url.URL implementation for regular URLs. * add internal implementation for SCP-like URLs.
Diffstat (limited to 'plumbing/transport/common.go')
-rw-r--r--plumbing/transport/common.go131
1 files changed, 112 insertions, 19 deletions
diff --git a/plumbing/transport/common.go b/plumbing/transport/common.go
index d3cfe21..0ff9e89 100644
--- a/plumbing/transport/common.go
+++ b/plumbing/transport/common.go
@@ -18,6 +18,7 @@ import (
"io"
"net/url"
"regexp"
+ "strconv"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
@@ -28,7 +29,7 @@ var (
ErrRepositoryNotFound = errors.New("repository not found")
ErrEmptyRemoteRepository = errors.New("remote repository is empty")
ErrAuthenticationRequired = errors.New("authentication required")
- ErrAuthorizationFailed = errors.New("authorization failed")
+ ErrAuthorizationFailed = errors.New("authorization failed")
ErrEmptyUploadPackRequest = errors.New("empty git-upload-pack given")
ErrInvalidAuthMethod = errors.New("invalid auth method")
ErrAlreadyConnected = errors.New("session already established")
@@ -88,42 +89,134 @@ type ReceivePackSession interface {
ReceivePack(*packp.ReferenceUpdateRequest) (*packp.ReportStatus, error)
}
-type Endpoint url.URL
-
-var (
- isSchemeRegExp = regexp.MustCompile("^[^:]+://")
- scpLikeUrlRegExp = regexp.MustCompile("^(?P<user>[^@]+@)?(?P<host>[^:]+):/?(?P<path>.+)$")
-)
+// Endpoint represents a Git URL in any supported protocol.
+type Endpoint interface {
+ // Protocol returns the protocol (e.g. git, https, file). It should never
+ // return the empty string.
+ Protocol() string
+ // User returns the user or an empty string if none is given.
+ User() string
+ // Password returns the password or an empty string if none is given.
+ Password() string
+ // Host returns the host or an empty string if none is given.
+ Host() string
+ // Port returns the port or 0 if there is no port or a default should be
+ // used.
+ Port() int
+ // Path returns the repository path.
+ Path() string
+ // String returns a string representation of the Git URL.
+ String() string
+}
func NewEndpoint(endpoint string) (Endpoint, error) {
- endpoint = transformSCPLikeIfNeeded(endpoint)
+ if e, ok := parseSCPLike(endpoint); ok {
+ return e, nil
+ }
u, err := url.Parse(endpoint)
if err != nil {
- return Endpoint{}, plumbing.NewPermanentError(err)
+ return nil, plumbing.NewPermanentError(err)
}
if !u.IsAbs() {
- return Endpoint{}, plumbing.NewPermanentError(fmt.Errorf(
+ return nil, plumbing.NewPermanentError(fmt.Errorf(
"invalid endpoint: %s", endpoint,
))
}
- return Endpoint(*u), nil
+ return urlEndpoint{u}, nil
+}
+
+type urlEndpoint struct {
+ *url.URL
+}
+
+func (e urlEndpoint) Protocol() string { return e.URL.Scheme }
+func (e urlEndpoint) Host() string { return e.URL.Hostname() }
+
+func (e urlEndpoint) User() string {
+ if e.URL.User == nil {
+ return ""
+ }
+
+ return e.URL.User.Username()
+}
+
+func (e urlEndpoint) Password() string {
+ if e.URL.User == nil {
+ return ""
+ }
+
+ p, _ := e.URL.User.Password()
+ return p
+}
+
+func (e urlEndpoint) Port() int {
+ p := e.URL.Port()
+ if p == "" {
+ return 0
+ }
+
+ i, err := strconv.Atoi(e.URL.Port())
+ if err != nil {
+ return 0
+ }
+
+ return i
}
-func (e *Endpoint) String() string {
- u := url.URL(*e)
- return u.String()
+func (e urlEndpoint) Path() string {
+ var res string = e.URL.Path
+ if e.URL.RawQuery != "" {
+ res += "?" + e.URL.RawQuery
+ }
+
+ if e.URL.Fragment != "" {
+ res += "#" + e.URL.Fragment
+ }
+
+ return res
}
-func transformSCPLikeIfNeeded(endpoint string) string {
- if !isSchemeRegExp.MatchString(endpoint) && scpLikeUrlRegExp.MatchString(endpoint) {
- m := scpLikeUrlRegExp.FindStringSubmatch(endpoint)
- return fmt.Sprintf("ssh://%s%s/%s", m[1], m[2], m[3])
+type scpEndpoint struct {
+ user string
+ host string
+ path string
+}
+
+func (e *scpEndpoint) Protocol() string { return "ssh" }
+func (e *scpEndpoint) User() string { return e.user }
+func (e *scpEndpoint) Password() string { return "" }
+func (e *scpEndpoint) Host() string { return e.host }
+func (e *scpEndpoint) Port() int { return 22 }
+func (e *scpEndpoint) Path() string { return e.path }
+
+func (e *scpEndpoint) String() string {
+ var user string
+ if e.user != "" {
+ user = fmt.Sprintf("%s@", e.user)
+ }
+
+ return fmt.Sprintf("%s%s:%s", user, e.host, e.path)
+}
+
+var (
+ isSchemeRegExp = regexp.MustCompile("^[^:]+://")
+ scpLikeUrlRegExp = regexp.MustCompile("^(?:(?P<user>[^@]+)@)?(?P<host>[^:]+):/?(?P<path>.+)$")
+)
+
+func parseSCPLike(endpoint string) (Endpoint, bool) {
+ if isSchemeRegExp.MatchString(endpoint) || !scpLikeUrlRegExp.MatchString(endpoint) {
+ return nil, false
}
- return endpoint
+ m := scpLikeUrlRegExp.FindStringSubmatch(endpoint)
+ return &scpEndpoint{
+ user: m[1],
+ host: m[2],
+ path: m[3],
+ }, true
}
// UnsupportedCapabilities are the capabilities not supported by any client