package common import ( "bytes" "fmt" "io" "net/http" "github.com/gin-gonic/gin" ) func CloseResponseBodyGracefully(httpResponse *http.Response) { if httpResponse == nil || httpResponse.Body == nil { return } err := httpResponse.Body.Close() if err != nil { SysError("failed to close response body: " + err.Error()) } } func IOCopyBytesGracefully(c *gin.Context, src *http.Response, data []byte) { if c.Writer == nil { return } body := io.NopCloser(bytes.NewBuffer(data)) // We shouldn't set the header before we parse the response body, because the parse part may fail. // And then we will have to send an error response, but in this case, the header has already been set. // So the httpClient will be confused by the response. // For example, Postman will report error, and we cannot check the response at all. if src != nil { for k, v := range src.Header { // avoid setting Content-Length if k == "Content-Length" { continue } c.Writer.Header().Set(k, v[0]) } } // set Content-Length header manually BEFORE calling WriteHeader c.Writer.Header().Set("Content-Length", fmt.Sprintf("%d", len(data))) // Write header with status code (this sends the headers) if src != nil { c.Writer.WriteHeader(src.StatusCode) } else { c.Writer.WriteHeader(http.StatusOK) } _, err := io.Copy(c.Writer, body) if err != nil { LogError(c, fmt.Sprintf("failed to copy response body: %s", err.Error())) } }