Skip to content

Send a User-Agent from web_fetch and http_request - #52

Merged
senamakel merged 9 commits into
tinyhumansai:mainfrom
sanil-23:fix/network-tools-user-agent
Oct 8, 2026
Merged

senamakel merged 9 commits into
tinyhumansai:mainfrom
sanil-23:fix/network-tools-user-agent

Conversation

@sanil-23

@sanil-23 sanil-23 commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Problem

reqwest sends no User-Agent unless told to, and some APIs refuse a request without one. GitHub's REST API is the clearest case:

403 Request forbidden by administrative rules.
    Please make sure your request has a User-Agent header

Reproducible on demand — the same URL in the same second:

$ curl -s -o /dev/null -w '%{http_code}\n' -H 'User-Agent:' \
    https://api.github.com/repos/<owner>/<repo>/issues/1
403
$ curl -s -o /dev/null -w '%{http_code}\n' -A 'example' \
    https://api.github.com/repos/<owner>/<repo>/issues/1
200

So every api.github.com call through http_request failed, deterministically — not intermittently. Observed in a live agent run as eight consecutive failures, which were then classified downstream as an authentication problem and sent the operator looking for credentials that were never involved.

Several other APIs require a User-Agent too, and anonymous traffic is the first thing a rate limiter penalises.

Change

  • gate::USER_AGENT = tinytools/<crate version>, shared by both tools
  • set on the http_request and web_fetch clients

Identifying rather than disguised: a server that wants to throttle or block this traffic should be able to name it, and the version keeps builds distinguishable.

Testing

cargo test -p tinytools-std --lib network:: — 92 passed, 0 failed.

The new test asserts the header left the process, read back from serve's raw-request log, rather than asserting on the builder:

assert!(lower.contains("user-agent: tinytools/"), "...");

cargo fmt -p tinytools-std -- --check clean.

Risk

Low. One header added to outbound requests; no public API, schema or behaviour change beyond the header itself.

🤖 Generated with Claude Code

reqwest sends no `User-Agent` unless told to, and some APIs refuse a request
without one. GitHub's REST API is the clearest case:

    403 Request forbidden by administrative rules.
        Please make sure your request has a User-Agent header

Reproducible on demand — the same URL in the same second answers 403 with no
header and 200 with one. Every `api.github.com` call through these tools
therefore failed, deterministically, and the 403 was then read downstream as a
credentials problem, which sends whoever is debugging it after the wrong thing
entirely.

Adds `gate::USER_AGENT` (`tinytools/<crate version>`) and sets it on both
clients. Identifying rather than disguised: a server that wants to throttle or
block this traffic should be able to name it, and a version makes builds
distinguishable.

The test asserts the header left the process, read back from `serve`'s raw
request log, rather than asserting on the builder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tinysweeper

tinysweeper Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Tiny Sweeper reviewed this change across 6 lane(s) and found 2 active actionable finding(s). Detailed lane evidence and any incomplete work are listed below.

State: Ready for maintainer review
Priority: none
Reviewed head: a25500ce912e
Updated: 1791459555 (Unix time)

Review snapshot

Change surface Files Review signal Count
Production 3 Active findings 2
Tests 2 Noted findings 0
Documentation 0 Resolved findings 15
Configuration 0 Pending checks/questions 0

Completeness: Complete
Test assessment: No supported feature-to-test mapping was available; this does not mean tests are absent or passed.

What changed

The review could not produce a supported behavioral summary; inspect the cited changed surface and lane details below.

Features

None identified with supported citations.

Tests

No supported feature-to-test mapping was produced. Test execution is not inferred.

Findings

Previously reported and still active

  • web\_fetch's User-Agent change has no test
  • Add a web\_fetch test for the User-Agent header

Resolved this pass

  • web_fetch's User-Agent change has no test
  • Add a web_fetch test for the User-Agent header
  • Read the complete request before checking its headers
  • Read the complete request before checking its headers
  • web_fetch's User-Agent change has no test
  • Add a web_fetch test for the User-Agent header
  • Read the complete request before checking its headers
  • Read the complete request before checking its headers
  • web_fetch's User-Agent change has no test
  • Add a web_fetch test for the User-Agent header
  • Read the complete request before checking its headers
  • Read the complete request before checking its headers
  • Add a web_fetch test for the User-Agent header
  • web_fetch's User-Agent change has no test
  • Read the complete request before checking its headers

