2015-02-12 23:18:47 +00:00
|
|
|
package hawser
|
2013-10-04 17:09:03 +00:00
|
|
|
|
|
|
|
import (
|
2015-01-23 22:11:10 +00:00
|
|
|
"bytes"
|
2013-10-31 22:12:30 +00:00
|
|
|
"encoding/base64"
|
2013-10-31 19:00:09 +00:00
|
|
|
"encoding/json"
|
2014-03-27 14:59:32 +00:00
|
|
|
"errors"
|
2013-10-04 17:09:03 +00:00
|
|
|
"fmt"
|
2014-03-12 15:31:41 +00:00
|
|
|
"github.com/cheggaaa/pb"
|
2015-01-23 22:11:10 +00:00
|
|
|
"github.com/rubyist/tracerx"
|
2013-10-22 18:21:01 +00:00
|
|
|
"io"
|
2014-03-12 15:31:41 +00:00
|
|
|
"io/ioutil"
|
2014-04-16 14:30:13 +00:00
|
|
|
"mime"
|
2013-10-04 17:09:03 +00:00
|
|
|
"net/http"
|
|
|
|
"os"
|
2014-08-08 17:52:58 +00:00
|
|
|
"path/filepath"
|
2013-10-04 17:09:03 +00:00
|
|
|
)
|
|
|
|
|
2013-12-09 15:34:29 +00:00
|
|
|
const (
|
2015-01-31 23:33:04 +00:00
|
|
|
gitMediaType = "application/vnd.git-media"
|
2014-04-16 15:28:04 +00:00
|
|
|
gitMediaMetaType = gitMediaType + "+json; charset=utf-8"
|
2013-12-09 15:34:29 +00:00
|
|
|
)
|
|
|
|
|
2015-01-23 22:11:10 +00:00
|
|
|
type linkMeta struct {
|
|
|
|
Links map[string]*link `json:"_links,omitempty"`
|
|
|
|
}
|
|
|
|
|
2015-02-13 20:37:19 +00:00
|
|
|
func (l *linkMeta) Rel(name string) (*link, bool) {
|
|
|
|
if l.Links == nil {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
lnk, ok := l.Links[name]
|
|
|
|
return lnk, ok
|
|
|
|
}
|
|
|
|
|
2015-01-23 22:11:10 +00:00
|
|
|
type link struct {
|
|
|
|
Href string `json:"href"`
|
|
|
|
Header map[string]string `json:"header,omitempty"`
|
|
|
|
}
|
|
|
|
|
2015-02-12 02:28:42 +00:00
|
|
|
type UploadRequest struct {
|
|
|
|
OidPath string
|
|
|
|
Filename string
|
2015-02-12 23:18:47 +00:00
|
|
|
CopyCallback CopyCallback
|
2015-02-12 02:28:42 +00:00
|
|
|
}
|
|
|
|
|
2015-02-13 19:01:25 +00:00
|
|
|
func Download(oidPath string) (io.ReadCloser, int64, *WrappedError) {
|
|
|
|
oid := filepath.Base(oidPath)
|
2015-02-12 23:22:54 +00:00
|
|
|
req, creds, err := clientRequest("GET", oid)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, Error(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
req.Header.Set("Accept", gitMediaType)
|
|
|
|
res, wErr := doRequest(req, creds)
|
|
|
|
|
|
|
|
if wErr != nil {
|
|
|
|
return nil, 0, wErr
|
|
|
|
}
|
|
|
|
|
|
|
|
contentType := res.Header.Get("Content-Type")
|
|
|
|
if contentType == "" {
|
|
|
|
wErr = Error(errors.New("Empty Content-Type"))
|
|
|
|
setErrorResponseContext(wErr, res)
|
|
|
|
return nil, 0, wErr
|
|
|
|
}
|
|
|
|
|
|
|
|
if ok, wErr := validateMediaHeader(contentType, res.Body); !ok {
|
|
|
|
setErrorResponseContext(wErr, res)
|
|
|
|
return nil, 0, wErr
|
|
|
|
}
|
|
|
|
|
|
|
|
return res.Body, res.ContentLength, nil
|
|
|
|
}
|
|
|
|
|
2015-02-13 19:01:25 +00:00
|
|
|
func Upload(oidPath, filename string, cb CopyCallback) *WrappedError {
|
|
|
|
linkMeta, status, err := callPost(oidPath, filename)
|
2015-02-12 02:28:42 +00:00
|
|
|
if err != nil && status != 302 {
|
2015-02-12 23:18:47 +00:00
|
|
|
return Errorf(err, "Error starting file upload.")
|
2015-02-12 02:28:42 +00:00
|
|
|
}
|
|
|
|
|
2015-02-13 19:01:25 +00:00
|
|
|
oid := filepath.Base(oidPath)
|
2015-02-12 02:28:42 +00:00
|
|
|
|
|
|
|
switch status {
|
2015-02-13 20:37:19 +00:00
|
|
|
case 200: // object exists on the server
|
2015-02-12 02:28:42 +00:00
|
|
|
case 405, 302:
|
|
|
|
// Do the old style OPTIONS + PUT
|
2015-02-13 19:01:25 +00:00
|
|
|
status, err := callOptions(oidPath)
|
2015-02-12 02:28:42 +00:00
|
|
|
if err != nil {
|
2015-02-13 19:01:25 +00:00
|
|
|
return Errorf(err, "Error getting options for file %s (%s)", filename, oid)
|
2015-02-12 02:28:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if status != 200 {
|
2015-02-13 19:01:25 +00:00
|
|
|
err = callPut(oidPath, filename, cb)
|
2015-02-12 02:28:42 +00:00
|
|
|
if err != nil {
|
2015-02-13 19:01:25 +00:00
|
|
|
return Errorf(err, "Error uploading file %s (%s)", filename, oid)
|
2015-02-12 02:28:42 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-13 20:37:19 +00:00
|
|
|
case 202:
|
|
|
|
// the server responded with hypermedia links to upload and verify the object.
|
2015-02-13 19:01:25 +00:00
|
|
|
err = callExternalPut(oidPath, filename, linkMeta, cb)
|
2015-02-12 02:28:42 +00:00
|
|
|
if err != nil {
|
2015-02-13 19:01:25 +00:00
|
|
|
return Errorf(err, "Error uploading file %s (%s)", filename, oid)
|
2015-02-12 02:28:42 +00:00
|
|
|
}
|
|
|
|
default:
|
2015-02-12 23:18:47 +00:00
|
|
|
return Errorf(err, "Unexpected HTTP response: %d", status)
|
2015-02-12 02:28:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-02-12 23:32:42 +00:00
|
|
|
func callOptions(filehash string) (int, error) {
|
2014-08-08 17:52:58 +00:00
|
|
|
oid := filepath.Base(filehash)
|
2014-04-01 15:17:04 +00:00
|
|
|
_, err := os.Stat(filehash)
|
|
|
|
if err != nil {
|
2014-09-22 15:46:19 +00:00
|
|
|
return 0, err
|
2014-04-01 15:17:04 +00:00
|
|
|
}
|
|
|
|
|
2015-01-23 22:11:10 +00:00
|
|
|
tracerx.Printf("api_options: %s", oid)
|
2014-04-01 15:17:04 +00:00
|
|
|
req, creds, err := clientRequest("OPTIONS", oid)
|
|
|
|
if err != nil {
|
2014-09-22 15:46:19 +00:00
|
|
|
return 0, err
|
2014-04-01 15:17:04 +00:00
|
|
|
}
|
|
|
|
|
2015-01-29 15:55:52 +00:00
|
|
|
res, wErr := doRequest(req, creds)
|
2014-08-08 19:38:02 +00:00
|
|
|
if wErr != nil {
|
2014-09-22 15:46:19 +00:00
|
|
|
return 0, wErr
|
2014-04-01 15:17:04 +00:00
|
|
|
}
|
2015-01-29 15:55:52 +00:00
|
|
|
tracerx.Printf("api_options_status: %d", res.StatusCode)
|
2014-04-01 15:17:04 +00:00
|
|
|
|
2015-01-29 15:55:52 +00:00
|
|
|
return res.StatusCode, nil
|
2014-04-01 15:17:04 +00:00
|
|
|
}
|
|
|
|
|
2015-02-12 23:32:42 +00:00
|
|
|
func callPut(filehash, filename string, cb CopyCallback) error {
|
2014-03-12 14:55:01 +00:00
|
|
|
if filename == "" {
|
|
|
|
filename = filehash
|
|
|
|
}
|
|
|
|
|
2014-08-08 17:52:58 +00:00
|
|
|
oid := filepath.Base(filehash)
|
2014-08-07 17:37:04 +00:00
|
|
|
file, err := os.Open(filehash)
|
2013-10-31 19:22:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-08-07 17:37:04 +00:00
|
|
|
defer file.Close()
|
2013-10-31 19:22:33 +00:00
|
|
|
|
2014-08-07 17:37:04 +00:00
|
|
|
stat, err := file.Stat()
|
2013-10-04 17:09:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2013-11-02 00:54:33 +00:00
|
|
|
req, creds, err := clientRequest("PUT", oid)
|
2013-10-04 17:09:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-11-02 00:31:11 +00:00
|
|
|
|
2014-08-07 17:37:04 +00:00
|
|
|
fileSize := stat.Size()
|
2015-02-12 23:18:47 +00:00
|
|
|
reader := &CallbackReader{
|
2014-08-07 17:37:04 +00:00
|
|
|
C: cb,
|
|
|
|
TotalSize: fileSize,
|
|
|
|
Reader: file,
|
|
|
|
}
|
|
|
|
|
|
|
|
bar := pb.StartNew(int(fileSize))
|
2014-03-12 15:31:41 +00:00
|
|
|
bar.SetUnits(pb.U_BYTES)
|
|
|
|
bar.Start()
|
|
|
|
|
2013-12-09 15:34:29 +00:00
|
|
|
req.Header.Set("Content-Type", gitMediaType)
|
|
|
|
req.Header.Set("Accept", gitMediaMetaType)
|
2014-08-07 17:37:04 +00:00
|
|
|
req.Body = ioutil.NopCloser(bar.NewProxyReader(reader))
|
|
|
|
req.ContentLength = fileSize
|
2013-10-04 17:09:03 +00:00
|
|
|
|
2014-03-12 15:31:41 +00:00
|
|
|
fmt.Printf("Sending %s\n", filename)
|
|
|
|
|
2015-01-23 22:11:10 +00:00
|
|
|
tracerx.Printf("api_put: %s %s", oid, filename)
|
2015-01-29 15:55:52 +00:00
|
|
|
res, wErr := doRequest(req, creds)
|
2014-08-08 19:38:02 +00:00
|
|
|
if wErr != nil {
|
|
|
|
return wErr
|
2013-10-04 17:09:03 +00:00
|
|
|
}
|
2015-01-29 15:55:52 +00:00
|
|
|
tracerx.Printf("api_put_status: %d", res.StatusCode)
|
2013-10-04 17:09:03 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2013-10-22 18:21:01 +00:00
|
|
|
|
2015-02-12 23:32:42 +00:00
|
|
|
func callExternalPut(filehash, filename string, lm *linkMeta, cb CopyCallback) error {
|
2015-02-13 20:37:19 +00:00
|
|
|
if lm == nil {
|
|
|
|
return Error(errors.New("No hypermedia links provided"))
|
|
|
|
}
|
|
|
|
|
|
|
|
link, ok := lm.Rel("upload")
|
2015-01-23 22:11:10 +00:00
|
|
|
if !ok {
|
2015-02-12 23:18:47 +00:00
|
|
|
return Error(errors.New("No upload link provided"))
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
file, err := os.Open(filehash)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
stat, err := file.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
fileSize := stat.Size()
|
2015-02-12 23:18:47 +00:00
|
|
|
reader := &CallbackReader{
|
2015-01-23 22:11:10 +00:00
|
|
|
C: cb,
|
|
|
|
TotalSize: fileSize,
|
|
|
|
Reader: file,
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("PUT", link.Href, nil)
|
|
|
|
if err != nil {
|
2015-02-12 23:18:47 +00:00
|
|
|
return Error(err)
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
|
|
|
for h, v := range link.Header {
|
|
|
|
req.Header.Set(h, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
bar := pb.StartNew(int(fileSize))
|
|
|
|
bar.SetUnits(pb.U_BYTES)
|
|
|
|
bar.Start()
|
|
|
|
|
|
|
|
req.Body = ioutil.NopCloser(bar.NewProxyReader(reader))
|
|
|
|
req.ContentLength = fileSize
|
|
|
|
|
|
|
|
tracerx.Printf("external_put: %s %s", filepath.Base(filehash), req.URL)
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
|
|
if err != nil {
|
2015-02-12 23:18:47 +00:00
|
|
|
return Error(err)
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
|
|
|
tracerx.Printf("external_put_status: %d", res.StatusCode)
|
|
|
|
|
2015-02-13 20:37:19 +00:00
|
|
|
// Run the verify callback
|
|
|
|
if cb, ok := lm.Rel("verify"); ok {
|
2015-01-23 22:11:10 +00:00
|
|
|
oid := filepath.Base(filehash)
|
|
|
|
|
|
|
|
cbreq, err := http.NewRequest("POST", cb.Href, nil)
|
|
|
|
if err != nil {
|
2015-02-12 23:18:47 +00:00
|
|
|
return Error(err)
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
|
|
|
for h, v := range cb.Header {
|
|
|
|
cbreq.Header.Set(h, v)
|
|
|
|
}
|
|
|
|
|
2015-02-13 20:37:19 +00:00
|
|
|
d := fmt.Sprintf(`{"oid":"%s", "size":%d}`, oid, fileSize)
|
2015-01-23 22:11:10 +00:00
|
|
|
cbreq.Body = ioutil.NopCloser(bytes.NewBufferString(d))
|
|
|
|
|
2015-02-13 20:37:19 +00:00
|
|
|
tracerx.Printf("verify: %s %s", oid, cb.Href)
|
2015-01-23 22:11:10 +00:00
|
|
|
cbres, err := http.DefaultClient.Do(cbreq)
|
|
|
|
if err != nil {
|
2015-02-12 23:18:47 +00:00
|
|
|
return Error(err)
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
2015-02-13 20:37:19 +00:00
|
|
|
tracerx.Printf("verify_status: %d", cbres.StatusCode)
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-02-12 23:32:42 +00:00
|
|
|
func callPost(filehash, filename string) (*linkMeta, int, error) {
|
2015-01-23 22:11:10 +00:00
|
|
|
oid := filepath.Base(filehash)
|
|
|
|
req, creds, err := clientRequest("POST", "")
|
|
|
|
if err != nil {
|
2015-02-12 23:18:47 +00:00
|
|
|
return nil, 0, Error(err)
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
file, err := os.Open(filehash)
|
|
|
|
if err != nil {
|
2015-02-12 23:18:47 +00:00
|
|
|
return nil, 0, Error(err)
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
stat, err := file.Stat()
|
|
|
|
if err != nil {
|
2015-02-12 23:18:47 +00:00
|
|
|
return nil, 0, Error(err)
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
|
|
|
fileSize := stat.Size()
|
|
|
|
|
|
|
|
d := fmt.Sprintf(`{"oid":"%s", "size":%d}`, oid, fileSize)
|
|
|
|
req.Body = ioutil.NopCloser(bytes.NewBufferString(d))
|
|
|
|
|
|
|
|
req.Header.Set("Accept", gitMediaMetaType)
|
|
|
|
|
|
|
|
tracerx.Printf("api_post: %s %s", oid, filename)
|
|
|
|
res, wErr := doRequest(req, creds)
|
|
|
|
if wErr != nil {
|
|
|
|
return nil, 0, wErr
|
|
|
|
}
|
|
|
|
tracerx.Printf("api_post_status: %d", res.StatusCode)
|
|
|
|
|
2015-02-13 20:37:19 +00:00
|
|
|
if res.StatusCode == 202 {
|
|
|
|
lm := &linkMeta{}
|
2015-01-23 22:11:10 +00:00
|
|
|
dec := json.NewDecoder(res.Body)
|
2015-02-13 20:37:19 +00:00
|
|
|
err := dec.Decode(lm)
|
2015-01-23 22:11:10 +00:00
|
|
|
if err != nil {
|
2015-02-12 23:18:47 +00:00
|
|
|
return nil, res.StatusCode, Errorf(err, "Error decoding JSON from %s %s.", req.Method, req.URL)
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
|
|
|
|
2015-02-13 20:37:19 +00:00
|
|
|
return lm, res.StatusCode, nil
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil, res.StatusCode, nil
|
|
|
|
}
|
|
|
|
|
2015-02-12 23:18:47 +00:00
|
|
|
func validateMediaHeader(contentType string, reader io.Reader) (bool, *WrappedError) {
|
2014-04-16 15:02:58 +00:00
|
|
|
mediaType, params, err := mime.ParseMediaType(contentType)
|
|
|
|
if err != nil {
|
2015-02-12 23:18:47 +00:00
|
|
|
return false, Errorf(err, "Invalid Media Type: %s", contentType)
|
2014-04-16 15:02:58 +00:00
|
|
|
}
|
2014-03-27 14:59:32 +00:00
|
|
|
|
2015-01-23 22:11:10 +00:00
|
|
|
if mediaType == gitMediaType {
|
2014-03-27 14:59:32 +00:00
|
|
|
|
2015-01-23 22:11:10 +00:00
|
|
|
givenHeader, ok := params["header"]
|
|
|
|
if !ok {
|
2015-02-12 23:18:47 +00:00
|
|
|
return false, Error(fmt.Errorf("Missing Git Media header in %s", contentType))
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
2013-10-22 18:21:01 +00:00
|
|
|
|
2015-01-23 22:11:10 +00:00
|
|
|
fullGivenHeader := "--" + givenHeader + "\n"
|
2014-04-16 15:02:58 +00:00
|
|
|
|
2015-01-23 22:11:10 +00:00
|
|
|
header := make([]byte, len(fullGivenHeader))
|
|
|
|
_, err = io.ReadAtLeast(reader, header, len(fullGivenHeader))
|
|
|
|
if err != nil {
|
2015-02-12 23:18:47 +00:00
|
|
|
return false, Errorf(err, "Error reading response body.")
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
2014-04-16 15:02:58 +00:00
|
|
|
|
2015-01-23 22:11:10 +00:00
|
|
|
if string(header) != fullGivenHeader {
|
2015-02-12 23:18:47 +00:00
|
|
|
return false, Error(fmt.Errorf("Invalid header: %s expected, got %s", fullGivenHeader, header))
|
2015-01-23 22:11:10 +00:00
|
|
|
}
|
2014-04-16 15:02:58 +00:00
|
|
|
}
|
|
|
|
return true, nil
|
2013-10-22 18:21:01 +00:00
|
|
|
}
|
|
|
|
|
2015-02-12 23:18:47 +00:00
|
|
|
func doRequest(req *http.Request, creds Creds) (*http.Response, *WrappedError) {
|
|
|
|
res, err := HttpClient().Do(req)
|
2013-11-02 00:31:11 +00:00
|
|
|
|
2015-02-12 23:18:47 +00:00
|
|
|
var wErr *WrappedError
|
2014-08-08 17:31:33 +00:00
|
|
|
|
2015-02-12 23:18:47 +00:00
|
|
|
if err == RedirectError {
|
2015-02-01 23:34:59 +00:00
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
|
2013-11-02 00:23:37 +00:00
|
|
|
if err == nil {
|
|
|
|
if res.StatusCode > 299 {
|
2014-08-08 16:32:20 +00:00
|
|
|
// An auth error should be 403. Could be 404 also.
|
|
|
|
if res.StatusCode < 405 {
|
|
|
|
execCreds(creds, "reject")
|
2013-11-02 00:54:33 +00:00
|
|
|
|
2015-02-12 23:18:47 +00:00
|
|
|
apierr := &ClientError{}
|
2015-01-27 00:00:43 +00:00
|
|
|
dec := json.NewDecoder(res.Body)
|
|
|
|
if err := dec.Decode(apierr); err != nil {
|
2015-02-12 23:18:47 +00:00
|
|
|
wErr = Errorf(err, "Error decoding JSON from response")
|
2015-01-27 00:00:43 +00:00
|
|
|
} else {
|
2015-02-12 23:18:47 +00:00
|
|
|
wErr = Errorf(apierr, "Invalid response: %d", res.StatusCode)
|
2015-01-27 00:00:43 +00:00
|
|
|
}
|
2013-11-02 00:23:37 +00:00
|
|
|
}
|
2014-08-08 17:31:33 +00:00
|
|
|
} else {
|
|
|
|
execCreds(creds, "approve")
|
2013-11-02 00:23:37 +00:00
|
|
|
}
|
2015-02-01 23:34:59 +00:00
|
|
|
} else if res.StatusCode != 302 { // hack for pre-release
|
2015-02-12 23:18:47 +00:00
|
|
|
wErr = Errorf(err, "Error sending HTTP request to %s", req.URL.String())
|
2014-08-08 17:31:33 +00:00
|
|
|
}
|
2014-07-28 23:46:12 +00:00
|
|
|
|
2014-08-08 17:31:33 +00:00
|
|
|
if wErr != nil {
|
|
|
|
if res != nil {
|
|
|
|
setErrorResponseContext(wErr, res)
|
|
|
|
} else {
|
|
|
|
setErrorRequestContext(wErr, req)
|
|
|
|
}
|
2013-11-02 00:23:37 +00:00
|
|
|
}
|
2013-11-02 00:31:11 +00:00
|
|
|
|
2014-08-08 17:31:33 +00:00
|
|
|
return res, wErr
|
|
|
|
}
|
|
|
|
|
2014-08-08 20:02:44 +00:00
|
|
|
var hiddenHeaders = map[string]bool{
|
|
|
|
"Authorization": true,
|
|
|
|
}
|
|
|
|
|
2015-02-12 23:18:47 +00:00
|
|
|
func setErrorRequestContext(err *WrappedError, req *http.Request) {
|
|
|
|
err.Set("Endpoint", Config.Endpoint())
|
2014-08-08 20:02:44 +00:00
|
|
|
err.Set("URL", fmt.Sprintf("%s %s", req.Method, req.URL.String()))
|
|
|
|
setErrorHeaderContext(err, "Response", req.Header)
|
2014-08-08 17:31:33 +00:00
|
|
|
}
|
|
|
|
|
2015-02-12 23:18:47 +00:00
|
|
|
func setErrorResponseContext(err *WrappedError, res *http.Response) {
|
2014-08-08 17:31:33 +00:00
|
|
|
err.Set("Status", res.Status)
|
2014-08-08 20:02:44 +00:00
|
|
|
setErrorHeaderContext(err, "Request", res.Header)
|
2014-08-08 17:31:33 +00:00
|
|
|
setErrorRequestContext(err, res.Request)
|
2013-11-02 00:23:37 +00:00
|
|
|
}
|
|
|
|
|
2015-02-12 23:18:47 +00:00
|
|
|
func setErrorHeaderContext(err *WrappedError, prefix string, head http.Header) {
|
2014-08-08 20:02:44 +00:00
|
|
|
for key, _ := range head {
|
|
|
|
contextKey := fmt.Sprintf("%s:%s", prefix, key)
|
|
|
|
if _, skip := hiddenHeaders[key]; skip {
|
|
|
|
err.Set(contextKey, "--")
|
|
|
|
} else {
|
|
|
|
err.Set(contextKey, head.Get(key))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-02 00:54:33 +00:00
|
|
|
func clientRequest(method, oid string) (*http.Request, Creds, error) {
|
2015-02-12 23:25:18 +00:00
|
|
|
u := Config.ObjectUrl(oid)
|
2013-10-31 22:12:30 +00:00
|
|
|
req, err := http.NewRequest(method, u.String(), nil)
|
2015-02-12 23:18:47 +00:00
|
|
|
req.Header.Set("User-Agent", UserAgent)
|
2013-10-31 22:12:30 +00:00
|
|
|
if err == nil {
|
|
|
|
creds, err := credentials(u)
|
|
|
|
if err != nil {
|
2013-11-01 23:19:04 +00:00
|
|
|
return req, nil, err
|
2013-10-31 22:12:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
token := fmt.Sprintf("%s:%s", creds["username"], creds["password"])
|
|
|
|
auth := "Basic " + base64.URLEncoding.EncodeToString([]byte(token))
|
|
|
|
req.Header.Set("Authorization", auth)
|
2013-11-01 23:19:04 +00:00
|
|
|
return req, creds, nil
|
2013-10-31 22:12:30 +00:00
|
|
|
}
|
|
|
|
|
2013-11-01 23:19:04 +00:00
|
|
|
return req, nil, err
|
2013-10-31 21:33:57 +00:00
|
|
|
}
|
|
|
|
|
2015-02-12 23:18:47 +00:00
|
|
|
type ClientError struct {
|
2013-10-31 19:00:09 +00:00
|
|
|
Message string `json:"message"`
|
|
|
|
RequestId string `json:"request_id,omitempty"`
|
|
|
|
}
|
|
|
|
|
2015-02-12 23:18:47 +00:00
|
|
|
func (e *ClientError) Error() string {
|
2013-10-31 19:00:09 +00:00
|
|
|
return e.Message
|
|
|
|
}
|