refactor: trim Cloudflare API, bundle interceptors + Priority, silo warm-proxy - #116
Merged
Conversation
Four related cleanups to the public surface and CI (0.x, breaking is fine). 1. Remove WithFlushInterval + LIBTUNNEL__CLOUDFLARE_FLUSH_INTERVAL. The reverse proxy still fronts the origin with the stdlib default; the flush lever is gone (it was the last vestige of the removed streaming shim). 2. Rename WithApiURL -> WithProvider(host) and LIBTUNNEL__CLOUDFLARE_API_URL -> LIBTUNNEL__CLOUDFLARE_PROVIDER. Takes a bare host (api.trycloudflare.com) and synthesizes https://<host>/tunnel; a value carrying a scheme is used verbatim (escape hatch for a mock/alternate endpoint). 3. WithInterceptor(Interceptor) instead of (MatchFn, InterceptFn). The pair is already the Interceptor struct; taking it as one value lets reusable interceptors ship as constructors: tun.WithInterceptor(addHeader(...)). README leads with a plain header-adding middleware example (wrap ctx.Handler()); added TestInterceptWrapsDefaultHandler. 4. CI: move "warm proxy.golang.org" out of the release job into its own warm-proxy job (needs: release, reads release outputs tag/skip). A transient proxy.golang.org 5xx now fails only that job — a one-click isolated rerun — instead of false-failing an already-published release. Release exports tag/skip as job outputs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a Priority int to Interceptor. WithInterceptor keeps the registry sorted by descending Priority (stable, so equal-Priority interceptors keep registration order), and Intercept's first-match scan therefore yields the highest-Priority match. The zero value leaves everything equal, i.e. plain registration order — so this is behavior-compatible with the prior first-registered-wins semantics until a Priority is set. Lets a specific interceptor override a broad one explicitly, without depending on the order it was added (the ALB-listener-rule model), which is safer than add-order for a live-mutable registry. Docs (v1 struct/registry godoc, README) + TestInterceptPriorityWins. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add Priority int to Interceptor. Registration keeps the registry sorted by descending Priority (stable → equal Priorities keep registration order), so Intercept's first-match scan yields the highest-Priority match. A Priority of 0 is auto-assigned an increasing value via an atomic counter (10, 20, …), so among unprioritized interceptors the later-registered one wins. An explicit non-zero Priority raises that lane to its value — monotonic, never lowering it, so a low fallback Priority can't drag the lane down — and the next default then lands one step above it. Docs (v1 struct/registry godoc, README) + tests: later-default-wins, explicit-high-wins, explicit-low-is-fallback. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…cessor Reorient Interceptor.Priority to AWS-ALB semantics and expose the registry. - Priority is now uint16; the LOWEST wins (0 = highest precedence). The registry is kept sorted ascending, ties in registration order. - A Priority of 0 (unset) is auto-assigned from math.MaxUint16 downward (atomic counter, saturating at 0), so unprioritized interceptors sit at the low-precedence end: a later-registered one wins over an earlier one, and any explicit Priority outranks them all. Explicit priorities no longer nudge the auto counter. - Add Tunnel.Interceptors() returning a snapshot in precedence order (auto Priorities resolved) for visibility into the effective ordering. Docs (v1 struct/registry/Tunnel godoc, README) + tests: later-default-wins, lower-Priority-wins, explicit-beats-default, and the accessor ordering. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0 is the zero value that triggers auto-assignment, not a usable precedence, so 1 is the highest settable Priority (65535 the lowest). Spell that out in the Interceptor.Priority / Interceptors godoc and the README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <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.
Four related cleanups to the public surface and CI. Pre-v1 (0.x), so the breaking API changes are fine.
1. Remove
WithFlushIntervalDrops the method and
LIBTUNNEL__CLOUDFLARE_FLUSH_INTERVAL. The reverse proxy still always fronts the origin (stdlib-default flushing); the flush-interval lever — the last vestige of the removed streaming shim — is gone.newOriginProxyloses itsflushIntervalparam.2.
WithApiURL→WithProvider(host)Rename +
LIBTUNNEL__CLOUDFLARE_API_URL→LIBTUNNEL__CLOUDFLARE_PROVIDER. Takes a bare host (api.trycloudflare.com) and synthesizeshttps://<host>/tunnel. A value carrying a scheme (http://127.0.0.1:8080/tunnel) is used verbatim — the escape hatch that keeps the mock-endpoint tests working. Default unchanged in effect.3.
WithInterceptor(Interceptor)Was
WithInterceptor(match MatchFn, handler InterceptFn); now takes the existingInterceptor{Match, Handler}struct as one value. Semantics of both fns unchanged. This lets reusable interceptors ship as constructors:README leads with a dead-simple header-adding middleware (wrap
ctx.Handler(), set a header,next(w, r));TestInterceptWrapsDefaultHandlerpins that pattern.Interceptor.Priority
Interceptorgains aPriority int.WithInterceptorkeeps the registry sorted by descending Priority (stable — equal-Priority keeps registration order), so the highest-Priority match wins. The zero value = plain registration order, so it stays behavior-compatible until a Priority is set. Lets a specific interceptor override a broad one explicitly (the ALB-rule model), safer than add-order for a live-mutable registry.4. CI: silo the proxy warmer
warm proxy.golang.orgmoves out of thereleasejob into its ownwarm-proxyjob (needs: release, reads newreleaseoutputstag/skip). A transient proxy.golang.org 5xx now fails only that job — a one-click isolated rerun — instead of false-failing an already-published release (v0.0.34/v0.0.35 both hit this).continue-on-error/|| truedropped so the failure is visible and rerunnable; curl retries kept.Build/vet/fmt/tests/
-raceall green.🤖 Generated with Claude Code