github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/tequilapi/client/http_client.go (about)

     1  /*
     2   * Copyright (C) 2017 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package client
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/json"
    23  	"fmt"
    24  	"io"
    25  	"net/http"
    26  	"net/url"
    27  	"time"
    28  
    29  	"github.com/mysteriumnetwork/go-rest/apierror"
    30  	"github.com/rs/zerolog/log"
    31  
    32  	"github.com/mysteriumnetwork/node/requests"
    33  )
    34  
    35  type httpClientInterface interface {
    36  	SetToken(token string)
    37  	Get(path string, values url.Values) (*http.Response, error)
    38  	Post(path string, payload interface{}) (*http.Response, error)
    39  	Put(path string, payload interface{}) (*http.Response, error)
    40  	Delete(path string, payload interface{}) (*http.Response, error)
    41  }
    42  
    43  type httpRequestInterface interface {
    44  	Do(req *http.Request) (*http.Response, error)
    45  }
    46  
    47  func newHTTPClient(baseURL string, ua string) *httpClient {
    48  	return &httpClient{
    49  		http:    requests.NewHTTPClient("0.0.0.0", 100*time.Second),
    50  		baseURL: baseURL,
    51  		ua:      ua,
    52  	}
    53  }
    54  
    55  type httpClient struct {
    56  	http      httpRequestInterface
    57  	authToken string
    58  	baseURL   string
    59  	ua        string
    60  }
    61  
    62  func (client *httpClient) SetToken(token string) {
    63  	client.authToken = token
    64  }
    65  
    66  func (client *httpClient) Get(path string, values url.Values) (*http.Response, error) {
    67  	basePath := fmt.Sprintf("%v/%v", client.baseURL, path)
    68  
    69  	var fullPath string
    70  	params := values.Encode()
    71  	if params == "" {
    72  		fullPath = basePath
    73  	} else {
    74  		fullPath = fmt.Sprintf("%v?%v", basePath, params)
    75  	}
    76  	return client.executeRequest("GET", fullPath, nil)
    77  }
    78  
    79  func (client *httpClient) Post(path string, payload interface{}) (*http.Response, error) {
    80  	return client.doPayloadRequest("POST", path, payload)
    81  }
    82  
    83  func (client *httpClient) Put(path string, payload interface{}) (*http.Response, error) {
    84  	return client.doPayloadRequest("PUT", path, payload)
    85  }
    86  
    87  func (client *httpClient) Delete(path string, payload interface{}) (*http.Response, error) {
    88  	return client.doPayloadRequest("DELETE", path, payload)
    89  }
    90  
    91  func (client httpClient) doPayloadRequest(method, path string, payload interface{}) (*http.Response, error) {
    92  	payloadJSON, err := json.Marshal(payload)
    93  	if err != nil {
    94  		log.Error().Err(err).Msg("")
    95  		return nil, err
    96  	}
    97  
    98  	return client.executeRequest(method, client.baseURL+"/"+path, payloadJSON)
    99  }
   100  
   101  func (client *httpClient) executeRequest(method, fullPath string, payloadJSON []byte) (*http.Response, error) {
   102  	request, err := http.NewRequest(method, fullPath, bytes.NewBuffer(payloadJSON))
   103  	if err != nil {
   104  		log.Error().Err(err).Msg("")
   105  		return nil, err
   106  	}
   107  	request.Header.Set("User-Agent", client.ua)
   108  	request.Header.Set("Content-Type", "application/json")
   109  	request.Header.Set("Accept", "application/json")
   110  	if client.authToken != "" {
   111  		request.Header.Set("Authorization", "Bearer "+client.authToken)
   112  	}
   113  
   114  	response, err := client.http.Do(request)
   115  	if err != nil {
   116  		log.Error().Err(err).Msg("")
   117  		return response, err
   118  	}
   119  
   120  	err = parseResponseError(response)
   121  	if err != nil {
   122  		log.Error().Err(err).Msg("")
   123  		return response, err
   124  	}
   125  
   126  	return response, nil
   127  }
   128  
   129  func parseResponseError(response *http.Response) error {
   130  	if response.StatusCode < 200 || response.StatusCode >= 300 {
   131  		return apierror.Parse(response)
   132  	}
   133  	return nil
   134  }
   135  
   136  func parseResponseJSON(response *http.Response, dto interface{}) error {
   137  	b := bytes.NewBuffer(make([]byte, 0))
   138  	reader := io.TeeReader(response.Body, b)
   139  
   140  	if err := json.NewDecoder(reader).Decode(dto); err != nil {
   141  		return err
   142  	}
   143  
   144  	defer response.Body.Close()
   145  
   146  	// NopCloser returns a ReadCloser with a no-op Close method wrapping the provided Reader r.
   147  	// parseResponseError "empties" the contents of an errored response
   148  	// this way the response can be read and parsed again further down the line
   149  	response.Body = io.NopCloser(b)
   150  
   151  	return nil
   152  }