本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

275 lines
7.0KB

  1. // Package transport includes the implementation for different transport
  2. // protocols.
  3. //
  4. // `Client` can be used to fetch and send packfiles to a git server.
  5. // The `client` package provides higher level functions to instantiate the
  6. // appropriate `Client` based on the repository URL.
  7. //
  8. // go-git supports HTTP and SSH (see `Protocols`), but you can also install
  9. // your own protocols (see the `client` package).
  10. //
  11. // Each protocol has its own implementation of `Client`, but you should
  12. // generally not use them directly, use `client.NewClient` instead.
  13. package transport
  14. import (
  15. "bytes"
  16. "context"
  17. "errors"
  18. "fmt"
  19. "io"
  20. "net/url"
  21. "strconv"
  22. "strings"
  23. giturl "gopkg.in/src-d/go-git.v4/internal/url"
  24. "gopkg.in/src-d/go-git.v4/plumbing"
  25. "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp"
  26. "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability"
  27. )
  28. var (
  29. ErrRepositoryNotFound = errors.New("repository not found")
  30. ErrEmptyRemoteRepository = errors.New("remote repository is empty")
  31. ErrAuthenticationRequired = errors.New("authentication required")
  32. ErrAuthorizationFailed = errors.New("authorization failed")
  33. ErrEmptyUploadPackRequest = errors.New("empty git-upload-pack given")
  34. ErrInvalidAuthMethod = errors.New("invalid auth method")
  35. ErrAlreadyConnected = errors.New("session already established")
  36. )
  37. const (
  38. UploadPackServiceName = "git-upload-pack"
  39. ReceivePackServiceName = "git-receive-pack"
  40. )
  41. // Transport can initiate git-upload-pack and git-receive-pack processes.
  42. // It is implemented both by the client and the server, making this a RPC.
  43. type Transport interface {
  44. // NewUploadPackSession starts a git-upload-pack session for an endpoint.
  45. NewUploadPackSession(*Endpoint, AuthMethod) (UploadPackSession, error)
  46. // NewReceivePackSession starts a git-receive-pack session for an endpoint.
  47. NewReceivePackSession(*Endpoint, AuthMethod) (ReceivePackSession, error)
  48. }
  49. type Session interface {
  50. // AdvertisedReferences retrieves the advertised references for a
  51. // repository.
  52. // If the repository does not exist, returns ErrRepositoryNotFound.
  53. // If the repository exists, but is empty, returns ErrEmptyRemoteRepository.
  54. AdvertisedReferences() (*packp.AdvRefs, error)
  55. io.Closer
  56. }
  57. type AuthMethod interface {
  58. fmt.Stringer
  59. Name() string
  60. }
  61. // UploadPackSession represents a git-upload-pack session.
  62. // A git-upload-pack session has two steps: reference discovery
  63. // (AdvertisedReferences) and uploading pack (UploadPack).
  64. type UploadPackSession interface {
  65. Session
  66. // UploadPack takes a git-upload-pack request and returns a response,
  67. // including a packfile. Don't be confused by terminology, the client
  68. // side of a git-upload-pack is called git-fetch-pack, although here
  69. // the same interface is used to make it RPC-like.
  70. UploadPack(context.Context, *packp.UploadPackRequest) (*packp.UploadPackResponse, error)
  71. }
  72. // ReceivePackSession represents a git-receive-pack session.
  73. // A git-receive-pack session has two steps: reference discovery
  74. // (AdvertisedReferences) and receiving pack (ReceivePack).
  75. // In that order.
  76. type ReceivePackSession interface {
  77. Session
  78. // ReceivePack sends an update references request and a packfile
  79. // reader and returns a ReportStatus and error. Don't be confused by
  80. // terminology, the client side of a git-receive-pack is called
  81. // git-send-pack, although here the same interface is used to make it
  82. // RPC-like.
  83. ReceivePack(context.Context, *packp.ReferenceUpdateRequest) (*packp.ReportStatus, error)
  84. }
  85. // Endpoint represents a Git URL in any supported protocol.
  86. type Endpoint struct {
  87. // Protocol is the protocol of the endpoint (e.g. git, https, file).
  88. Protocol string
  89. // User is the user.
  90. User string
  91. // Password is the password.
  92. Password string
  93. // Host is the host.
  94. Host string
  95. // Port is the port to connect, if 0 the default port for the given protocol
  96. // wil be used.
  97. Port int
  98. // Path is the repository path.
  99. Path string
  100. }
  101. var defaultPorts = map[string]int{
  102. "http": 80,
  103. "https": 443,
  104. "git": 9418,
  105. "ssh": 22,
  106. }
  107. // String returns a string representation of the Git URL.
  108. func (u *Endpoint) String() string {
  109. var buf bytes.Buffer
  110. if u.Protocol != "" {
  111. buf.WriteString(u.Protocol)
  112. buf.WriteByte(':')
  113. }
  114. if u.Protocol != "" || u.Host != "" || u.User != "" || u.Password != "" {
  115. buf.WriteString("//")
  116. if u.User != "" || u.Password != "" {
  117. buf.WriteString(url.PathEscape(u.User))
  118. if u.Password != "" {
  119. buf.WriteByte(':')
  120. buf.WriteString(url.PathEscape(u.Password))
  121. }
  122. buf.WriteByte('@')
  123. }
  124. if u.Host != "" {
  125. buf.WriteString(u.Host)
  126. if u.Port != 0 {
  127. port, ok := defaultPorts[strings.ToLower(u.Protocol)]
  128. if !ok || ok && port != u.Port {
  129. fmt.Fprintf(&buf, ":%d", u.Port)
  130. }
  131. }
  132. }
  133. }
  134. if u.Path != "" && u.Path[0] != '/' && u.Host != "" {
  135. buf.WriteByte('/')
  136. }
  137. buf.WriteString(u.Path)
  138. return buf.String()
  139. }
  140. func NewEndpoint(endpoint string) (*Endpoint, error) {
  141. if e, ok := parseSCPLike(endpoint); ok {
  142. return e, nil
  143. }
  144. if e, ok := parseFile(endpoint); ok {
  145. return e, nil
  146. }
  147. return parseURL(endpoint)
  148. }
  149. func parseURL(endpoint string) (*Endpoint, error) {
  150. u, err := url.Parse(endpoint)
  151. if err != nil {
  152. return nil, err
  153. }
  154. if !u.IsAbs() {
  155. return nil, plumbing.NewPermanentError(fmt.Errorf(
  156. "invalid endpoint: %s", endpoint,
  157. ))
  158. }
  159. var user, pass string
  160. if u.User != nil {
  161. user = u.User.Username()
  162. pass, _ = u.User.Password()
  163. }
  164. return &Endpoint{
  165. Protocol: u.Scheme,
  166. User: user,
  167. Password: pass,
  168. Host: u.Hostname(),
  169. Port: getPort(u),
  170. Path: getPath(u),
  171. }, nil
  172. }
  173. func getPort(u *url.URL) int {
  174. p := u.Port()
  175. if p == "" {
  176. return 0
  177. }
  178. i, err := strconv.Atoi(p)
  179. if err != nil {
  180. return 0
  181. }
  182. return i
  183. }
  184. func getPath(u *url.URL) string {
  185. var res string = u.Path
  186. if u.RawQuery != "" {
  187. res += "?" + u.RawQuery
  188. }
  189. if u.Fragment != "" {
  190. res += "#" + u.Fragment
  191. }
  192. return res
  193. }
  194. func parseSCPLike(endpoint string) (*Endpoint, bool) {
  195. if giturl.MatchesScheme(endpoint) || !giturl.MatchesScpLike(endpoint) {
  196. return nil, false
  197. }
  198. user, host, portStr, path := giturl.FindScpLikeComponents(endpoint)
  199. port, err := strconv.Atoi(portStr)
  200. if err != nil {
  201. port = 22
  202. }
  203. return &Endpoint{
  204. Protocol: "ssh",
  205. User: user,
  206. Host: host,
  207. Port: port,
  208. Path: path,
  209. }, true
  210. }
  211. func parseFile(endpoint string) (*Endpoint, bool) {
  212. if giturl.MatchesScheme(endpoint) {
  213. return nil, false
  214. }
  215. path := endpoint
  216. return &Endpoint{
  217. Protocol: "file",
  218. Path: path,
  219. }, true
  220. }
  221. // UnsupportedCapabilities are the capabilities not supported by any client
  222. // implementation
  223. var UnsupportedCapabilities = []capability.Capability{
  224. capability.MultiACK,
  225. capability.MultiACKDetailed,
  226. capability.ThinPack,
  227. }
  228. // FilterUnsupportedCapabilities it filter out all the UnsupportedCapabilities
  229. // from a capability.List, the intended usage is on the client implementation
  230. // to filter the capabilities from an AdvRefs message.
  231. func FilterUnsupportedCapabilities(list *capability.List) {
  232. for _, c := range UnsupportedCapabilities {
  233. list.Delete(c)
  234. }
  235. }
上海开阖软件有限公司 沪ICP备12045867号-1