feat(dir2config): defaults
This commit is contained in:
48
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/client/client.go
generated
vendored
Normal file
48
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/client/client.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
// Package client contains helper function to deal with the different client
|
||||
// protocols.
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/file"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/git"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
|
||||
)
|
||||
|
||||
// Protocols are the protocols supported by default.
|
||||
var Protocols = map[string]transport.Transport{
|
||||
"http": http.DefaultClient,
|
||||
"https": http.DefaultClient,
|
||||
"ssh": ssh.DefaultClient,
|
||||
"git": git.DefaultClient,
|
||||
"file": file.DefaultClient,
|
||||
}
|
||||
|
||||
// InstallProtocol adds or modifies an existing protocol.
|
||||
func InstallProtocol(scheme string, c transport.Transport) {
|
||||
if c == nil {
|
||||
delete(Protocols, scheme)
|
||||
return
|
||||
}
|
||||
|
||||
Protocols[scheme] = c
|
||||
}
|
||||
|
||||
// NewClient returns the appropriate client among of the set of known protocols:
|
||||
// http://, https://, ssh:// and file://.
|
||||
// See `InstallProtocol` to add or modify protocols.
|
||||
func NewClient(endpoint *transport.Endpoint) (transport.Transport, error) {
|
||||
f, ok := Protocols[endpoint.Protocol]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported scheme %q", endpoint.Protocol)
|
||||
}
|
||||
|
||||
if f == nil {
|
||||
return nil, fmt.Errorf("malformed client for scheme %q, client is defined as nil", endpoint.Protocol)
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
274
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/common.go
generated
vendored
Normal file
274
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/common.go
generated
vendored
Normal file
@ -0,0 +1,274 @@
|
||||
// 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)
|
||||
}
|
||||
}
|
156
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/file/client.go
generated
vendored
Normal file
156
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/file/client.go
generated
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
// Package file implements the file transport protocol.
|
||||
package file
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common"
|
||||
)
|
||||
|
||||
// DefaultClient is the default local client.
|
||||
var DefaultClient = NewClient(
|
||||
transport.UploadPackServiceName,
|
||||
transport.ReceivePackServiceName,
|
||||
)
|
||||
|
||||
type runner struct {
|
||||
UploadPackBin string
|
||||
ReceivePackBin string
|
||||
}
|
||||
|
||||
// NewClient returns a new local client using the given git-upload-pack and
|
||||
// git-receive-pack binaries.
|
||||
func NewClient(uploadPackBin, receivePackBin string) transport.Transport {
|
||||
return common.NewClient(&runner{
|
||||
UploadPackBin: uploadPackBin,
|
||||
ReceivePackBin: receivePackBin,
|
||||
})
|
||||
}
|
||||
|
||||
func prefixExecPath(cmd string) (string, error) {
|
||||
// Use `git --exec-path` to find the exec path.
|
||||
execCmd := exec.Command("git", "--exec-path")
|
||||
|
||||
stdout, err := execCmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdoutBuf := bufio.NewReader(stdout)
|
||||
|
||||
err = execCmd.Start()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
execPathBytes, isPrefix, err := stdoutBuf.ReadLine()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if isPrefix {
|
||||
return "", errors.New("Couldn't read exec-path line all at once")
|
||||
}
|
||||
|
||||
err = execCmd.Wait()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
execPath := string(execPathBytes)
|
||||
execPath = strings.TrimSpace(execPath)
|
||||
cmd = filepath.Join(execPath, cmd)
|
||||
|
||||
// Make sure it actually exists.
|
||||
_, err = exec.LookPath(cmd)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func (r *runner) Command(cmd string, ep *transport.Endpoint, auth transport.AuthMethod,
|
||||
) (common.Command, error) {
|
||||
|
||||
switch cmd {
|
||||
case transport.UploadPackServiceName:
|
||||
cmd = r.UploadPackBin
|
||||
case transport.ReceivePackServiceName:
|
||||
cmd = r.ReceivePackBin
|
||||
}
|
||||
|
||||
_, err := exec.LookPath(cmd)
|
||||
if err != nil {
|
||||
if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
|
||||
cmd, err = prefixExecPath(cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &command{cmd: exec.Command(cmd, ep.Path)}, nil
|
||||
}
|
||||
|
||||
type command struct {
|
||||
cmd *exec.Cmd
|
||||
stderrCloser io.Closer
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (c *command) Start() error {
|
||||
return c.cmd.Start()
|
||||
}
|
||||
|
||||
func (c *command) StderrPipe() (io.Reader, error) {
|
||||
// Pipe returned by Command.StderrPipe has a race with Read + Command.Wait.
|
||||
// We use an io.Pipe and close it after the command finishes.
|
||||
r, w := io.Pipe()
|
||||
c.cmd.Stderr = w
|
||||
c.stderrCloser = r
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (c *command) StdinPipe() (io.WriteCloser, error) {
|
||||
return c.cmd.StdinPipe()
|
||||
}
|
||||
|
||||
func (c *command) StdoutPipe() (io.Reader, error) {
|
||||
return c.cmd.StdoutPipe()
|
||||
}
|
||||
|
||||
func (c *command) Kill() error {
|
||||
c.cmd.Process.Kill()
|
||||
return c.Close()
|
||||
}
|
||||
|
||||
// Close waits for the command to exit.
|
||||
func (c *command) Close() error {
|
||||
if c.closed {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
c.closed = true
|
||||
_ = c.stderrCloser.Close()
|
||||
|
||||
}()
|
||||
|
||||
err := c.cmd.Wait()
|
||||
if _, ok := err.(*os.PathError); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// When a repository does not exist, the command exits with code 128.
|
||||
if _, ok := err.(*exec.ExitError); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
53
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/file/server.go
generated
vendored
Normal file
53
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/file/server.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/server"
|
||||
"gopkg.in/src-d/go-git.v4/utils/ioutil"
|
||||
)
|
||||
|
||||
// ServeUploadPack serves a git-upload-pack request using standard output, input
|
||||
// and error. This is meant to be used when implementing a git-upload-pack
|
||||
// command.
|
||||
func ServeUploadPack(path string) error {
|
||||
ep, err := transport.NewEndpoint(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: define and implement a server-side AuthMethod
|
||||
s, err := server.DefaultServer.NewUploadPackSession(ep, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating session: %s", err)
|
||||
}
|
||||
|
||||
return common.ServeUploadPack(srvCmd, s)
|
||||
}
|
||||
|
||||
// ServeReceivePack serves a git-receive-pack request using standard output,
|
||||
// input and error. This is meant to be used when implementing a
|
||||
// git-receive-pack command.
|
||||
func ServeReceivePack(path string) error {
|
||||
ep, err := transport.NewEndpoint(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: define and implement a server-side AuthMethod
|
||||
s, err := server.DefaultServer.NewReceivePackSession(ep, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating session: %s", err)
|
||||
}
|
||||
|
||||
return common.ServeReceivePack(srvCmd, s)
|
||||
}
|
||||
|
||||
var srvCmd = common.ServerCommand{
|
||||
Stdin: os.Stdin,
|
||||
Stdout: ioutil.WriteNopCloser(os.Stdout),
|
||||
Stderr: os.Stderr,
|
||||
}
|
109
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/git/common.go
generated
vendored
Normal file
109
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/git/common.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
// Package git implements the git transport protocol.
|
||||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common"
|
||||
"gopkg.in/src-d/go-git.v4/utils/ioutil"
|
||||
)
|
||||
|
||||
// DefaultClient is the default git client.
|
||||
var DefaultClient = common.NewClient(&runner{})
|
||||
|
||||
const DefaultPort = 9418
|
||||
|
||||
type runner struct{}
|
||||
|
||||
// Command returns a new Command for the given cmd in the given Endpoint
|
||||
func (r *runner) Command(cmd string, ep *transport.Endpoint, auth transport.AuthMethod) (common.Command, error) {
|
||||
// auth not allowed since git protocol doesn't support authentication
|
||||
if auth != nil {
|
||||
return nil, transport.ErrInvalidAuthMethod
|
||||
}
|
||||
c := &command{command: cmd, endpoint: ep}
|
||||
if err := c.connect(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type command struct {
|
||||
conn net.Conn
|
||||
connected bool
|
||||
command string
|
||||
endpoint *transport.Endpoint
|
||||
}
|
||||
|
||||
// Start executes the command sending the required message to the TCP connection
|
||||
func (c *command) Start() error {
|
||||
cmd := endpointToCommand(c.command, c.endpoint)
|
||||
|
||||
e := pktline.NewEncoder(c.conn)
|
||||
return e.Encode([]byte(cmd))
|
||||
}
|
||||
|
||||
func (c *command) connect() error {
|
||||
if c.connected {
|
||||
return transport.ErrAlreadyConnected
|
||||
}
|
||||
|
||||
var err error
|
||||
c.conn, err = net.Dial("tcp", c.getHostWithPort())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.connected = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) getHostWithPort() string {
|
||||
host := c.endpoint.Host
|
||||
port := c.endpoint.Port
|
||||
if port <= 0 {
|
||||
port = DefaultPort
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%d", host, port)
|
||||
}
|
||||
|
||||
// StderrPipe git protocol doesn't have any dedicated error channel
|
||||
func (c *command) StderrPipe() (io.Reader, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// StdinPipe return the underlying connection as WriteCloser, wrapped to prevent
|
||||
// call to the Close function from the connection, a command execution in git
|
||||
// protocol can't be closed or killed
|
||||
func (c *command) StdinPipe() (io.WriteCloser, error) {
|
||||
return ioutil.WriteNopCloser(c.conn), nil
|
||||
}
|
||||
|
||||
// StdoutPipe return the underlying connection as Reader
|
||||
func (c *command) StdoutPipe() (io.Reader, error) {
|
||||
return c.conn, nil
|
||||
}
|
||||
|
||||
func endpointToCommand(cmd string, ep *transport.Endpoint) string {
|
||||
host := ep.Host
|
||||
if ep.Port != DefaultPort {
|
||||
host = fmt.Sprintf("%s:%d", ep.Host, ep.Port)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %s%chost=%s%c", cmd, ep.Path, 0, host, 0)
|
||||
}
|
||||
|
||||
// Close closes the TCP connection and connection.
|
||||
func (c *command) Close() error {
|
||||
if !c.connected {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.connected = false
|
||||
return c.conn.Close()
|
||||
}
|
281
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/http/common.go
generated
vendored
Normal file
281
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/http/common.go
generated
vendored
Normal file
@ -0,0 +1,281 @@
|
||||
// Package http implements the HTTP transport protocol.
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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/transport"
|
||||
"gopkg.in/src-d/go-git.v4/utils/ioutil"
|
||||
)
|
||||
|
||||
// it requires a bytes.Buffer, because we need to know the length
|
||||
func applyHeadersToRequest(req *http.Request, content *bytes.Buffer, host string, requestType string) {
|
||||
req.Header.Add("User-Agent", "git/1.0")
|
||||
req.Header.Add("Host", host) // host:port
|
||||
|
||||
if content == nil {
|
||||
req.Header.Add("Accept", "*/*")
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Add("Accept", fmt.Sprintf("application/x-%s-result", requestType))
|
||||
req.Header.Add("Content-Type", fmt.Sprintf("application/x-%s-request", requestType))
|
||||
req.Header.Add("Content-Length", strconv.Itoa(content.Len()))
|
||||
}
|
||||
|
||||
const infoRefsPath = "/info/refs"
|
||||
|
||||
func advertisedReferences(s *session, serviceName string) (ref *packp.AdvRefs, err error) {
|
||||
url := fmt.Sprintf(
|
||||
"%s%s?service=%s",
|
||||
s.endpoint.String(), infoRefsPath, serviceName,
|
||||
)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.ApplyAuthToRequest(req)
|
||||
applyHeadersToRequest(req, nil, s.endpoint.Host, serviceName)
|
||||
res, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.ModifyEndpointIfRedirect(res)
|
||||
defer ioutil.CheckClose(res.Body, &err)
|
||||
|
||||
if err = NewErr(res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ar := packp.NewAdvRefs()
|
||||
if err = ar.Decode(res.Body); err != nil {
|
||||
if err == packp.ErrEmptyAdvRefs {
|
||||
err = transport.ErrEmptyRemoteRepository
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
transport.FilterUnsupportedCapabilities(ar.Capabilities)
|
||||
s.advRefs = ar
|
||||
|
||||
return ar, nil
|
||||
}
|
||||
|
||||
type client struct {
|
||||
c *http.Client
|
||||
}
|
||||
|
||||
// DefaultClient is the default HTTP client, which uses `http.DefaultClient`.
|
||||
var DefaultClient = NewClient(nil)
|
||||
|
||||
// NewClient creates a new client with a custom net/http client.
|
||||
// See `InstallProtocol` to install and override default http client.
|
||||
// Unless a properly initialized client is given, it will fall back into
|
||||
// `http.DefaultClient`.
|
||||
//
|
||||
// Note that for HTTP client cannot distinguist between private repositories and
|
||||
// unexistent repositories on GitHub. So it returns `ErrAuthorizationRequired`
|
||||
// for both.
|
||||
func NewClient(c *http.Client) transport.Transport {
|
||||
if c == nil {
|
||||
return &client{http.DefaultClient}
|
||||
}
|
||||
|
||||
return &client{
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) NewUploadPackSession(ep *transport.Endpoint, auth transport.AuthMethod) (
|
||||
transport.UploadPackSession, error) {
|
||||
|
||||
return newUploadPackSession(c.c, ep, auth)
|
||||
}
|
||||
|
||||
func (c *client) NewReceivePackSession(ep *transport.Endpoint, auth transport.AuthMethod) (
|
||||
transport.ReceivePackSession, error) {
|
||||
|
||||
return newReceivePackSession(c.c, ep, auth)
|
||||
}
|
||||
|
||||
type session struct {
|
||||
auth AuthMethod
|
||||
client *http.Client
|
||||
endpoint *transport.Endpoint
|
||||
advRefs *packp.AdvRefs
|
||||
}
|
||||
|
||||
func newSession(c *http.Client, ep *transport.Endpoint, auth transport.AuthMethod) (*session, error) {
|
||||
s := &session{
|
||||
auth: basicAuthFromEndpoint(ep),
|
||||
client: c,
|
||||
endpoint: ep,
|
||||
}
|
||||
if auth != nil {
|
||||
a, ok := auth.(AuthMethod)
|
||||
if !ok {
|
||||
return nil, transport.ErrInvalidAuthMethod
|
||||
}
|
||||
|
||||
s.auth = a
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *session) ApplyAuthToRequest(req *http.Request) {
|
||||
if s.auth == nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.auth.setAuth(req)
|
||||
}
|
||||
|
||||
func (s *session) ModifyEndpointIfRedirect(res *http.Response) {
|
||||
if res.Request == nil {
|
||||
return
|
||||
}
|
||||
|
||||
r := res.Request
|
||||
if !strings.HasSuffix(r.URL.Path, infoRefsPath) {
|
||||
return
|
||||
}
|
||||
|
||||
h, p, err := net.SplitHostPort(r.URL.Host)
|
||||
if err != nil {
|
||||
h = r.URL.Host
|
||||
}
|
||||
if p != "" {
|
||||
port, err := strconv.Atoi(p)
|
||||
if err == nil {
|
||||
s.endpoint.Port = port
|
||||
}
|
||||
}
|
||||
s.endpoint.Host = h
|
||||
|
||||
s.endpoint.Protocol = r.URL.Scheme
|
||||
s.endpoint.Path = r.URL.Path[:len(r.URL.Path)-len(infoRefsPath)]
|
||||
}
|
||||
|
||||
func (*session) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AuthMethod is concrete implementation of common.AuthMethod for HTTP services
|
||||
type AuthMethod interface {
|
||||
transport.AuthMethod
|
||||
setAuth(r *http.Request)
|
||||
}
|
||||
|
||||
func basicAuthFromEndpoint(ep *transport.Endpoint) *BasicAuth {
|
||||
u := ep.User
|
||||
if u == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &BasicAuth{u, ep.Password}
|
||||
}
|
||||
|
||||
// BasicAuth represent a HTTP basic auth
|
||||
type BasicAuth struct {
|
||||
Username, Password string
|
||||
}
|
||||
|
||||
func (a *BasicAuth) setAuth(r *http.Request) {
|
||||
if a == nil {
|
||||
return
|
||||
}
|
||||
|
||||
r.SetBasicAuth(a.Username, a.Password)
|
||||
}
|
||||
|
||||
// Name is name of the auth
|
||||
func (a *BasicAuth) Name() string {
|
||||
return "http-basic-auth"
|
||||
}
|
||||
|
||||
func (a *BasicAuth) String() string {
|
||||
masked := "*******"
|
||||
if a.Password == "" {
|
||||
masked = "<empty>"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s - %s:%s", a.Name(), a.Username, masked)
|
||||
}
|
||||
|
||||
// TokenAuth implements an http.AuthMethod that can be used with http transport
|
||||
// to authenticate with HTTP token authentication (also known as bearer
|
||||
// authentication).
|
||||
//
|
||||
// IMPORTANT: If you are looking to use OAuth tokens with popular servers (e.g.
|
||||
// GitHub, Bitbucket, GitLab) you should use BasicAuth instead. These servers
|
||||
// use basic HTTP authentication, with the OAuth token as user or password.
|
||||
// Check the documentation of your git server for details.
|
||||
type TokenAuth struct {
|
||||
Token string
|
||||
}
|
||||
|
||||
func (a *TokenAuth) setAuth(r *http.Request) {
|
||||
if a == nil {
|
||||
return
|
||||
}
|
||||
r.Header.Add("Authorization", fmt.Sprintf("Bearer %s", a.Token))
|
||||
}
|
||||
|
||||
// Name is name of the auth
|
||||
func (a *TokenAuth) Name() string {
|
||||
return "http-token-auth"
|
||||
}
|
||||
|
||||
func (a *TokenAuth) String() string {
|
||||
masked := "*******"
|
||||
if a.Token == "" {
|
||||
masked = "<empty>"
|
||||
}
|
||||
return fmt.Sprintf("%s - %s", a.Name(), masked)
|
||||
}
|
||||
|
||||
// Err is a dedicated error to return errors based on status code
|
||||
type Err struct {
|
||||
Response *http.Response
|
||||
}
|
||||
|
||||
// NewErr returns a new Err based on a http response
|
||||
func NewErr(r *http.Response) error {
|
||||
if r.StatusCode >= http.StatusOK && r.StatusCode < http.StatusMultipleChoices {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch r.StatusCode {
|
||||
case http.StatusUnauthorized:
|
||||
return transport.ErrAuthenticationRequired
|
||||
case http.StatusForbidden:
|
||||
return transport.ErrAuthorizationFailed
|
||||
case http.StatusNotFound:
|
||||
return transport.ErrRepositoryNotFound
|
||||
}
|
||||
|
||||
return plumbing.NewUnexpectedError(&Err{r})
|
||||
}
|
||||
|
||||
// StatusCode returns the status code of the response
|
||||
func (e *Err) StatusCode() int {
|
||||
return e.Response.StatusCode
|
||||
}
|
||||
|
||||
func (e *Err) Error() string {
|
||||
return fmt.Sprintf("unexpected requesting %q status code: %d",
|
||||
e.Response.Request.URL, e.Response.StatusCode,
|
||||
)
|
||||
}
|
106
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/http/receive_pack.go
generated
vendored
Normal file
106
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/http/receive_pack.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"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"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
||||
"gopkg.in/src-d/go-git.v4/utils/ioutil"
|
||||
)
|
||||
|
||||
type rpSession struct {
|
||||
*session
|
||||
}
|
||||
|
||||
func newReceivePackSession(c *http.Client, ep *transport.Endpoint, auth transport.AuthMethod) (transport.ReceivePackSession, error) {
|
||||
s, err := newSession(c, ep, auth)
|
||||
return &rpSession{s}, err
|
||||
}
|
||||
|
||||
func (s *rpSession) AdvertisedReferences() (*packp.AdvRefs, error) {
|
||||
return advertisedReferences(s.session, transport.ReceivePackServiceName)
|
||||
}
|
||||
|
||||
func (s *rpSession) ReceivePack(ctx context.Context, req *packp.ReferenceUpdateRequest) (
|
||||
*packp.ReportStatus, error) {
|
||||
url := fmt.Sprintf(
|
||||
"%s/%s",
|
||||
s.endpoint.String(), transport.ReceivePackServiceName,
|
||||
)
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
if err := req.Encode(buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := s.doRequest(ctx, http.MethodPost, url, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := ioutil.NonEmptyReader(res.Body)
|
||||
if err == ioutil.ErrEmptyReader {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var d *sideband.Demuxer
|
||||
if req.Capabilities.Supports(capability.Sideband64k) {
|
||||
d = sideband.NewDemuxer(sideband.Sideband64k, r)
|
||||
} else if req.Capabilities.Supports(capability.Sideband) {
|
||||
d = sideband.NewDemuxer(sideband.Sideband, r)
|
||||
}
|
||||
if d != nil {
|
||||
d.Progress = req.Progress
|
||||
r = d
|
||||
}
|
||||
|
||||
rc := ioutil.NewReadCloser(r, res.Body)
|
||||
|
||||
report := packp.NewReportStatus()
|
||||
if err := report.Decode(rc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return report, report.Error()
|
||||
}
|
||||
|
||||
func (s *rpSession) doRequest(
|
||||
ctx context.Context, method, url string, content *bytes.Buffer,
|
||||
) (*http.Response, error) {
|
||||
|
||||
var body io.Reader
|
||||
if content != nil {
|
||||
body = content
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
return nil, plumbing.NewPermanentError(err)
|
||||
}
|
||||
|
||||
applyHeadersToRequest(req, content, s.endpoint.Host, transport.ReceivePackServiceName)
|
||||
s.ApplyAuthToRequest(req)
|
||||
|
||||
res, err := s.client.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, plumbing.NewUnexpectedError(err)
|
||||
}
|
||||
|
||||
if err := NewErr(res); err != nil {
|
||||
_ = res.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
123
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/http/upload_pack.go
generated
vendored
Normal file
123
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/http/upload_pack.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common"
|
||||
"gopkg.in/src-d/go-git.v4/utils/ioutil"
|
||||
)
|
||||
|
||||
type upSession struct {
|
||||
*session
|
||||
}
|
||||
|
||||
func newUploadPackSession(c *http.Client, ep *transport.Endpoint, auth transport.AuthMethod) (transport.UploadPackSession, error) {
|
||||
s, err := newSession(c, ep, auth)
|
||||
return &upSession{s}, err
|
||||
}
|
||||
|
||||
func (s *upSession) AdvertisedReferences() (*packp.AdvRefs, error) {
|
||||
return advertisedReferences(s.session, transport.UploadPackServiceName)
|
||||
}
|
||||
|
||||
func (s *upSession) UploadPack(
|
||||
ctx context.Context, req *packp.UploadPackRequest,
|
||||
) (*packp.UploadPackResponse, error) {
|
||||
|
||||
if req.IsEmpty() {
|
||||
return nil, transport.ErrEmptyUploadPackRequest
|
||||
}
|
||||
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf(
|
||||
"%s/%s",
|
||||
s.endpoint.String(), transport.UploadPackServiceName,
|
||||
)
|
||||
|
||||
content, err := uploadPackRequestToReader(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := s.doRequest(ctx, http.MethodPost, url, content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := ioutil.NonEmptyReader(res.Body)
|
||||
if err != nil {
|
||||
if err == ioutil.ErrEmptyReader || err == io.ErrUnexpectedEOF {
|
||||
return nil, transport.ErrEmptyUploadPackRequest
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rc := ioutil.NewReadCloser(r, res.Body)
|
||||
return common.DecodeUploadPackResponse(rc, req)
|
||||
}
|
||||
|
||||
// Close does nothing.
|
||||
func (s *upSession) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *upSession) doRequest(
|
||||
ctx context.Context, method, url string, content *bytes.Buffer,
|
||||
) (*http.Response, error) {
|
||||
|
||||
var body io.Reader
|
||||
if content != nil {
|
||||
body = content
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
return nil, plumbing.NewPermanentError(err)
|
||||
}
|
||||
|
||||
applyHeadersToRequest(req, content, s.endpoint.Host, transport.UploadPackServiceName)
|
||||
s.ApplyAuthToRequest(req)
|
||||
|
||||
res, err := s.client.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, plumbing.NewUnexpectedError(err)
|
||||
}
|
||||
|
||||
if err := NewErr(res); err != nil {
|
||||
_ = res.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func uploadPackRequestToReader(req *packp.UploadPackRequest) (*bytes.Buffer, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
e := pktline.NewEncoder(buf)
|
||||
|
||||
if err := req.UploadRequest.Encode(buf); err != nil {
|
||||
return nil, fmt.Errorf("sending upload-req message: %s", err)
|
||||
}
|
||||
|
||||
if err := req.UploadHaves.Encode(buf, false); err != nil {
|
||||
return nil, fmt.Errorf("sending haves message: %s", err)
|
||||
}
|
||||
|
||||
if err := e.EncodeString("done\n"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
467
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common/common.go
generated
vendored
Normal file
467
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common/common.go
generated
vendored
Normal file
@ -0,0 +1,467 @@
|
||||
// Package common implements the git pack protocol with a pluggable transport.
|
||||
// This is a low-level package to implement new transports. Use a concrete
|
||||
// implementation instead (e.g. http, file, ssh).
|
||||
//
|
||||
// A simple example of usage can be found in the file package.
|
||||
package common
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
stdioutil "io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/format/pktline"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
||||
"gopkg.in/src-d/go-git.v4/utils/ioutil"
|
||||
)
|
||||
|
||||
const (
|
||||
readErrorSecondsTimeout = 10
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTimeoutExceeded = errors.New("timeout exceeded")
|
||||
)
|
||||
|
||||
// Commander creates Command instances. This is the main entry point for
|
||||
// transport implementations.
|
||||
type Commander interface {
|
||||
// Command creates a new Command for the given git command and
|
||||
// endpoint. cmd can be git-upload-pack or git-receive-pack. An
|
||||
// error should be returned if the endpoint is not supported or the
|
||||
// command cannot be created (e.g. binary does not exist, connection
|
||||
// cannot be established).
|
||||
Command(cmd string, ep *transport.Endpoint, auth transport.AuthMethod) (Command, error)
|
||||
}
|
||||
|
||||
// Command is used for a single command execution.
|
||||
// This interface is modeled after exec.Cmd and ssh.Session in the standard
|
||||
// library.
|
||||
type Command interface {
|
||||
// StderrPipe returns a pipe that will be connected to the command's
|
||||
// standard error when the command starts. It should not be called after
|
||||
// Start.
|
||||
StderrPipe() (io.Reader, error)
|
||||
// StdinPipe returns a pipe that will be connected to the command's
|
||||
// standard input when the command starts. It should not be called after
|
||||
// Start. The pipe should be closed when no more input is expected.
|
||||
StdinPipe() (io.WriteCloser, error)
|
||||
// StdoutPipe returns a pipe that will be connected to the command's
|
||||
// standard output when the command starts. It should not be called after
|
||||
// Start.
|
||||
StdoutPipe() (io.Reader, error)
|
||||
// Start starts the specified command. It does not wait for it to
|
||||
// complete.
|
||||
Start() error
|
||||
// Close closes the command and releases any resources used by it. It
|
||||
// will block until the command exits.
|
||||
Close() error
|
||||
}
|
||||
|
||||
// CommandKiller expands the Command interface, enableing it for being killed.
|
||||
type CommandKiller interface {
|
||||
// Kill and close the session whatever the state it is. It will block until
|
||||
// the command is terminated.
|
||||
Kill() error
|
||||
}
|
||||
|
||||
type client struct {
|
||||
cmdr Commander
|
||||
}
|
||||
|
||||
// NewClient creates a new client using the given Commander.
|
||||
func NewClient(runner Commander) transport.Transport {
|
||||
return &client{runner}
|
||||
}
|
||||
|
||||
// NewUploadPackSession creates a new UploadPackSession.
|
||||
func (c *client) NewUploadPackSession(ep *transport.Endpoint, auth transport.AuthMethod) (
|
||||
transport.UploadPackSession, error) {
|
||||
|
||||
return c.newSession(transport.UploadPackServiceName, ep, auth)
|
||||
}
|
||||
|
||||
// NewReceivePackSession creates a new ReceivePackSession.
|
||||
func (c *client) NewReceivePackSession(ep *transport.Endpoint, auth transport.AuthMethod) (
|
||||
transport.ReceivePackSession, error) {
|
||||
|
||||
return c.newSession(transport.ReceivePackServiceName, ep, auth)
|
||||
}
|
||||
|
||||
type session struct {
|
||||
Stdin io.WriteCloser
|
||||
Stdout io.Reader
|
||||
Command Command
|
||||
|
||||
isReceivePack bool
|
||||
advRefs *packp.AdvRefs
|
||||
packRun bool
|
||||
finished bool
|
||||
firstErrLine chan string
|
||||
}
|
||||
|
||||
func (c *client) newSession(s string, ep *transport.Endpoint, auth transport.AuthMethod) (*session, error) {
|
||||
cmd, err := c.cmdr.Command(s, ep, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &session{
|
||||
Stdin: stdin,
|
||||
Stdout: stdout,
|
||||
Command: cmd,
|
||||
firstErrLine: c.listenFirstError(stderr),
|
||||
isReceivePack: s == transport.ReceivePackServiceName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *client) listenFirstError(r io.Reader) chan string {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errLine := make(chan string, 1)
|
||||
go func() {
|
||||
s := bufio.NewScanner(r)
|
||||
if s.Scan() {
|
||||
errLine <- s.Text()
|
||||
} else {
|
||||
close(errLine)
|
||||
}
|
||||
|
||||
_, _ = io.Copy(stdioutil.Discard, r)
|
||||
}()
|
||||
|
||||
return errLine
|
||||
}
|
||||
|
||||
// AdvertisedReferences retrieves the advertised references from the server.
|
||||
func (s *session) AdvertisedReferences() (*packp.AdvRefs, error) {
|
||||
if s.advRefs != nil {
|
||||
return s.advRefs, nil
|
||||
}
|
||||
|
||||
ar := packp.NewAdvRefs()
|
||||
if err := ar.Decode(s.Stdout); err != nil {
|
||||
if err := s.handleAdvRefDecodeError(err); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
transport.FilterUnsupportedCapabilities(ar.Capabilities)
|
||||
s.advRefs = ar
|
||||
return ar, nil
|
||||
}
|
||||
|
||||
func (s *session) handleAdvRefDecodeError(err error) error {
|
||||
// If repository is not found, we get empty stdout and server writes an
|
||||
// error to stderr.
|
||||
if err == packp.ErrEmptyInput {
|
||||
s.finished = true
|
||||
if err := s.checkNotFoundError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
// For empty (but existing) repositories, we get empty advertised-references
|
||||
// message. But valid. That is, it includes at least a flush.
|
||||
if err == packp.ErrEmptyAdvRefs {
|
||||
// Empty repositories are valid for git-receive-pack.
|
||||
if s.isReceivePack {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := s.finish(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return transport.ErrEmptyRemoteRepository
|
||||
}
|
||||
|
||||
// Some server sends the errors as normal content (git protocol), so when
|
||||
// we try to decode it fails, we need to check the content of it, to detect
|
||||
// not found errors
|
||||
if uerr, ok := err.(*packp.ErrUnexpectedData); ok {
|
||||
if isRepoNotFoundError(string(uerr.Data)) {
|
||||
return transport.ErrRepositoryNotFound
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// UploadPack performs a request to the server to fetch a packfile. A reader is
|
||||
// returned with the packfile content. The reader must be closed after reading.
|
||||
func (s *session) UploadPack(ctx context.Context, req *packp.UploadPackRequest) (*packp.UploadPackResponse, error) {
|
||||
if req.IsEmpty() {
|
||||
return nil, transport.ErrEmptyUploadPackRequest
|
||||
}
|
||||
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := s.AdvertisedReferences(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.packRun = true
|
||||
|
||||
in := s.StdinContext(ctx)
|
||||
out := s.StdoutContext(ctx)
|
||||
|
||||
if err := uploadPack(in, out, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := ioutil.NonEmptyReader(out)
|
||||
if err == ioutil.ErrEmptyReader {
|
||||
if c, ok := s.Stdout.(io.Closer); ok {
|
||||
_ = c.Close()
|
||||
}
|
||||
|
||||
return nil, transport.ErrEmptyUploadPackRequest
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rc := ioutil.NewReadCloser(r, s)
|
||||
return DecodeUploadPackResponse(rc, req)
|
||||
}
|
||||
|
||||
func (s *session) StdinContext(ctx context.Context) io.WriteCloser {
|
||||
return ioutil.NewWriteCloserOnError(
|
||||
ioutil.NewContextWriteCloser(ctx, s.Stdin),
|
||||
s.onError,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *session) StdoutContext(ctx context.Context) io.Reader {
|
||||
return ioutil.NewReaderOnError(
|
||||
ioutil.NewContextReader(ctx, s.Stdout),
|
||||
s.onError,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *session) onError(err error) {
|
||||
if k, ok := s.Command.(CommandKiller); ok {
|
||||
_ = k.Kill()
|
||||
}
|
||||
|
||||
_ = s.Close()
|
||||
}
|
||||
|
||||
func (s *session) ReceivePack(ctx context.Context, req *packp.ReferenceUpdateRequest) (*packp.ReportStatus, error) {
|
||||
if _, err := s.AdvertisedReferences(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.packRun = true
|
||||
|
||||
w := s.StdinContext(ctx)
|
||||
if err := req.Encode(w); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := w.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !req.Capabilities.Supports(capability.ReportStatus) {
|
||||
// If we don't have report-status, we can only
|
||||
// check return value error.
|
||||
return nil, s.Command.Close()
|
||||
}
|
||||
|
||||
r := s.StdoutContext(ctx)
|
||||
|
||||
var d *sideband.Demuxer
|
||||
if req.Capabilities.Supports(capability.Sideband64k) {
|
||||
d = sideband.NewDemuxer(sideband.Sideband64k, r)
|
||||
} else if req.Capabilities.Supports(capability.Sideband) {
|
||||
d = sideband.NewDemuxer(sideband.Sideband, r)
|
||||
}
|
||||
if d != nil {
|
||||
d.Progress = req.Progress
|
||||
r = d
|
||||
}
|
||||
|
||||
report := packp.NewReportStatus()
|
||||
if err := report.Decode(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := report.Error(); err != nil {
|
||||
defer s.Close()
|
||||
return report, err
|
||||
}
|
||||
|
||||
return report, s.Command.Close()
|
||||
}
|
||||
|
||||
func (s *session) finish() error {
|
||||
if s.finished {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.finished = true
|
||||
|
||||
// If we did not run a upload/receive-pack, we close the connection
|
||||
// gracefully by sending a flush packet to the server. If the server
|
||||
// operates correctly, it will exit with status 0.
|
||||
if !s.packRun {
|
||||
_, err := s.Stdin.Write(pktline.FlushPkt)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *session) Close() (err error) {
|
||||
err = s.finish()
|
||||
|
||||
defer ioutil.CheckClose(s.Command, &err)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *session) checkNotFoundError() error {
|
||||
t := time.NewTicker(time.Second * readErrorSecondsTimeout)
|
||||
defer t.Stop()
|
||||
|
||||
select {
|
||||
case <-t.C:
|
||||
return ErrTimeoutExceeded
|
||||
case line, ok := <-s.firstErrLine:
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if isRepoNotFoundError(line) {
|
||||
return transport.ErrRepositoryNotFound
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown error: %s", line)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
githubRepoNotFoundErr = "ERROR: Repository not found."
|
||||
bitbucketRepoNotFoundErr = "conq: repository does not exist."
|
||||
localRepoNotFoundErr = "does not appear to be a git repository"
|
||||
gitProtocolNotFoundErr = "ERR \n Repository not found."
|
||||
gitProtocolNoSuchErr = "ERR no such repository"
|
||||
gitProtocolAccessDeniedErr = "ERR access denied"
|
||||
gogsAccessDeniedErr = "Gogs: Repository does not exist or you do not have access"
|
||||
)
|
||||
|
||||
func isRepoNotFoundError(s string) bool {
|
||||
if strings.HasPrefix(s, githubRepoNotFoundErr) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, bitbucketRepoNotFoundErr) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasSuffix(s, localRepoNotFoundErr) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, gitProtocolNotFoundErr) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, gitProtocolNoSuchErr) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, gitProtocolAccessDeniedErr) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s, gogsAccessDeniedErr) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
var (
|
||||
nak = []byte("NAK")
|
||||
eol = []byte("\n")
|
||||
)
|
||||
|
||||
// uploadPack implements the git-upload-pack protocol.
|
||||
func uploadPack(w io.WriteCloser, r io.Reader, req *packp.UploadPackRequest) error {
|
||||
// TODO support multi_ack mode
|
||||
// TODO support multi_ack_detailed mode
|
||||
// TODO support acks for common objects
|
||||
// TODO build a proper state machine for all these processing options
|
||||
|
||||
if err := req.UploadRequest.Encode(w); err != nil {
|
||||
return fmt.Errorf("sending upload-req message: %s", err)
|
||||
}
|
||||
|
||||
if err := req.UploadHaves.Encode(w, true); err != nil {
|
||||
return fmt.Errorf("sending haves message: %s", err)
|
||||
}
|
||||
|
||||
if err := sendDone(w); err != nil {
|
||||
return fmt.Errorf("sending done message: %s", err)
|
||||
}
|
||||
|
||||
if err := w.Close(); err != nil {
|
||||
return fmt.Errorf("closing input: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendDone(w io.Writer) error {
|
||||
e := pktline.NewEncoder(w)
|
||||
|
||||
return e.Encodef("done\n")
|
||||
}
|
||||
|
||||
// DecodeUploadPackResponse decodes r into a new packp.UploadPackResponse
|
||||
func DecodeUploadPackResponse(r io.ReadCloser, req *packp.UploadPackRequest) (
|
||||
*packp.UploadPackResponse, error,
|
||||
) {
|
||||
res := packp.NewUploadPackResponse(req)
|
||||
if err := res.Decode(r); err != nil {
|
||||
return nil, fmt.Errorf("error decoding upload-pack response: %s", err)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
73
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common/server.go
generated
vendored
Normal file
73
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common/server.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
||||
"gopkg.in/src-d/go-git.v4/utils/ioutil"
|
||||
)
|
||||
|
||||
// ServerCommand is used for a single server command execution.
|
||||
type ServerCommand struct {
|
||||
Stderr io.Writer
|
||||
Stdout io.WriteCloser
|
||||
Stdin io.Reader
|
||||
}
|
||||
|
||||
func ServeUploadPack(cmd ServerCommand, s transport.UploadPackSession) (err error) {
|
||||
ioutil.CheckClose(cmd.Stdout, &err)
|
||||
|
||||
ar, err := s.AdvertisedReferences()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ar.Encode(cmd.Stdout); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := packp.NewUploadPackRequest()
|
||||
if err := req.Decode(cmd.Stdin); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var resp *packp.UploadPackResponse
|
||||
resp, err = s.UploadPack(context.TODO(), req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return resp.Encode(cmd.Stdout)
|
||||
}
|
||||
|
||||
func ServeReceivePack(cmd ServerCommand, s transport.ReceivePackSession) error {
|
||||
ar, err := s.AdvertisedReferences()
|
||||
if err != nil {
|
||||
return fmt.Errorf("internal error in advertised references: %s", err)
|
||||
}
|
||||
|
||||
if err := ar.Encode(cmd.Stdout); err != nil {
|
||||
return fmt.Errorf("error in advertised references encoding: %s", err)
|
||||
}
|
||||
|
||||
req := packp.NewReferenceUpdateRequest()
|
||||
if err := req.Decode(cmd.Stdin); err != nil {
|
||||
return fmt.Errorf("error decoding: %s", err)
|
||||
}
|
||||
|
||||
rs, err := s.ReceivePack(context.TODO(), req)
|
||||
if rs != nil {
|
||||
if err := rs.Encode(cmd.Stdout); err != nil {
|
||||
return fmt.Errorf("error in encoding report status %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in receive pack: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
64
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/server/loader.go
generated
vendored
Normal file
64
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/server/loader.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/cache"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/storer"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
||||
"gopkg.in/src-d/go-git.v4/storage/filesystem"
|
||||
|
||||
"gopkg.in/src-d/go-billy.v4"
|
||||
"gopkg.in/src-d/go-billy.v4/osfs"
|
||||
)
|
||||
|
||||
// DefaultLoader is a filesystem loader ignoring host and resolving paths to /.
|
||||
var DefaultLoader = NewFilesystemLoader(osfs.New(""))
|
||||
|
||||
// Loader loads repository's storer.Storer based on an optional host and a path.
|
||||
type Loader interface {
|
||||
// Load loads a storer.Storer given a transport.Endpoint.
|
||||
// Returns transport.ErrRepositoryNotFound if the repository does not
|
||||
// exist.
|
||||
Load(ep *transport.Endpoint) (storer.Storer, error)
|
||||
}
|
||||
|
||||
type fsLoader struct {
|
||||
base billy.Filesystem
|
||||
}
|
||||
|
||||
// NewFilesystemLoader creates a Loader that ignores host and resolves paths
|
||||
// with a given base filesystem.
|
||||
func NewFilesystemLoader(base billy.Filesystem) Loader {
|
||||
return &fsLoader{base}
|
||||
}
|
||||
|
||||
// Load looks up the endpoint's path in the base file system and returns a
|
||||
// storer for it. Returns transport.ErrRepositoryNotFound if a repository does
|
||||
// not exist in the given path.
|
||||
func (l *fsLoader) Load(ep *transport.Endpoint) (storer.Storer, error) {
|
||||
fs, err := l.base.Chroot(ep.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := fs.Stat("config"); err != nil {
|
||||
return nil, transport.ErrRepositoryNotFound
|
||||
}
|
||||
|
||||
return filesystem.NewStorage(fs, cache.NewObjectLRUDefault()), nil
|
||||
}
|
||||
|
||||
// MapLoader is a Loader that uses a lookup map of storer.Storer by
|
||||
// transport.Endpoint.
|
||||
type MapLoader map[string]storer.Storer
|
||||
|
||||
// Load returns a storer.Storer for given a transport.Endpoint by looking it up
|
||||
// in the map. Returns transport.ErrRepositoryNotFound if the endpoint does not
|
||||
// exist.
|
||||
func (l MapLoader) Load(ep *transport.Endpoint) (storer.Storer, error) {
|
||||
s, ok := l[ep.String()]
|
||||
if !ok {
|
||||
return nil, transport.ErrRepositoryNotFound
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
427
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/server/server.go
generated
vendored
Normal file
427
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/server/server.go
generated
vendored
Normal file
@ -0,0 +1,427 @@
|
||||
// Package server implements the git server protocol. For most use cases, the
|
||||
// transport-specific implementations should be used.
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/format/packfile"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/revlist"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/storer"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
||||
"gopkg.in/src-d/go-git.v4/utils/ioutil"
|
||||
)
|
||||
|
||||
var DefaultServer = NewServer(DefaultLoader)
|
||||
|
||||
type server struct {
|
||||
loader Loader
|
||||
handler *handler
|
||||
}
|
||||
|
||||
// NewServer returns a transport.Transport implementing a git server,
|
||||
// independent of transport. Each transport must wrap this.
|
||||
func NewServer(loader Loader) transport.Transport {
|
||||
return &server{
|
||||
loader,
|
||||
&handler{asClient: false},
|
||||
}
|
||||
}
|
||||
|
||||
// NewClient returns a transport.Transport implementing a client with an
|
||||
// embedded server.
|
||||
func NewClient(loader Loader) transport.Transport {
|
||||
return &server{
|
||||
loader,
|
||||
&handler{asClient: true},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) NewUploadPackSession(ep *transport.Endpoint, auth transport.AuthMethod) (transport.UploadPackSession, error) {
|
||||
sto, err := s.loader.Load(ep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.handler.NewUploadPackSession(sto)
|
||||
}
|
||||
|
||||
func (s *server) NewReceivePackSession(ep *transport.Endpoint, auth transport.AuthMethod) (transport.ReceivePackSession, error) {
|
||||
sto, err := s.loader.Load(ep)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.handler.NewReceivePackSession(sto)
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
asClient bool
|
||||
}
|
||||
|
||||
func (h *handler) NewUploadPackSession(s storer.Storer) (transport.UploadPackSession, error) {
|
||||
return &upSession{
|
||||
session: session{storer: s, asClient: h.asClient},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *handler) NewReceivePackSession(s storer.Storer) (transport.ReceivePackSession, error) {
|
||||
return &rpSession{
|
||||
session: session{storer: s, asClient: h.asClient},
|
||||
cmdStatus: map[plumbing.ReferenceName]error{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type session struct {
|
||||
storer storer.Storer
|
||||
caps *capability.List
|
||||
asClient bool
|
||||
}
|
||||
|
||||
func (s *session) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *session) SetAuth(transport.AuthMethod) error {
|
||||
//TODO: deprecate
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *session) checkSupportedCapabilities(cl *capability.List) error {
|
||||
for _, c := range cl.All() {
|
||||
if !s.caps.Supports(c) {
|
||||
return fmt.Errorf("unsupported capability: %s", c)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type upSession struct {
|
||||
session
|
||||
}
|
||||
|
||||
func (s *upSession) AdvertisedReferences() (*packp.AdvRefs, error) {
|
||||
ar := packp.NewAdvRefs()
|
||||
|
||||
if err := s.setSupportedCapabilities(ar.Capabilities); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.caps = ar.Capabilities
|
||||
|
||||
if err := setReferences(s.storer, ar); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := setHEAD(s.storer, ar); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if s.asClient && len(ar.References) == 0 {
|
||||
return nil, transport.ErrEmptyRemoteRepository
|
||||
}
|
||||
|
||||
return ar, nil
|
||||
}
|
||||
|
||||
func (s *upSession) UploadPack(ctx context.Context, req *packp.UploadPackRequest) (*packp.UploadPackResponse, error) {
|
||||
if req.IsEmpty() {
|
||||
return nil, transport.ErrEmptyUploadPackRequest
|
||||
}
|
||||
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if s.caps == nil {
|
||||
s.caps = capability.NewList()
|
||||
if err := s.setSupportedCapabilities(s.caps); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.checkSupportedCapabilities(req.Capabilities); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.caps = req.Capabilities
|
||||
|
||||
if len(req.Shallows) > 0 {
|
||||
return nil, fmt.Errorf("shallow not supported")
|
||||
}
|
||||
|
||||
objs, err := s.objectsToUpload(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
e := packfile.NewEncoder(pw, s.storer, false)
|
||||
go func() {
|
||||
// TODO: plumb through a pack window.
|
||||
_, err := e.Encode(objs, 10)
|
||||
pw.CloseWithError(err)
|
||||
}()
|
||||
|
||||
return packp.NewUploadPackResponseWithPackfile(req,
|
||||
ioutil.NewContextReadCloser(ctx, pr),
|
||||
), nil
|
||||
}
|
||||
|
||||
func (s *upSession) objectsToUpload(req *packp.UploadPackRequest) ([]plumbing.Hash, error) {
|
||||
haves, err := revlist.Objects(s.storer, req.Haves, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return revlist.Objects(s.storer, req.Wants, haves)
|
||||
}
|
||||
|
||||
func (*upSession) setSupportedCapabilities(c *capability.List) error {
|
||||
if err := c.Set(capability.Agent, capability.DefaultAgent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.Set(capability.OFSDelta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type rpSession struct {
|
||||
session
|
||||
cmdStatus map[plumbing.ReferenceName]error
|
||||
firstErr error
|
||||
unpackErr error
|
||||
}
|
||||
|
||||
func (s *rpSession) AdvertisedReferences() (*packp.AdvRefs, error) {
|
||||
ar := packp.NewAdvRefs()
|
||||
|
||||
if err := s.setSupportedCapabilities(ar.Capabilities); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.caps = ar.Capabilities
|
||||
|
||||
if err := setReferences(s.storer, ar); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := setHEAD(s.storer, ar); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ar, nil
|
||||
}
|
||||
|
||||
var (
|
||||
ErrUpdateReference = errors.New("failed to update ref")
|
||||
)
|
||||
|
||||
func (s *rpSession) ReceivePack(ctx context.Context, req *packp.ReferenceUpdateRequest) (*packp.ReportStatus, error) {
|
||||
if s.caps == nil {
|
||||
s.caps = capability.NewList()
|
||||
if err := s.setSupportedCapabilities(s.caps); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.checkSupportedCapabilities(req.Capabilities); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.caps = req.Capabilities
|
||||
|
||||
//TODO: Implement 'atomic' update of references.
|
||||
|
||||
r := ioutil.NewContextReadCloser(ctx, req.Packfile)
|
||||
if err := s.writePackfile(r); err != nil {
|
||||
s.unpackErr = err
|
||||
s.firstErr = err
|
||||
return s.reportStatus(), err
|
||||
}
|
||||
|
||||
s.updateReferences(req)
|
||||
return s.reportStatus(), s.firstErr
|
||||
}
|
||||
|
||||
func (s *rpSession) updateReferences(req *packp.ReferenceUpdateRequest) {
|
||||
for _, cmd := range req.Commands {
|
||||
exists, err := referenceExists(s.storer, cmd.Name)
|
||||
if err != nil {
|
||||
s.setStatus(cmd.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
switch cmd.Action() {
|
||||
case packp.Create:
|
||||
if exists {
|
||||
s.setStatus(cmd.Name, ErrUpdateReference)
|
||||
continue
|
||||
}
|
||||
|
||||
ref := plumbing.NewHashReference(cmd.Name, cmd.New)
|
||||
err := s.storer.SetReference(ref)
|
||||
s.setStatus(cmd.Name, err)
|
||||
case packp.Delete:
|
||||
if !exists {
|
||||
s.setStatus(cmd.Name, ErrUpdateReference)
|
||||
continue
|
||||
}
|
||||
|
||||
err := s.storer.RemoveReference(cmd.Name)
|
||||
s.setStatus(cmd.Name, err)
|
||||
case packp.Update:
|
||||
if !exists {
|
||||
s.setStatus(cmd.Name, ErrUpdateReference)
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.setStatus(cmd.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
ref := plumbing.NewHashReference(cmd.Name, cmd.New)
|
||||
err := s.storer.SetReference(ref)
|
||||
s.setStatus(cmd.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *rpSession) writePackfile(r io.ReadCloser) error {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := packfile.UpdateObjectStorage(s.storer, r); err != nil {
|
||||
_ = r.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
return r.Close()
|
||||
}
|
||||
|
||||
func (s *rpSession) setStatus(ref plumbing.ReferenceName, err error) {
|
||||
s.cmdStatus[ref] = err
|
||||
if s.firstErr == nil && err != nil {
|
||||
s.firstErr = err
|
||||
}
|
||||
}
|
||||
|
||||
func (s *rpSession) reportStatus() *packp.ReportStatus {
|
||||
if !s.caps.Supports(capability.ReportStatus) {
|
||||
return nil
|
||||
}
|
||||
|
||||
rs := packp.NewReportStatus()
|
||||
rs.UnpackStatus = "ok"
|
||||
|
||||
if s.unpackErr != nil {
|
||||
rs.UnpackStatus = s.unpackErr.Error()
|
||||
}
|
||||
|
||||
if s.cmdStatus == nil {
|
||||
return rs
|
||||
}
|
||||
|
||||
for ref, err := range s.cmdStatus {
|
||||
msg := "ok"
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
}
|
||||
status := &packp.CommandStatus{
|
||||
ReferenceName: ref,
|
||||
Status: msg,
|
||||
}
|
||||
rs.CommandStatuses = append(rs.CommandStatuses, status)
|
||||
}
|
||||
|
||||
return rs
|
||||
}
|
||||
|
||||
func (*rpSession) setSupportedCapabilities(c *capability.List) error {
|
||||
if err := c.Set(capability.Agent, capability.DefaultAgent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.Set(capability.OFSDelta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.Set(capability.DeleteRefs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Set(capability.ReportStatus)
|
||||
}
|
||||
|
||||
func setHEAD(s storer.Storer, ar *packp.AdvRefs) error {
|
||||
ref, err := s.Reference(plumbing.HEAD)
|
||||
if err == plumbing.ErrReferenceNotFound {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ref.Type() == plumbing.SymbolicReference {
|
||||
if err := ar.AddReference(ref); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ref, err = storer.ResolveReference(s, ref.Target())
|
||||
if err == plumbing.ErrReferenceNotFound {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if ref.Type() != plumbing.HashReference {
|
||||
return plumbing.ErrInvalidType
|
||||
}
|
||||
|
||||
h := ref.Hash()
|
||||
ar.Head = &h
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setReferences(s storer.Storer, ar *packp.AdvRefs) error {
|
||||
//TODO: add peeled references.
|
||||
iter, err := s.IterReferences()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return iter.ForEach(func(ref *plumbing.Reference) error {
|
||||
if ref.Type() != plumbing.HashReference {
|
||||
return nil
|
||||
}
|
||||
|
||||
ar.References[ref.Name().String()] = ref.Hash()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func referenceExists(s storer.ReferenceStorer, n plumbing.ReferenceName) (bool, error) {
|
||||
_, err := s.Reference(n)
|
||||
if err == plumbing.ErrReferenceNotFound {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return err == nil, err
|
||||
}
|
322
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/ssh/auth_method.go
generated
vendored
Normal file
322
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/ssh/auth_method.go
generated
vendored
Normal file
@ -0,0 +1,322 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/xanzy/ssh-agent"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/knownhosts"
|
||||
)
|
||||
|
||||
const DefaultUsername = "git"
|
||||
|
||||
// AuthMethod is the interface all auth methods for the ssh client
|
||||
// must implement. The clientConfig method returns the ssh client
|
||||
// configuration needed to establish an ssh connection.
|
||||
type AuthMethod interface {
|
||||
transport.AuthMethod
|
||||
// ClientConfig should return a valid ssh.ClientConfig to be used to create
|
||||
// a connection to the SSH server.
|
||||
ClientConfig() (*ssh.ClientConfig, error)
|
||||
}
|
||||
|
||||
// The names of the AuthMethod implementations. To be returned by the
|
||||
// Name() method. Most git servers only allow PublicKeysName and
|
||||
// PublicKeysCallbackName.
|
||||
const (
|
||||
KeyboardInteractiveName = "ssh-keyboard-interactive"
|
||||
PasswordName = "ssh-password"
|
||||
PasswordCallbackName = "ssh-password-callback"
|
||||
PublicKeysName = "ssh-public-keys"
|
||||
PublicKeysCallbackName = "ssh-public-key-callback"
|
||||
)
|
||||
|
||||
// KeyboardInteractive implements AuthMethod by using a
|
||||
// prompt/response sequence controlled by the server.
|
||||
type KeyboardInteractive struct {
|
||||
User string
|
||||
Challenge ssh.KeyboardInteractiveChallenge
|
||||
HostKeyCallbackHelper
|
||||
}
|
||||
|
||||
func (a *KeyboardInteractive) Name() string {
|
||||
return KeyboardInteractiveName
|
||||
}
|
||||
|
||||
func (a *KeyboardInteractive) String() string {
|
||||
return fmt.Sprintf("user: %s, name: %s", a.User, a.Name())
|
||||
}
|
||||
|
||||
func (a *KeyboardInteractive) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.KeyboardInteractiveChallenge(a.Challenge),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Password implements AuthMethod by using the given password.
|
||||
type Password struct {
|
||||
User string
|
||||
Password string
|
||||
HostKeyCallbackHelper
|
||||
}
|
||||
|
||||
func (a *Password) Name() string {
|
||||
return PasswordName
|
||||
}
|
||||
|
||||
func (a *Password) String() string {
|
||||
return fmt.Sprintf("user: %s, name: %s", a.User, a.Name())
|
||||
}
|
||||
|
||||
func (a *Password) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{ssh.Password(a.Password)},
|
||||
})
|
||||
}
|
||||
|
||||
// PasswordCallback implements AuthMethod by using a callback
|
||||
// to fetch the password.
|
||||
type PasswordCallback struct {
|
||||
User string
|
||||
Callback func() (pass string, err error)
|
||||
HostKeyCallbackHelper
|
||||
}
|
||||
|
||||
func (a *PasswordCallback) Name() string {
|
||||
return PasswordCallbackName
|
||||
}
|
||||
|
||||
func (a *PasswordCallback) String() string {
|
||||
return fmt.Sprintf("user: %s, name: %s", a.User, a.Name())
|
||||
}
|
||||
|
||||
func (a *PasswordCallback) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{ssh.PasswordCallback(a.Callback)},
|
||||
})
|
||||
}
|
||||
|
||||
// PublicKeys implements AuthMethod by using the given key pairs.
|
||||
type PublicKeys struct {
|
||||
User string
|
||||
Signer ssh.Signer
|
||||
HostKeyCallbackHelper
|
||||
}
|
||||
|
||||
// NewPublicKeys returns a PublicKeys from a PEM encoded private key. An
|
||||
// encryption password should be given if the pemBytes contains a password
|
||||
// encrypted PEM block otherwise password should be empty. It supports RSA
|
||||
// (PKCS#1), DSA (OpenSSL), and ECDSA private keys.
|
||||
func NewPublicKeys(user string, pemBytes []byte, password string) (*PublicKeys, error) {
|
||||
block, _ := pem.Decode(pemBytes)
|
||||
if block == nil {
|
||||
return nil, errors.New("invalid PEM data")
|
||||
}
|
||||
if x509.IsEncryptedPEMBlock(block) {
|
||||
key, err := x509.DecryptPEMBlock(block, []byte(password))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block = &pem.Block{Type: block.Type, Bytes: key}
|
||||
pemBytes = pem.EncodeToMemory(block)
|
||||
}
|
||||
|
||||
signer, err := ssh.ParsePrivateKey(pemBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PublicKeys{User: user, Signer: signer}, nil
|
||||
}
|
||||
|
||||
// NewPublicKeysFromFile returns a PublicKeys from a file containing a PEM
|
||||
// encoded private key. An encryption password should be given if the pemBytes
|
||||
// contains a password encrypted PEM block otherwise password should be empty.
|
||||
func NewPublicKeysFromFile(user, pemFile, password string) (*PublicKeys, error) {
|
||||
bytes, err := ioutil.ReadFile(pemFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewPublicKeys(user, bytes, password)
|
||||
}
|
||||
|
||||
func (a *PublicKeys) Name() string {
|
||||
return PublicKeysName
|
||||
}
|
||||
|
||||
func (a *PublicKeys) String() string {
|
||||
return fmt.Sprintf("user: %s, name: %s", a.User, a.Name())
|
||||
}
|
||||
|
||||
func (a *PublicKeys) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{ssh.PublicKeys(a.Signer)},
|
||||
})
|
||||
}
|
||||
|
||||
func username() (string, error) {
|
||||
var username string
|
||||
if user, err := user.Current(); err == nil {
|
||||
username = user.Username
|
||||
} else {
|
||||
username = os.Getenv("USER")
|
||||
}
|
||||
|
||||
if username == "" {
|
||||
return "", errors.New("failed to get username")
|
||||
}
|
||||
|
||||
return username, nil
|
||||
}
|
||||
|
||||
// PublicKeysCallback implements AuthMethod by asking a
|
||||
// ssh.agent.Agent to act as a signer.
|
||||
type PublicKeysCallback struct {
|
||||
User string
|
||||
Callback func() (signers []ssh.Signer, err error)
|
||||
HostKeyCallbackHelper
|
||||
}
|
||||
|
||||
// NewSSHAgentAuth returns a PublicKeysCallback based on a SSH agent, it opens
|
||||
// a pipe with the SSH agent and uses the pipe as the implementer of the public
|
||||
// key callback function.
|
||||
func NewSSHAgentAuth(u string) (*PublicKeysCallback, error) {
|
||||
var err error
|
||||
if u == "" {
|
||||
u, err = username()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
a, _, err := sshagent.New()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating SSH agent: %q", err)
|
||||
}
|
||||
|
||||
return &PublicKeysCallback{
|
||||
User: u,
|
||||
Callback: a.Signers,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *PublicKeysCallback) Name() string {
|
||||
return PublicKeysCallbackName
|
||||
}
|
||||
|
||||
func (a *PublicKeysCallback) String() string {
|
||||
return fmt.Sprintf("user: %s, name: %s", a.User, a.Name())
|
||||
}
|
||||
|
||||
func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(a.Callback)},
|
||||
})
|
||||
}
|
||||
|
||||
// NewKnownHostsCallback returns ssh.HostKeyCallback based on a file based on a
|
||||
// known_hosts file. http://man.openbsd.org/sshd#SSH_KNOWN_HOSTS_FILE_FORMAT
|
||||
//
|
||||
// If list of files is empty, then it will be read from the SSH_KNOWN_HOSTS
|
||||
// environment variable, example:
|
||||
// /home/foo/custom_known_hosts_file:/etc/custom_known/hosts_file
|
||||
//
|
||||
// If SSH_KNOWN_HOSTS is not set the following file locations will be used:
|
||||
// ~/.ssh/known_hosts
|
||||
// /etc/ssh/ssh_known_hosts
|
||||
func NewKnownHostsCallback(files ...string) (ssh.HostKeyCallback, error) {
|
||||
var err error
|
||||
|
||||
if len(files) == 0 {
|
||||
if files, err = getDefaultKnownHostsFiles(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if files, err = filterKnownHostsFiles(files...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return knownhosts.New(files...)
|
||||
}
|
||||
|
||||
func getDefaultKnownHostsFiles() ([]string, error) {
|
||||
files := filepath.SplitList(os.Getenv("SSH_KNOWN_HOSTS"))
|
||||
if len(files) != 0 {
|
||||
return files, nil
|
||||
}
|
||||
|
||||
homeDirPath, err := homedir.Dir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []string{
|
||||
filepath.Join(homeDirPath, "/.ssh/known_hosts"),
|
||||
"/etc/ssh/ssh_known_hosts",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func filterKnownHostsFiles(files ...string) ([]string, error) {
|
||||
var out []string
|
||||
for _, file := range files {
|
||||
_, err := os.Stat(file)
|
||||
if err == nil {
|
||||
out = append(out, file)
|
||||
continue
|
||||
}
|
||||
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(out) == 0 {
|
||||
return nil, fmt.Errorf("unable to find any valid known_hosts file, set SSH_KNOWN_HOSTS env variable")
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// HostKeyCallbackHelper is a helper that provides common functionality to
|
||||
// configure HostKeyCallback into a ssh.ClientConfig.
|
||||
type HostKeyCallbackHelper struct {
|
||||
// HostKeyCallback is the function type used for verifying server keys.
|
||||
// If nil default callback will be create using NewKnownHostsCallback
|
||||
// without argument.
|
||||
HostKeyCallback ssh.HostKeyCallback
|
||||
}
|
||||
|
||||
// SetHostKeyCallback sets the field HostKeyCallback in the given cfg. If
|
||||
// HostKeyCallback is empty a default callback is created using
|
||||
// NewKnownHostsCallback.
|
||||
func (m *HostKeyCallbackHelper) SetHostKeyCallback(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) {
|
||||
var err error
|
||||
if m.HostKeyCallback == nil {
|
||||
if m.HostKeyCallback, err = NewKnownHostsCallback(); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
}
|
||||
|
||||
cfg.HostKeyCallback = m.HostKeyCallback
|
||||
return cfg, nil
|
||||
}
|
203
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/ssh/common.go
generated
vendored
Normal file
203
vendor/gopkg.in/src-d/go-git.v4/plumbing/transport/ssh/common.go
generated
vendored
Normal file
@ -0,0 +1,203 @@
|
||||
// Package ssh implements the SSH transport protocol.
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/transport/internal/common"
|
||||
|
||||
"github.com/kevinburke/ssh_config"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// DefaultClient is the default SSH client.
|
||||
var DefaultClient = NewClient(nil)
|
||||
|
||||
// DefaultSSHConfig is the reader used to access parameters stored in the
|
||||
// system's ssh_config files. If nil all the ssh_config are ignored.
|
||||
var DefaultSSHConfig sshConfig = ssh_config.DefaultUserSettings
|
||||
|
||||
type sshConfig interface {
|
||||
Get(alias, key string) string
|
||||
}
|
||||
|
||||
// NewClient creates a new SSH client with an optional *ssh.ClientConfig.
|
||||
func NewClient(config *ssh.ClientConfig) transport.Transport {
|
||||
return common.NewClient(&runner{config: config})
|
||||
}
|
||||
|
||||
// DefaultAuthBuilder is the function used to create a default AuthMethod, when
|
||||
// the user doesn't provide any.
|
||||
var DefaultAuthBuilder = func(user string) (AuthMethod, error) {
|
||||
return NewSSHAgentAuth(user)
|
||||
}
|
||||
|
||||
const DefaultPort = 22
|
||||
|
||||
type runner struct {
|
||||
config *ssh.ClientConfig
|
||||
}
|
||||
|
||||
func (r *runner) Command(cmd string, ep *transport.Endpoint, auth transport.AuthMethod) (common.Command, error) {
|
||||
c := &command{command: cmd, endpoint: ep, config: r.config}
|
||||
if auth != nil {
|
||||
c.setAuth(auth)
|
||||
}
|
||||
|
||||
if err := c.connect(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type command struct {
|
||||
*ssh.Session
|
||||
connected bool
|
||||
command string
|
||||
endpoint *transport.Endpoint
|
||||
client *ssh.Client
|
||||
auth AuthMethod
|
||||
config *ssh.ClientConfig
|
||||
}
|
||||
|
||||
func (c *command) setAuth(auth transport.AuthMethod) error {
|
||||
a, ok := auth.(AuthMethod)
|
||||
if !ok {
|
||||
return transport.ErrInvalidAuthMethod
|
||||
}
|
||||
|
||||
c.auth = a
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) Start() error {
|
||||
return c.Session.Start(endpointToCommand(c.command, c.endpoint))
|
||||
}
|
||||
|
||||
// Close closes the SSH session and connection.
|
||||
func (c *command) Close() error {
|
||||
if !c.connected {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.connected = false
|
||||
|
||||
//XXX: If did read the full packfile, then the session might be already
|
||||
// closed.
|
||||
_ = c.Session.Close()
|
||||
|
||||
return c.client.Close()
|
||||
}
|
||||
|
||||
// connect connects to the SSH server, unless a AuthMethod was set with
|
||||
// SetAuth method, by default uses an auth method based on PublicKeysCallback,
|
||||
// it connects to a SSH agent, using the address stored in the SSH_AUTH_SOCK
|
||||
// environment var.
|
||||
func (c *command) connect() error {
|
||||
if c.connected {
|
||||
return transport.ErrAlreadyConnected
|
||||
}
|
||||
|
||||
if c.auth == nil {
|
||||
if err := c.setAuthFromEndpoint(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
config, err := c.auth.ClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
overrideConfig(c.config, config)
|
||||
|
||||
c.client, err = ssh.Dial("tcp", c.getHostWithPort(), config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Session, err = c.client.NewSession()
|
||||
if err != nil {
|
||||
_ = c.client.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
c.connected = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *command) getHostWithPort() string {
|
||||
if addr, found := c.doGetHostWithPortFromSSHConfig(); found {
|
||||
return addr
|
||||
}
|
||||
|
||||
host := c.endpoint.Host
|
||||
port := c.endpoint.Port
|
||||
if port <= 0 {
|
||||
port = DefaultPort
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%d", host, port)
|
||||
}
|
||||
|
||||
func (c *command) doGetHostWithPortFromSSHConfig() (addr string, found bool) {
|
||||
if DefaultSSHConfig == nil {
|
||||
return
|
||||
}
|
||||
|
||||
host := c.endpoint.Host
|
||||
port := c.endpoint.Port
|
||||
|
||||
configHost := DefaultSSHConfig.Get(c.endpoint.Host, "Hostname")
|
||||
if configHost != "" {
|
||||
host = configHost
|
||||
found = true
|
||||
}
|
||||
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
configPort := DefaultSSHConfig.Get(c.endpoint.Host, "Port")
|
||||
if configPort != "" {
|
||||
if i, err := strconv.Atoi(configPort); err == nil {
|
||||
port = i
|
||||
}
|
||||
}
|
||||
|
||||
addr = fmt.Sprintf("%s:%d", host, port)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *command) setAuthFromEndpoint() error {
|
||||
var err error
|
||||
c.auth, err = DefaultAuthBuilder(c.endpoint.User)
|
||||
return err
|
||||
}
|
||||
|
||||
func endpointToCommand(cmd string, ep *transport.Endpoint) string {
|
||||
return fmt.Sprintf("%s '%s'", cmd, ep.Path)
|
||||
}
|
||||
|
||||
func overrideConfig(overrides *ssh.ClientConfig, c *ssh.ClientConfig) {
|
||||
if overrides == nil {
|
||||
return
|
||||
}
|
||||
|
||||
t := reflect.TypeOf(*c)
|
||||
vc := reflect.ValueOf(c).Elem()
|
||||
vo := reflect.ValueOf(overrides).Elem()
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := t.Field(i)
|
||||
vcf := vc.FieldByName(f.Name)
|
||||
vof := vo.FieldByName(f.Name)
|
||||
vcf.Set(vof)
|
||||
}
|
||||
|
||||
*c = vc.Interface().(ssh.ClientConfig)
|
||||
}
|
Reference in New Issue
Block a user