aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/transport/common.go
diff options
context:
space:
mode:
authorSantiago M. Mola <santi@mola.io>2016-11-23 15:30:34 +0100
committerMáximo Cuadros <mcuadros@gmail.com>2016-11-23 15:38:12 +0100
commit08e08d771ef03df80248c80d81475fe7c5ea6fe7 (patch)
treed12e9befa22409e8cf50c5bbc4895e69fd8a5f48 /plumbing/transport/common.go
parent844169a739fb8bf1f252d416f10d8c7034db9fe2 (diff)
downloadgo-git-08e08d771ef03df80248c80d81475fe7c5ea6fe7.tar.gz
transport: create Client interface (#132)
* plumbing: move plumbing/client package to plumbing/transport. * transport: create Client interface. * A Client can instantiate any client transport service. * InstallProtocol installs a Client for a given protocol, instead of just a UploadPackService. * A Client can open a session for fetch-pack or send-pack for a specific Endpoint. * Adapt ssh and http clients to the new client interface. * updated doc
Diffstat (limited to 'plumbing/transport/common.go')
-rw-r--r--plumbing/transport/common.go121
1 files changed, 121 insertions, 0 deletions
diff --git a/plumbing/transport/common.go b/plumbing/transport/common.go
new file mode 100644
index 0000000..cc30564
--- /dev/null
+++ b/plumbing/transport/common.go
@@ -0,0 +1,121 @@
+// Package transport includes the implementation for different transport
+// protocols.
+//
+// `Client` can be used to fetch and send packfiles to a git server.
+// The `client` package provides higher level functions to instantiate the
+// appropiate `Client` based on the repository URL.
+//
+// Go-git supports HTTP and SSH (see `Protocols`), but you can also install
+// your own protocols (see the `client` package).
+//
+// Each protocol has its own implementation of `Client`, but you should
+// generally not use them directly, use `client.NewClient` instead.
+package transport
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net/url"
+ "regexp"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+)
+
+var (
+ ErrRepositoryNotFound = errors.New("repository not found")
+ ErrAuthorizationRequired = errors.New("authorization required")
+ ErrEmptyUploadPackRequest = errors.New("empty git-upload-pack given")
+ ErrInvalidAuthMethod = errors.New("invalid auth method")
+)
+
+const (
+ UploadPackServiceName = "git-upload-pack"
+)
+
+// Client can initiate git-fetch-pack and git-send-pack processes.
+type Client interface {
+ // NewFetchPackSession starts a git-fetch-pack session for an endpoint.
+ NewFetchPackSession(Endpoint) (FetchPackSession, error)
+ // NewSendPackSession starts a git-send-pack session for an endpoint.
+ NewSendPackSession(Endpoint) (SendPackSession, error)
+}
+
+type Session interface {
+ SetAuth(auth AuthMethod) error
+ io.Closer
+}
+
+type AuthMethod interface {
+ fmt.Stringer
+ Name() string
+}
+
+// FetchPackSession represents a git-fetch-pack session.
+// A git-fetch-pack session has two steps: reference discovery
+// (`AdvertisedReferences` function) and fetching pack (`FetchPack` function).
+// In that order.
+type FetchPackSession interface {
+ Session
+ // AdvertisedReferences retrieves the advertised references for a
+ // repository. It should be called before FetchPack, and it cannot be
+ // called after FetchPack.
+ AdvertisedReferences() (*UploadPackInfo, error)
+ // FetchPack takes a request and returns a reader for the packfile
+ // received from the server.
+ FetchPack(req *UploadPackRequest) (io.ReadCloser, error)
+}
+
+// FetchPackSession represents a git-send-pack session.
+// A git-send-pack session has two steps: reference discovery
+// (`AdvertisedReferences` function) and sending pack (`SendPack` function).
+// In that order.
+type SendPackSession interface {
+ Session
+ // AdvertisedReferences retrieves the advertised references for a
+ // repository. It should be called before FetchPack, and it cannot be
+ // called after FetchPack.
+ AdvertisedReferences() (*UploadPackInfo, error)
+ // UpdateReferences sends an update references request and returns a
+ // writer to be used for packfile writing.
+ //TODO: Complete signature.
+ SendPack() (io.WriteCloser, error)
+}
+
+type Endpoint url.URL
+
+var (
+ isSchemeRegExp = regexp.MustCompile("^[^:]+://")
+ scpLikeUrlRegExp = regexp.MustCompile("^(?P<user>[^@]+@)?(?P<host>[^:]+):/?(?P<path>.+)$")
+)
+
+func NewEndpoint(endpoint string) (Endpoint, error) {
+ endpoint = transformSCPLikeIfNeeded(endpoint)
+
+ u, err := url.Parse(endpoint)
+ if err != nil {
+ return Endpoint{}, plumbing.NewPermanentError(err)
+ }
+
+ if !u.IsAbs() {
+ return Endpoint{}, plumbing.NewPermanentError(fmt.Errorf(
+ "invalid endpoint: %s", endpoint,
+ ))
+ }
+
+ return Endpoint(*u), nil
+}
+
+func (e *Endpoint) String() string {
+ u := url.URL(*e)
+ return u.String()
+}
+
+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])
+ }
+
+ return endpoint
+}