(mockHeader map[string]string)
| 207 | } |
| 208 | |
| 209 | func ToHTTPHeader(mockHeader map[string]string) http.Header { |
| 210 | header := http.Header{} |
| 211 | for i, j := range mockHeader { |
| 212 | // Emit one wire header per YAML key. The recorded value is kept |
| 213 | // intact even when it contains commas — this is the canonical |
| 214 | // comma-folded form for RFC 7230 list-valued headers (Accept, |
| 215 | // Accept-Encoding, Baggage, Forwarded, Via, ...) and is what |
| 216 | // most HTTP clients (axios, requests, fetch) actually emit on |
| 217 | // the wire. Splitting on "," and sending one wire header per |
| 218 | // element fans out into multiple same-name headers; that breaks |
| 219 | // strict-mapping server middlewares such as |
| 220 | // `Response(headers=dict(**request.headers), ...)` which raise |
| 221 | // `TypeError: dict() got multiple values for keyword argument |
| 222 | // 'accept'` on duplicate field names — a known FastAPI / |
| 223 | // Starlette failure mode reproducible during replay even when |
| 224 | // the original client sent exactly one Accept header. |
| 225 | // |
| 226 | // Trade-off: a recording whose on-wire request actually used |
| 227 | // repeated same-name headers (rare on the request side; common |
| 228 | // only for Set-Cookie which is a response header and never |
| 229 | // built by this helper) replays as a single comma-folded |
| 230 | // header. Per RFC 7230 §3.2.2 the receiver-side parse is |
| 231 | // semantically equivalent for list-valued headers, so this |
| 232 | // folding is wire-safe for the headers that actually matter. |
| 233 | header[i] = []string{j} |
| 234 | } |
| 235 | return header |
| 236 | } |
| 237 | |
| 238 | // looksLikeTime is a fast-path byte-prefix check that rejects strings which |
| 239 | // cannot possibly match any of the date/time formats we try below. Avoiding |
no outgoing calls