aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/transport/http/fetch_pack.go
diff options
context:
space:
mode:
authorSantiago M. Mola <santi@mola.io>2016-11-23 15:30:34 +0100
committerMáximo Cuadros <mcuadros@gmail.com>2016-11-23 15:38:12 +0100
commit08e08d771ef03df80248c80d81475fe7c5ea6fe7 (patch)
treed12e9befa22409e8cf50c5bbc4895e69fd8a5f48 /plumbing/transport/http/fetch_pack.go
parent844169a739fb8bf1f252d416f10d8c7034db9fe2 (diff)
downloadgo-git-08e08d771ef03df80248c80d81475fe7c5ea6fe7.tar.gz
transport: create Client interface (#132)
* plumbing: move plumbing/client package to plumbing/transport. * transport: create Client interface. * A Client can instantiate any client transport service. * InstallProtocol installs a Client for a given protocol, instead of just a UploadPackService. * A Client can open a session for fetch-pack or send-pack for a specific Endpoint. * Adapt ssh and http clients to the new client interface. * updated doc
Diffstat (limited to 'plumbing/transport/http/fetch_pack.go')
-rw-r--r--plumbing/transport/http/fetch_pack.go155
1 files changed, 155 insertions, 0 deletions
diff --git a/plumbing/transport/http/fetch_pack.go b/plumbing/transport/http/fetch_pack.go
new file mode 100644
index 0000000..0c32672
--- /dev/null
+++ b/plumbing/transport/http/fetch_pack.go
@@ -0,0 +1,155 @@
+package http
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+
+ "gopkg.in/src-d/go-git.v4/plumbing"
+ "gopkg.in/src-d/go-git.v4/plumbing/format/packp/pktline"
+ "gopkg.in/src-d/go-git.v4/plumbing/transport"
+)
+
+type fetchPackSession struct {
+ *session
+}
+
+func newFetchPackSession(c *http.Client,
+ ep transport.Endpoint) transport.FetchPackSession {
+
+ return &fetchPackSession{
+ session: &session{
+ auth: basicAuthFromEndpoint(ep),
+ client: c,
+ endpoint: ep,
+ },
+ }
+}
+
+func (s *fetchPackSession) AdvertisedReferences() (*transport.UploadPackInfo,
+ error) {
+
+ url := fmt.Sprintf(
+ "%s/info/refs?service=%s",
+ s.endpoint.String(), transport.UploadPackServiceName,
+ )
+
+ req, err := http.NewRequest(http.MethodGet, url, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ s.applyAuthToRequest(req)
+ s.applyHeadersToRequest(req, nil)
+ res, err := s.client.Do(req)
+ if err != nil {
+ return nil, err
+ }
+
+ defer res.Body.Close()
+ if res.StatusCode == http.StatusUnauthorized {
+ return nil, transport.ErrAuthorizationRequired
+ }
+
+ i := transport.NewUploadPackInfo()
+ return i, i.Decode(res.Body)
+}
+
+func (s *fetchPackSession) FetchPack(r *transport.UploadPackRequest) (io.ReadCloser, error) {
+ url := fmt.Sprintf(
+ "%s/%s",
+ s.endpoint.String(), transport.UploadPackServiceName,
+ )
+
+ res, err := s.doRequest("POST", url, r.Reader())
+ if err != nil {
+ return nil, err
+ }
+
+ reader := newBufferedReadCloser(res.Body)
+ if _, err := reader.Peek(1); err != nil {
+ if err == io.ErrUnexpectedEOF {
+ return nil, transport.ErrEmptyUploadPackRequest
+ }
+
+ return nil, err
+ }
+
+ if err := discardResponseInfo(reader); err != nil {
+ return nil, err
+ }
+
+ return reader, nil
+}
+
+// Close does nothing.
+func (s *fetchPackSession) Close() error {
+ return nil
+}
+
+func discardResponseInfo(r io.Reader) error {
+ s := pktline.NewScanner(r)
+ for s.Scan() {
+ if bytes.Equal(s.Bytes(), []byte{'N', 'A', 'K', '\n'}) {
+ break
+ }
+ }
+
+ return s.Err()
+}
+
+func (s *fetchPackSession) doRequest(method, url string, content *strings.Reader) (*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)
+ }
+
+ s.applyHeadersToRequest(req, content)
+ s.applyAuthToRequest(req)
+
+ res, err := s.client.Do(req)
+ if err != nil {
+ return nil, plumbing.NewUnexpectedError(err)
+ }
+
+ if err := NewErr(res); err != nil {
+ _ = res.Body.Close()
+ return nil, err
+ }
+
+ return res, nil
+}
+
+func (s *fetchPackSession) applyHeadersToRequest(req *http.Request, content *strings.Reader) {
+ req.Header.Add("User-Agent", "git/1.0")
+ req.Header.Add("Host", "github.com")
+
+ if content == nil {
+ req.Header.Add("Accept", "*/*")
+ } else {
+ req.Header.Add("Accept", "application/x-git-upload-pack-result")
+ req.Header.Add("Content-Type", "application/x-git-upload-pack-request")
+ req.Header.Add("Content-Length", string(content.Len()))
+ }
+}
+
+type bufferedReadCloser struct {
+ *bufio.Reader
+ closer io.Closer
+}
+
+func newBufferedReadCloser(r io.ReadCloser) *bufferedReadCloser {
+ return &bufferedReadCloser{bufio.NewReader(r), r}
+}
+
+func (r *bufferedReadCloser) Close() error {
+ return r.closer.Close()
+}