-
Notifications
You must be signed in to change notification settings - Fork 66
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
Add RFC 006: Format Negotiation Protocol #115
base: master
Are you sure you want to change the base?
Changes from 2 commits
b8ced49
9bc2218
82df676
f4bffe6
cd241b0
e338def
2724321
84c3e14
d37ec2d
580a84d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# RFC: Conjure Format Negotiation | ||
|
||
2018-10-04 | ||
|
||
Today, Conjure supports one wire format: PLAIN for primitive data types in headers, path parameters, and query | ||
parameters, and JSON for complex objects (and primitives) in HTTP bodies. In the future, we may want to introduce new | ||
versions of this wire format, or entirely new wire formats like CBOR. This RFC defines a negotiation strategy by which | ||
clients and servers agree on the wire format used for requests and responses. | ||
|
||
We have the following requirements for the format negotiation protocol: | ||
- The protocol should in almost all cases not incur additional round-trips | ||
- Clients and servers will eventually agree on the protocol that is (1) most preferred by the client and (2) | ||
supported by the server. That, clients drive the negotiation, constrained by server capabilities. | ||
|
||
### Definitions | ||
|
||
- A *version* is a non-zero integer | ||
- A *Conjure format identifier* is a string of the `application/<format>; v=<version>` where `<format>` is a | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just wondering if we could maybe use "conjure_version" or "conjure_protocol" instead of just "v"? Or is this a standardized version parameter (I didn't read the standard). It could be more discoverable that way if someone bumps into this in the wild. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. meant to change this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you push? I don't see the changes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. now I did |
||
non-empty string over `[a-z]` (e.g., `json`) and `<version>` is a version string (as above) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should include hyphen in the format specification. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks! |
||
- A *Conjure format list* is a comma-separated, ordered list of Conjure format identifiers | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't think the version is related to quality? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As using it to describe the priority order of the conjure format identifiers instead of assuming that this list is ordered, which the spec doesn't explicitly call out? But according to this, order of the list doesn't seem to matter if no
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think I've seen the quality parameter used in practice, most implementations I've worked with use order. If the quality of types matches we would use order to break ties. Given that, I don't see any benefit that the quality parameter provides. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it technically harder to support the quality factor? If not, then regardless of other people's use of it I would argue it's probably beneficial to stick to the advertised standard, even if not just for discoverability (e.g. 'search-engine of choice'ing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it means we'd eat an extra parse and sort pass for each request. probably doesn't matter much. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You'd just cache results; doesn't matter at all :) |
||
- The `Accept` and `Content-Type` HTTP headers are defined as per the | ||
[HTTP 1.1 spec](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html). Note that `Accept: <format list>` is a | ||
valid HTTP 1.1 Accept header, and that `Content-Type: <format identifier>` is a valid HTTP 1.1 Content-Type header. | ||
|
||
### Wire format versioning | ||
|
||
We propose that every revision of the Conjure wire format be labelled with a format identifier. | ||
The current PLAIN+JSON format shall be labelled `application/json; v=1`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What exactly is this versioning? JSON bit is covered by application/json, so really this is kinda conjure DSL (IR?) format version, not "PLAIN+JSON" format. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if PLAIN should be considered part of the format here. @jkozlowski I think it's fine to say it's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should version the PLAIN and the structured format together. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we do want that. It just seemed weird to requisition There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yep There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should also probably just put down that "application/json" is also fine for backcompat. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added. |
||
|
||
### Format capabilities | ||
|
||
We assume that clients and servers implement both serialization and deserialization for a given format: | ||
|
||
- A server can consume requests with a given format iff it can produce responses with the same format. | ||
- A client can produce requests with a given format iff it can consume responses with the same format. | ||
|
||
### Negotiation protocol | ||
|
||
**Requests.** | ||
Clients send the format identifier used to encode the request as a Content-Type header, and a format list as an Accept | ||
header. The format indicates the preference-ordered list of formats that the client supports. The supported formats | ||
must include the format use to encode the request, i.e., the format specified in the Content-Type request header. | ||
|
||
**Responses.** | ||
Servers that do not support the request format respond with Conjure error UNSUPPORTED/415. Otherwise, if the server | ||
does supported the request format, it uses the most-preferred (as per Accept request header) format to encode the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: "does support the" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
response and advertise the chosen format in the response Content-Type header. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this means I can send "application/json" in my first request, but actually put CBOR as my preference, so I'll get CBOR request? Neat, and then I can followup all my other ones with CBOR There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... you'd get a CBOR response, yes. |
||
|
||
Every response (including non-success responses) must send a preference-ordered format list of supported formats as | ||
Accept response header. (Note that this is a non-standard header for HTTP 1.1 responses.) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It may be worth switching from non-standard use of the There is also some question around including the servers There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 for We could leave this part off in favor of using the response There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The draft seems abandoned (GitHub repo dead), or at least it never made it into an RFC. So I think 'Accept' and 'Accept-Post' are equally 'non-standard'. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think letting the server choose the format based on the preference list sent by the client should be roughly equivalent. Thoughts on that simpler protocol, @markelliot ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fine with me; I think the server returning what it supports is a nice-to-have. If we find a lot of problems in this space. There's an interesting thing here around what content type should be used for errors, I'd propose all errors are sent as JSON format, and thus we can spec the details of server support into the error body as Conjure error params, if we want. |
||
|
||
**Negotiation.** | ||
Format negotiation takes place in the context of a "session" of subsequent requests made by a client to a server (or | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when do we consider a client-server negotiation is done? and when do client and server need to renegotiate the content format? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will clarify |
||
service). A client may assume, loosely, that a session is active to a given server or service as long as it receives | ||
responses with non-error responses. | ||
|
||
At the beginning of a session, clients have no knowledge of the list of formats supported by the server. Clients may | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this negotiation done per IP? So if I have many nodes of my service, each of them could potentially be talking different wire formats? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about the case of a blue/green deploy behind a proxy? Does Conjure version become part of rolling-upgrade state (as in, the server cannot advertise the new conjure protocol until all nodes have upgraded)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need to explicitly handle this complexity. Clients receiving a 415 error can choose to retry the request with a more compatible format. |
||
update the list of formats supported by a given server or service as per the formats advertised in the Accept headers of | ||
responses in the session. Typically, a client will pick its most preferred format that is advertised by the server | ||
in the previous response of the session. To bootstrap the negotiation, clients shall assume that servers support the | ||
most recent variant of the `application/json` format known to the client, and use this version for the first request of | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess that works, but maybe it's not so bad to always send application/json; v=1 on the first request than to fail the request because you jumped the gun on upgrading your client before everyone else. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think both are OK. |
||
every session. | ||
|
||
**Example.** The following sequence of two requests and corresponding responses are between a client that supports | ||
CBOR v=2, CBOR v=1, and JSON v=1, and that prefers formats in that order, and a server that supports CBOR v=1 and | ||
JSON v=1. To bootstrap the session, the client encodes the first request with the latest known JSON format, v=1. The | ||
servers encodes the response with the format most preferred by the client that it also supports itself, CBOR v=1. The | ||
second request is encoded with the format most preferred by the client that the server supports, CBOR v=1. | ||
|
||
|
||
```text | ||
Client ---------> Server | ||
Content-Type: application/json; v=1 | ||
Accept: application/cbor; v=2, application/cbor; v=1, application/json; v=1 | ||
|
||
Client <--------- Server | ||
Content-Type: application/cbor; v=1 | ||
Accept: application/cbor; v=1, application/json; v=1 | ||
|
||
Client ---------> Server | ||
Content-Type: application/cbor; v=1 | ||
Accept: application/cbor; v=2, application/cbor; v=1, application/json; v=1 | ||
|
||
Client <--------- Server | ||
Content-Type: application/cbor; v=1 | ||
Accept: application/cbor; v=1, application/json; v=1 | ||
``` | ||
|
||
In the rare case that server does not support the bootstrap format, the error response will carry a list of supported | ||
formats (see above) from which the client can choose when retrying the request. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we think errors will indeed be rare then we could always fallback to use of an additional There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yep, that's also an option |
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.
Grammatically weird?
That, clients drive the negotiation, constrained [...]