-
Notifications
You must be signed in to change notification settings - Fork 112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose request method of unary requests to clients and server handlers #502
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the general approach will work. I'm not thrilled by it, but when I tried to do this for the client I ran into the same thing. It's not great since obviously the earliest point you can know a request was going to be a GET request is after it's sent, and there's really no elegant way to implement this with how connect-go is currently architected. Therefore, I don't think there's much reasonable I can do other than give my general approval, albeit with the note that it is a bit begrudged :) At the very least, I think the API portion of this is good and what I think most people would expect, and that's the part we can't ever break, so that's the important bit.
I hope we can clean this up a bit later down the road by reorganizing the code to make it a bit less awkward, but there's no sense in trying to do it right now in my opinion. So yes. LGTM, with some caveats in the comments.
I didn't realize that So I guess the question -- potentially for @akshayjshah -- is whether this attribute should be added to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems obvious to me that users need a way to distinguish GET requests in order to implement conditional requests. They shouldn't just look at If-*
request headers.
The tests suggest:
It does not seem very intuitive. Method() string
looks more discoverable to me, but I am wondering whether a Cacheable() bool
might be a better fit since we are in RPC land, not HTTP land.
I think I'm in favor of As for From a user ergonomics standpoint, I definitely think calling Another less-valid reason for why not to use |
I'm not opposed to a more semantic method, such as I'd like to defer to @akshayjshah who can hopefully take a look at this early next week. |
This might become more urgent to figure out if we're going to want to resolve the TODO in connectrpc/connect-es#624 before merging, for similar functionality in connect-es. It'd be nice to have functional parity and unity on the API design front. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm on board with exposing this on Request
rather than Peer
. I'd prefer to directly expose the HTTP verb rather than something semantic like Cacheable
- let's stick as close to HTTP as we can.
In retrospect, I should probably have put the query params onto Request
too. We can add a passthrough Request.Query()
later on if there's demand - it doesn't seem likely to be used enough to double the API surface immediately.
@jhump When you add tests, can you please make sure to nail down how the new method behaves in interceptors? I'm particularly concerned that the current behavior will be a little misleading for client-side interceptors. Also make sure to update the example that Timo pointed out - it's part of the GoDoc.
connect.go
Outdated
// On a newly created request, via NewRequest, this will return "POST" and | ||
// only changes to return to "GET" after it is actually sent to a server | ||
// using GET as the method. | ||
func (r *Request[_]) Method() string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Do we expect clients to have much use for this in application code? I can imagine it being helpful in metrics and logging interceptors, but I'm having a hard time thinking beyond that. That's not a problem, I'm just wondering if I'm missing something.
- I'm unsure about the user-visible behavior from the client side - it feels misleading to return POST before we know what the method will be. Maybe we could return the empty string and then change to either POST or GET once we're sure? I think we could do that by explicitly setting the method to POST in
NewClient.unaryFunc
(just before the block you've already added) and inClient.CallServerStream
. Server-side, we'd need to set the method explicitly inNewServerStreamHandler
. Then we could remove the block below that's special-casing the zero value. - I avoided
Method
inconnect-go
because it's so ambiguous - are we talking about a method on a protobuf RPC service, or are we talking about HTTP methods? I choseProcedure
for the former. Maybe we could name thisHTTPMethod()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we expect clients to have much use for this in application code?
Like you mention, could be useful for metrics or logs. It's just to provide some observability of under-the-hood mechanics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to HTTPMethod()
and also updated the behavior and docs (and verified in tests) that it returns empty string until the method is actually known (after RPC is sent).
connect.go
Outdated
type clientConnWithRequestMethod interface { | ||
StreamingClientConn | ||
onSetMethod(fn func(string)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do these interfaces need to embed StreamingHandlerConn
and StreamingClientConn
...and do we need them at all? I think we could get away with anonymous foo.(interface { getMethod() string})
-style casts instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I redid this. This is now using an anonymous interface as suggested for the handler side. For the client-side, to implement the new semantics (always blank until after RPC is sent, and set after), we need to know when the RPC is sent for all client conn impls. So now they all implement it, and there's no need for a type assertion.
@akshayjshah, I think this is ready for another pass. I'm not sure what's up with CI. |
Yeah, don't worry about that, GitHub Actions is having trouble again today. I noticed the same. https://www.githubstatus.com/ |
@akshayjshah, ping, I was hoping for an LGTM from you before merging. |
Will look from the plane today! I'm in airport purgatory RN. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM - some small nits and suggestions, but no need for more review from me.
assert.Equal(t, int32(2), client3.Load()) | ||
assert.Equal(t, int32(2), handler1.Load()) | ||
assert.Equal(t, int32(2), handler2.Load()) | ||
assert.Equal(t, int32(2), handler3.Load()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 appreciate the improvement here!
Fixes #502 to expose the HTTP method in handlers.
[](https://renovatebot.com) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [github.com/bufbuild/connect-go](https://github.com/bufbuild/connect-go) | require | minor | `v1.7.0` -> `v1.8.0` | --- ### Release Notes <details> <summary>bufbuild/connect-go</summary> ### [`v1.8.0`](https://github.com/bufbuild/connect-go/releases/tag/v1.8.0) [Compare Source](https://github.com/bufbuild/connect-go/compare/v1.7.0...v1.8.0) #### What's Changed ##### Enhancements - Expose HTTP method of unary requests (see [documentation][http-method-docs]) by [@​jhump](https://github.com/jhump) and [@​akshayjshah](https://github.com/akshayjshah) in [https://github.com/bufbuild/connect-go/pull/502](https://github.com/bufbuild/connect-go/pull/502) and [https://github.com/bufbuild/connect-go/pull/509](https://github.com/bufbuild/connect-go/pull/509) - Implement `fmt.Stringer` interface for StreamType to improve debug logging by [@​svrana](https://github.com/svrana) in [https://github.com/bufbuild/connect-go/pull/495](https://github.com/bufbuild/connect-go/pull/495) ##### Bugfixes - Fix documentation comments to use correct identifier name by [@​cuishuang](https://github.com/cuishuang) in [https://github.com/bufbuild/connect-go/pull/498](https://github.com/bufbuild/connect-go/pull/498) - Fix Makefile quotes to handle paths with spaces by [@​bufdev](https://github.com/bufdev) in [https://github.com/bufbuild/connect-go/pull/508](https://github.com/bufbuild/connect-go/pull/508) - Always close response body in `CloseRead` by [@​pkwarren](https://github.com/pkwarren) in [https://github.com/bufbuild/connect-go/pull/515](https://github.com/bufbuild/connect-go/pull/515) #### New Contributors - [@​cuishuang](https://github.com/cuishuang) made their first contribution in [https://github.com/bufbuild/connect-go/pull/498](https://github.com/bufbuild/connect-go/pull/498) - [@​svrana](https://github.com/svrana) made their first contribution in [https://github.com/bufbuild/connect-go/pull/495](https://github.com/bufbuild/connect-go/pull/495) **Full Changelog**: bufbuild/connect-go@v1.7.0...v1.8.0 [http-method-docs]: https://connect.build/docs/go/get-requests-and-caching#distinguishing-get-requests </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/open-feature/flagd). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNS4xMDIuMTAiLCJ1cGRhdGVkSW5WZXIiOiIzNS4xMDIuMTAiLCJ0YXJnZXRCcmFuY2giOiJtYWluIn0=--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
In order for #494 (sentinel error to return "304 not modified" status) to be safe to use, the handler actually needs to know whether the incoming request used a POST vs GET. It's only safe to use that error for conditional GET requests.
I haven't added tests yet because I wanted to maybe get a little feedback first on whether these additions are acceptable. The primary concern is about adding another unexported method to
AnyRequest
. Also, this is very targeted to only unary RPCs (nothing in the streaming flows ever attempts to expose this since they should always be POST anyway). But I didn't know if we should try to push more of this down further into the abstractions (though that's a bit tricky since the abstractions are exported interfacesStreamingClientConn
andStreamingHandlerConn
...).Anyhow, if this idea seems work-able, then I'll finish this PR out with some tests.
In addition to making the request method available to server handlers, this also makes it available to clients -- when the unary RPC completes, the caller can inspect the request's method to see if it was sent via GET or POST.
WDYT?