package httputil import ( "bytes" "fmt" "io" "net/http" "net/http/httputil" ) // AllHTTPMethods contains all available HTTP methods func AllHTTPMethods() []string { return []string{ http.MethodGet, http.MethodHead, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete, http.MethodConnect, http.MethodOptions, http.MethodTrace, } } // DumpRequest to string func DumpRequest(req *http.Request) (string, error) { dump, err := httputil.DumpRequestOut(req, true) return string(dump), err } // DumpResponseHeadersAndRaw returns http headers and response as strings func DumpResponseHeadersAndRaw(resp *http.Response) (headers, fullresp []byte, err error) { // httputil.DumpResponse does not work with websockets if resp.StatusCode >= http.StatusContinue && resp.StatusCode <= http.StatusEarlyHints { raw := resp.Status + "\n" for h, v := range resp.Header { raw += fmt.Sprintf("%s: %s\n", h, v) } return []byte(raw), []byte(raw), nil } headers, err = httputil.DumpResponse(resp, false) if err != nil { return } // logic same as httputil.DumpResponse(resp, true) but handles // the edge case when we get both error and data on reading resp.Body var buf1, buf2 bytes.Buffer b := resp.Body if _, err = buf1.ReadFrom(b); err != nil { if buf1.Len() <= 0 { return } } if err == nil { _ = b.Close() } // rewind the body to allow full dump resp.Body = io.NopCloser(bytes.NewReader(buf1.Bytes())) err = resp.Write(&buf2) fullresp = buf2.Bytes() // rewind once more to allow further reuses resp.Body = io.NopCloser(bytes.NewReader(buf1.Bytes())) return }