diff options
Diffstat (limited to 'golib/httpclient.go')
-rw-r--r-- | golib/httpclient.go | 411 |
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...) -} |