Skip to content

feat(netprobe): HTTP probe v3 — response assertions (json/body/size/header/version) + per-target resolve/ip_version - #794

Merged
leoparente merged 12 commits into
developfrom
feat/netprobe-v3
Jul 22, 2026
Merged

feat(netprobe): HTTP probe v3 — response assertions (json/body/size/header/version) + per-target resolve/ip_version#794
leoparente merged 12 commits into
developfrom
feat/netprobe-v3

Conversation

@leoparente

Copy link
Copy Markdown
Contributor

Summary

v3 of the netprobe HTTP probe (follow-up to #793): full response-assertion parity with the mainstream HTTP probers (cloudprober's HTTP validator, the OTel httpcheckreceiver, and the Prometheus blackbox_exporter HTTP module), plus per-target resolution controls. Every new assertion failure reuses v2's content_failures counter — no new metrics or signals.

New response assertions (http test type, stream-level):

  • json_path + json_equals — evaluate an RFC 6901 JSON Pointer (e.g. /data/status) against the JSON body; json_equals compares the value's string form (so "200" matches numeric 200). json_path alone asserts presence.
  • not_contains / body_not_matches_regex — the inverses of v2's expected_body/expected_body_regex.
  • min_response_size_bytes / max_response_size_bytes — bound the response size (true downloaded size, exact even if the captured body was truncated).
  • fail_if_header_matches / fail_if_header_not_matches — maps of header-name: value_regex over the response headers.
  • max_last_modified_diff_secs — fail if Last-Modified is older than N seconds (or absent).
  • valid_http_versions — assert the negotiated HTTP version ∈ {1.0,1.1,2,3}.

Per-target resolution controls (http + doh):

  • targets.<name>.ip_version (4/6 → CURLOPT_IPRESOLVE) and targets.<name>.resolve (host:port:addressCURLOPT_RESOLVE).

Design notes

  • All assertions are ANDed, evaluated probe-side, only after the status check passes, and fold into the existing HttpSample.content_check tri-state → no signal/POD/metric changes. Any assertion failure → content_failures (status failures stay http_status_failures).
  • Truncation-safe: body-reading assertions (expected_body/regex, not_contains/regex, json_path) are skipped (with a warning) when the body exceeded body_check_max_bytes; size/header/version assertions are unaffected (size uses the true downloaded size).
  • Final-response headers only: response-header capture resets on each HTTP/ status line and sets CURLOPT_SUPPRESS_CONNECT_HEADERS, so a redirect chain's intermediate-hop headers never leak into the match set (matches blackbox/cloudprober semantics).
  • Security: the new secret-bearing config values (not_contains, body_not_matches_regex, json_equals, and the header value_regexs) join v2's redaction — scrubbed from both info_json and the tap-config echo.
  • JSON via nlohmann/json (no new grammar/dep — JSON Pointer is built in); dates via curl_getdate; case-insensitive header matching via a portable helper — MSVC-portable, no POSIX-only APIs.

Out of scope (v4+)

Per-target overrides of stream-level checks; CEL / full JSONPath; OAuth2; basic_auth sugar; response compression/enable_http3; DoH EDNS(0)/DNSSEC/per-target qname.

Testing

  • unit-tests-visor-http-check (64) — JSON-pointer eval across value types, inverse body checks, header matchers (present/absent/multi-value, case-insensitive), http-date parsing, version mapping.
  • unit-tests-visor-http-client (76) — response-header capture with per-response reset (redirect test), http_version, plus all v2 cases.
  • unit-tests-input-netprobe (219, 2× stable) — config grammar/scoping/validation errors (secret-free), the v3 secret-scrub redaction test, and an e2e matrix: json_path pass/fail, not_contains, size bound, fail_if_header_matches/not_matches, valid_http_versions, max_last_modified_diff, per-target resolve (pins a bogus host to the test server, proving CURLOPT_RESOLVE), and a 301→200 test proving header assertions see only the final response.
  • unit-tests-handler-netprobe (100) — unchanged (no metric/signal changes).

All suites pass locally (Release).

🤖 Generated with Claude Code

@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9e4e10497c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/inputs/netprobe/HttpProbe.cpp
Comment thread src/inputs/netprobe/HttpProbe.cpp Outdated
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

LCOV of commit ee26cc4 during Debug Builds #217

  lines......: 84.7% (17074 of 20150 lines)
  functions..: 76.3% (1697 of 2224 functions)
  branches...: no data found

Files changed coverage rate: n/a

Full coverage report

…_response_size_bytes:0 requires an empty body
@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f5e5d0e0c8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread libs/visor_http_client/HttpClient.cpp Outdated
…VE leaked across same-host targets on the shared multi)
@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5ea9c1d8c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread libs/visor_http_client/HttpClient.cpp Outdated
Comment thread src/inputs/netprobe/NetProbeInputStream.cpp Outdated
…alformed header-matcher config

