github.com/crowdsecurity/crowdsec@v1.6.1/pkg/apiclient/client_http.go (about) 1 package apiclient 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io" 10 "net/http" 11 "net/http/httputil" 12 "net/url" 13 "strings" 14 15 log "github.com/sirupsen/logrus" 16 ) 17 18 func (c *ApiClient) NewRequest(method, url string, body interface{}) (*http.Request, error) { 19 if !strings.HasSuffix(c.BaseURL.Path, "/") { 20 return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL) 21 } 22 23 u, err := c.BaseURL.Parse(url) 24 if err != nil { 25 return nil, err 26 } 27 28 var buf io.ReadWriter 29 if body != nil { 30 buf = &bytes.Buffer{} 31 enc := json.NewEncoder(buf) 32 enc.SetEscapeHTML(false) 33 34 if err = enc.Encode(body); err != nil { 35 return nil, err 36 } 37 } 38 39 req, err := http.NewRequest(method, u.String(), buf) 40 if err != nil { 41 return nil, err 42 } 43 44 if body != nil { 45 req.Header.Set("Content-Type", "application/json") 46 } 47 48 return req, nil 49 } 50 51 func (c *ApiClient) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) { 52 if ctx == nil { 53 return nil, errors.New("context must be non-nil") 54 } 55 56 req = req.WithContext(ctx) 57 58 // Check rate limit 59 60 if c.UserAgent != "" { 61 req.Header.Add("User-Agent", c.UserAgent) 62 } 63 64 if log.GetLevel() >= log.DebugLevel { 65 log.Debugf("[URL] %s %s", req.Method, req.URL) 66 } 67 68 resp, err := c.client.Do(req) 69 if resp != nil && resp.Body != nil { 70 defer resp.Body.Close() 71 } 72 73 if err != nil { 74 // If we got an error, and the context has been canceled, 75 // the context's error is probably more useful. 76 select { 77 case <-ctx.Done(): 78 return nil, ctx.Err() 79 default: 80 } 81 82 // If the error type is *url.Error, sanitize its URL before returning. 83 if e, ok := err.(*url.Error); ok { 84 if url, err := url.Parse(e.URL); err == nil { 85 e.URL = url.String() 86 return newResponse(resp), e 87 } 88 89 return newResponse(resp), err 90 } 91 92 return newResponse(resp), err 93 } 94 95 if log.GetLevel() >= log.DebugLevel { 96 for k, v := range resp.Header { 97 log.Debugf("[headers] %s: %s", k, v) 98 } 99 100 dump, err := httputil.DumpResponse(resp, true) 101 if err == nil { 102 log.Debugf("Response: %s", string(dump)) 103 } 104 } 105 106 response := newResponse(resp) 107 108 err = CheckResponse(resp) 109 if err != nil { 110 return response, err 111 } 112 113 if v != nil { 114 w, ok := v.(io.Writer) 115 if !ok { 116 decErr := json.NewDecoder(resp.Body).Decode(v) 117 if errors.Is(decErr, io.EOF) { 118 decErr = nil // ignore EOF errors caused by empty response body 119 } 120 121 return response, decErr 122 } 123 124 io.Copy(w, resp.Body) 125 } 126 127 return response, err 128 }