1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
//
// gocommon - Go library to interact with the JoyentCloud
//
//
// Copyright (c) 2013 Joyent Inc.
//
// Written by Daniele Stroppa <daniele.stroppa@joyent.com>
//
package client
import (
"fmt"
"log"
"net/url"
"strings"
"sync"
"time"
joyenthttp "github.com/joyent/gocommon/http"
"github.com/joyent/gosign/auth"
)
const (
// The HTTP request methods.
GET = "GET"
POST = "POST"
PUT = "PUT"
DELETE = "DELETE"
HEAD = "HEAD"
COPY = "COPY"
)
// Client implementations sends service requests to the JoyentCloud.
type Client interface {
SendRequest(method, apiCall, rfc1123Date string, request *joyenthttp.RequestData, response *joyenthttp.ResponseData) (err error)
// MakeServiceURL prepares a full URL to a service endpoint, with optional
// URL parts. It uses the first endpoint it can find for the given service type.
MakeServiceURL(parts []string) string
SignURL(path string, expires time.Time) (string, error)
}
// This client sends requests without authenticating.
type client struct {
mu sync.Mutex
logger *log.Logger
baseURL string
creds *auth.Credentials
httpClient *joyenthttp.Client
}
var _ Client = (*client)(nil)
func newClient(baseURL string, credentials *auth.Credentials, httpClient *joyenthttp.Client, logger *log.Logger) Client {
client := client{baseURL: baseURL, logger: logger, creds: credentials, httpClient: httpClient}
return &client
}
func NewClient(baseURL, apiVersion string, credentials *auth.Credentials, logger *log.Logger) Client {
sharedHttpClient := joyenthttp.New(credentials, apiVersion, logger)
return newClient(baseURL, credentials, sharedHttpClient, logger)
}
func (c *client) sendRequest(method, url, rfc1123Date string, request *joyenthttp.RequestData, response *joyenthttp.ResponseData) (err error) {
if request.ReqValue != nil || response.RespValue != nil {
err = c.httpClient.JsonRequest(method, url, rfc1123Date, request, response)
} else {
err = c.httpClient.BinaryRequest(method, url, rfc1123Date, request, response)
}
return
}
func (c *client) SendRequest(method, apiCall, rfc1123Date string, request *joyenthttp.RequestData, response *joyenthttp.ResponseData) (err error) {
url := c.MakeServiceURL([]string{c.creds.UserAuthentication.User, apiCall})
err = c.sendRequest(method, url, rfc1123Date, request, response)
return
}
func makeURL(base string, parts []string) string {
if !strings.HasSuffix(base, "/") && len(parts) > 0 {
base += "/"
}
if parts[1] == "" {
return base + parts[0]
}
return base + strings.Join(parts, "/")
}
func (c *client) MakeServiceURL(parts []string) string {
return makeURL(c.baseURL, parts)
}
func (c *client) SignURL(path string, expires time.Time) (string, error) {
parsedURL, err := url.Parse(c.baseURL)
if err != nil {
return "", fmt.Errorf("bad Manta endpoint URL %q: %v", c.baseURL, err)
}
userAuthentication := c.creds.UserAuthentication
userAuthentication.Algorithm = "RSA-SHA1"
keyId := url.QueryEscape(fmt.Sprintf("/%s/keys/%s", userAuthentication.User, c.creds.MantaKeyId))
params := fmt.Sprintf("algorithm=%s&expires=%d&keyId=%s", userAuthentication.Algorithm, expires.Unix(), keyId)
signingLine := fmt.Sprintf("GET\n%s\n%s\n%s", parsedURL.Host, path, params)
signature, err := auth.GetSignature(userAuthentication, signingLine)
if err != nil {
return "", fmt.Errorf("cannot generate URL signature: %v", err)
}
signedURL := fmt.Sprintf("%s%s?%s&signature=%s", c.baseURL, path, params, url.QueryEscape(signature))
return signedURL, nil
}
|