Repository navigation
Fix issues found during the connection-reuse review - #24
Merged
Merged
Conversation
The three errors.Wrapf calls that wrap a failure to read the body of an error response also formatted that failure into the message, so the read error appeared twice in Error(). Wrap it once, and add a test that drives the read error with a server declaring a longer Content-Length than it sends. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
LookupFieldsWithContext appended "?fields=..." to the URL, but newGetRequestWithContext then replaced the whole query string with the API key, so the fields never reached the API and every LookupFields call received the full response. newGetRequestWithContext now takes the extra query parameters as a url.Values and encodes them together with the API key, which always takes precedence. LookupFields passes the comma-joined field list that way. The single-IP lookups share one request path (rawLookup, lookup) instead of a second copy of the response handling. Tests assert that the request the server receives carries fields and api-key (and no fields parameter when none were given), that the partial response is decoded, and that newGetRequestWithContext encodes the parameters without modifying the caller's values. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The default *http.Client bounds dialling and the TLS handshake but puts no limit on waiting for or reading the response, so without a context deadline a request can wait indefinitely. WithTimeout(d) sets Timeout on the *http.Client a Client uses. It is applied after all other options, so it also covers an *http.Client given with WithHTTPClient, in either order: that client is copied with the Timeout set and the caller's value is left untouched. Without the option nothing changes. Tests cover both constructors, the option order, the untouched custom client, WithTimeout(0), and lookups against a server that answers too late failing with an error whose Timeout() reports true. The README and the package documentation describe the default and the option. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
doc.go pointed issue reports at the repository's former location; it now names github.com/ipdata/go/issues. Scores had no doc comment and the Threat comment did not mention its Blocklists and Scores fields; both are documented now, and the README's "Nested Types" section gains the Scores entry it was missing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
go.mod requires Go 1.24.7, so the 1.21, 1.22 and 1.23 jobs switch to the 1.24.7 toolchain through GOTOOLCHAIN=auto and the matrix exercised only Go 1.24. Add the releases since then so the tests also run on current toolchains; the existing entries are kept. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #23, fixing what that pull request listed under "Noticed, not changed":
LookupFieldsnever sent itsfieldsparameter, a read error on an error response appeared twice in the error text, there was no way to put an overall time limit on a request while keeping the library's transport,doc.gopointed issue reports at the repository's former location,Scoreswas undocumented, and the CI matrix exercised Go 1.24 only. All changes are additive: no existing signature or default behaviour changes.Changes
LookupFieldssends thefieldsparameter.LookupFieldsWithContextappended?fields=...to the URL, butnewGetRequestWithContextthen replaced the whole query string with the API key, so the API always returned the full response. The request builder now takes extra query parameters as aurl.Valuesand encodes them together withapi-key, which always takes precedence; the single-IP lookups share one request path instead of two copies of the response handling. Users ofLookupFieldsnow receive the partial response they asked for.errors.Wrapf(err, "... %q: %s", resp.Status, err)calls formatted the read error into the message they also wrapped, soError()showed it twice; it now readsfailed to read body from response with status code "500 Internal Server Error": unexpected EOF.WithTimeout(d time.Duration)sets an overall time limit for every request (http.Client.Timeout) while keeping the library's connection pool and HTTP/2 transport. It is applied after all other options, so it also covers an*http.Clientgiven withWithHTTPClientin either order: that client is copied withTimeoutset and the caller's value is left untouched;WithTimeout(0)means no limit. A request over the limit fails with an error whoseTimeout()reports true, and bulk lookups are never replayed after a timeout. The README gains a "Timeouts" section and the package documentation states the default.doc.gopoints issue reports atgithub.com/ipdata/go/issues;Scoreshas a doc comment and theThreatcomment names itsBlocklistsandScoresfields; the README "Nested Types" section listsScores.Not changed
go.modrequires 1.24.7, so these jobs switch to the 1.24.7 toolchain throughGOTOOLCHAIN=autoand test the same thing as the 1.24 job; dropping them is a matrix reduction left to the maintainers.http.Client.Timeoutby default would cut off existing callers' long-running requests, so the limit is opt-in throughWithTimeout, and requests without it stay bounded by their context only.Verification
Local, go1.24.7 linux/amd64, all on the final tree:
gofmt -l .prints nothing.go vet ./...clean;go mod verifyreports all modules verified.go test -race -covermode atomic -cover -v ./...:ok github.com/ipdata/go, coverage 95.2% of statements (88.8% before); 28 top-level tests and 68 subtests pass, 0 fail.go run honnef.co/go/tools/cmd/staticcheck@latest ./...(staticcheck 2026.2.2, 0.8.2): no findings.go vet ./...andgo test -race -covermode atomic -cover ./...repeated withGOTOOLCHAIN=go1.25.14,GOTOOLCHAIN=go1.26.10andGOTOOLCHAIN=go1.27.3: ok on all three.Test_client_LookupFields,TestClient_truncatedErrorResponse,TestNewClient_WithTimeout,TestClient_WithTimeout_request) with-race -count=10: ok.fields=ip,country_nameandapi-key(and nofieldsparameter when none are given) and the partial response is decoded;newGetRequestWithContextencodes the parameters, keepsapi-keyin charge and leaves the caller's values unchanged; a server declaring a longerContent-Lengththan it sends drives the body read error forLookup,RawLookupandBulkLookup;WithTimeoutwith the default and a custom*http.Clientin both option orders,WithTimeout(0), and lookups against a server that answers too late.08f7c71) completed with conclusionsuccess:Test (Go 1.21),Test (Go 1.22),Test (Go 1.23),Test (Go 1.24),Test (Go 1.25),Test (Go 1.26),Test (Go 1.27)andStatic Analysisall succeeded.🤖 Generated with Claude Code