blob: baedb631a4b4a6336b403109573163bc744b4a04 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"path/filepath"
"crypto/tls"
"net"
"time"
"strings"
)
type Network struct {
BrooklynUrl string
BrooklynUser string
BrooklynPass string
SkipSslChecks bool
Verbosity string
UserHeaders map[string]interface{}
CredentialsRequired bool
}
func NewNetwork(brooklynUrl, brooklynUser, brooklynPass string, skipSslChecks bool, verbose string, credentialsRequired bool, userHeaders map[string]interface{}) (net *Network) {
net = new(Network)
net.BrooklynUrl = brooklynUrl
net.BrooklynUser = brooklynUser
net.BrooklynPass = brooklynPass
net.SkipSslChecks = skipSslChecks
net.Verbosity = verbose
net.CredentialsRequired = credentialsRequired
net.UserHeaders = userHeaders
return
}
func (net *Network) NewRequest(method, path string, body io.Reader) *http.Request {
req, _ := http.NewRequest(method, net.BrooklynUrl+path, body)
if len(net.UserHeaders)>0{
for k, v :=range net.UserHeaders {
req.Header.Add(k,v.(string))
}
}
fmt.Printf("CredentialsRequired %q \n",net.CredentialsRequired)
if net.CredentialsRequired {
req.SetBasicAuth(net.BrooklynUser, net.BrooklynPass)
}
return req
}
func (net *Network) NewGetRequest(url string) *http.Request {
return net.NewRequest("GET", url, nil)
}
func (net *Network) NewPostRequest(url string, body io.Reader) *http.Request {
return net.NewRequest("POST", url, body)
}
func (net *Network) NewDeleteRequest(url string) *http.Request {
return net.NewRequest("DELETE", url, nil)
}
type HttpError struct {
Code int
Status string
Headers http.Header
Body string
}
func (err HttpError) Error() string {
return err.Status
}
func makeError(resp *http.Response, code int, body []byte) error {
theError := HttpError{
Code: code,
Status: resp.Status,
Headers: resp.Header,
}
return makeErrorBody(theError, body)
}
func makeSimpleError(code int, body []byte) error {
theError := HttpError{
Code: code,
}
return makeErrorBody(theError, body)
}
func makeErrorBody(theError HttpError, body []byte) error {
details := make(map[string]interface{})
if err := json.Unmarshal(body, &details); nil == err {
if message, ok := details["message"]; ok {
theError.Body = message.(string)
return theError
}
}
theError.Body = string(body)
return theError
}
func (net *Network) SendRequest(req *http.Request) ([]byte, error) {
body, _, err := net.SendRequestGetStatusCode(req)
return body, err
}
func (net *Network) makeClient() (*http.Client) {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: net.SkipSslChecks},
}
client := &http.Client{Transport: tr}
return client
}
func debug(verbosity string, supp func(b bool) ([]byte, error)) {
writer := func(data []byte, err error) {
if err == nil {
fmt.Fprintf(os.Stderr, "%s", data)
// include newline if data doesn't have one
if data[len(data)-1] != '\n' {
fmt.Fprintln(os.Stderr, "")
}
} else {
log.Fatalf("%s\n", err)
}
}
switch verbosity {
case "verbose":
writer(supp(false))
case "vverbose":
writer(supp(true))
}
}
func (net *Network) SendRequestGetStatusCode(req *http.Request) ([]byte, int, error) {
client := net.makeClient()
debug(net.Verbosity, func (includeBody bool) ([]byte, error) {
var authHeader = req.Header.Get("Authorization")
if authHeader != "" {
req.Header.Set("Authorization", "******")
}
data, err := httputil.DumpRequestOut(req, includeBody)
if authHeader != "" {
req.Header.Set("Authorization", authHeader)
}
return data, err
})
resp, err := client.Do(req)
debug(net.Verbosity, func (includeBody bool) ([]byte, error) {
return httputil.DumpResponse(resp, includeBody)
})
if err != nil {
return nil, 0, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
body = nil
}
if failed := unsuccessful(resp.StatusCode); failed {
return nil, resp.StatusCode, makeError(resp, resp.StatusCode, body)
}
return body, resp.StatusCode, err
}
const httpSuccessSeriesFrom = 200
const httpSuccessSeriesTo = 300
func unsuccessful(code int) (bool) {
return code < httpSuccessSeriesFrom || httpSuccessSeriesTo <= code
}
func (net *Network) SendGetRequest(url string) ([]byte, error) {
req := net.NewGetRequest(url)
req.Header.Set("Accept", "application/json, text/plain")
body, err := net.SendRequest(req)
return body, err
}
func (net *Network) SendDeleteRequest(url string) ([]byte, error) {
req := net.NewDeleteRequest(url)
body, code, err := net.SendRequestGetStatusCode(req)
if nil != err {
return nil, err
}
if unsuccessful(code) {
return nil, makeSimpleError(code, body)
}
return body, err
}
func (net *Network) SendEmptyPostRequest(url string) ([]byte, error) {
req := net.NewPostRequest(url, nil)
body, err := net.SendRequest(req)
return body, err
}
func (net *Network) SendPostRequestWithContentType(urlStr string, data []byte, contentType string) ([]byte, error) {
req := net.NewPostRequest(urlStr, bytes.NewBuffer(data))
req.Header.Set("Content-Type", contentType)
body, err := net.SendRequest(req)
return body, err
}
func (net *Network) SendPostRequest(urlStr string, data []byte) ([]byte, error) {
return net.SendPostRequestWithContentType(urlStr, data, "application/json")
}
func (net *Network) SendPostResourceRequest(restUrl string, resourceUrl string, contentType string) ([]byte, error) {
resource, err := net.openResource(resourceUrl)
if err != nil {
return nil, err
}
defer resource.Close()
req := net.NewPostRequest(restUrl, resource)
req.Header.Set("Content-Type", contentType)
body, err := net.SendRequest(req)
return body, err
}
func (net *Network) openResource(resourceUrl string) (io.ReadCloser, error) {
u, err := url.Parse(resourceUrl)
if err != nil {
return nil, err
}
if "" == u.Scheme || "file" == u.Scheme {
return net.openFileResource(u)
} else if "http" == u.Scheme || "https" == u.Scheme {
return net.openHttpResource(resourceUrl)
} else {
return nil, errors.New("Unrecognised protocol scheme: " + u.Scheme)
}
}
func (net *Network) openFileResource(url *url.URL) (io.ReadCloser, error) {
filePath := url.Path;
file, err := os.Open(filepath.Clean(filePath))
if err != nil {
return nil, err
}
return file, nil
}
func (net *Network) openHttpResource(resourceUrl string) (io.ReadCloser, error) {
client := net.makeClient()
resp, err := client.Get(resourceUrl)
if err != nil {
return nil, err
}
if failed := unsuccessful(resp.StatusCode) ; failed {
return nil, errors.New("Error retrieving " + resourceUrl + " (" + resp.Status + ")")
}
return resp.Body, nil
}
func VerifyLoginURL(network *Network) error {
url, err := url.Parse(network.BrooklynUrl)
if err != nil {
return err
}
if url.Scheme != "http" && url.Scheme != "https" {
return errors.New("Use login command to set Brooklyn URL with a scheme of \"http\" or \"https\"")
}
if url.Host == "" {
return errors.New("Use login command to set Brooklyn URL with a valid host[:port]")
}
if len(strings.Split(url.Host, ":")) < 2 {
if url.Scheme == "https" {
url.Host += ":443"
network.BrooklynUrl = url.String()
} else if url.Scheme == "http" {
url.Host += ":80"
network.BrooklynUrl = url.String()
}
}
_, err = net.DialTimeout("tcp", url.Host, time.Duration(30) * time.Second)
if err != nil {
return errors.New("Could not connect to " + url.Host)
}
return nil
}