[net] internal/http3: add net/http.ResponseController support

6 views
Skip to first unread message

Nicholas Husin (Gerrit)

unread,
Jun 17, 2026, 6:22:28 PM (10 days ago) Jun 17
to goph...@pubsubhelper.golang.org, Damien Neil, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com
Attention needed from Damien Neil

New activity on the change

Open in Gerrit

Related details

Attention is currently required from:
  • Damien Neil
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: net
Gerrit-Branch: master
Gerrit-Change-Id: I0821d6eec4e534e01a33f1ea923ae59d6a6a6964
Gerrit-Change-Number: 792000
Gerrit-PatchSet: 2
Gerrit-Owner: Nicholas Husin <n...@golang.org>
Gerrit-Reviewer: Damien Neil <dn...@google.com>
Gerrit-Reviewer: Nicholas Husin <n...@golang.org>
Gerrit-Attention: Damien Neil <dn...@google.com>
Gerrit-Comment-Date: Wed, 17 Jun 2026 22:22:24 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: No
unsatisfied_requirement
satisfied_requirement
open
diffy

Nicholas Husin (Gerrit)

unread,
Jun 18, 2026, 11:09:11 AM (9 days ago) Jun 18
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
Attention needed from Damien Neil

Nicholas Husin uploaded new patchset

Nicholas Husin uploaded patch set #3 to this change.
Following approvals got outdated and were removed:
Open in Gerrit

Related details

Attention is currently required from:
  • Damien Neil
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newpatchset
Gerrit-Project: net
Gerrit-Branch: master
Gerrit-Change-Id: I0821d6eec4e534e01a33f1ea923ae59d6a6a6964
Gerrit-Change-Number: 792000
Gerrit-PatchSet: 3
unsatisfied_requirement
satisfied_requirement
open
diffy

Damien Neil (Gerrit)

unread,
Jun 22, 2026, 6:19:30 PM (5 days ago) Jun 22
to Nicholas Husin, goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com
Attention needed from Nicholas Husin

Damien Neil added 4 comments

File internal/http3/stream.go
Line 61, Patchset 3 (Latest): writeCtx, writeCancel := context.WithCancelCause(context.Background())
Damien Neil . unresolved

Need to cancel these when the stream is closed.

Line 97, Patchset 3 (Latest): return nil
Damien Neil . unresolved

This can be just `return context.Cause(ds.ctx)`, which returns nil if the context is not canceled.

Line 190, Patchset 3 (Latest): if err := st.readDeadline.err(); err != nil {
Damien Neil . unresolved

Add a comment mentioning that we're checking the deadline before potentially writing to a stream buffer? (In other places as well.)

It's not obvious why we can't just call Read or Write and let it fail when the deadline has passed.

But I wonder if this would all be simpler if we handled deadlines by half-closing the stream (CloseRead/CloseWrite) on deadline expiry. Then we don't need to check anything here other than checking after a read/write error to see if we need to map it into a deadline error.

(I believe half-closing the stream will still let fast-path reads/writes succeed, but that's fine since the fast-path never blocks and will revert to the slow path as soon as the buffer fills.)

Line 290, Patchset 3 (Latest): return 0, err
Damien Neil . unresolved

If err is io.EOF, this should still be errH3FrameError (truncated varint).

Open in Gerrit

Related details

Attention is currently required from:
  • Nicholas Husin
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: net
    Gerrit-Branch: master
    Gerrit-Change-Id: I0821d6eec4e534e01a33f1ea923ae59d6a6a6964
    Gerrit-Change-Number: 792000
    Gerrit-PatchSet: 3
    Gerrit-Owner: Nicholas Husin <n...@golang.org>
    Gerrit-Reviewer: Damien Neil <dn...@google.com>
    Gerrit-Reviewer: Nicholas Husin <n...@golang.org>
    Gerrit-Attention: Nicholas Husin <n...@golang.org>
    Gerrit-Comment-Date: Mon, 22 Jun 2026 22:19:24 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    satisfied_requirement
    open
    diffy

    Nicholas Husin (Gerrit)

    unread,
    Jun 22, 2026, 10:53:45 PM (4 days ago) Jun 22
    to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
    Attention needed from Nicholas Husin

    Nicholas Husin uploaded new patchset

    Nicholas Husin uploaded patch set #4 to this change.
    Following approvals got outdated and were removed:

    Related details

    Attention is currently required from:
    • Nicholas Husin
    Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: net
      Gerrit-Branch: master
      Gerrit-Change-Id: I0821d6eec4e534e01a33f1ea923ae59d6a6a6964
      Gerrit-Change-Number: 792000
      Gerrit-PatchSet: 4
      unsatisfied_requirement
      open
      diffy

      Nicholas Husin (Gerrit)

      unread,
      Jun 22, 2026, 11:05:53 PM (4 days ago) Jun 22
      to goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, Damien Neil, golang-co...@googlegroups.com
      Attention needed from Damien Neil

      Nicholas Husin added 4 comments

      File internal/http3/stream.go
      Line 61, Patchset 3: writeCtx, writeCancel := context.WithCancelCause(context.Background())
      Damien Neil . unresolved

      Need to cancel these when the stream is closed.

      Nicholas Husin

      Right, thanks! Done.

      (We still have some usage like `st.stream.stream.CloseWrite` which bypasses the context cancelation elsewhere in the codebase, but only in tests. I'll send it as a separate CL to keep the diff clearer here unless you prefer otherwise.)

      Line 97, Patchset 3: return nil
      Damien Neil . resolved

      This can be just `return context.Cause(ds.ctx)`, which returns nil if the context is not canceled.

      Nicholas Husin

      Done (for posterity, I still kept the `err` method rather than inlining it for clarity).

      Line 190, Patchset 3: if err := st.readDeadline.err(); err != nil {
      Damien Neil . resolved

      Add a comment mentioning that we're checking the deadline before potentially writing to a stream buffer? (In other places as well.)

      It's not obvious why we can't just call Read or Write and let it fail when the deadline has passed.

      But I wonder if this would all be simpler if we handled deadlines by half-closing the stream (CloseRead/CloseWrite) on deadline expiry. Then we don't need to check anything here other than checking after a read/write error to see if we need to map it into a deadline error.

      (I believe half-closing the stream will still let fast-path reads/writes succeed, but that's fine since the fast-path never blocks and will revert to the slow path as soon as the buffer fills.)

      Nicholas Husin

      Added comments for now. I think immediate failures regardless of buffer is probably a less confusing behavior to have.

      Also, I might be missing something, but I think half-closing the stream makes it so the client thinks that a server's response is successful rather than cancelled, since the server will send a `FIN` rather than a `RESET_STREAM` to the client. We can make it so that when the timer expires, we send a `RESET_STREAM`, but that also introduces complications like making the client discard buffered data that was received prior to the deadline exceeding I think.

      Line 290, Patchset 3: return 0, err
      Damien Neil . resolved

      If err is io.EOF, this should still be errH3FrameError (truncated varint).

      Nicholas Husin

      Thanks, done!

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Damien Neil
      Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: net
        Gerrit-Branch: master
        Gerrit-Change-Id: I0821d6eec4e534e01a33f1ea923ae59d6a6a6964
        Gerrit-Change-Number: 792000
        Gerrit-PatchSet: 4
        Gerrit-Owner: Nicholas Husin <n...@golang.org>
        Gerrit-Reviewer: Damien Neil <dn...@google.com>
        Gerrit-Reviewer: Nicholas Husin <n...@golang.org>
        Gerrit-Attention: Damien Neil <dn...@google.com>
        Gerrit-Comment-Date: Tue, 23 Jun 2026 03:05:49 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Damien Neil <dn...@google.com>
        unsatisfied_requirement
        satisfied_requirement
        open
        diffy

        Damien Neil (Gerrit)

        unread,
        Jun 26, 2026, 5:12:33 PM (14 hours ago) Jun 26
        to Nicholas Husin, goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com
        Attention needed from Nicholas Husin

        Damien Neil voted and added 2 comments

        Votes added by Damien Neil

        Code-Review+2

        2 comments

        File internal/http3/stream.go
        Line 61, Patchset 3: writeCtx, writeCancel := context.WithCancelCause(context.Background())
        Damien Neil . resolved

        Need to cancel these when the stream is closed.

        Nicholas Husin

        Right, thanks! Done.

        (We still have some usage like `st.stream.stream.CloseWrite` which bypasses the context cancelation elsewhere in the codebase, but only in tests. I'll send it as a separate CL to keep the diff clearer here unless you prefer otherwise.)

        Damien Neil

        Acknowledged

        Line 190, Patchset 3: if err := st.readDeadline.err(); err != nil {
        Damien Neil . unresolved

        Add a comment mentioning that we're checking the deadline before potentially writing to a stream buffer? (In other places as well.)

        It's not obvious why we can't just call Read or Write and let it fail when the deadline has passed.

        But I wonder if this would all be simpler if we handled deadlines by half-closing the stream (CloseRead/CloseWrite) on deadline expiry. Then we don't need to check anything here other than checking after a read/write error to see if we need to map it into a deadline error.

        (I believe half-closing the stream will still let fast-path reads/writes succeed, but that's fine since the fast-path never blocks and will revert to the slow path as soon as the buffer fills.)

        Nicholas Husin

        Added comments for now. I think immediate failures regardless of buffer is probably a less confusing behavior to have.

        Also, I might be missing something, but I think half-closing the stream makes it so the client thinks that a server's response is successful rather than cancelled, since the server will send a `FIN` rather than a `RESET_STREAM` to the client. We can make it so that when the timer expires, we send a `RESET_STREAM`, but that also introduces complications like making the client discard buffered data that was received prior to the deadline exceeding I think.

        Damien Neil

        Current approach seems okay if you prefer it, but I think this would be simpler without the contexts.

        I think the simpler approach would be two time.Timers, one each for read and write deadline. Timer is created lazily at the time of the first Set{Read,Write}Deadline call.

        Read timer calls quic.Stream.CloseRead, which interrupts any reads in progress.

        Write timer calls quic.Stream.Reset(H3_REQUEST_CANCELLED) (https://www.rfc-editor.org/info/rfc9114/#section-8.1-2.26.1), which interrupts any writes in progress and sends a RESET_STREAM to the peer.

        When a body read or write encounters an error, it checks for an expired timeout and returns a timeout error if appropriate. (We assume that any error encountered was caused by the timeout.)

        Cleanup stops the timers, if they were created.

        I think this almost entirely keeps the timeout behavior local to Set{Read,Write}Deadline, except for the need to check for an expired timeout to return the right error.

        If we really feel that an expired timeout should cause an error on an operation which can be handled by the fast-path buffers, we can make the quic.Stream fast-path return an error when the stream is (half-)closed; the fast-path buffers are guarded by mutexes now, so CloseRead/CloseWrite can easily set some field guarded by the mutex to indicate closure.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Nicholas Husin
        Submit Requirements:
        • requirement satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: net
        Gerrit-Branch: master
        Gerrit-Change-Id: I0821d6eec4e534e01a33f1ea923ae59d6a6a6964
        Gerrit-Change-Number: 792000
        Gerrit-PatchSet: 4
        Gerrit-Owner: Nicholas Husin <n...@golang.org>
        Gerrit-Reviewer: Damien Neil <dn...@google.com>
        Gerrit-Reviewer: Nicholas Husin <n...@golang.org>
        Gerrit-Attention: Nicholas Husin <n...@golang.org>
        Gerrit-Comment-Date: Fri, 26 Jun 2026 21:12:29 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: Yes
        Comment-In-Reply-To: Damien Neil <dn...@google.com>
        Comment-In-Reply-To: Nicholas Husin <n...@golang.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy
        Reply all
        Reply to author
        Forward
        0 new messages