aboutsummaryrefslogtreecommitdiffstats
path: root/golib/httpclient.go
diff options
context:
space:
mode:
authorSebastien Douheret <sebastien.douheret@iot.bzh>2019-04-04 23:45:56 +0200
committerSebastien Douheret <sebastien.douheret@iot.bzh>2019-04-04 23:45:56 +0200
commit89ea6ebd3671e6ebbf6101525a5416427806f318 (patch)
tree5db52146365a9c2c439b77485f938cc8c2e3a727 /golib/httpclient.go
parentee147062c3bebed83e34bf5ce71019c95f62b96f (diff)
Fixed package tree and go mod filev0.5.0
Change-Id: I1047094d5b80d0622e2c2ce674979f18207b8c0f Signed-off-by: Sebastien Douheret <sebastien.douheret@iot.bzh>
Diffstat (limited to 'golib/httpclient.go')
-rw-r--r--golib/httpclient.go411
1 files changed, 0 insertions, 411 deletions
diff --git a/golib/httpclient.go b/golib/httpclient.go
deleted file mode 100644
index f4880e6..0000000
--- a/golib/httpclient.go
+++ /dev/null
@@ -1,411 +0,0 @@
-package common
-
-import (
- "bytes"
- "crypto/tls"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "os"
- "strings"
-)
-
-// HTTPClient .
-type HTTPClient struct {
- LoggerOut io.Writer
- LoggerLevel int
- LoggerPrefix string
-
- httpClient http.Client
- initDone bool
- endpoint string
- apikey string
- username string
- password string
- id string
- csrf string
- conf HTTPClientConfig
-}
-
-// HTTPClientConfig is used to config HTTPClient
-type HTTPClientConfig struct {
- URLPrefix string
- ContentType string
- HeaderAPIKeyName string
- Apikey string
- HeaderClientKeyName string
- CsrfDisable bool
- LogOut io.Writer
- LogLevel int
- LogPrefix string
-}
-
-// Logger levels constants
-const (
- HTTPLogLevelPanic = 0
- HTTPLogLevelError = 1
- HTTPLogLevelWarning = 2
- HTTPLogLevelInfo = 3
- HTTPLogLevelDebug = 4
-)
-
-// Inspired by syncthing/cmd/cli
-
-const insecure = false
-
-// HTTPNewClient creates a new HTTP client to deal with Syncthing
-func HTTPNewClient(baseURL string, cfg HTTPClientConfig) (*HTTPClient, error) {
-
- // Create w new Http client
- httpClient := http.Client{
- Transport: &http.Transport{
- TLSClientConfig: &tls.Config{
- InsecureSkipVerify: insecure,
- },
- },
- }
-
- lOut := cfg.LogOut
- if cfg.LogOut == nil {
- lOut = os.Stdout
- }
- client := HTTPClient{
- LoggerOut: lOut,
- LoggerLevel: cfg.LogLevel,
- LoggerPrefix: cfg.LogPrefix,
-
- httpClient: httpClient,
- initDone: false,
- endpoint: baseURL,
- apikey: cfg.Apikey,
- conf: cfg,
- /* TODO - add user + pwd support
- username: c.GlobalString("username"),
- password: c.GlobalString("password"),
- */
- }
-
- // Default set Content-Type to json
- if client.conf.ContentType == "" {
- client.conf.ContentType = "application/json"
- }
-
- if err := client.getCidAndCsrf(); err != nil {
- client.log(HTTPLogLevelError, "Cannot retrieve Client ID and/or CSRF: %v", err)
- return &client, err
- }
-
- client.log(HTTPLogLevelDebug, "HTTP client url %s init Done", client.endpoint)
- client.initDone = true
- return &client, nil
-}
-
-// GetLogLevel Get a readable string representing the log level
-func (c *HTTPClient) GetLogLevel() string {
- return c.LogLevelToString(c.LoggerLevel)
-}
-
-// LogLevelToString Convert an integer log level to string
-func (c *HTTPClient) LogLevelToString(lvl int) string {
- switch lvl {
- case HTTPLogLevelPanic:
- return "panic"
- case HTTPLogLevelError:
- return "error"
- case HTTPLogLevelWarning:
- return "warning"
- case HTTPLogLevelInfo:
- return "info"
- case HTTPLogLevelDebug:
- return "debug"
- }
- return "Unknown"
-}
-
-// SetLogLevel set the log level from a readable string
-func (c *HTTPClient) SetLogLevel(lvl string) error {
- switch strings.ToLower(lvl) {
- case "panic":
- c.LoggerLevel = HTTPLogLevelPanic
- case "error":
- c.LoggerLevel = HTTPLogLevelError
- case "warn", "warning":
- c.LoggerLevel = HTTPLogLevelWarning
- case "info":
- c.LoggerLevel = HTTPLogLevelInfo
- case "debug":
- c.LoggerLevel = HTTPLogLevelDebug
- default:
- return fmt.Errorf("Unknown level")
- }
- return nil
-}
-
-// GetClientID returns the id
-func (c *HTTPClient) GetClientID() string {
- return c.id
-}
-
-/***
-** High level functions
-***/
-
-// Get Send a Get request to client and return directly data of body response
-func (c *HTTPClient) Get(url string, out interface{}) error {
- return c._Request("GET", url, nil, out)
-}
-
-// Post Send a Post request to client and return directly data of body response
-func (c *HTTPClient) Post(url string, in interface{}, out interface{}) error {
- return c._Request("POST", url, in, out)
-}
-
-// Put Send a Put request to client and return directly data of body response
-func (c *HTTPClient) Put(url string, in interface{}, out interface{}) error {
- return c._Request("PUT", url, in, out)
-}
-
-// Delete Send a Delete request to client and return directly data of body response
-func (c *HTTPClient) Delete(url string, out interface{}) error {
- return c._Request("DELETE", url, nil, out)
-}
-
-/***
-** Low level functions
-***/
-
-// HTTPGet Send a Get request to client and return an error object
-func (c *HTTPClient) HTTPGet(url string, data *[]byte) error {
- _, err := c._HTTPRequest("GET", url, nil, data)
- return err
-}
-
-// HTTPGetWithRes Send a Get request to client and return both response and error
-func (c *HTTPClient) HTTPGetWithRes(url string, data *[]byte) (*http.Response, error) {
- return c._HTTPRequest("GET", url, nil, data)
-}
-
-// HTTPPost Send a POST request to client and return an error object
-func (c *HTTPClient) HTTPPost(url string, body string) error {
- _, err := c._HTTPRequest("POST", url, &body, nil)
- return err
-}
-
-// HTTPPostWithRes Send a POST request to client and return both response and error
-func (c *HTTPClient) HTTPPostWithRes(url string, body string) (*http.Response, error) {
- return c._HTTPRequest("POST", url, &body, nil)
-}
-
-// HTTPPut Send a PUT request to client and return an error object
-func (c *HTTPClient) HTTPPut(url string, body string) error {
- _, err := c._HTTPRequest("PUT", url, &body, nil)
- return err
-}
-
-// HTTPPutWithRes Send a PUT request to client and return both response and error
-func (c *HTTPClient) HTTPPutWithRes(url string, body string) (*http.Response, error) {
- return c._HTTPRequest("PUT", url, &body, nil)
-}
-
-// HTTPDelete Send a DELETE request to client and return an error object
-func (c *HTTPClient) HTTPDelete(url string) error {
- _, err := c._HTTPRequest("DELETE", url, nil, nil)
- return err
-}
-
-// HTTPDeleteWithRes Send a DELETE request to client and return both response and error
-func (c *HTTPClient) HTTPDeleteWithRes(url string) (*http.Response, error) {
- return c._HTTPRequest("DELETE", url, nil, nil)
-}
-
-// ResponseToBArray converts an Http response to a byte array
-func (c *HTTPClient) ResponseToBArray(response *http.Response) []byte {
- defer response.Body.Close()
- bytes, err := ioutil.ReadAll(response.Body)
- if err != nil {
- c.log(HTTPLogLevelError, "ResponseToBArray failure: %v", err.Error())
- }
- return bytes
-}
-
-/***
-** Private functions
-***/
-
-// _HTTPRequest Generic function used by high level function to send requests
-func (c *HTTPClient) _Request(method string, url string, in interface{}, out interface{}) error {
- var err error
- var res *http.Response
- var body []byte
- if in != nil {
- body, err = json.Marshal(in)
- if err != nil {
- return err
- }
- sb := string(body)
- res, err = c._HTTPRequest(method, url, &sb, nil)
- } else {
- res, err = c._HTTPRequest(method, url, nil, nil)
- }
- if err != nil {
- return err
- }
- if res.StatusCode != 200 {
- return fmt.Errorf("HTTP status %s", res.Status)
- }
-
- // Don't decode response if no out data pointer is nil
- if out == nil {
- return nil
- }
- return json.Unmarshal(c.ResponseToBArray(res), out)
-}
-
-// _HTTPRequest Generic function that returns a new Request given a method, URL, and optional body and data.
-func (c *HTTPClient) _HTTPRequest(method, url string, body *string, data *[]byte) (*http.Response, error) {
- if !c.initDone {
- if err := c.getCidAndCsrf(); err == nil {
- c.initDone = true
- }
- }
-
- var err error
- var request *http.Request
- if body != nil {
- request, err = http.NewRequest(method, c.formatURL(url), bytes.NewBufferString(*body))
- } else {
- request, err = http.NewRequest(method, c.formatURL(url), nil)
- }
-
- if err != nil {
- return nil, err
- }
- res, err := c.handleRequest(request)
- if err != nil {
- return res, err
- }
- if res.StatusCode != 200 {
- return res, errors.New(res.Status)
- }
-
- if data != nil {
- *data = c.ResponseToBArray(res)
- }
-
- return res, nil
-}
-
-func (c *HTTPClient) handleRequest(request *http.Request) (*http.Response, error) {
- if c.conf.ContentType != "" {
- request.Header.Set("Content-Type", c.conf.ContentType)
- }
- if c.conf.HeaderAPIKeyName != "" && c.apikey != "" {
- request.Header.Set(c.conf.HeaderAPIKeyName, c.apikey)
- }
- if c.conf.HeaderClientKeyName != "" && c.id != "" {
- request.Header.Set(c.conf.HeaderClientKeyName, c.id)
- }
- if c.username != "" || c.password != "" {
- request.SetBasicAuth(c.username, c.password)
- }
- if c.csrf != "" {
- request.Header.Set("X-CSRF-Token-"+c.id[:5], c.csrf)
- }
-
- c.log(HTTPLogLevelDebug, "HTTP %s %v", request.Method, request.URL)
- response, err := c.httpClient.Do(request)
- c.log(HTTPLogLevelDebug, "HTTP RESPONSE: %v\n", response)
- if err != nil {
- c.log(HTTPLogLevelInfo, "%v", err)
- return nil, err
- }
-
- // Detect client ID change
- cid := response.Header.Get(c.conf.HeaderClientKeyName)
- if cid != "" && c.id != cid {
- c.id = cid
- }
-
- // Detect CSR token change
- for _, item := range response.Cookies() {
- if c.id != "" && item.Name == "CSRF-Token-"+c.id[:5] {
- c.csrf = item.Value
- goto csrffound
- }
- }
- // OK CSRF found
-csrffound:
-
- if response.StatusCode == 404 {
- return nil, errors.New("Invalid endpoint or API call")
- } else if response.StatusCode == 401 {
- return nil, errors.New("Invalid username or password")
- } else if response.StatusCode == 403 {
- if c.apikey == "" {
- // Request a new Csrf for next requests
- c.getCidAndCsrf()
- return nil, errors.New("Invalid CSRF token")
- }
- return nil, errors.New("Invalid API key")
- } else if response.StatusCode != 200 {
- data := make(map[string]interface{})
- // Try to decode error field of APIError struct
- json.Unmarshal(c.ResponseToBArray(response), &data)
- if err, found := data["error"]; found {
- return nil, fmt.Errorf(err.(string))
- }
- body := strings.TrimSpace(string(c.ResponseToBArray(response)))
- if body != "" {
- return nil, fmt.Errorf(body)
- }
- return nil, errors.New("Unknown HTTP status returned: " + response.Status)
- }
- return response, nil
-}
-
-// formatURL Build full url by concatenating all parts
-func (c *HTTPClient) formatURL(endURL string) string {
- url := c.endpoint
- if !strings.HasSuffix(url, "/") {
- url += "/"
- }
- url += strings.TrimLeft(c.conf.URLPrefix, "/")
- if !strings.HasSuffix(url, "/") {
- url += "/"
- }
- return url + strings.TrimLeft(endURL, "/")
-}
-
-// Send request to retrieve Client id and/or CSRF token
-func (c *HTTPClient) getCidAndCsrf() error {
- // Don't use cid + csrf when apikey is set
- if c.apikey != "" {
- return nil
- }
- request, err := http.NewRequest("GET", c.endpoint, nil)
- if err != nil {
- return err
- }
- if _, err := c.handleRequest(request); err != nil {
- return err
- }
- if c.id == "" {
- return errors.New("Failed to get device ID")
- }
- if !c.conf.CsrfDisable && c.csrf == "" {
- return errors.New("Failed to get CSRF token")
- }
- return nil
-}
-
-// log Internal logger function
-func (c *HTTPClient) log(level int, format string, args ...interface{}) {
- if level > c.LoggerLevel {
- return
- }
- sLvl := strings.ToUpper(c.LogLevelToString(level))
- fmt.Fprintf(c.LoggerOut, sLvl+": "+c.LoggerPrefix+format+"\n", args...)
-}