github.com/mundipagg/boleto-api@v0.0.0-20230620145841-3f9ec742599f/util/http.go (about) 1 package util 2 3 import ( 4 "context" 5 "crypto" 6 "crypto/tls" 7 "crypto/x509" 8 "encoding/base64" 9 "encoding/pem" 10 "errors" 11 "fmt" 12 "io/ioutil" 13 "net" 14 "net/http" 15 "net/url" 16 "strconv" 17 "strings" 18 "sync" 19 "time" 20 21 "github.com/mundipagg/boleto-api/certificate" 22 "github.com/mundipagg/boleto-api/log" 23 24 s "github.com/fullsailor/pkcs7" 25 "github.com/mundipagg/boleto-api/config" 26 ) 27 28 var defaultDialer = &net.Dialer{Timeout: 16 * time.Second, KeepAlive: 16 * time.Second} 29 30 var ( 31 client *http.Client 32 onceDefaultClient = &sync.Once{} 33 onceTransport = &sync.Once{} 34 icpCert certificate.ICPCertificate 35 transport *http.Transport 36 ) 37 38 // HTTPInterface is an abstraction for HTTP client 39 type HTTPInterface interface { 40 Post(url string, headers map[string]string, body interface{}) (*http.Response, error) 41 } 42 43 // HTTPClient is the struct for making requests 44 type HTTPClient struct{} 45 46 // PostFormEncoded is a function for making requests using Post Http method with content-type application/x-www-form-urlencoded. 47 // 48 // It receives an endpoint, params and pointer for log and it creates a new Post request, returning []byte and a error. 49 func (hc *HTTPClient) PostFormURLEncoded(endpoint string, params map[string]string, log *log.Log) ([]byte, error) { 50 client := &http.Client{ 51 Timeout: time.Second * 10, 52 } 53 54 uri, err := url.ParseRequestURI(endpoint) 55 if err != nil { 56 return []byte(""), err 57 } 58 59 values := uri.Query() 60 for k, v := range params { 61 values.Set(k, v) 62 } 63 64 req, err := http.NewRequest(http.MethodPost, uri.String(), strings.NewReader(values.Encode())) // URL-encoded payload 65 66 if err != nil { 67 return []byte(""), err 68 } 69 70 header := map[string]string{ 71 "content-type": "application/x-www-form-urlencoded", 72 "content-length": strconv.Itoa(len(values.Encode())), 73 } 74 75 for k, v := range header { 76 req.Header.Add(k, v) 77 } 78 79 log.Request(params, endpoint, header) 80 resp, err := client.Do(req) 81 if err != nil { 82 return []byte(""), err 83 } 84 defer resp.Body.Close() 85 86 if resp.StatusCode != http.StatusOK { 87 return []byte(""), fmt.Errorf("stone authentication returns status code %d", resp.StatusCode) 88 } 89 90 respByte, err := ioutil.ReadAll(resp.Body) 91 92 respBody := string(respByte) 93 94 mask := NewMask(log, "Response") 95 96 log.Response(mask.MaskJsonContentFields(respBody), endpoint, nil) 97 98 return respByte, err 99 } 100 101 // DefaultHTTPClient retorna um cliente http configurado para dar um skip na validação do certificado digital 102 func DefaultHTTPClient() *http.Client { 103 onceDefaultClient.Do(func() { 104 client = &http.Client{ 105 Transport: &http.Transport{ 106 Dial: defaultDialer.Dial, 107 TLSHandshakeTimeout: 16 * time.Second, 108 TLSClientConfig: &tls.Config{ 109 InsecureSkipVerify: true, 110 }, 111 }, 112 } 113 }) 114 return client 115 } 116 117 //Post faz um requisição POST para uma URL e retorna o response, status e erro 118 func PostReponseWithHeader(url, body, timeout string, header map[string]string) (string, string, int, error) { 119 return doRequest("POST", url, body, timeout, header) 120 } 121 122 //Post faz um requisição POST para uma URL e retorna o response, status e erro 123 func Post(url, body, timeout string, header map[string]string) (string, int, error) { 124 resp, _, st, err := doRequest("POST", url, body, timeout, header) 125 return resp, st, err 126 } 127 128 //PostWithHeader faz um requisição POST para uma URL e retorna o response, status e erro 129 func PostWithHeader(url, body, timeout string, header map[string]string) (string, map[string]interface{}, int, error) { 130 resp, respHeader, st, err := doRequestWithHeaderObject("POST", url, body, timeout, header) 131 return resp, respHeader, st, err 132 } 133 134 func doRequest(method, url, body, timeout string, header map[string]string) (string, string, int, error) { 135 t := GetDurationTimeoutRequest(timeout) * time.Second 136 137 ctx, cls := context.WithTimeout(context.Background(), t) 138 defer cls() 139 140 client := DefaultHTTPClient() 141 142 message := strings.NewReader(body) 143 144 req, err := http.NewRequestWithContext(ctx, method, url, message) 145 if err != nil { 146 return "", "", http.StatusInternalServerError, err 147 } 148 if header != nil { 149 for k, v := range header { 150 req.Header.Add(k, v) 151 } 152 } 153 resp, errResp := client.Do(req) 154 if errResp != nil { 155 return "", "", 0, errResp 156 } 157 defer resp.Body.Close() 158 respHeader := fmt.Sprintf("%v", resp.Header) 159 160 data, errResponse := ioutil.ReadAll(resp.Body) 161 if errResponse != nil { 162 return "", respHeader, resp.StatusCode, errResponse 163 } 164 sData := string(data) 165 return sData, respHeader, resp.StatusCode, nil 166 } 167 168 func doRequestWithHeaderObject(method, url, body, timeout string, header map[string]string) (string, map[string]interface{}, int, error) { 169 t := GetDurationTimeoutRequest(timeout) * time.Second 170 171 ctx, cls := context.WithTimeout(context.Background(), t) 172 defer cls() 173 174 client := DefaultHTTPClient() 175 176 message := strings.NewReader(body) 177 178 req, err := http.NewRequestWithContext(ctx, method, url, message) 179 if err != nil { 180 return "", nil, http.StatusInternalServerError, err 181 } 182 183 for k, v := range header { 184 req.Header.Add(k, v) 185 } 186 187 resp, errResp := client.Do(req) 188 if errResp != nil { 189 return "", nil, 0, errResp 190 } 191 defer resp.Body.Close() 192 193 respHeader := convertHeader(resp.Header) 194 195 data, errResponse := ioutil.ReadAll(resp.Body) 196 if errResponse != nil { 197 return "", respHeader, resp.StatusCode, errResponse 198 } 199 sData := string(data) 200 return sData, respHeader, resp.StatusCode, nil 201 } 202 203 func convertHeader(respHeader map[string][]string) map[string]interface{} { 204 m2 := make(map[string]interface{}, len(respHeader)) 205 for k, v := range respHeader { 206 m2[k] = v[0] 207 } 208 return m2 209 } 210 211 // BuildTLSTransport creates a TLS Client Transport from crt, ca and key files 212 func BuildTLSTransport(con certificate.TLSCertificate) (*http.Transport, error) { 213 214 if config.Get().MockMode { 215 return nil, nil 216 } 217 218 key, err := certificate.GetCertificateFromStore(con.Key) 219 if err != nil { 220 return nil, err 221 } 222 223 crt, err := certificate.GetCertificateFromStore(con.Crt) 224 if err != nil { 225 return nil, err 226 } 227 228 cert, err := tls.X509KeyPair(getCertificateByType(crt), getCertificateByType(key)) 229 230 if err != nil { 231 return nil, err 232 } 233 234 transport = &http.Transport{ 235 Dial: defaultDialer.Dial, 236 TLSHandshakeTimeout: 16 * time.Second, 237 TLSClientConfig: &tls.Config{ 238 Certificates: []tls.Certificate{cert}, 239 InsecureSkipVerify: true, 240 }, 241 } 242 243 return transport, nil 244 } 245 246 func getCertificateByType(key interface{}) []byte { 247 switch v := key.(type) { 248 case certificate.SSLCertificate: 249 return v.PemData 250 default: 251 return v.([]byte) 252 } 253 } 254 255 //Sign request 256 func SignRequest(request string) (string, error) { 257 258 if icpCert == (certificate.ICPCertificate{}) { 259 icp, err := certificate.GetCertificateFromStore(config.Get().CertificateICPName) 260 if err != nil { 261 return "", err 262 } 263 icpCert = icp.(certificate.ICPCertificate) 264 } 265 266 signedData, err := s.NewSignedData([]byte(request)) 267 if err != nil { 268 return "", err 269 } 270 271 if err := signedData.AddSigner(icpCert.Certificate, icpCert.RsaPrivateKey, s.SignerInfoConfig{}); err != nil { 272 return "", err 273 } 274 275 detachedSignature, err := signedData.Finish() 276 if err != nil { 277 return "", err 278 } 279 280 signedRequest := base64.StdEncoding.EncodeToString(detachedSignature) 281 282 return signedRequest, nil 283 } 284 285 //Read privatekey and parse to PKCS#1 286 func parsePrivateKey() (crypto.PrivateKey, error) { 287 288 pkeyBytes, err := ioutil.ReadFile(config.Get().CertICP_PathPkey) 289 if err != nil { 290 return nil, err 291 } 292 293 block, _ := pem.Decode(pkeyBytes) 294 if block == nil { 295 return nil, errors.New("Key Not Found") 296 } 297 298 switch block.Type { 299 case "RSA PRIVATE KEY": 300 rsa, err := x509.ParsePKCS1PrivateKey(block.Bytes) 301 if err != nil { 302 return nil, err 303 } 304 return rsa, nil 305 default: 306 return nil, fmt.Errorf("SSH: Unsupported key type %q", block.Type) 307 } 308 309 } 310 311 ///Read chainCertificates and adapter to x509.Certificate 312 func parseChainCertificates() (*x509.Certificate, error) { 313 314 chainCertsBytes, err := ioutil.ReadFile(config.Get().CertICP_PathChainCertificates) 315 if err != nil { 316 return nil, err 317 } 318 319 block, _ := pem.Decode(chainCertsBytes) 320 if block == nil { 321 return nil, errors.New("Key Not Found") 322 } 323 324 cert, err := x509.ParseCertificate(block.Bytes) 325 if err != nil { 326 return nil, err 327 } 328 329 return cert, nil 330 } 331 332 func doRequestTLS(method, url, body, timeout string, header map[string]string, transport *http.Transport) (string, int, error) { 333 tlsClient := &http.Client{} 334 tlsClient.Transport = transport 335 tlsClient.Timeout = GetDurationTimeoutRequest(timeout) * time.Second 336 b := strings.NewReader(body) 337 req, err := http.NewRequest(method, url, b) 338 if err != nil { 339 return "", 0, err 340 } 341 342 if header != nil { 343 for k, v := range header { 344 req.Header.Add(k, v) 345 } 346 } 347 resp, err := tlsClient.Do(req) 348 if err != nil { 349 return "", 0, err 350 } 351 defer resp.Body.Close() 352 // Dump response 353 data, err := ioutil.ReadAll(resp.Body) 354 if err != nil { 355 return "", 0, err 356 } 357 sData := string(data) 358 return sData, resp.StatusCode, nil 359 } 360 361 func doRequestTLSWithHeader(method, url, body, timeout string, header map[string]string, transport *http.Transport) (string, map[string]interface{}, int, error) { 362 tlsClient := &http.Client{} 363 tlsClient.Transport = transport 364 tlsClient.Timeout = GetDurationTimeoutRequest(timeout) * time.Second 365 b := strings.NewReader(body) 366 req, err := http.NewRequest(method, url, b) 367 if err != nil { 368 return "", nil, 0, err 369 } 370 371 for k, v := range header { 372 req.Header.Add(k, v) 373 } 374 375 resp, err := tlsClient.Do(req) 376 if err != nil { 377 return "", nil, 0, err 378 } 379 respHeader := convertHeader(resp.Header) 380 defer resp.Body.Close() 381 // Dump response 382 data, err := ioutil.ReadAll(resp.Body) 383 if err != nil { 384 return "", nil, 0, err 385 } 386 sData := string(data) 387 return sData, respHeader, resp.StatusCode, nil 388 } 389 390 func PostTLS(url, body, timeout string, header map[string]string, transport *http.Transport) (string, int, error) { 391 return doRequestTLS("POST", url, body, timeout, header, transport) 392 } 393 394 func PostTLSWithHeader(url, body, timeout string, header map[string]string, transport *http.Transport) (string, map[string]interface{}, int, error) { 395 return doRequestTLSWithHeader("POST", url, body, timeout, header, transport) 396 } 397 398 //HeaderToMap converte um http Header para um dicionário string -> string 399 func HeaderToMap(header http.Header) map[string]string { 400 headerMap := make(map[string]string) 401 for key, value := range header { 402 if key == "Authorization" { 403 headerMap[key] = "[REDACTED]" 404 } else { 405 headerMap[key] = value[0] 406 } 407 } 408 return headerMap 409 }