Skip to content

feat: add ErrorHandler to AcceptOptions - #588

Draft
mafredri wants to merge 18 commits into
masterfrom
mafredri/accept-error-handler
Draft

mafredri wants to merge 18 commits into
masterfrom
mafredri/accept-error-handler

Conversation

@mafredri

@mafredri mafredri commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Accept writes a plain text http.Error response when it rejects a handshake request. A server that needs a structured error body, such as RFC 9457 JSON (#545), cannot replace that response. A second write after Accept returns only appends to the plain text body: the status and text/plain content type stay Accept's, and net/http logs a superfluous WriteHeader call.

AcceptOptions.ErrorHandler func(w http.ResponseWriter, r *http.Request, status int, err error) writes that response instead. It receives the status of the rejection and the error Accept returns, before wrapping. w.Header() already holds the headers Accept set, such as Sec-WebSocket-Version: 13 for an unsupported version. It is called for every rejection before the 101 response: request validation, origin, and a ResponseWriter without http.Hijacker. It is not called when hijacking fails, because Accept has already written 101 Switching Protocols. A handler that writes nothing leaves net/http's default 200 OK with an empty body.

With a nil ErrorHandler, the response is the same as before.

For an invalid OriginPatterns entry, Accept now returns an error that names the entry and wraps path.ErrBadPattern, instead of "Forbidden", and the handler receives it. The client still gets 403 Forbidden.

Tests cover the handler for all 13 rejections. A separate test checks the default status, headers and body for all 13, which are unchanged from master. Only the error Accept returns and logs for an invalid pattern differs.

Fixes #545

Decision log
  • Handler field instead of a typed error plus an opt-out flag: with the flag, callers must write after a rejection but not after a hijack failure, and a wrong branch gives 200 OK or a double write.
  • The handler receives r, so one handler shared across requests can use it, for example r.Context(). Allow customize how HTTP errors are rendered when websocket.Accept fails to upgrade #545 discussed capturing the request in a closure instead, which does not work for shared options.
  • Documentation alone was rejected because callers still could not change the body (Allow customize how HTTP errors are rendered when websocket.Accept fails to upgrade #545, Support disabling error writing via AcceptOptions #152).
  • No exported per-reason errors; status and error text are enough to render a response, and sentinels can be added later.
  • For an invalid OriginPatterns entry and without http.Hijacker, the handler receives the descriptive error while the nil default body is "Forbidden" or "Not Implemented".
  • No recover around the handler: a panic behaves like any other handler panic in net/http.
  • The hijack failure after the 101 is unchanged; whether to hijack before writing 101 is a separate decision.
  • The field is also added to the js/wasm AcceptOptions stub so shared code compiles.

🤖 This PR was created with the help of Coder Agents.

@mafredri
mafredri force-pushed the mafredri/accept-error-handler branch from ff05d69 to f154711 Compare October 30, 2026 15:18
Accept's rejection responses had no test that checked the status,
headers and body together. Pinning them for every rejection before the
101 lets a change to how the response is written prove that the default
output stays the same.
Accept always wrote a plain text http.Error response when it rejected a
handshake, so servers with a structured error format had to write a
second response, which net/http appends to Accept's body and logs as a
superfluous WriteHeader call. ErrorHandler lets the caller write that
response instead, with the same signature as gorilla's Upgrader.Error;
nil keeps the previous output.

Refs #545
@mafredri
mafredri force-pushed the mafredri/accept-error-handler branch from f154711 to 0eca8fe Compare November 3, 2026 19:14
@mafredri

This comment has been minimized.

@coder-agents-review

coder-agents-review Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Chat: Review posted | View chat
Requested: 2026-11-04 16:30 UTC by @mafredri
Spend: $28.17 / $100.00

Review history
  • R1 (2026-11-03): 12 reviewers, 2 Nit, 5 P3, 3 P4, COMMENT. Review
  • R2 (2026-11-03): 12 reviewers, 3 Nit, 5 P3, 6 P4, COMMENT. Review
  • R3 (2026-11-04): 6 reviewers, 3 Nit, 5 P3, 7 P4, COMMENT. Review
  • R4 (2026-11-04): 13 reviewers, 4 Nit, 6 P3, 9 P4, COMMENT. Review

deep-review v0.13.0 | Round 4 | e1a21e5..e6ff172

Last posted: Round 4, 19 findings (6 P3, 9 P4, 4 Nit), COMMENT. Review

Finding inventory

Finding inventory: coder/websocket PR #588

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P3 Author fixed (5728d8e) accept.go:88 No test asserts ErrorHandler is not called when Hijack fails R1 Netero Yes
CRF-2 Note Note (body) accept_test.go:211 newHandshakeRequest duplicates inline handshake setup in TestAccept (inline copies predate PR) R1 Netero No
CRF-3 P3 Author fixed (273a0ac) accept_test.go:388 server subtest leaks net/http transport goroutines; TestMain leak check fails filtered runs R1 Bisky, Pariston Yes
CRF-4 P3 Author fixed (d01f39d) accept.go:144 ErrorHandler doc does not define whether err is client-safe; call sites differ R1 Meruem P3, Leorio P3, Pariston Note Yes
CRF-5 P4 Author fixed (56eccc2) accept.go:136 Bad OriginPatterns error masked as "Forbidden" before ErrorHandler runs R1 Hisoka P4, Chopper P4, Ryosuke Note Yes
CRF-6 P3 Author fixed (5cf85d0) accept_test.go:220 Rejection table claims exhaustive but omits two authenticateOrigin rejections; description says 11, there are 13 R1 Mafu-san P2, Hisoka Note, Chopper Nit Yes
CRF-7 P3 Author fixed (38ae558) accept_test.go:334 No test asserts ErrorHandler receives the request passed to Accept R1 Bisky Yes
CRF-8 P4 Author fixed (0f9f7d7) accept_test.go:411 want map duplicates acceptRejectionTests data keyed by name R1 Bisky P4, Meruem P4 Yes
CRF-9 Note Dropped by orchestrator (contradicted by Leorio, who found the "must write" sentence useful; remaining cuts are a few words of a correct doc) accept.go:83 ErrorHandler doc wording could be shorter R1 Gon P2 No
CRF-10 Nit Author fixed (06b6aac) accept.go:197 Rename body to defaultBody and delete writeRejection doc comment R1 Gon P2 Yes
CRF-11 P4 Author fixed (09ddb1e) accept_test.go:319 badOriginPattern comment describes only the default path R1 Gon P2 (R1, re-raised R2) Yes
CRF-12 Nit Author fixed (273a0ac, b10f4ad) accept_test.go:355 server subtest comment restates its table cases R1 Gon P2 Yes
CRF-13 Note Dropped by orchestrator (comment states the test pins existing behavior, which the name does not) accept_test.go:401 Delete TestAcceptRejectionDefault doc comment R1 Gon P2 No
CRF-14 P4 Author contested; panel closed R2 (1/1 accept) accept.go:91 Name ErrorHandler implies all Accept errors; hijack failure excluded R1 Gon P4 Yes
CRF-15 Note Note (body) accept_test.go:229 In 7bd7405 the wantHeader comment refers to a handler added in 0eca8fe R1 Gon No
CRF-16 P4 Note (body) PR description "For four rejections ... byte for byte" names neither the rejections nor the command R1 Leorio P4, Mafu-san Note No
CRF-17 Nit Note (body) PR description PR body says Fixes #545, commit 0eca8fe says Refs #545 R1 Leorio No
CRF-18 Note Dropped by orchestrator (commit message already records the gorilla reason; Ryosuke and Mafu-san confirmed the parity) PR description Decision log omits gorilla Upgrader.Error signature parity R1 Leorio, Ryosuke, Mafu-san No
CRF-19 Note Dropped by orchestrator (informational, asks for no change) accept.go:86 "must write a response" sentence gives requirement and result R1 Leorio No
CRF-20 Note Dropped by orchestrator (informational, asks for no change) accept_test.go:314 Table tests check real behavior R1 Bisky No
CRF-21 OOS Out of scope (body) accept.go:175 Hijack failure calls http.Error after WriteHeader(101); 500 never sent R1 Netero, Ryosuke, Mafuuu, Chopper, Meruem No
CRF-22 OOS Out of scope (body) accept.go:143 Hijacker error text does not name usual causes R1 Leorio No
CRF-23 OOS Out of scope (body) accept.go:91 No stable rejection reason identity; sentinels deferred without ticket R1 Chopper Note, Pariston No
CRF-24 OOS Out of scope (body) accept.go:207 errCode vs status naming for the same value R1 Gon No
CRF-25 OOS Out of scope (body) accept.go:385 go fix reports strings.SplitSeq and dead tc := tc copies in unchanged code R1 Ging-Go No
CRF-26 Note Note (body) accept.go:135 Invalid OriginPatterns error now both logged via log.Printf and returned/passed to ErrorHandler R2 Netero No
CRF-27 Nit Author fixed (d6d333a) accept.go:86 "so ErrorHandler decides what to send to the client" repeats the doc's first sentence R2 Gon P2 Yes
CRF-28 Note Note (body) accept_test.go:439 "Upgrade, close" has no comment; removing close changed no result in 5 runs R2 Gon P4 No
CRF-29 P4 Author fixed (4b82827) accept.go:273 Pattern error now returned from Accept says "path pattern", not OriginPatterns R2 Leorio Yes
CRF-30 P4 Author fixed (f7f3599) accept.go:83 ErrorHandler doc does not state that err wraps path.ErrBadPattern for a bad OriginPatterns entry R2 Leorio Yes
CRF-31 Note Note (body) accept_test.go:483 TestAcceptRejectionDefault no longer passes against master accept.go, so "match master" cannot be rechecked by running it R2 Mafu-san No
CRF-32 Note Dropped by orchestrator (praise, asks for no change) accept_test.go:222 One rejection row drives both table tests R2 Kite No
CRF-33 P4 Author fixed (61a7faf, 618a580, e6ff172) accept.go:47 "Patterns are tried in order" documents order-dependent validation that no test pins R3 Ryosuke P4, Kite P4 Yes
CRF-34 Note Note (body) accept.go:89 err passed to ErrorHandler quotes request header values with %q, which does not escape HTML R3 Kurapika No
CRF-35 Note Dropped by orchestrator (informational, asks for no change) accept.go:47 OriginPatterns sentences match the code; 403 is the status Accept chooses, ErrorHandler may write another R3 Leorio No
CRF-36 P3 Open accept.go:255 Origin-validation behavior change (reject every handshake, 500) bundled into the ErrorHandler feat PR; upgrade turns partial service into full outage R4 Zoro P3, Pariston Note Yes
CRF-37 P4 Open accept.go:36 "The request host is always authorized" is false after 61a7faf R4 Leorio Yes
CRF-38 P4 Open accept.go:47 No test covers the InsecureSkipVerify exception for an invalid pattern R4 Bisky Yes
CRF-39 Nit Open accept.go:255 Pre-check comment restates the loop; state why it runs before the Origin checks R4 Gon P2 Yes
CRF-40 Note Dropped by orchestrator (keep-argument: three words repeat "every handshake"; dropped because CRF-37 shows readers assume same-origin requests are exempt, and "whatever its origin" states they are not) accept.go:48 "whatever its origin" repeats "every handshake" R4 Gon P2 No
CRF-41 Note Note (body) accept_test.go:321 badOriginPattern row still sets a cross-origin Origin that no longer affects the result R4 Bisky, Gon No
CRF-42 Note Note (body) accept.go:137 log.Printf for an invalid pattern now runs on every handshake R4 Netero No
CRF-43 Note Dropped by orchestrator (informational: discarding the match error is safe, verified by fuzzing) accept.go:282 match(p, "") reports every pattern error match(p, target) can R4 Chopper, Knov, Netero, Bisky, Ryosuke, Pariston, Gon, Leorio No
CRF-44 OOS Out of scope (body) accept.go:282 IPv6 literal origin pattern is a valid character class that never matches the literal origin R4 Pariston No

Contested and acknowledged

CRF-14 (P4, accept.go:91) - Name ErrorHandler implies all Accept errors

  • Finding: Gon proposed renaming ErrorHandler to RejectionHandler, because the handler does not run when hijacking fails and a caller who reads only the name may log every Accept failure from it.
  • Author defense: Maintainer decision to keep ErrorHandler. The field doc states the hijack exception, and httputil.ReverseProxy.ErrorHandler and gorilla's Upgrader.Error use the same kind of name.
  • Panel closure (R2, 1/1): Gon, the agent that raised it (same agent as R1, resumed), accepted. A caller who logs every Accept failure from the handler misses only the hijack failure, Accept still returns that error, and the field doc states the exception at accept.go:89-91. gorilla's Upgrader.Error has the same kind of exception for errors after it hijacks.

Round log

Round 1

Full panel (round 1). Netero: 1 P3, 1 Note, no P0. Panel: ging-go, ryosuke, kurapika, pariston, mafuuu, bisky, gon, leorio, hisoka, chopper, mafu-san; wildcard meruem (random from distilled set). Kurapika added for the origin rejection path, which put the panel in the 9-12 target. Posted 5 P3, 3 P4, 2 Nit. Gon's comment-wording P2s rated Nit (style, code correct) or dropped. Mafu-san's P2 on rejection table coverage set to P3: both missing branches go through the tested call site at accept.go:136 in unchanged authenticateOrigin; the gap is the default-response pin and the claim. PR description items (CRF-16, CRF-17) have no file location and are posted as a body process observation. Orchestrator verified CRF-3 (1/30 failures of TestAcceptErrorHandler filtered run) and CRF-21 (net/http server.go:1172 excludes 101 from 1xx handling). Reviewed against e1a21e5..0eca8fe.

Round 2

Fix round: no files added or renamed, no exported signature changed, no function rewritten, +47 effective additions since R1. Churn guard PROCEED: 9 addressed, 1 contested (CRF-14). CRF-11 reopened because 56eccc2 passes the pattern error to ErrorHandler, removing the drop premise. Netero: 1 Note (CRF-26). Panel: gon (resumed R1 agent for CRF-14 dispute check-in), bisky, pariston, meruem, leorio, hisoka, chopper, ryosuke, mafu-san (raised findings the new commits touch), ging-go, kurapika (changed Go files and origin path); wildcard kite. All verified fixes held (CRF-1, 3, 4, 5, 6, 7, 8, 10, 12). CRF-14 closed 1/1. New: 2 P4 (CRF-29, CRF-30), 1 Nit (CRF-27); CRF-11 re-raised and posted at P4 (inaccurate test comment, no runtime effect). Gon's CRF-27 P2 rated Nit (wording of a correct doc). Gon's CRF-28 P4 moved to Note (body): keep-argument is that an unexplained header token leaves readers unsure whether the leak fix depends on it; it stays a note because close is the standard HTTP token for a one-request connection and changed no observed result. Reviewed against e1a21e5..56eccc2.

Round 3

Fix round: 4 commits since R2 change docs, one error string, and one test comment; no files added, no exported signatures changed. Churn guard PROCEED: CRF-11, CRF-27, CRF-29, CRF-30 addressed. Netero: no findings, all four fixes verified. Panel: gon, leorio (raised the fixed findings), kurapika (origin path), ging-go, ryosuke (Go files); wildcard kite. All four fixes verified. New: 1 P4 (CRF-33, convergent Ryosuke and Kite), 1 Note (body). Reviewed against e1a21e5..f7f3599.

Round 4

Restructure round: the PR description adds a new behavior change to OriginPatterns validation (61a7faf, 618a580, e6ff172) beyond the ErrorHandler goal. Churn guard PROCEED: CRF-33 addressed. Netero: no findings, 1 Note. Full panel (multi-domain, security-sensitive origin code, target 9-12): ging-go, kurapika, ryosuke, killua (specialists); pariston, mafuuu (core); bisky, hisoka, chopper, gon, leorio, knov; wildcard zoro. Eight reviewers independently verified that path.Match(p, "") reports every syntax error that matching against a target reports (up to 8.5M fuzz executions), so the discarded match error is safe. New: 1 P3 (CRF-36), 2 P4, 1 Nit, 1 OOS. Reviewed against e1a21e5..e6ff172.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Komugi flake/determinism
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@coder-agents-review coder-agents-review 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.

This PR adds AcceptOptions.ErrorHandler, which writes the HTTP response when Accept rejects a handshake request before the 101 response. 5 P3, 3 P4, 2 Nit.

PR description: "For four rejections, the raw responses match master byte for byte" names neither the four rejections nor the command, so a reviewer cannot repeat it; a real-server comparison of all 12 rejections a server can produce matched base e1a21e5 byte for byte excluding Date, so the sentence can say that. The PR body says Fixes #545 and commit 0eca8fe says Refs #545; use one keyword in both.

Out of scope (needs a ticket or explicit acceptance by a human):

  • accept.go:175: after hj.Hijack() fails, Accept calls http.Error after WriteHeader(101). net/http treats 101 as the final status (net/http/server.go excludes StatusSwitchingProtocols from 1xx handling), so the 500 is a superfluous WriteHeader and the client receives a 101 with no usable connection.
  • accept.go:143: "http.ResponseWriter does not implement http.Hijacker" does not name the usual causes (an HTTP/2 request, or middleware that wraps the ResponseWriter without Unwrap), so an operator has to find the cause without help.
  • accept.go:91: rejection reasons have no stable identity (status 400 covers four reasons), so a handler that maps reasons to RFC 9457 type values must match error strings. The decision log defers sentinel errors without a ticket.
  • accept.go:207: verifyClientRequest names the status errCode, while ErrorHandler and writeRejection name the same value status.
  • accept.go:385, accept_test.go:566, and other unchanged lines: go fix -diff . reports a strings.SplitSeq rewrite and dead tc := tc copies.

Notes:

  • accept_test.go:211: newHandshakeRequest builds the same request that several TestAccept cases build inline; the inline copies predate this PR.
  • accept_test.go:229: in commit 7bd7405, the wantHeader comment refers to a handler that is added in 0eca8fe.

🤖 This review was automatically generated with Coder Agents.

Comment thread accept.go Resolved
Comment thread accept_test.go Resolved
Comment thread accept.go Resolved
Comment thread accept.go Resolved
Comment thread accept_test.go Resolved
Comment thread accept_test.go Resolved
Comment thread accept_test.go Resolved
Comment thread accept.go Resolved
Comment thread accept_test.go Resolved
Comment thread accept.go Resolved
The doc comment on writeRejection only existed to explain that body is
the fallback text for a nil ErrorHandler. The name defaultBody says that
at each call site, so the comment is removed.
The ErrorHandler doc did not say whether err is safe to send to the
client. For noHijacker, err names a server-side interface, so a handler
that writes err.Error() into the response would expose server details.
No test failed if Accept called ErrorHandler after a failed hijack, when
the 101 status line is already written. The new case uses a hijacker
that returns an error and asserts the handler never runs.
The http.Client transport goroutines in the server subtest exited after
TestMain counted goroutines, so filtered runs such as -run TestAccept
failed the leak check intermittently. A raw TCP connection with
Connection: close leaves no client goroutines behind.
Every test handler discarded the request argument, so passing nil or a
copy to ErrorHandler left the tests passing. The table test now asserts
the handler gets the same *http.Request that was passed to Accept.
TestAcceptRejectionDefault repeated each row's header and body in a map
keyed by row name, and a new row without a map entry failed with a
header mismatch that did not name the missing key. The header and body
now come from the row, with wantDefaultBody for the one row whose
default body is not wantErr.
acceptRejectionTests is documented as covering every rejection, but the
Origin parse failure and the Origin without a host had no row, so their
responses were not pinned.
The previous wording said err is not meant for the client, but Accept's
default response sends err.Error() for every rejection except
noHijacker. The doc now leaves the choice of what to send to the
handler.
The earlier rewrite dropped the reason for testing over a real
connection along with the part that restated the writesNothing row. Only
the restated part needed to go.
Accept replaced an invalid OriginPatterns error with "Forbidden", so an
ErrorHandler could not tell a misconfigured server from a disallowed
origin. The real error now reaches ErrorHandler and Accept's caller, and
the default response body stays "Forbidden".
@mafredri

This comment has been minimized.

coder-agents-review[bot]

This comment was marked as outdated.

The first sentence says ErrorHandler writes the response, so "so
ErrorHandler decides what to send to the client" added nothing to "err
may contain server details".
Accept now returns this error and passes it to ErrorHandler, and "failed
to parse path pattern" did not say which option held the pattern.
ErrorHandler now receives the pattern error, so only the default body
keeps it from the client.
@mafredri

This comment has been minimized.

@mafredri

This comment has been minimized.

coder-agents-review[bot]

This comment was marked as outdated.

@mafredri

mafredri commented Nov 4, 2026

Copy link
Copy Markdown
Member Author

/coder-agents-review

🤖

@coder-agents-review coder-agents-review 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.

Re-review at e6ff172. 1 P3, 2 P4, 1 Nit.

The round-3 finding is fixed by removing the order dependence: authenticateOrigin checks every pattern before the Origin checks, and the new Test_authenticateOrigin rows fail without that check. Reviewers fuzzed path.Match on go1.26.5 (up to 8.5M executions) and found no pattern where match(p, "") and match(p, target) disagree on ErrBadPattern, so discarding the error at accept.go:282 drops nothing.

Out of scope (needs a ticket or explicit acceptance by a human):

  • accept.go:282: an IPv6 literal pattern such as https://[::1]:8080 is a valid path.Match pattern in which [::1] is a character class, so it never matches the origin https://[::1]:8080 and the new check does not report it.

Notes:

  • accept.go:137: with a bad entry, log.Printf now writes one line per handshake, not one per cross-origin request that reached the entry.
  • accept_test.go:321: the badOriginPattern row still sets Origin: https://other.com, which no longer affects the result. Without modify, both table tests would check the every-handshake rejection at the Accept level; today only Test_authenticateOrigin checks same-origin and no-Origin requests.

accept.go:36

P4 [CRF-37] "The request host is always authorized." is false after 61a7faf: with an invalid entry, a same-origin request is rejected with 500. (Leorio)

The pattern check now runs before the strings.EqualFold(r.Host, u.Host) return, and invalidPatternSameOrigin asserts the error, so the doc says both "always authorized" and "reject every handshake". A developer who reads only the first lines expects same-origin clients to keep working with a bad entry. Change it to The request host is authorized without a matching pattern.

🤖

🤖 This review was automatically generated with Coder Agents.

Comment thread accept.go Outdated
}

func authenticateOrigin(r *http.Request, originHosts []string) error {
// Check every pattern first, so an invalid one rejects every request,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 [CRF-36] 61a7faf and 618a580 change origin validation for every server, with or without ErrorHandler, inside a PR that the squash merge will title feat: add AcceptOptions.ErrorHandler. (Zoro, Pariston)

A deployed server with one invalid OriginPatterns entry serves same-origin requests, requests without Origin (most non-browser clients), and origins matched by an earlier entry today; after upgrading, every handshake gets 500. Master is squash merged with one title per PR (ci: ... (#594), fix: ... (#552)), so this change is listed under a new optional field, and reverting it later also reverts ErrorHandler. ErrorHandler needs only 56eccc2; move 61a7faf, 618a580 and e6ff172 to a separate fix: PR, or state the outage-on-upgrade in the squash commit message and release notes.

🤖

Comment thread accept.go Outdated
// https://golang.org/pkg/path/#Match). By default, it is matched
// against the request origin host. If the pattern contains a URI
// scheme ("://"), it will be matched against "scheme://host".
// Unless InsecureSkipVerify is set, an invalid pattern makes Accept reject

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P4 [CRF-38] No test covers the "Unless InsecureSkipVerify is set" exception that e6ff172 adds to the exported doc. (Bisky)

The only test with InsecureSkipVerify: true (conn_test.go:454) sets no patterns, so a change that validates patterns once in cloneWithDefaults would reject these handshakes with 500 and every test would pass. Add a test that calls Accept with AcceptOptions{InsecureSkipVerify: true, OriginPatterns: []string{"["}}, a cross-origin newHandshakeRequest(), and a hijackable recorder, and asserts success.

🤖

Comment thread accept.go Outdated
}

func authenticateOrigin(r *http.Request, originHosts []string) error {
// Check every pattern first, so an invalid one rejects every request,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit [CRF-39] "Check every pattern first" restates the loop below it; the fact a reader cannot see is that the placement before the Origin checks is deliberate. (Gon)

Replace both lines with // Before the Origin checks, so an invalid pattern rejects every request.

🤖

The ErrorHandler doc started sentences with the argument names status
and err, and said Accept chose the status. Test comments said a body
hides an error, goroutines outlive a server and a test pins a response.
The doc explained the arguments the signature already shows and said
what Accept returns, which is Accept's behavior. It now says what the
field is for and what a handler can rely on.
@mafredri
mafredri force-pushed the mafredri/accept-error-handler branch from 1d52f8e to 1718326 Compare November 4, 2026 17:05
It repeated when ErrorHandler runs, which the field doc states, and told
callers not to write after an error, which follows from Accept writing
the response.
@mafredri mafredri changed the title feat: add AcceptOptions.ErrorHandler feat: add ErrorHandler to AcceptOptions Nov 4, 2026

This branch has not been deployed

No deployments
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.

Allow customize how HTTP errors are rendered when websocket.Accept fails to upgrade

1 participant