Before merge

  • Address carried finding web\_fetch's User-Agent change has no test.
  • Address carried finding Add a web\_fetch test for the User-Agent header.

How this fits together

flowchart LR
  n0["HttpRequestTool<br/>changed"]:::changed
  n1["WebFetchTool<br/>changed"]:::changed
  n2["fetch"]:::impacted
  n3["test_tool"]:::impacted
  n4["execute_request"]:::impacted
  n5["...d_once_with_the_hooks_headers_and_settled"]:::impacted
  n6["..._is_returned_as_the_error_without_a_retry"]:::impacted
  n2 -->|uses| n1
  n3 -->|uses| n0
  n5 -->|calls| n3
  n5 -->|tests| n3
  n5 -->|calls| n4
  n5 -->|tests| n4
  n6 -->|calls| n3
  n6 -->|tests| n3
  n6 -->|calls| n4
  n6 -->|tests| n4
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading
Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change adds a focused integration test verifying that web_fetch sends the expected User-Agent and reads the complete request headers before asserting them. The earlier concerns about missing coverage and incomplete header reads are resolved, and this change is safe to merge. (2 earlier finding(s) still open) _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change adds a focused regression test verifying that web_fetch sends the expected User-Agent and safely reads the complete request headers. The change looks sound and is safe to merge. (2 earlier finding(s) still open) _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The User-Agent change is now covered on both paths: the new web_fetch test asserts the exact `tinytools/<version>` header value on a live request, and reads the complete request before checking headers, resolving the earlier findings. The change looks sound. (2 earlier finding(s) still open) _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._

commits

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: Nothing sensitive found in what this pull request commits.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The PR adds a shared `User-Agent` to both network tools with a test per tool; the previously missing `web_fetch` test and the incomplete-request read in the server loop are now both fixed. The new test is correct — it lowercases the raw request before matching, asserts the exact header value, and `unwrap()` use is allowed in tests. Looks sound to merge. (2 earlier finding(s) still open) _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._

e2e

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: No end-to-end harness in this repository: no e2e test files and no e2e workflow.
Evidence and run details
  • Models: gpt-5.6-luna, glm-5.3-flash
  • Spend: $0.008483
  • Tokens: 86664 input · 3920 output · 11460 cached · 0 embedding
Head State Pass summary
00bace0da725 ready for maintainer review 2 active finding(s), 0 resolved finding(s) (at 1791396227)
f0e779f6f8f8 ready for maintainer review 2 active finding(s), 17 resolved finding(s) (at 1791458418)
0ade83e5fab8 ready for maintainer review 1 active finding(s), 14 resolved finding(s) (at 1791458928)
61bda35247d9 ready for maintainer review 1 active finding(s), 11 resolved finding(s) (at 1791459104)
a25500ce912e ready for maintainer review 0 active finding(s), 15 resolved finding(s) (at 1791459555)

tinysweeper 0.1.0

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

  • Run on-demand review

This review includes 5 billable files and costs up to $1.25.

Or wait 47 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f5dddaed-177a-4a5d-a62c-292a70f4ecb0
📥 Commits

Reviewing files that changed from the base of the PR and between 68163b1 and a25500c.

📒 Files selected for processing (5)
  • crates/tinytools-std/src/network/gate.rs
  • crates/tinytools-std/src/network/http_request.rs
  • crates/tinytools-std/src/network/http_request_tests.rs
  • crates/tinytools-std/src/network/web_fetch.rs
  • crates/tinytools-std/src/network/web_fetch_tests.rs
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@tinysweeper tinysweeper 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.

tinysweeper found nothing blocking. Approving.

             $0.0028 · 69,009 in / 2,596 out · 6,182 cached (9%)  · gpt-5.6-luna, glm-5.3-flash
