|
- // 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
- // appropriate `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 (
- "bytes"
- "context"
- "errors"
- "fmt"
- "io"
- "net/url"
- "strconv"
- "strings"
-
- giturl "gopkg.in/src-d/go-git.v4/internal/url"
- "gopkg.in/src-d/go-git.v4/plumbing"
- "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
- "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
- )
-
- var (
- ErrRepositoryNotFound = errors.New("repository not found")
- ErrEmptyRemoteRepository = errors.New("remote repository is empty")
- ErrAuthenticationRequired = errors.New("authentication required")
- 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")
- )
-
- const (
- UploadPackServiceName = "git-upload-pack"
- ReceivePackServiceName = "git-receive-pack"
- )
-
- // Transport can initiate git-upload-pack and git-receive-pack processes.
- // It is implemented both by the client and the server, making this a RPC.
- type Transport interface {
- // NewUploadPackSession starts a git-upload-pack session for an endpoint.
- NewUploadPackSession(*Endpoint, AuthMethod) (UploadPackSession, error)
- // NewReceivePackSession starts a git-receive-pack session for an endpoint.
- NewReceivePackSession(*Endpoint, AuthMethod) (ReceivePackSession, error)
- }
-
- type Session interface {
- // AdvertisedReferences retrieves the advertised references for a
- // repository.
- // If the repository does not exist, returns ErrRepositoryNotFound.
- // If the repository exists, but is empty, returns ErrEmptyRemoteRepository.
- AdvertisedReferences() (*packp.AdvRefs, error)
- io.Closer
- }
-
- type AuthMethod interface {
- fmt.Stringer
- Name() string
- }
-
- // UploadPackSession represents a git-upload-pack session.
- // A git-upload-pack session has two steps: reference discovery
- // (AdvertisedReferences) and uploading pack (UploadPack).
- type UploadPackSession interface {
- Session
- // UploadPack takes a git-upload-pack request and returns a response,
- // including a packfile. Don't be confused by terminology, the client
- // side of a git-upload-pack is called git-fetch-pack, although here
- // the same interface is used to make it RPC-like.
- UploadPack(context.Context, *packp.UploadPackRequest) (*packp.UploadPackResponse, error)
- }
-
- // ReceivePackSession represents a git-receive-pack session.
- // A git-receive-pack session has two steps: reference discovery
- // (AdvertisedReferences) and receiving pack (ReceivePack).
- // In that order.
- type ReceivePackSession interface {
- Session
- // ReceivePack sends an update references request and a packfile
- // reader and returns a ReportStatus and error. Don't be confused by
- // terminology, the client side of a git-receive-pack is called
- // git-send-pack, although here the same interface is used to make it
- // RPC-like.
- ReceivePack(context.Context, *packp.ReferenceUpdateRequest) (*packp.ReportStatus, error)
- }
-
- // Endpoint represents a Git URL in any supported protocol.
- type Endpoint struct {
- // Protocol is the protocol of the endpoint (e.g. git, https, file).
- Protocol string
- // User is the user.
- User string
- // Password is the password.
- Password string
- // Host is the host.
- Host string
- // Port is the port to connect, if 0 the default port for the given protocol
- // wil be used.
- Port int
- // Path is the repository path.
- Path string
- }
-
- var defaultPorts = map[string]int{
- "http": 80,
- "https": 443,
- "git": 9418,
- "ssh": 22,
- }
-
- // String returns a string representation of the Git URL.
- func (u *Endpoint) String() string {
- var buf bytes.Buffer
- if u.Protocol != "" {
- buf.WriteString(u.Protocol)
- buf.WriteByte(':')
- }
-
- if u.Protocol != "" || u.Host != "" || u.User != "" || u.Password != "" {
- buf.WriteString("//")
-
- if u.User != "" || u.Password != "" {
- buf.WriteString(url.PathEscape(u.User))
- if u.Password != "" {
- buf.WriteByte(':')
- buf.WriteString(url.PathEscape(u.Password))
- }
-
- buf.WriteByte('@')
- }
-
- if u.Host != "" {
- buf.WriteString(u.Host)
-
- if u.Port != 0 {
- port, ok := defaultPorts[strings.ToLower(u.Protocol)]
- if !ok || ok && port != u.Port {
- fmt.Fprintf(&buf, ":%d", u.Port)
- }
- }
- }
- }
-
- if u.Path != "" && u.Path[0] != '/' && u.Host != "" {
- buf.WriteByte('/')
- }
-
- buf.WriteString(u.Path)
- return buf.String()
- }
-
- func NewEndpoint(endpoint string) (*Endpoint, error) {
- if e, ok := parseSCPLike(endpoint); ok {
- return e, nil
- }
-
- if e, ok := parseFile(endpoint); ok {
- return e, nil
- }
-
- return parseURL(endpoint)
- }
-
- func parseURL(endpoint string) (*Endpoint, error) {
- u, err := url.Parse(endpoint)
- if err != nil {
- return nil, err
- }
-
- if !u.IsAbs() {
- return nil, plumbing.NewPermanentError(fmt.Errorf(
- "invalid endpoint: %s", endpoint,
- ))
- }
-
- var user, pass string
- if u.User != nil {
- user = u.User.Username()
- pass, _ = u.User.Password()
- }
-
- return &Endpoint{
- Protocol: u.Scheme,
- User: user,
- Password: pass,
- Host: u.Hostname(),
- Port: getPort(u),
- Path: getPath(u),
- }, nil
- }
-
- func getPort(u *url.URL) int {
- p := u.Port()
- if p == "" {
- return 0
- }
-
- i, err := strconv.Atoi(p)
- if err != nil {
- return 0
- }
-
- return i
- }
-
- func getPath(u *url.URL) string {
- var res string = u.Path
- if u.RawQuery != "" {
- res += "?" + u.RawQuery
- }
-
- if u.Fragment != "" {
- res += "#" + u.Fragment
- }
-
- return res
- }
-
- func parseSCPLike(endpoint string) (*Endpoint, bool) {
- if giturl.MatchesScheme(endpoint) || !giturl.MatchesScpLike(endpoint) {
- return nil, false
- }
-
- user, host, portStr, path := giturl.FindScpLikeComponents(endpoint)
- port, err := strconv.Atoi(portStr)
- if err != nil {
- port = 22
- }
-
- return &Endpoint{
- Protocol: "ssh",
- User: user,
- Host: host,
- Port: port,
- Path: path,
- }, true
- }
-
- func parseFile(endpoint string) (*Endpoint, bool) {
- if giturl.MatchesScheme(endpoint) {
- return nil, false
- }
-
- path := endpoint
- return &Endpoint{
- Protocol: "file",
- Path: path,
- }, true
- }
-
- // UnsupportedCapabilities are the capabilities not supported by any client
- // implementation
- var UnsupportedCapabilities = []capability.Capability{
- capability.MultiACK,
- capability.MultiACKDetailed,
- capability.ThinPack,
- }
-
- // FilterUnsupportedCapabilities it filter out all the UnsupportedCapabilities
- // from a capability.List, the intended usage is on the client implementation
- // to filter the capabilities from an AdvRefs message.
- func FilterUnsupportedCapabilities(list *capability.List) {
- for _, c := range UnsupportedCapabilities {
- list.Delete(c)
- }
- }
|