-
Notifications
You must be signed in to change notification settings - Fork 10
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
Editorial suggestions #32
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,19 +35,17 @@ informative: | |
|
||
--- abstract | ||
|
||
The HTTP Idempotency-Key request header field can be used to carry idempotency key in order to make non-idempotent HTTP methods such as `POST` or `PATCH` fault-tolerant. | ||
The HTTP Idempotency-Key request header field can be used to make non-idempotent HTTP methods such as `POST` or `PATCH` fault-tolerant. | ||
|
||
--- middle | ||
|
||
# Introduction | ||
|
||
Idempotence is the property of certain operations in mathematics and computer science whereby they can be applied multiple times without changing the result beyond the initial application. It does not matter if the operation is called only once, or 10s of times over. | ||
In mathematics and computer science, an idempotent operation is one that can be applied multiple times without changing the result beyond the initial application. It does not matter if the operation is called only once or tens of times over. | ||
|
||
Idempotency is important in building a fault-tolerant HTTP API. An HTTP request method is considered `idempotent` if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request. According to {{!RFC7231}}, HTTP methods `OPTIONS`, `HEAD`, `GET`, `PUT` and `DELETE` are idempotent while methods `POST` and `PATCH` are not. | ||
Idempotency is important in building fault-tolerant HTTP APIs. An HTTP request method is considered `idempotent` if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request. Per {{!RFC9110}}, the methods `OPTIONS`, `HEAD`, `GET`, `PUT` and `DELETE` are idempotent while methods `POST` and `PATCH` are not. | ||
|
||
Let's say a client of an HTTP API wants to create (or update) a resource using a `POST` method. Since `POST` is NOT an idempotent method, calling it multiple times can result in duplication or wrong updates. Consider a scenario where the client sent a `POST` request to the server, but it got a timeout. Following questions arise : Is the resource actually created (or updated)? Did the timeout occur during sending of the request, or when receiving of the response? Can the client safely retry the request, or does it need to figure out what happened in the first place? If `POST` had been an idempotent method, such questions may not arise. Client would safely retry a request until it actually gets a valid response from the server. | ||
|
||
For many use cases of HTTP APIs, duplicated resources are a severe problem from a business perspective. For example, duplicate records for requests involving any kind of money transfer `MUST NOT` be allowed. In other cases, processing of duplicate webhook delivery is not expected. | ||
Let's say a client of an HTTP API wants to create (or update) a resource using a `POST` method. Repeating the request multiple times can result in duplication or incorrect updates. Consider a scenario where the client sent a `POST` request to the server, but the request timed out. The client does not know if the resource was created (or updated), because it does not know when the timeout occured from the server's perspective. Furthermore, the client does not know if it can safely retry the request. | ||
|
||
|
||
## Notational Conventions | ||
|
@@ -56,43 +54,34 @@ For many use cases of HTTP APIs, duplicated resources are a severe problem from | |
|
||
This specification uses the Augmented Backus-Naur Form (ABNF) notation of {{!RFC5234}} and includes, by reference, the IMF-fixdate rule as defined in Section 7.1.1.1 of {{!RFC7231}}. | ||
|
||
The term "resource" is to be interpreted as defined in Section 2 of {{!RFC7231}}, that is identified by an URI. The term "resource server" is to be interpreted as "origin server" as defined in Section 3 of {{!RFC7231}}. | ||
The term "resource" is to be interpreted as defined in Section 2 of {{!RFC7231}}, that is identified by an URI. | ||
|
||
# The Idempotency-Key HTTP Request Header Field | ||
|
||
An idempotency key is a unique value generated by the client which the resource server uses to recognize subsequent retries of the same request. The `Idempotency-Key` HTTP request header field carries this key. | ||
|
||
|
||
An idempotency key is a unique value generated by the client which the resource uses to recognize subsequent retries of the same request. The `Idempotency-Key` HTTP request header field carries this value. | ||
|
||
## Syntax | ||
|
||
`Idempotency-Key` is an Item Structured Header {{!RFC8941}}. Its value MUST be a String (Section 3.3.3 of {{!RFC8941}}). | ||
|
||
`Idempotency-Key` is an Item Structured Header {{!RFC8941}}. Its value MUST be a String. Refer to Section 3.3.3 of {{!RFC8941}} for ABNF of `sf-string`: | ||
|
||
Idempotency-Key = sf-string | ||
## Uniqueness of Idempotency Key | ||
|
||
The idempotency key MUST be unique and MUST NOT be reused with another request with a different request payload. | ||
|
||
Clients MUST NOT include more than one `Idempotency-Key` header field in the same request. | ||
Uniqueness of the key MUST be defined by the resource owner and MUST be implemented by the clients of the resource. It is RECOMMENDED that a UUID {{!RFC4122}} or a similar random identifier be used as an idempotency key. | ||
|
||
The following example shows an idempotency key using `UUID` {{!RFC4122}}: | ||
The following example shows an idempotency key whose value is a UUID {{!RFC4122}}: | ||
|
||
Idempotency-Key: "8e03978e-40d5-43e8-bc93-6894a57f9324" | ||
|
||
|
||
## Uniqueness of Idempotency Key | ||
|
||
The idempotency key that is supplied as part of every `POST` request MUST be unique and MUST NOT be reused with another request with a different request payload. | ||
|
||
Uniqueness of the key MUST be defined by the resource owner and MUST be implemented by the clients of the resource server. It is RECOMMENDED that `UUID` {{!RFC4122}} or a similar random identifier be used as an idempotency key. | ||
|
||
## Idempotency Key Validity and Expiry | ||
|
||
The resource MAY enforce time based idempotency keys, thus, be able to purge or delete a key upon its expiry. The resource server SHOULD define such expiration policy and publish it in the documentation. | ||
|
||
The resource MAY require time based idempotency keys to be able to purge or delete a key upon its expiry. The resource SHOULD define such expiration policy and publish it in the documentation. | ||
|
||
## Idempotency Fingerprint | ||
|
||
An idempotency fingerprint MAY be used in conjunction with an idempotency key to determine the uniqueness of a request. Such a fingerprint is generated from request payload data by the resource server. An idempotency fingerprint generation algorithm MAY use one of the following or similar approaches to create a fingerprint. | ||
An idempotency fingerprint MAY be used in conjunction with an idempotency key to determine the uniqueness of a request. Such a fingerprint is generated from request payload data by the resource. An idempotency fingerprint generation algorithm MAY use one of the following or similar approaches to create a fingerprint. | ||
|
||
* Checksum of the entire request payload. | ||
* Checksum of selected element(s) in the request payload. | ||
|
@@ -103,48 +92,49 @@ An idempotency fingerprint MAY be used in conjunction with an idempotency key to | |
|
||
## Responsibilities | ||
|
||
Client | ||
### Client | ||
|
||
Clients of HTTP API requiring idempotency, SHOULD understand the idempotency related requirements as published by the server and use appropriate algorithm to generate idempotency keys. | ||
Clients of HTTP API requiring idempotency SHOULD understand the idempotency related requirements as published by the server and use appropriate algorithm to generate idempotency keys. | ||
|
||
Clients MAY choose to send an Idempotency-Key field with any valid random sf-string to indicate the user's intent is to only perform this action once. Without a priori knowledge, a general client cannot assume the server will respect this request. | ||
Clients MAY choose to send an Idempotency-Key field with any valid value to indicate the user's intent is to only perform this action once. Without a priori knowledge, a general client cannot assume the server will respect this request. | ||
|
||
For each request, client SHOULD | ||
For each request, a client SHOULD: | ||
|
||
* Send a unique idempotency key in the HTTP `Idempotency-Key` request header field. | ||
|
||
Resource Server | ||
### Resource | ||
|
||
Resource server MUST publish idempotency related specification. This specification MUST include expiration related policy if applicable. Server is responsible for managing the lifecycle of the idempotency key. | ||
Resources MUST publish a idempotency related specification. This specification MUST include expiration related policy if applicable. A resource is responsible for managing the lifecycle of the idempotency key. | ||
|
||
For each request, server SHOULD | ||
For each request, a server SHOULD: | ||
|
||
* Identify idempotency key from the HTTP `Idempotency-Key` request header field. | ||
* Generate idempotency fingerprint if required. | ||
* Check for idempotency considering various scenarios including the ones described in section below. | ||
* Enforce idempotency (see below) | ||
|
||
|
||
## Idempotency Enforcement Scenarios | ||
## Idempotency Enforcement | ||
|
||
* First time request (idempotency key and fingerprint has not been seen) | ||
|
||
The resource server SHOULD process the request normally and respond with an appropriate response and status code. | ||
The resource SHOULD process the request normally and respond with an appropriate response and status code. | ||
|
||
* Duplicate request (idempotency key and fingerprint has been seen) | ||
|
||
Retry | ||
|
||
The request was retried after the original request completed. The resource server SHOULD respond with the result of the previously completed operation, success or an error. See Error Scenarios for details on errors. | ||
The request was retried after the original request completed. The resource SHOULD respond with the result of the previously completed operation, success or an error. See Error Scenarios for details on errors. | ||
|
||
Concurrent Request | ||
|
||
The request was retried before the original request completed. The resource server SHOULD respond with a resource conflict error. See Error Scenarios for details. | ||
The request was retried before the original request completed. The resource SHOULD respond with a resource conflict error. See Error Scenarios for details. | ||
|
||
|
||
## Error Scenarios | ||
## Error Handling | ||
|
||
If the `Idempotency-Key` request header is missing for a documented idempotent operation requiring this header, the resource server SHOULD reply with an HTTP `400` status code with body containing a link pointing to relevant documentation. Following examples shows an error response describing the problem using {{!RFC7807}}. | ||
If the `Idempotency-Key` request header is missing for a documented idempotent operation requiring this header, the resource SHOULD reply with an HTTP `400` status code with body containing a link pointing to relevant documentation. Following examples shows an error response describing the problem using {{!RFC7807}}. | ||
|
||
~~~ http-message | ||
HTTP/1.1 400 Bad Request | ||
Content-Type: application/problem+json | ||
Content-Language: en | ||
|
@@ -154,16 +144,19 @@ If the `Idempotency-Key` request header is missing for a documented idempotent o | |
"detail": "This operation is idempotent and it requires correct | ||
usage of Idempotency Key.", | ||
} | ||
~~~ | ||
|
||
Alternately, using the HTTP header `Link`, the client can be informed about the error as shown below. | ||
|
||
~~~ http-message | ||
HTTP/1.1 400 Bad Request | ||
Link: <https://developer.example.com/idempotency>; | ||
rel="describedby"; type="text/html" | ||
~~~ | ||
|
||
If there is an attempt to reuse an idempotency key with a different request payload, the resource server SHOULD reply with a HTTP `422` status code with body containing a link pointing to relevant documentation. The status code `422` is defined in Section 11.2 of {{!RFC4918}}. | ||
|
||
If there is an attempt to reuse an idempotency key with a different request payload, the resource SHOULD reply with a HTTP `422` status code with body containing a link pointing to relevant documentation. The status code `422` is defined in Section 11.2 of {{!RFC4918}}. | ||
|
||
~~~ http-message | ||
HTTP/1.1 422 Unprocessable Content | ||
Content-Type: application/problem+json | ||
Content-Language: en | ||
|
@@ -174,17 +167,19 @@ If there is an attempt to reuse an idempotency key with a different request payl | |
correct usage of Idempotency Key. Idempotency Key MUST not be | ||
reused across different payloads of this operation.", | ||
} | ||
|
||
~~~ | ||
|
||
The server can also inform the client by using the HTTP header `Link` as shown below. | ||
|
||
~~~ http-message | ||
HTTP/1.1 422 Unprocessable Content | ||
Link: <https://developer.example.com/idempotency>; | ||
rel="describedby"; type="text/html" | ||
~~~ | ||
|
||
If the request is retried, while the original request is still being processed, the resource server SHOULD reply with an HTTP `409` status code with body containing problem description. | ||
|
||
If the request is retried, while the original request is still being processed, the resource SHOULD reply with an HTTP `409` status code with body containing problem description. | ||
|
||
~~~ http-message | ||
HTTP/1.1 409 Conflict | ||
Content-Type: application/problem+json | ||
Content-Language: en | ||
|
@@ -194,16 +189,19 @@ If the request is retried, while the original request is still being processed, | |
"detail": "A request with the same Idempotency-Key for the | ||
same operation is being processed or is outstanding.", | ||
} | ||
~~~ | ||
|
||
Or, alternately using the HTTP header `Link` pointing to the relevant documentation | ||
|
||
~~~ http-message | ||
HTTP/1.1 409 Conflict | ||
Link: <https://developer.example.com/idempotency>; | ||
rel="describedby"; type="text/html" | ||
~~~ | ||
|
||
Error scenarios above describe the status of failed idempotent requests after the resource server prcocesses them. Clients MUST correct the requests (with the exception of 409 where no correction is required) before performing a retry operation, or the the resource server MUST fail the request and return one of the above errors. | ||
Error scenarios above describe the status of failed idempotent requests after the resource prcocesses them. Clients MUST correct the requests (with the exception of 409 where no correction is required) before performing a retry operation, or the the resource MUST fail the request and return one of the above errors. | ||
|
||
For other 4xx/5xx errors, such as 401, 403, 500, 502, 503, 504, 429, or any other HTTP error code that is not listed here, the client SHOULD act appropriately by following the resource server's documentation. | ||
For other 4xx/5xx errors, such as 401, 403, 500, 502, 503, 504, 429, or any other HTTP error code that is not listed here, the client SHOULD act appropriately by following the resource's documentation. | ||
|
||
|
||
# IANA Considerations | ||
|
@@ -335,16 +333,16 @@ Organization: WebEngage | |
This section is meant to inform developers, information providers, | ||
and users of known security concerns specific to the idempotency keys. | ||
|
||
Resource servers that do not implement strong idempotency keys, such as UUIDs, or have appropriate controls to validate the idempotency keys, could be victim to various forms of security attacks from malicious clients: | ||
Resources that do not implement strong idempotency keys, such as UUIDs, or have appropriate controls to validate the idempotency keys, could be victim to various forms of security attacks from malicious clients: | ||
|
||
* Injection attacks-When the resource server does not validate the idempotency key in the client request and performs a idempotent cache lookup, there can be security attacks (primarily in the form of injection), compromising the server. | ||
* Injection attacks - When the resources does not validate the idempotency key in the client request and performs a idempotent cache lookup, there can be security attacks (primarily in the form of injection), compromising the server. | ||
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 the resource |
||
* Data leaks-When an idempotency implementation allows low entropy keys, attackers MAY determine other keys and use them to fetch existing idempotent cache entries, belonging to other clients. | ||
|
||
To prevent such situations, the specification recommends the following best practices for idempotency key implementation in the resource server. | ||
To prevent such situations, the specification recommends the following best practices for idempotency key implementation in the resource. | ||
|
||
* Establish a fixed format for the idempotency key and publish the key’s specification. | ||
* Always validate the key as per its published specification before processing any request. | ||
* On the resource server, implement a unique composite key as the idempotent cache lookup key. For example, a composite key MAY be implemented by combining the idempotency key sent by the client with other client specific attributes known only to the resource server. | ||
* On the resource, implement a unique composite key as the idempotent cache lookup key. For example, a composite key MAY be implemented by combining the idempotency key sent by the client with other client specific attributes known only to the resource. | ||
|
||
|
||
# Examples | ||
|
@@ -353,20 +351,13 @@ The first example shows an idempotency-key header field with key value using UUI | |
|
||
Idempotency-Key: "8e03978e-40d5-43e8-bc93-6894a57f9324" | ||
|
||
Second example shows an idempotency-key header field with key value using some random string generator: | ||
Second example shows an idempotency-key header field with key value using a random string generator: | ||
|
||
Idempotency-Key: "clkyoesmbgybucifusbbtdsbohtyuuwz" | ||
|
||
|
||
--- back | ||
|
||
# Imported ABNF | ||
|
||
The following core rules are included by reference, as defined in Appendix B.1 of {{!RFC5234}}: ALPHA (letters), CR (carriage return), CRLF (CR LF), CTL (controls), DIGIT (decimal 0-9), DQUOTE (double quote), HEXDIG (hexadecimal 0-9/A-F/a-f), LF (line feed), OCTET (any 8-bit sequence of data), SP (space), and VCHAR (any visible US-ASCII character). | ||
|
||
The rules below are defined in {{!RFC7230}}: | ||
|
||
obs-text = <obs-text, see [RFC7230], Section 3.2.6> | ||
|
||
# Acknowledgments | ||
{:numbered="false"} | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 was old. Now it should refer RFC9457 :)