|
7 | 7 | "io" |
8 | 8 | "net" |
9 | 9 | "net/http" |
10 | | - "net/http/httputil" |
11 | 10 | "net/url" |
12 | 11 | "os" |
13 | 12 | "strconv" |
@@ -236,49 +235,47 @@ get_response: |
236 | 235 |
|
237 | 236 | resp.Headers = httpresp.Header.Clone() |
238 | 237 |
|
239 | | - // Dump headers only (does not consume body) |
240 | | - headers, err := httputil.DumpResponse(httpresp, false) |
| 238 | + if h.Options.MaxResponseBodySizeToRead > 0 { |
| 239 | + httpresp.Body = io.NopCloser(io.LimitReader(httpresp.Body, h.Options.MaxResponseBodySizeToRead)) |
| 240 | + defer func() { |
| 241 | + _, _ = io.Copy(io.Discard, httpresp.Body) |
| 242 | + _ = httpresp.Body.Close() |
| 243 | + }() |
| 244 | + } |
| 245 | + |
| 246 | + // httputil.DumpResponse does not handle websockets |
| 247 | + headers, rawResp, err := pdhttputil.DumpResponseHeadersAndRaw(httpresp) |
241 | 248 | if err != nil { |
242 | 249 | if stringsutil.ContainsAny(err.Error(), "tls: user canceled") { |
243 | 250 | shouldIgnoreErrors = true |
244 | 251 | shouldIgnoreBodyErrors = true |
245 | 252 | } |
| 253 | + |
| 254 | + // Edge case - some servers respond with gzip encoding header but uncompressed body, in this case the standard library configures the reader as gzip, triggering an error when read. |
| 255 | + // The bytes slice is not accessible because of abstraction, therefore we need to perform the request again tampering the Accept-Encoding header |
| 256 | + if !gzipRetry && strings.Contains(err.Error(), "gzip: invalid header") { |
| 257 | + gzipRetry = true |
| 258 | + req.Header.Set("Accept-Encoding", "identity") |
| 259 | + goto get_response |
| 260 | + } |
246 | 261 | if !shouldIgnoreErrors { |
247 | 262 | return nil, err |
248 | 263 | } |
249 | 264 | } |
250 | | - |
| 265 | + resp.Raw = string(rawResp) |
251 | 266 | resp.RawHeaders = string(headers) |
252 | | - |
253 | 267 | var respbody []byte |
254 | 268 | // body shouldn't be read with the following status codes |
255 | 269 | // 101 - Switching Protocols => websockets don't have a readable body |
256 | 270 | // 304 - Not Modified => no body the response terminates with latest header newline |
257 | 271 | if !generic.EqualsAny(httpresp.StatusCode, http.StatusSwitchingProtocols, http.StatusNotModified) { |
258 | | - |
| 272 | + var err error |
259 | 273 | respbody, err = io.ReadAll(io.LimitReader(httpresp.Body, h.Options.MaxResponseBodySizeToRead)) |
260 | | - if err != nil { |
261 | | - // Edge case: some servers respond with gzip encoding header but uncompressed body. |
262 | | - // Retry request with identity encoding. |
263 | | - if !gzipRetry && strings.Contains(err.Error(), "gzip: invalid header") { |
264 | | - gzipRetry = true |
265 | | - req.Header.Set("Accept-Encoding", "identity") |
266 | | - goto get_response |
267 | | - } |
268 | | - if !shouldIgnoreBodyErrors { |
269 | | - return nil, err |
270 | | - } |
| 274 | + if err != nil && !shouldIgnoreBodyErrors { |
| 275 | + return nil, err |
271 | 276 | } |
272 | 277 | } |
273 | 278 |
|
274 | | - // Build bounded raw response: headers + capped body |
275 | | - // NOTE: resp.Raw must be constructed from a capped body to avoid OOM on infinite streams. |
276 | | - |
277 | | - raw := make([]byte, 0, len(headers)+len(respbody)) |
278 | | - raw = append(raw, headers...) |
279 | | - raw = append(raw, respbody...) |
280 | | - resp.Raw = string(raw) |
281 | | - |
282 | 279 | closeErr := httpresp.Body.Close() |
283 | 280 | if closeErr != nil && !shouldIgnoreBodyErrors { |
284 | 281 | return nil, closeErr |
|
0 commit comments