-
Notifications
You must be signed in to change notification settings - Fork 2.5k
feat(http): add support for mapping headers to request fields #1005
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
base: master
Are you sure you want to change the base?
Conversation
rofrankel
left a comment
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.
+100 for the spec having a way to express this, thanks for drafting a proposal @toumorokoshi!
Added some comments on the details.
google/api/http.proto
Outdated
| // response header with the name of the key in the map will be populated | ||
| // with that value. | ||
| // | ||
| // NOTE: the referred field must be present at the top-level of the request |
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.
| // NOTE: the referred field must be present at the top-level of the request | |
| // NOTE: the referred field must be present at the top-level of the request |
(same two comments from above apply here)
google/api/http.proto
Outdated
| string response_body = 12; | ||
|
|
||
| // The name of fields whose values are mapped to HTTP headers in a response. | ||
| // If a field is in the response that matches a value in this map, an HTTP |
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.
Same comment as above, but inverted: what if multiple fields are mapped to the same header? (The use case for mapping the same field value to multiple headers also seems like a stretch, but at least the behavior is better defined...)
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.
This issue already exists today. For example, ?a=1&b=2&a=3&b=4
The current implementation, hence the semantics, is the last one wins.
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.
Query params are ordered, so "last one wins" is well-defined. But map entries are not ordered, so even if we wanted "last one wins" semantics, I'm not sure we could have them.
Inverting the maps seems to make this problem go away entirely. Do you see any downsides to doing so?
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.
Can you spell out the details and let's see if it can fully address ambiguity?
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 it's better to just state that the keys and values must all be unique here, allowing only 1-1 mappings.
Is there really a good reason for allowing many-to-one in either direction? I can't think of one that wouldn't just lead to more esoteric behavior (e.g. if there's two mappings, which one wins?)
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.
Updated to require all keys/values to be unique. thoughts?
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.
Can you spell out the details and let's see if it can fully address ambiguity?
Are the details in the corresponding comment on the request field enough? I didn't write it all out twice, but the details are pretty much the same.
I think it's better to just state that the keys and values must all be unique here, allowing only 1-1 mappings.
There's protocol-level enforcement of key uniqueness, but value uniqueness needs to be implemented separately (e.g. by a linter). Not the end of the world, of course, just noting that it makes compliance a bit more complicated (not everyone is necessarily going to use Google's API linter or the AEP linter).
Is there really a good reason for allowing many-to-one in either direction? I can't think of one that wouldn't just lead to more esoteric behavior (e.g. if there's two mappings, which one wins?)
If you allow a header to be mapped to multiple request fields, or a response field to be mapped to multiple headers, then doesn't the "if there's two mappings, which one wins?" question go away?
| // in the header. | ||
| // | ||
| // If a field is specified in this map, it will not be mapped to a query | ||
| // parameter. |
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.
The current implementation already allows a parameter be duplicated by body and HTTP query parameters. We cannot suddenly block a parameter from query parameter. That is a break changing.
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.
If an existing field was added to this map, then yes, I think that would be a breaking change. I imagined that, even once this mapping exists, then existing fields would not map to it.
I don't think the alternative is desirable: that would imply that any field not in the body would always be able to be provided as a query parameter, and then optionally a header parameter (by adding it to the map).
There would be no way to specify that a field is exclusively honored in the header.
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.
will not be => must not be
Note: This doesn't work for Google APIs. Every Google API request field can be specified by an HTTP request parameter, regardless whether it is mentioned by google.api.http. Please verify other implementations of gRPC transcoding. The current implementation is copy every value from request parameters into body blindly.
google/api/http.proto
Outdated
| string response_body = 12; | ||
|
|
||
| // The name of fields whose values are mapped to HTTP headers in a response. | ||
| // If a field is in the response that matches a value in this map, an HTTP |
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.
Can you spell out the details and let's see if it can fully address ambiguity?
This proposes a method to map HTTP headers to requests and responses in a protobuf request. This is useful for implementing IETF RFCs (such as 9110's if-match header) in HTTP+JSON apis that are exposed in gRPC-JSON transcoding.
Co-authored-by: Richard Frankel <[email protected]>
|
@jskeet @noahdietz hi! we were discussing offline and felt you might be the right people to loop into this discussion. We feel that a way to enable proto-HTTP header mappings is necessary for enabling gRPC APIs which can be transcoded to those that adhere to IETF RFCs (such as 9110's if-match). This PR itself probably will not got anywhere, but I'm wondering if you have suggestions on how we could move this conversation forward. |
|
Hi hi!
I've engaged with the folks that own this proto and the transcoding infra and will hopefully get someone from there involved.
Right, as you know, we'd need to update the internal proto and mirror it here.
Let's keep discussing here, and I will try to get others to weigh in. |
Awesome! thank you for the help. |
@toumorokoshi I'm trying to get a better understanding of where this will ultimately be used. Have you contacted Envoy or grpc-gateway, two of the major OSS projects that implement grpc-HTTP transcoding, yet? I'm not getting much traction myself and of course most teams are already committed to work for the foreseeable future... |
|
@noahdietz got it! Thanks for pushing. Our main use case is to adopt IETF RFCs, specifically things like If-Match. https://datatracker.ietf.org/doc/html/rfc7232#section-3.1. We plan to recommend it for aep.dev: aep-dev/aeps#289 We'll reach out to envoy and grpc-gateway too - thanks again! |
|
The level of effort required to support this change in the aformentioned codebases comes down to modifying these two repositories:
So it's not an intractable amount to get it working. Regarding not getting traction: @noahdietz is it possible to discuss this live with some group? I'd be great to see if there's some middle ground we can find here: both projects have said that having the annotation supported in google.api.http is a baseline requirement. |
| // message type. | ||
| string body = 7; | ||
|
|
||
| // A mapping from HTTP headers to request fields. If |
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.
A mapping from HTTP request headers to gRPC request fields. The map key corresponds to an HTTP header name (case-insensitive), and the map value corresponds to an gRPC request field path (using the same syntax as the path template).
|
|
||
| // A mapping from HTTP headers to request fields. If | ||
| // a header has a case-insensitive match to a key in this map, the field in | ||
| // the request that matches the value will have its value set to the value |
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.
that matches the value => the gRPC request field that is specified by the map value will ...
| // A mapping from HTTP headers to request fields. If | ||
| // a header has a case-insensitive match to a key in this map, the field in | ||
| // the request that matches the value will have its value set to the value | ||
| // in the header. |
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.
to the value in the header => to the value of the HTTP header.
We need to specify the precise encoding for string, bytes, number, etc.
| // | ||
| // All keys and values of this map **must** be unique. | ||
| // | ||
| // If a field is specified in this map, it will not be mapped to a query |
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.
a field => a request field
| // in the header. | ||
| // | ||
| // If a field is specified in this map, it will not be mapped to a query | ||
| // parameter. |
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.
will not be => must not be
Note: This doesn't work for Google APIs. Every Google API request field can be specified by an HTTP request parameter, regardless whether it is mentioned by google.api.http. Please verify other implementations of gRPC transcoding. The current implementation is copy every value from request parameters into body blindly.
| // If a field is specified in this map, it will not be mapped to a query | ||
| // parameter. | ||
| // | ||
| // The referred field is specified using the field path syntax in a path |
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 don't think this is necessary if we adopt the suggestion above.
| // message type. | ||
| string response_body = 12; | ||
|
|
||
| // A mapping from response fields to HTTP headers in the response. If a field |
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.
Similar comments as above.
|
What is the current status on this? |
|
I haven't had a lot of success getting traction, unfortunately. For my use case we've just forged ahead with third party header support and adding that wiring separately. For example via using context and metadata for GRPC: |
Hello! This is primarily a way to start a conversation, but I wanted to illustrate a somewhat clear proposal as well.
This proposes a method to map HTTP headers to requests and responses in a protobuf request.
This is useful for implementing IETF RFCs (such as 9110's if-match header) in HTTP+JSON apis that
are exposed in gRPC-JSON transcoding.
This was also brought up in a GitHub discussion: (see #749)