Documentation
¶
Overview ¶
Package httplog renders HTTP requests and responses to a diagnostic stream at an ordered level, and is the one place in this CLI that decides what must never be printed.
It exists as a shared package rather than a helper in each module because two of the three rules here are security rules. A masking bug fixed in one copy and not the other is invisible until it isn't, and per-module copies of shared rules have drifted in this repo before — that is what internal/testsupport exists to prevent for env lists.
Index ¶
Constants ¶
const MaxDumpBytes = 8192
MaxDumpBytes bounds each dumped body.
8 KB rather than the 512 that internal/starfleet/conn uses for error messages: that cap is right for quoting what a proxy said in a one-line error, and wrong here, where it would clip the middle of the cluster payload somebody enabled --debug to read. Unbounded is not an option either — an unbounded echo is a bug this repo has already fixed once, when a 200 KB proxy error page landed whole on stderr.
Variables ¶
This section is empty.
Functions ¶
func HeaderLines ¶
HeaderLines renders h as "Name: value" lines in sorted order, with the values of maskedHeaders replaced by the mask. Callers add their own prefix.
It is exported because the dry-run report renders headers too, and this package's whole reason for existing is that a masking rule with two implementations has one that is wrong. maskedHeaders keeps exactly one consumer path: this function.
Sorting is for reproducibility: Go's header map has no order, and an unordered dump is hard to diff between two runs.
func RedactBody ¶
RedactBody renders a body for a diagnostic stream: bounded at limit characters, with the VALUE of every credential-bearing field masked and everything else passed through byte for byte.
Byte-for-byte outside the masked spans is essential, and it is why this does not decode-and-re-encode. Re-encoding erases the difference between an explicit `"plan_expires_at": null` and an omitted key, which is precisely the distinction a typed parser already collapses and the wire dump exists to reveal. So the decoder here is used only to LOCATE each secret value's byte span; the surrounding bytes are never rewritten, reordered or reformatted.
Masking values rather than replacing the whole body is what keeps --debug useful. A service write reduced to its top-level key names is the single key "services" — enough to know a secret was present, not enough to debug anything. Masking per value keeps allow_writes, embedding_model and the service id readable while the key beside them is not.
Every field in secretFieldNames is masked at any depth: a wrapper object would otherwise smuggle a token past a top-level-only check. A composite value (an object, as with byoc's `credentials`) is masked whole, so nothing nested inside one can survive.
It fails CLOSED. If the spans cannot be computed for any reason, it falls back to replacing the whole body rather than risking an echo.
HTML does not parse as JSON, so the proxy/SSO-interstitial case that motivates dumping bodies at all is untouched.
func Wrap ¶
func Wrap(base http.RoundTripper, out io.Writer, lvl Level) http.RoundTripper
Wrap returns base wrapped so that traffic through it is rendered to out at lvl. At Off, or with no writer, it returns base itself rather than a wrapper that decides to print nothing — the quiet path is every normal command, and it should add neither an allocation nor a layer of indirection.
Types ¶
type Level ¶
type Level int
Level is how much of an exchange reaches the diagnostic stream. The values are ordered, so a comparison against Debug is meaningful and callers never have to enumerate.
const ( // Off prints nothing. Off Level = iota // Verbose prints the request line, a masked Authorization header // when one is present, and the response status with elapsed time. // It deliberately carries no bodies: a verbose run that dumped // payloads would bury the progress information it exists to show. Verbose // Debug prints everything Verbose does, plus every request and // response header and both bodies. Debug )
func LevelFor ¶
LevelFor maps the two global flags onto a level. **--debug implies --verbose**: that ordering is the documented contract, and stating it is what stops `--verbose --debug` from being a combination nobody can predict. Two booleans where one is a superset of the other are only coherent if the superset relation is defined somewhere, and this is where.
type Transport ¶
type Transport struct {
Base http.RoundTripper
Out io.Writer
Level Level
}
Transport renders each exchange to Out. Use Wrap rather than constructing one directly, so the Off case stays uniform.