Response-header capture is bounded by a 64KB byte cap. When a header line is
dropped at that cap, a "pass" from the header matchers can no longer be trusted
(a forbidden header could be among the dropped tail), so mark the result and
fail the header assertion conservatively with a warning.

Also redact the whole fail_if_header_matches/fail_if_header_not_matches key in
the config echo when its value arrives as a non-object (scalar/list) shape — a
malformed value can still carry a secret and must not be echoed raw.
@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 79e9f8e5a6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread libs/visor_http_client/HttpClient.cpp
Comment thread src/inputs/netprobe/NetProbeInputStream.cpp
… IPv6 in CONNECT_TO

Two Round-4 review fixes:

- header_capture(): the per-response reset (fired on each "HTTP/" status line so
  only the final response's headers survive a redirect) cleared the header list
  and byte count but not headers_truncated. An oversized intermediate redirect
  hop therefore left the flag set, making the probe fail a header assertion that
  should have passed on a small final response. Reset the flag alongside the rest.

- CONNECT_TO construction: an unbracketed IPv6 resolve address collided with the
  PORT2 field of curl's HOST1:PORT1:HOST2:PORT2 --connect-to format, so IPv6
  per-target resolve never connected. Extract a pure build_connect_to_entry()
  that brackets IPv6 literals (accepting bracketed or unbracketed input) and
  rebuilds the entry; malformed entries return "" and are skipped.
@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9102618c59

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/inputs/netprobe/HttpProbe.cpp Outdated
…der rules exist

A fail_if_header_not_matches (required-header) PASS is a positive presence proof
— the required header was found in the captured headers — so headers dropped at
the 64KB capture cap cannot invalidate it. Only a fail_if_header_matches
(forbidden-header) PASS is truncation-sensitive: a forbidden header could be
hiding in the dropped tail. The previous code failed conservatively on truncation
for any configured matcher, so a required-only config wrongly reported
content_failures on healthy endpoints with large header blocks.

Add HeaderMatchers::has_forbidden_rules() and gate the probe's truncation
fail-safe on it. Tests: required-only config passes despite truncation; a
combined forbidden+required config still fails; has_forbidden_rules() unit cases.
The two truncation e2e tests use a few ~40KB pad headers (any two exceed the cap,
so at most one is admitted) to guarantee the required header is captured
regardless of httplib's unordered header iteration order.
@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 08bc80a4a3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread libs/visor_http_client/HttpClient.cpp
…nfigured

HttpClient only set CURLOPT_PROXY when req.proxy was non-empty, so with no proxy
configured libcurl fell back to ambient http_proxy/https_proxy/all_proxy env
vars. For a monitoring probe with an explicit `proxy` config this made results
depend on the deployment/CI environment and defeated the per-target
resolve/CONNECT_TO pin (an env proxy + CONNECT_TO switches curl to tunnel mode,
asking the proxy to reach the pinned address instead of connecting locally).

Set CURLOPT_PROXY to "" for no-proxy requests, which disables proxy use even when
an env var is set. Applies to http and doh probes alike; the explicit-proxy path
(and its error_msg credential redaction) is unchanged.

Test: with a bogus http_proxy set, a no-proxy request to a local server still
connects directly (transport_ok, 200). The test is hermetic — an RAII guard
clears every proxy env var (http_proxy/https_proxy/all_proxy/no_proxy, both
cases) first, so an ambient no_proxy listing loopback can't defeat it — and
restores them on scope exit.
@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: ee26cc429e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@leoparente
leoparente marked this pull request as ready for review July 22, 2026 16:21
@leoparente
leoparente merged commit fb6dc23 into develop Jul 22, 2026
23 checks passed
@leoparente
leoparente deleted the feat/netprobe-v3 branch July 22, 2026 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants