kubernetes/vendor/github.com/vmware/photon-controller-go-sdk/photon/restclient.go
2016-11-08 09:36:16 -08:00

220 lines
6.2 KiB
Go

// Copyright (c) 2016 VMware, Inc. All Rights Reserved.
//
// This product is licensed to you under the Apache License, Version 2.0 (the "License").
// You may not use this product except in compliance with the License.
//
// This product may include a number of subcomponents with separate copyright notices and
// license terms. Your use of these subcomponents is subject to the terms and conditions
// of the subcomponent's license, as noted in the LICENSE file.
package photon
import (
"crypto/rand"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strings"
)
type restClient struct {
httpClient *http.Client
logger *log.Logger
}
type request struct {
Method string
URL string
ContentType string
Body io.Reader
Token string
}
type page struct {
Items []interface{} `json:"items"`
NextPageLink string `json:"nextPageLink"`
PreviousPageLink string `json:"previousPageLink"`
}
type documentList struct {
Items []interface{}
}
const appJson string = "application/json"
// From https://golang.org/src/mime/multipart/writer.go
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
func (client *restClient) AppendSlice(origSlice []interface{}, dataToAppend []interface{}) []interface{} {
origLen := len(origSlice)
newLen := origLen + len(dataToAppend)
if newLen > cap(origSlice) {
newSlice := make([]interface{}, (newLen+1)*2)
copy(newSlice, origSlice)
origSlice = newSlice
}
origSlice = origSlice[0:newLen]
copy(origSlice[origLen:newLen], dataToAppend)
return origSlice
}
func (client *restClient) Get(url string, token string) (res *http.Response, err error) {
req := request{"GET", url, "", nil, token}
res, err = client.Do(&req)
return
}
func (client *restClient) GetList(endpoint string, url string, token string) (result []byte, err error) {
req := request{"GET", url, "", nil, token}
res, err := client.Do(&req)
if err != nil {
return
}
res, err = getError(res)
if err != nil {
return
}
decoder := json.NewDecoder(res.Body)
decoder.UseNumber()
page := &page{}
err = decoder.Decode(page)
if err != nil {
return
}
documentList := &documentList{}
documentList.Items = client.AppendSlice(documentList.Items, page.Items)
for page.NextPageLink != "" {
req = request{"GET", endpoint + page.NextPageLink, "", nil, token}
res, err = client.Do(&req)
if err != nil {
return
}
res, err = getError(res)
if err != nil {
return
}
decoder = json.NewDecoder(res.Body)
decoder.UseNumber()
page.NextPageLink = ""
page.PreviousPageLink = ""
err = decoder.Decode(page)
if err != nil {
return
}
documentList.Items = client.AppendSlice(documentList.Items, page.Items)
}
result, err = json.Marshal(documentList)
return
}
func (client *restClient) Post(url string, contentType string, body io.Reader, token string) (res *http.Response, err error) {
if contentType == "" {
contentType = appJson
}
req := request{"POST", url, contentType, body, token}
res, err = client.Do(&req)
return
}
func (client *restClient) Delete(url string, token string) (res *http.Response, err error) {
req := request{"DELETE", url, "", nil, token}
res, err = client.Do(&req)
return
}
func (client *restClient) Do(req *request) (res *http.Response, err error) {
r, err := http.NewRequest(req.Method, req.URL, req.Body)
if err != nil {
client.logger.Printf("An error occured creating request %s on %s. Error: %s", req.Method, req.URL, err)
return
}
if req.ContentType != "" {
r.Header.Add("Content-Type", req.ContentType)
}
if req.Token != "" {
r.Header.Add("Authorization", "Bearer "+req.Token)
}
res, err = client.httpClient.Do(r)
if err != nil {
client.logger.Printf("An error occured when calling %s on %s. Error: %s", req.Method, req.URL, err)
return
}
client.logger.Printf("[%s] %s - %s %s", res.Header.Get("request-id"), res.Status, req.Method, req.URL)
return
}
func (client *restClient) MultipartUploadFile(url, filePath string, params map[string]string, token string) (res *http.Response, err error) {
file, err := os.Open(filePath)
if err != nil {
return
}
defer file.Close()
return client.MultipartUpload(url, file, filepath.Base(filePath), params, token)
}
func (client *restClient) MultipartUpload(url string, reader io.Reader, filename string, params map[string]string, token string) (res *http.Response, err error) {
// The mime/multipart package does not support streaming multipart data from disk,
// at least not without complicated, problematic goroutines that simultaneously read/write into a buffer.
// A much easier approach is to just construct the multipart request by hand, using io.MultiPart to
// concatenate the parts of the request together into a single io.Reader.
boundary := client.randomBoundary()
parts := []io.Reader{}
// Create a part for each key, val pair in params
for k, v := range params {
parts = append(parts, client.createFieldPart(k, v, boundary))
}
start := fmt.Sprintf("\r\n--%s\r\n", boundary)
start += fmt.Sprintf("Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n", quoteEscaper.Replace(filename))
start += fmt.Sprintf("Content-Type: application/octet-stream\r\n\r\n")
end := fmt.Sprintf("\r\n--%s--", boundary)
// The request will consist of a reader to begin the request, a reader which points
// to the file data on disk, and a reader containing the closing boundary of the request.
parts = append(parts, strings.NewReader(start), reader, strings.NewReader(end))
contentType := fmt.Sprintf("multipart/form-data; boundary=%s", boundary)
res, err = client.Do(&request{"POST", url, contentType, io.MultiReader(parts...), token})
return
}
// From https://golang.org/src/mime/multipart/writer.go
func (client *restClient) randomBoundary() string {
var buf [30]byte
_, err := io.ReadFull(rand.Reader, buf[:])
if err != nil {
panic(err)
}
return fmt.Sprintf("%x", buf[:])
}
// Creates a reader that encapsulates a single multipart form part
func (client *restClient) createFieldPart(fieldname, value, boundary string) io.Reader {
str := fmt.Sprintf("\r\n--%s\r\n", boundary)
str += fmt.Sprintf("Content-Disposition: form-data; name=\"%s\"\r\n\r\n", quoteEscaper.Replace(fieldname))
str += value
return strings.NewReader(str)
}