teach Download() how to follow hypermedia

This commit is contained in:
Rick Olson 2015-02-25 18:32:33 -07:00
parent 7728ac274c
commit 2dd5983841
2 changed files with 119 additions and 1 deletions

@ -14,15 +14,20 @@ import (
"net/http"
"os"
"path/filepath"
"strings"
)
const (
// Legacy type
gitMediaType = "application/vnd.git-media"
// The main type, sub type, and suffix. Use this when ensuring the type from
// an HTTP response is correct.
gitMediaMetaTypePrefix = gitMediaType + "+json"
// Adds the extra mime params. Use this when sending the type in an HTTP
// request.
gitMediaMetaType = gitMediaType + "+json; charset=utf-8"
gitMediaMetaType = gitMediaMetaTypePrefix + "; charset=utf-8"
)
func Download(oidPath string) (io.ReadCloser, int64, *WrappedError) {
@ -46,6 +51,47 @@ func Download(oidPath string) (io.ReadCloser, int64, *WrappedError) {
return nil, 0, wErr
}
if strings.HasPrefix(contentType, gitMediaMetaTypePrefix) {
obj := &objectResource{}
err := json.NewDecoder(res.Body).Decode(obj)
res.Body.Close()
if err != nil {
wErr := Error(err)
setErrorResponseContext(wErr, res)
return nil, 0, wErr
}
dlReq, err := obj.NewRequest("download", "GET")
if err != nil {
wErr := Error(err)
setErrorResponseContext(wErr, res)
return nil, 0, wErr
}
dlCreds, err := setRequestHeaders(dlReq)
if err != nil {
return nil, 0, Errorf(err, "Error attempting to GET %s", oidPath)
}
dlRes, err := DoHTTP(Config, dlReq)
if err != nil {
wErr := Error(err)
setErrorResponseContext(wErr, res)
return nil, 0, wErr
}
saveCredentials(dlCreds, dlRes)
contentType := dlRes.Header.Get("Content-Type")
if contentType == "" {
wErr = Error(errors.New("Empty Content-Type"))
setErrorResponseContext(wErr, res)
return nil, 0, wErr
}
res = dlRes
}
ok, headerSize, wErr := validateMediaHeader(contentType, res.Body)
if !ok {
setErrorResponseContext(wErr, res)

@ -1,6 +1,7 @@
package hawser
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
@ -49,6 +50,77 @@ func TestDownload(t *testing.T) {
}
}
func TestDownloadFromMeta(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
tmp := tempdir(t)
defer server.Close()
defer os.RemoveAll(tmp)
// simulates an endpoint that returns the meta data for every request.
// this way downloads keep working with the older prototype server during
// the pre-release.
mux.HandleFunc("/media/objects/oid", func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
w.WriteHeader(405)
return
}
obj := &objectResource{
Oid: "oid",
Size: 4,
Links: map[string]*linkRelation{
"download": &linkRelation{
Href: server.URL + "/media/download/oid",
},
},
}
by, err := json.Marshal(obj)
if err != nil {
t.Errorf("Error marshaling json: %s", err)
}
head := w.Header()
head.Set("Content-Type", "application/vnd.git-media+json")
w.WriteHeader(200)
w.Write(by)
})
mux.HandleFunc("/media/download/oid", func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
w.WriteHeader(405)
return
}
head := w.Header()
head.Set("Content-Type", "application/octet-stream")
head.Set("Content-Length", "4")
w.WriteHeader(200)
w.Write([]byte("test"))
})
Config.SetConfig("hawser.url", server.URL+"/media")
reader, size, wErr := Download("whatever/oid")
if wErr != nil {
t.Fatalf("unexpected error: %s", wErr)
}
defer reader.Close()
if size != 4 {
t.Errorf("unexpected size: %d", size)
}
by, err := ioutil.ReadAll(reader)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if body := string(by); body != "test" {
t.Errorf("unexpected body: %s", body)
}
}
func TestDownloadWithRedirect(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)