critique:    $0.0017 · 34,605 in / 780 out   · 4,284 cached (12%) · gpt-5.6-luna
security:    $0.0010 · 20,718 in / 284 out   · 1,898 cached (9%)  · gpt-5.6-luna
tests:       $0.0001 · 5,206 in  / 432 out   · 0 cached (0%)      · glm-5.3-flash
description: $0.0000 · 5,002 in  / 332 out   · 0 cached (0%)      · glm-5.3-flash

Comment thread crates/tinytools-std/src/network/web_fetch.rs
@senamakel senamakel self-assigned this Oct 8, 2026
senamakel and others added 2 commits October 8, 2026 14:15
Add a test that spins up a local listener, performs a validated fetch, and
checks the captured request headers for a tinytools user agent. This guards
against regressions where the header is dropped from outgoing requests.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The USER_AGENT constant is now crate-visible rather than public, since it is
only consumed within the network module. The user agent test was reworked to
return a Result and propagate errors with ?, replacing the expect calls.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@tinysweeper tinysweeper 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.

tinysweeper found nothing blocking. Approving.

             $0.0100 · 110,077 in / 5,973 out · 9,878 cached (9%)  · gpt-5.6-luna, glm-5.3-flash
critique:    $0.0052 · 51,837 in  / 2,747 out · 4,575 cached (9%)  · gpt-5.6-luna, glm-5.3-flash
security:    $0.0046 · 41,705 in  / 2,163 out · 3,575 cached (9%)  · gpt-5.6-luna
tests:       $0.0000 · 6,179 in   / 92 out    · 1,536 cached (25%) · glm-5.3-flash
description: $0.0000 · 5,963 in   / 99 out    · 64 cached (1%)     · glm-5.3-flash

Comment thread crates/tinytools-std/src/network/gate.rs
senamakel and others added 2 commits October 8, 2026 14:21
The user agent test now extracts the header line and compares it against the exact expected value instead of a substring match, so a wrong or truncated version string is caught rather than passing on the prefix alone.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformat the user agent assertion in the outgoing request test to satisfy rustfmt line width limits. No behaviour change.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@tinysweeper tinysweeper 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.

tinysweeper found nothing blocking. Approving.

             $0.0043 · 38,294 in / 2,927 out · 64 cached (0%) · gpt-5.6-luna, glm-5.3-flash
critique:    $0.0024 · 13,482 in / 1,115 out · 64 cached (0%) · gpt-5.6-luna, glm-5.3-flash
security:    $0.0018 · 8,146 in  / 511 out   · 0 cached (0%)  · gpt-5.6-luna
tests:       $0.0000 · 6,199 in  / 246 out   · 0 cached (0%)  · glm-5.3-flash
description: $0.0000 · 5,983 in  / 152 out   · 0 cached (0%)  · glm-5.3-flash

Comment thread crates/tinytools-std/src/network/web_fetch_tests.rs Outdated
The test server now reads until the end of the header block instead of
assuming a single read captures the whole request, so the assertion no
longer fails when the request arrives split across packets.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>

@tinysweeper tinysweeper 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.

tinysweeper found nothing blocking. Approving.

             $0.0052 · 61,579 in / 3,817 out · 15,314 cached (25%) · gpt-5.6-luna, glm-5.3-flash
critique:    $0.0030 · 27,875 in / 1,268 out · 7,381 cached (26%)  · gpt-5.6-luna
security:    $0.0021 · 16,569 in / 1,143 out · 4,861 cached (29%)  · gpt-5.6-luna
tests:       $0.0000 · 6,322 in  / 268 out   · 1,536 cached (24%)  · glm-5.3-flash
description: $0.0000 · 6,106 in  / 178 out   · 1,408 cached (23%)  · glm-5.3-flash

Comment thread crates/tinytools-std/src/network/web_fetch_tests.rs
senamakel and others added 3 commits October 8, 2026 14:34
The user agent test previously broke out of the read loop when the peer closed early, which let the assertion run against a truncated request. It now panics with a clear message so the failure points at the incomplete read instead of a confusing mismatch.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replace the explicit panic on a zero-length read with an assert! that carries the same message, keeping the test's intent while shortening the loop body.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformatted the assertion that checks the peer has not closed before sending complete HTTP headers so it matches the project's rustfmt style. No behaviour change.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@senamakel
senamakel merged commit b3e964f into tinyhumansai:main Oct 8, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants