package helper import ( "bytes" "context" "fmt" "io/ioutil" "log" "net" "net/http" "time" ) func SendRequest(method string, endpoint string, values []byte, authKey string, isCouchDbReq bool) ([]byte, error) { const ConnectMaxWaitTime = 30 * time.Second const RequestMaxWaitTime = 60 * time.Second client := http.Client{ Transport: &http.Transport{ DialContext: (&net.Dialer{ Timeout: ConnectMaxWaitTime, }).DialContext, }, } ctx, cancel := context.WithTimeout(context.Background(), RequestMaxWaitTime) defer cancel() emptyBody := []byte{} req, err := http.NewRequestWithContext(ctx, method, endpoint, bytes.NewBuffer(values)) req.Proto = "HTTP/2.0" if err != nil { log.Println(err.Error()) return emptyBody, err } q := req.URL.Query() req.URL.RawQuery = q.Encode() req.Header.Set("Connection", "close") req.Header.Add("Authorization", authKey) req.Close = true client.CloseIdleConnections() response, err := client.Do(req) if e, ok := err.(net.Error); ok && e.Timeout() { if response != nil { response.Body.Close() } log.Println(err.Error()) return emptyBody, err } else if err != nil { if response != nil { response.Body.Close() } log.Println(err.Error()) return emptyBody, err } // 200 OK // 201 Created // 202 Accepted // 404 Not Found // 409 Conflict if isCouchDbReq { // fmt.Printf("responseStatusCode: %d\n", response.StatusCode) if response.StatusCode == http.StatusNotFound { if response != nil { response.Body.Close() } return emptyBody, nil } // TODO: handle conflict error } else { if response.StatusCode != http.StatusOK { if response != nil { response.Body.Close() } err := fmt.Errorf("response: code: %d, body: %v", response.StatusCode, response.Body) log.Println(err.Error()) return emptyBody, err } } body, err := ioutil.ReadAll(response.Body) if err != nil { log.Println(err.Error()) if response != nil { response.Body.Close() } return emptyBody, nil } // if you call it in loop, don't use defer response.Body.Close() return body, nil }