(req *http.Request)
| 155 | } |
| 156 | |
| 157 | func captureRequestBody(req *http.Request) ([]byte, error) { |
| 158 | if req == nil || req.Body == nil { |
| 159 | return nil, nil |
| 160 | } |
| 161 | |
| 162 | if req.GetBody != nil { |
| 163 | clone, err := req.GetBody() |
| 164 | if err == nil { |
| 165 | limited, readErr := io.ReadAll(io.LimitReader(clone, maxRecordedRequestBodyBytes+1)) |
| 166 | _ = clone.Close() |
| 167 | // Some SDKs return the active body from GetBody instead of an |
| 168 | // independent reader. Restore the request body from GetBody so |
| 169 | // the upstream transport still receives the original bytes. |
| 170 | resetErr := resetRequestBody(req) |
| 171 | if resetErr != nil { |
| 172 | return nil, xerrors.Errorf("chatdebug: reset request body: %w", resetErr) |
| 173 | } |
| 174 | if readErr != nil { |
| 175 | return nil, nil |
| 176 | } |
| 177 | if len(limited) > maxRecordedRequestBodyBytes { |
| 178 | return []byte("[TRUNCATED]"), nil |
| 179 | } |
| 180 | return RedactJSONSecrets(limited), nil |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | // Without GetBody we cannot safely capture the request body without |
| 185 | // fully consuming a potentially large or streaming body before the |
| 186 | // request is sent. Skip capture in that case to keep debug logging |
| 187 | // lightweight and non-invasive. |
| 188 | return nil, nil |
| 189 | } |
| 190 | |
| 191 | // resetRequestBody replaces req.Body with a fresh reader from req.GetBody. |
| 192 | // It closes the previous request body before installing the replacement. |
no test coverage detected