From 7c98128aeb04de79caf03ac47de4ce4aaf62ef85 Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Mon, 23 Jun 2025 13:46:33 -0700 Subject: [PATCH 1/7] Provide guidance for Set-Cookie The Set-Cookie response header breaks the normal rules for headers with multiple values and requires special handling. --- src/oas.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/oas.md b/src/oas.md index 985304f993..ec8149786e 100644 --- a/src/oas.md +++ b/src/oas.md @@ -2524,6 +2524,72 @@ components: $ref: '#/components/mediaTypes/CollectionLinks' ``` +##### Representing the `Set-Cookie` Header + +As noted in [[!RFC9110]] [Section 5.3](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.3) the `Set-Cookie` response header violates the requirements for representing multiple values as a comma-separated list, as `style: "simple"` produces. + +```http +Set-Cookie: lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT +Set-Cookie: foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT +``` + +If these values were to be place on a single line using `style: "simple"`, the result would be `lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT,foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT`, which when split would then produce four values: `lang=en-US; Expires=Wed`, `09 Jun 2021 10:18:14 GMT`, `foo=bar; Expires=Wed`, and `09 Jun 2021 10:18:14 GMT`. +While the two dates (`09...`) are not valid `Set-Cookie` values on their own, [[?RFC6265]] does not provide any guarantee that all such embedded uses of commas will produce detectable errors when split in this way. + +RFC9110 therefore advises recipients to 'handle "Set-Cookie" as a special case while processing fields,' so the OAS similarly special-cases its handling of `Set-Cookie` as follows: + +For the `Set-Cookie` response header _**only**_, `style: "simple"` MUST be treated as producing a newline-delimited list instead of a comma-separated list, with each line corresponding to the value of a single `Set-Cookie:` header field. +This newline-delimited format MUST be used whenever a string representing the values is required, including in the [Example Object's](#example-object) serialized example fields, and when using `content` with a `text/plain` [Media Type Object](#media-type-object) as is necessary to prevent percent-encoding whitespace. + +The following example shows two different ways to describe `Set-Cookie` headers that require cookies named `"lang"` and `"foo"`. The first uses `content` to preserve the necessary whitespace in the `"Expires"` cookie attribute, while the second shows the use of `style: "simple"` and forbids whitespace to ensure that values work with this serialization approach: + +```yaml +components: + headers: + SetCookieWithExpires: + # Spaces within the Expires values prevent the use of `schema` and + # `style` as they would be percent-encoded, even with `allowReserved`. + content: + text/plain: + schema: + type: string + allOf: + - pattern: "^lang=[^;];.*Expires=" + - pattern: "^foo=[^;];.*Expires=" + examples: + WithExpires: + # This demonstrates that the text is required to be provided + # in the final format, and is not changed by serialization. + # In practice, it is not necessary to show both fields here. + dataValue: | + lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT + foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT + serializedValue: | + lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT + foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT + SetCookieWithNoSpaces: + schema: + type: object + required: + - lang + - foo + additionalProperties: + type: string + pattern: "^[^[:space:]]$" + style: simple + explode: true + allowReserved: true # "=", ";", and " " are reserved + examples: + SetCookies: + dataValue: { + "lang": "en-US", + "foo": "bar" + } + serializedValue: | + lang=en-US + foo=bar +``` + ##### Header Object Example A simple header of type `integer`: From d2d0badc9ba9002be3f70890336197a805e9000f Mon Sep 17 00:00:00 2001 From: Henry Andrews Date: Mon, 30 Jun 2025 10:14:03 -0700 Subject: [PATCH 2/7] Fix typo Co-authored-by: Phil Sturgeon <67381+philsturgeon@users.noreply.github.com> --- src/oas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oas.md b/src/oas.md index ec8149786e..3bf14fdf8b 100644 --- a/src/oas.md +++ b/src/oas.md @@ -2533,7 +2533,7 @@ Set-Cookie: lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT Set-Cookie: foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT ``` -If these values were to be place on a single line using `style: "simple"`, the result would be `lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT,foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT`, which when split would then produce four values: `lang=en-US; Expires=Wed`, `09 Jun 2021 10:18:14 GMT`, `foo=bar; Expires=Wed`, and `09 Jun 2021 10:18:14 GMT`. +If these values were to be placed on a single line using `style: "simple"`, the result would be `lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT,foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT`, which when split would then produce four values: `lang=en-US; Expires=Wed`, `09 Jun 2021 10:18:14 GMT`, `foo=bar; Expires=Wed`, and `09 Jun 2021 10:18:14 GMT`. While the two dates (`09...`) are not valid `Set-Cookie` values on their own, [[?RFC6265]] does not provide any guarantee that all such embedded uses of commas will produce detectable errors when split in this way. RFC9110 therefore advises recipients to 'handle "Set-Cookie" as a special case while processing fields,' so the OAS similarly special-cases its handling of `Set-Cookie` as follows: From 1323840093d84d871b6ac5b6aa3510b2f1f77f89 Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Fri, 4 Jul 2025 09:52:17 -0700 Subject: [PATCH 3/7] Explain when Set-Cookie workaround is needed Also remove a stray line from an example that didn't really hurt but wasn't needed and could have been confusing. --- src/oas.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/oas.md b/src/oas.md index 3bf14fdf8b..9711bda9d9 100644 --- a/src/oas.md +++ b/src/oas.md @@ -2536,7 +2536,11 @@ Set-Cookie: foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT If these values were to be placed on a single line using `style: "simple"`, the result would be `lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT,foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT`, which when split would then produce four values: `lang=en-US; Expires=Wed`, `09 Jun 2021 10:18:14 GMT`, `foo=bar; Expires=Wed`, and `09 Jun 2021 10:18:14 GMT`. While the two dates (`09...`) are not valid `Set-Cookie` values on their own, [[?RFC6265]] does not provide any guarantee that all such embedded uses of commas will produce detectable errors when split in this way. -RFC9110 therefore advises recipients to 'handle "Set-Cookie" as a special case while processing fields,' so the OAS similarly special-cases its handling of `Set-Cookie` as follows: +RFC9110 therefore advises recipients to 'handle "Set-Cookie" as a special case while processing fields,' so the OAS similarly special-cases its handling of `Set-Cookie`. + +When an OAS implementation is mapping directly between the multi-`Set-Cookie:` header line format and an array representation, without any intermediate single string holding the multiple values, no special handling is needed as the behavior is the same as for headers that can be either on a single line with comma-separated values or on multiple lines. + +However, if a multi-value text representation is needed, such as for a `text/plain` representation (using the `content` field) or in an Example Object, the following special handling is required: For the `Set-Cookie` response header _**only**_, `style: "simple"` MUST be treated as producing a newline-delimited list instead of a comma-separated list, with each line corresponding to the value of a single `Set-Cookie:` header field. This newline-delimited format MUST be used whenever a string representing the values is required, including in the [Example Object's](#example-object) serialized example fields, and when using `content` with a `text/plain` [Media Type Object](#media-type-object) as is necessary to prevent percent-encoding whitespace. @@ -2578,7 +2582,6 @@ components: pattern: "^[^[:space:]]$" style: simple explode: true - allowReserved: true # "=", ";", and " " are reserved examples: SetCookies: dataValue: { From 8d618d140e32b67f41366c3ab012ccc9c08ba87a Mon Sep 17 00:00:00 2001 From: Henry Andrews Date: Tue, 8 Jul 2025 14:33:45 -0700 Subject: [PATCH 4/7] Better wording and grammar Co-authored-by: Lorna Jane Mitchell --- src/oas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oas.md b/src/oas.md index 9711bda9d9..75ca83972c 100644 --- a/src/oas.md +++ b/src/oas.md @@ -2536,7 +2536,7 @@ Set-Cookie: foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT If these values were to be placed on a single line using `style: "simple"`, the result would be `lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT,foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT`, which when split would then produce four values: `lang=en-US; Expires=Wed`, `09 Jun 2021 10:18:14 GMT`, `foo=bar; Expires=Wed`, and `09 Jun 2021 10:18:14 GMT`. While the two dates (`09...`) are not valid `Set-Cookie` values on their own, [[?RFC6265]] does not provide any guarantee that all such embedded uses of commas will produce detectable errors when split in this way. -RFC9110 therefore advises recipients to 'handle "Set-Cookie" as a special case while processing fields,' so the OAS similarly special-cases its handling of `Set-Cookie`. +RFC9110 therefore advises recipients to 'handle "Set-Cookie" as a special case while processing fields,' so the OAS similarly applies a special case to its handling of `Set-Cookie`. When an OAS implementation is mapping directly between the multi-`Set-Cookie:` header line format and an array representation, without any intermediate single string holding the multiple values, no special handling is needed as the behavior is the same as for headers that can be either on a single line with comma-separated values or on multiple lines. From 5a76ba6d94ea6660d48236bb2c4e950d268305f4 Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Fri, 4 Jul 2025 09:52:17 -0700 Subject: [PATCH 5/7] Revamp with more examples and less explanation Explain when Set-Cookie workaround is needed Also remove a stray line from an example that didn't really hurt but wasn't needed and could have been confusing. --- src/oas.md | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/oas.md b/src/oas.md index 75ca83972c..10646a8a6b 100644 --- a/src/oas.md +++ b/src/oas.md @@ -2526,24 +2526,27 @@ components: ##### Representing the `Set-Cookie` Header -As noted in [[!RFC9110]] [Section 5.3](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.3) the `Set-Cookie` response header violates the requirements for representing multiple values as a comma-separated list, as `style: "simple"` produces. +The `Set-Cookie` header is noted in [[!RFC9110]] [Section 5.3](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.3) as an exception to the normal rules of headers with multiple values. + +For most headers using the general syntax defined in RFC9110, the multiple-line and comma-separaed single-line forms are interchangeable, meaning that this: ```http -Set-Cookie: lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT -Set-Cookie: foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT +Accept-Encoding: compress;q=0.5 +Accept-Encoding: gzip;q=1.0 ``` -If these values were to be placed on a single line using `style: "simple"`, the result would be `lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT,foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT`, which when split would then produce four values: `lang=en-US; Expires=Wed`, `09 Jun 2021 10:18:14 GMT`, `foo=bar; Expires=Wed`, and `09 Jun 2021 10:18:14 GMT`. -While the two dates (`09...`) are not valid `Set-Cookie` values on their own, [[?RFC6265]] does not provide any guarantee that all such embedded uses of commas will produce detectable errors when split in this way. +is interchangeable with the one-line form that works well with the OAS's `style: "simple"` option: -RFC9110 therefore advises recipients to 'handle "Set-Cookie" as a special case while processing fields,' so the OAS similarly applies a special case to its handling of `Set-Cookie`. +```http +Accept-Encoding: compress;q=0.5,gzip;q=1.0 +``` -When an OAS implementation is mapping directly between the multi-`Set-Cookie:` header line format and an array representation, without any intermediate single string holding the multiple values, no special handling is needed as the behavior is the same as for headers that can be either on a single line with comma-separated values or on multiple lines. +The OAS models such multi-value headers using the one-line form as it matches the behavior of `style: "simple"`, and works well when using `content` as the values are completely separate from the header name, but it does not matter which form is used in an actual HTTP message. -However, if a multi-value text representation is needed, such as for a `text/plain` representation (using the `content` field) or in an Example Object, the following special handling is required: +As also noted in the RFC, `Set-Cookie` is an exception as it allows unquoted, non-escaped commas in its values, and can only use the one-value-per-line form. +For HTTP messages, this is purely a serialization concern, and no more of a problem than a message that uses the multi-line form of any other header. -For the `Set-Cookie` response header _**only**_, `style: "simple"` MUST be treated as producing a newline-delimited list instead of a comma-separated list, with each line corresponding to the value of a single `Set-Cookie:` header field. -This newline-delimited format MUST be used whenever a string representing the values is required, including in the [Example Object's](#example-object) serialized example fields, and when using `content` with a `text/plain` [Media Type Object](#media-type-object) as is necessary to prevent percent-encoding whitespace. +However, because examples and values modeled with `content` do not incorporate the header name, for these fields `Set-Cookie` MUST be handled by placing each value on a separate line, without the header name or the `:` delimiter. The following example shows two different ways to describe `Set-Cookie` headers that require cookies named `"lang"` and `"foo"`. The first uses `content` to preserve the necessary whitespace in the `"Expires"` cookie attribute, while the second shows the use of `style: "simple"` and forbids whitespace to ensure that values work with this serialization approach: @@ -2564,7 +2567,7 @@ components: WithExpires: # This demonstrates that the text is required to be provided # in the final format, and is not changed by serialization. - # In practice, it is not necessary to show both fields here. + # In practice, it is not necessary to show both value fields. dataValue: | lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT @@ -2593,6 +2596,20 @@ components: foo=bar ``` +In an HTTP message, the serialized example with Expires would look like: + +```http +Set-Cookie: lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GM +Set-Cookie: foo=bar; Expires=Wed, 09 Jun 2021 10:18:14 GMT +``` + +and the example without Expires would look like: + +```http +Set-Cookie: lang=en-US +Set-Cookie: foo=bar +``` + ##### Header Object Example A simple header of type `integer`: From 3f57f4957befbaa43f1400975aa7589d6f9d0fe0 Mon Sep 17 00:00:00 2001 From: Henry Andrews Date: Wed, 23 Jul 2025 18:55:45 -0700 Subject: [PATCH 6/7] Fix spelling Co-authored-by: Ralf Handl --- src/oas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oas.md b/src/oas.md index 10646a8a6b..f0f92d7758 100644 --- a/src/oas.md +++ b/src/oas.md @@ -2528,7 +2528,7 @@ components: The `Set-Cookie` header is noted in [[!RFC9110]] [Section 5.3](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.3) as an exception to the normal rules of headers with multiple values. -For most headers using the general syntax defined in RFC9110, the multiple-line and comma-separaed single-line forms are interchangeable, meaning that this: +For most headers using the general syntax defined in RFC9110, the multiple-line and comma-separated single-line forms are interchangeable, meaning that this: ```http Accept-Encoding: compress;q=0.5 From cb8baa3d7a1c1ea62f12ffbc08529f3b3034d415 Mon Sep 17 00:00:00 2001 From: "Henry H. Andrews" Date: Wed, 23 Jul 2025 19:02:30 -0700 Subject: [PATCH 7/7] Fix examples (review feedback) --- src/oas.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/oas.md b/src/oas.md index f0f92d7758..f3f21d5586 100644 --- a/src/oas.md +++ b/src/oas.md @@ -2559,10 +2559,9 @@ components: content: text/plain: schema: + # Due to lack of support for multiline regular expressions + # in the `pattern` keyword, not much validation can be done. type: string - allOf: - - pattern: "^lang=[^;];.*Expires=" - - pattern: "^foo=[^;];.*Expires=" examples: WithExpires: # This demonstrates that the text is required to be provided @@ -2582,7 +2581,7 @@ components: - foo additionalProperties: type: string - pattern: "^[^[:space:]]$" + pattern: "^[^[:space:]]*$" style: simple explode: true examples: