Skip to content

rpcclient: add context.Context support for HTTP POST mode#2506

Open
seeforschauer wants to merge 2 commits intobtcsuite:masterfrom
seeforschauer:rpcclient-context-support
Open

rpcclient: add context.Context support for HTTP POST mode#2506
seeforschauer wants to merge 2 commits intobtcsuite:masterfrom
seeforschauer:rpcclient-context-support

Conversation

@seeforschauer
Copy link
Copy Markdown

Change Description

Add context.Context support to rpcclient's HTTP POST mode, allowing callers to cancel or timeout individual RPC calls. Fixes #2499.

Currently, rpcclient methods like GetBlockCount() don't accept a context, which means callers have no way to cancel or set deadlines on individual RPC calls. In HTTP POST mode, handleSendPostMessage uses http.NewRequest without context, and the 10-retry loop doesn't respect external cancellation.

Changes

  • Add ctx field to jsonRequest struct
  • Use http.NewRequestWithContext in handleSendPostMessage when ctx is set
  • Respect context cancellation during retry backoff (select on reqCtx.Done())
  • Add SendCmdWithContext(ctx, cmd) to Client
  • Add GetBlockCountWithContext(ctx) as the first context-aware method variant
  • Existing methods are unchanged — fully backwards compatible

Design decisions

  • SendCmdWithContext mirrors SendCmd with an added ctx parameter
  • When ctx is nil (e.g., from existing SendCmd callers), context.Background() is used as fallback
  • Context cancellation during retry backoff returns immediately instead of waiting for next attempt
  • Additional *WithContext methods for other RPC calls can be added incrementally in follow-up PRs

Steps to Test

go test ./rpcclient/ -run TestGetBlockCountWithContext -v
go test ./rpcclient/ -run TestSendCmdWithContext -v
go test ./rpcclient/ -run TestGetBlockCount_Backwards -v

Pull Request Checklist

Testing

  • Your PR passes all CI checks.
  • Tests covering the positive and negative (error paths) are included.
  • Bug fixes contain tests triggering the bug to prevent regressions.

Code Style and Documentation

  • The change is not insubstantial.
  • The change obeys the Code Documentation and Commenting guidelines, and lines wrap at 80.
  • Commits follow the Ideal Git Commit Structure.
  • Any new logging statements use an appropriate subsystem and logging level.

Add SendCmdWithContext and GetBlockCountWithContext methods that
propagate a context.Context to the underlying HTTP request. This
allows callers to cancel or timeout individual RPC calls.

Changes:
- Add ctx field to jsonRequest struct
- Use http.NewRequestWithContext in handleSendPostMessage
- Respect context cancellation during retry backoff
- Add SendCmdWithContext to Client
- Add GetBlockCountWithContext as the first context-aware method
- Existing methods are unchanged (non-breaking)

Fixes btcsuite#2499
return c.GetBlockCountAsync().Receive()
}

// GetBlockCountWithContext is the context-aware variant of
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Alternatively, we could update all the methods to accept a context param, but via a functional option. This would break any interfaces created off the client, be direct callers would still compile.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That would be a larger diff tho, so perhaps we'll land this one extension for now, then return to this later.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Like this direction a lot — CallOption would avoid the WithContext method explosion across 100+ methods and is extensible for future options. Happy to do it as a follow-up PR once this one lands.


// TestGetBlockCountWithContext_Success verifies that the context-
// aware GetBlockCountWithContext returns the correct block height.
func TestGetBlockCountWithContext_Success(t *testing.T) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We could camel case everywhere, including tests.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — all test names use camelCase now.

// The context is used to cancel the underlying HTTP request in
// HTTP POST mode. In websocket mode the context is currently
// ignored. The returned channel delivers the response.
func (c *Client) SendCmdWithContext(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This duplicates SendCmd, let's refactor such that SendCmdWithContext can just call `SendCmd, but pass in the context from the user.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — SendCmd now delegates to SendCmdWithContext(context.Background(), cmd). Single implementation.


case <-c.shutdown:
return

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Above when we call c.httpClient.Do(httpReq), we should check the context error, as perhaps we should bail right there instead of retrying again.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — added reqCtx.Err() check right after httpClient.Do. If context is cancelled, we bail out immediately instead of entering the backoff/retry path.

Refactor SendCmd to delegate to SendCmdWithContext instead of
duplicating the marshalling logic. SendCmdWithContext is now the
single implementation; SendCmd passes context.Background().

Add a context error check after httpClient.Do in the retry loop
so a cancelled context bails out immediately instead of retrying.

Use camelCase for test function names.
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.

rpcclient: add context.Context support to RPC methods

2 participants