Skip to content

Commit 2dd5983

Browse files
committed
teach Download() how to follow hypermedia
1 parent 7728ac2 commit 2dd5983

File tree

2 files changed

+119
-1
lines changed

2 files changed

+119
-1
lines changed

hawser/client.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,20 @@ import (
1414
"net/http"
1515
"os"
1616
"path/filepath"
17+
"strings"
1718
)
1819

1920
const (
2021
// Legacy type
2122
gitMediaType = "application/vnd.git-media"
2223

24+
// The main type, sub type, and suffix. Use this when ensuring the type from
25+
// an HTTP response is correct.
26+
gitMediaMetaTypePrefix = gitMediaType + "+json"
27+
2328
// Adds the extra mime params. Use this when sending the type in an HTTP
2429
// request.
25-
gitMediaMetaType = gitMediaType + "+json; charset=utf-8"
30+
gitMediaMetaType = gitMediaMetaTypePrefix + "; charset=utf-8"
2631
)
2732

2833
func Download(oidPath string) (io.ReadCloser, int64, *WrappedError) {
@@ -46,6 +51,47 @@ func Download(oidPath string) (io.ReadCloser, int64, *WrappedError) {
4651
return nil, 0, wErr
4752
}
4853

54+
if strings.HasPrefix(contentType, gitMediaMetaTypePrefix) {
55+
obj := &objectResource{}
56+
err := json.NewDecoder(res.Body).Decode(obj)
57+
res.Body.Close()
58+
if err != nil {
59+
wErr := Error(err)
60+
setErrorResponseContext(wErr, res)
61+
return nil, 0, wErr
62+
}
63+
64+
dlReq, err := obj.NewRequest("download", "GET")
65+
if err != nil {
66+
wErr := Error(err)
67+
setErrorResponseContext(wErr, res)
68+
return nil, 0, wErr
69+
}
70+
71+
dlCreds, err := setRequestHeaders(dlReq)
72+
if err != nil {
73+
return nil, 0, Errorf(err, "Error attempting to GET %s", oidPath)
74+
}
75+
76+
dlRes, err := DoHTTP(Config, dlReq)
77+
if err != nil {
78+
wErr := Error(err)
79+
setErrorResponseContext(wErr, res)
80+
return nil, 0, wErr
81+
}
82+
83+
saveCredentials(dlCreds, dlRes)
84+
85+
contentType := dlRes.Header.Get("Content-Type")
86+
if contentType == "" {
87+
wErr = Error(errors.New("Empty Content-Type"))
88+
setErrorResponseContext(wErr, res)
89+
return nil, 0, wErr
90+
}
91+
92+
res = dlRes
93+
}
94+
4995
ok, headerSize, wErr := validateMediaHeader(contentType, res.Body)
5096
if !ok {
5197
setErrorResponseContext(wErr, res)

hawser/client_download_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package hawser
22

33
import (
4+
"encoding/json"
45
"io/ioutil"
56
"net/http"
67
"net/http/httptest"
@@ -49,6 +50,77 @@ func TestDownload(t *testing.T) {
4950
}
5051
}
5152

53+
func TestDownloadFromMeta(t *testing.T) {
54+
mux := http.NewServeMux()
55+
server := httptest.NewServer(mux)
56+
tmp := tempdir(t)
57+
defer server.Close()
58+
defer os.RemoveAll(tmp)
59+
60+
// simulates an endpoint that returns the meta data for every request.
61+
// this way downloads keep working with the older prototype server during
62+
// the pre-release.
63+
mux.HandleFunc("/media/objects/oid", func(w http.ResponseWriter, r *http.Request) {
64+
if r.Method != "GET" {
65+
w.WriteHeader(405)
66+
return
67+
}
68+
69+
obj := &objectResource{
70+
Oid: "oid",
71+
Size: 4,
72+
Links: map[string]*linkRelation{
73+
"download": &linkRelation{
74+
Href: server.URL + "/media/download/oid",
75+
},
76+
},
77+
}
78+
79+
by, err := json.Marshal(obj)
80+
if err != nil {
81+
t.Errorf("Error marshaling json: %s", err)
82+
}
83+
84+
head := w.Header()
85+
head.Set("Content-Type", "application/vnd.git-media+json")
86+
w.WriteHeader(200)
87+
w.Write(by)
88+
})
89+
90+
mux.HandleFunc("/media/download/oid", func(w http.ResponseWriter, r *http.Request) {
91+
if r.Method != "GET" {
92+
w.WriteHeader(405)
93+
return
94+
}
95+
96+
head := w.Header()
97+
head.Set("Content-Type", "application/octet-stream")
98+
head.Set("Content-Length", "4")
99+
w.WriteHeader(200)
100+
w.Write([]byte("test"))
101+
})
102+
103+
Config.SetConfig("hawser.url", server.URL+"/media")
104+
reader, size, wErr := Download("whatever/oid")
105+
if wErr != nil {
106+
t.Fatalf("unexpected error: %s", wErr)
107+
}
108+
defer reader.Close()
109+
110+
if size != 4 {
111+
t.Errorf("unexpected size: %d", size)
112+
}
113+
114+
by, err := ioutil.ReadAll(reader)
115+
if err != nil {
116+
t.Fatalf("unexpected error: %s", err)
117+
}
118+
119+
if body := string(by); body != "test" {
120+
t.Errorf("unexpected body: %s", body)
121+
}
122+
}
123+
52124
func TestDownloadWithRedirect(t *testing.T) {
53125
mux := http.NewServeMux()
54126
server := httptest.NewServer(mux)

0 commit comments

Comments
 (0)