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 }