Skip to content
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

Behavior of response.rawHeaders does not match native functionality #1553

Closed
mastermatt opened this issue May 11, 2019 · 2 comments
Closed

Comments

@mastermatt
Copy link
Member

mastermatt commented May 11, 2019

This issue is being pulled out other work in progress. #1520 (comment)

The intention should be that nock-produced IncomingMessage instances mimic the native behavior of http.IncomingMessage.rawHeaders.

The raw request/response headers list exactly as they were received.
...
Header names are not lowercased, and duplicates are not merged.
...
For example, the previous message header object might have a rawHeaders list like the following:

[ 'ConTent-Length', '123456',
  'content-LENGTH', '123',
  'content-type', 'text/plain',
  'CONNECTION', 'keep-alive',
  'Host', 'mysite.com',
  'accepT', '*/*' ]

Some work has been done on this front #707, however, notes taken while working on #1520 identified other edge cases where raw headers are not left pristine.

Because of the nature of how the native implementation receives the raw data, it won't be possible to have identical functionality. The largest factor to this being that headers provided in an object will have an arbitrary order in the resulting rawHeaders array.

Nocks's reply headers can be provided in a couple ways:
Scope.defaultReplyHeaders
Interceptor.reply(status, body, headers)
Interceptor.reply(() => [status, body, headers]
Not including the Scope's replyContentLength and replyDate methods, that add calculated values to the Interceptor instance when reply is called.

These suggested guidelines include breaking behavior, but note that rawHeaders is not currently documented and this could be filed as a bugfix.

  • All three of the methods above should accept header arguments as either an array in the raw header style, as a "plain" object, or as a Map.
  • Headers provided to defaultReplyHeaders should always come first in the responses raw headers.
  • When headers are provided in the raw format, their order must remain intact on the resulting response.
  • The final result of the responses raw headers should have keys exactly as they were provided (should non-strings cast or throw?).
  • The final result of the responses raw headers should be a flat array of strings.
  • When functions are provided to generate values, each function should be called exactly once. Even if keys would normalize to duplicate headers.
  • The Response's headers, with normalized keys, should be generated from the raw header style in FIFO order. Duplicate headers will result in the last entry from the raw array.
@mastermatt
Copy link
Member Author

I'm intending on picking this up, as it's naturally a continuation of #1520, but I'm waiting on that PR to merge in since it includes a few partial changes in the right direction.

mastermatt added a commit to mastermatt/nock that referenced this issue May 24, 2019
mastermatt added a commit to mastermatt/nock that referenced this issue May 24, 2019
Updates the header handling in the `Interceptor` and `RequestOverrider` with
the intention of mimicking the native behavior of `http.IncomingMessage.rawHeaders`.
> The raw request/response headers list exactly as they were received.

There are three fundamental changes in this changeset:

1) Header Input Type

Previously, headers could be provided to:
- `Scope.defaultReplyHeaders` as a plain object
- `Interceptor.reply(status, body, headers)` as a plain object or an array of raw headers
- `Interceptor.reply(() => [status, body, headers]` as a plain object

Now, all three allow consistent inputs where the headers can be provided as a
plain object, an array of raw headers, or a `Map`.

2) Duplicate Headers Folding

This change deviates from the suggested guidelines laid out in nock#1553 because
those guidelines didn't properly handle duplicate headers, especially when
some are defined as defaults.

This change was modeled to duplicate [Node's implementation](https://github.com/nodejs/node/blob/908292cf1f551c614a733d858528ffb13fb3a524/lib/_http_incoming.js#L245)
([relevant docs](https://nodejs.org/api/http.html#http_message_headers)).
It specifically lays out how duplicate headers are handled depending on the field name.

In the case of default headers, they are not included on the `Response`
(not even in the raw headers) if the field name exists in the reply headers
(using a case-insensitive comparison).

3) Raw Headers are the Source of Truth

Previously, the `Interceptor` and `RequestOverrider` mostly keep track of
headers as a plain object and the array of raw headers was created by looping
that object. This was the cause for inconsistencies with the final result of
the raw headers. The problem with that approach is that converting raw headers
to an object is a lossy process, so going backwards makes it impossible to
guarantee the correct results.

This change reverses that logic and now the `Interceptor` and `RequestOverrider`
maintain the header data in raw arrays. All additions to headers are only added
to raw headers. The plain headers object is never mutated directly, and instead
is [re]created from the raw headers as needed.
mastermatt added a commit to mastermatt/nock that referenced this issue Jun 2, 2019
Updates the header handling in the `Interceptor` and `RequestOverrider` with
the intention of mimicking the native behavior of `http.IncomingMessage.rawHeaders`.
> The raw request/response headers list exactly as they were received.

There are three fundamental changes in this changeset:

1) Header Input Type

Previously, headers could be provided to:
- `Scope.defaultReplyHeaders` as a plain object
- `Interceptor.reply(status, body, headers)` as a plain object or an array of raw headers
- `Interceptor.reply(() => [status, body, headers]` as a plain object

Now, all three allow consistent inputs where the headers can be provided as a
plain object, an array of raw headers, or a `Map`.

2) Duplicate Headers Folding

This change deviates from the suggested guidelines laid out in nock#1553 because
those guidelines didn't properly handle duplicate headers, especially when
some are defined as defaults.

This change was modeled to duplicate [Node's implementation](https://github.com/nodejs/node/blob/908292cf1f551c614a733d858528ffb13fb3a524/lib/_http_incoming.js#L245)
([relevant docs](https://nodejs.org/api/http.html#http_message_headers)).
It specifically lays out how duplicate headers are handled depending on the field name.

In the case of default headers, they are not included on the `Response`
(not even in the raw headers) if the field name exists in the reply headers
(using a case-insensitive comparison).

3) Raw Headers are the Source of Truth

Previously, the `Interceptor` and `RequestOverrider` mostly keep track of
headers as a plain object and the array of raw headers was created by looping
that object. This was the cause for inconsistencies with the final result of
the raw headers. The problem with that approach is that converting raw headers
to an object is a lossy process, so going backwards makes it impossible to
guarantee the correct results.

This change reverses that logic and now the `Interceptor` and `RequestOverrider`
maintain the header data in raw arrays. All additions to headers are only added
to raw headers. The plain headers object is never mutated directly, and instead
is [re]created from the raw headers as needed.
mastermatt added a commit that referenced this issue Jun 8, 2019
…ity. (#1564)

* Response headers to more closely match Node.

Updates the header handling in the `Interceptor` and `RequestOverrider` with
the intention of mimicking the native behavior of `http.IncomingMessage.rawHeaders`.
> The raw request/response headers list exactly as they were received.

There are three fundamental changes in this changeset:

1) Header Input Type

Previously, headers could be provided to:
- `Scope.defaultReplyHeaders` as a plain object
- `Interceptor.reply(status, body, headers)` as a plain object or an array of raw headers
- `Interceptor.reply(() => [status, body, headers]` as a plain object

Now, all three allow consistent inputs where the headers can be provided as a
plain object, an array of raw headers, or a `Map`.

2) Duplicate Headers Folding

This change deviates from the suggested guidelines laid out in #1553 because
those guidelines didn't properly handle duplicate headers, especially when
some are defined as defaults.

This change was modeled to duplicate [Node's implementation](https://github.com/nodejs/node/blob/908292cf1f551c614a733d858528ffb13fb3a524/lib/_http_incoming.js#L245)
([relevant docs](https://nodejs.org/api/http.html#http_message_headers)).
It specifically lays out how duplicate headers are handled depending on the field name.

In the case of default headers, they are not included on the `Response`
(not even in the raw headers) if the field name exists in the reply headers
(using a case-insensitive comparison).

3) Raw Headers are the Source of Truth

Previously, the `Interceptor` and `RequestOverrider` mostly keep track of
headers as a plain object and the array of raw headers was created by looping
that object. This was the cause for inconsistencies with the final result of
the raw headers. The problem with that approach is that converting raw headers
to an object is a lossy process, so going backwards makes it impossible to
guarantee the correct results.

This change reverses that logic and now the `Interceptor` and `RequestOverrider`
maintain the header data in raw arrays. All additions to headers are only added
to raw headers. The plain headers object is never mutated directly, and instead
is [re]created from the raw headers as needed.
@mastermatt
Copy link
Member Author

Fixed with #1564

gr2m pushed a commit that referenced this issue Sep 4, 2019
…ity. (#1564)

* Response headers to more closely match Node.

Updates the header handling in the `Interceptor` and `RequestOverrider` with
the intention of mimicking the native behavior of `http.IncomingMessage.rawHeaders`.
> The raw request/response headers list exactly as they were received.

There are three fundamental changes in this changeset:

1) Header Input Type

Previously, headers could be provided to:
- `Scope.defaultReplyHeaders` as a plain object
- `Interceptor.reply(status, body, headers)` as a plain object or an array of raw headers
- `Interceptor.reply(() => [status, body, headers]` as a plain object

Now, all three allow consistent inputs where the headers can be provided as a
plain object, an array of raw headers, or a `Map`.

2) Duplicate Headers Folding

This change deviates from the suggested guidelines laid out in #1553 because
those guidelines didn't properly handle duplicate headers, especially when
some are defined as defaults.

This change was modeled to duplicate [Node's implementation](https://github.com/nodejs/node/blob/908292cf1f551c614a733d858528ffb13fb3a524/lib/_http_incoming.js#L245)
([relevant docs](https://nodejs.org/api/http.html#http_message_headers)).
It specifically lays out how duplicate headers are handled depending on the field name.

In the case of default headers, they are not included on the `Response`
(not even in the raw headers) if the field name exists in the reply headers
(using a case-insensitive comparison).

3) Raw Headers are the Source of Truth

Previously, the `Interceptor` and `RequestOverrider` mostly keep track of
headers as a plain object and the array of raw headers was created by looping
that object. This was the cause for inconsistencies with the final result of
the raw headers. The problem with that approach is that converting raw headers
to an object is a lossy process, so going backwards makes it impossible to
guarantee the correct results.

This change reverses that logic and now the `Interceptor` and `RequestOverrider`
maintain the header data in raw arrays. All additions to headers are only added
to raw headers. The plain headers object is never mutated directly, and instead
is [re]created from the raw headers as needed.
gr2m pushed a commit that referenced this issue Sep 4, 2019
…ity. (#1564)

* Response headers to more closely match Node.

Updates the header handling in the `Interceptor` and `RequestOverrider` with
the intention of mimicking the native behavior of `http.IncomingMessage.rawHeaders`.
> The raw request/response headers list exactly as they were received.

There are three fundamental changes in this changeset:

1) Header Input Type

Previously, headers could be provided to:
- `Scope.defaultReplyHeaders` as a plain object
- `Interceptor.reply(status, body, headers)` as a plain object or an array of raw headers
- `Interceptor.reply(() => [status, body, headers]` as a plain object

Now, all three allow consistent inputs where the headers can be provided as a
plain object, an array of raw headers, or a `Map`.

2) Duplicate Headers Folding

This change deviates from the suggested guidelines laid out in #1553 because
those guidelines didn't properly handle duplicate headers, especially when
some are defined as defaults.

This change was modeled to duplicate [Node's implementation](https://github.com/nodejs/node/blob/908292cf1f551c614a733d858528ffb13fb3a524/lib/_http_incoming.js#L245)
([relevant docs](https://nodejs.org/api/http.html#http_message_headers)).
It specifically lays out how duplicate headers are handled depending on the field name.

In the case of default headers, they are not included on the `Response`
(not even in the raw headers) if the field name exists in the reply headers
(using a case-insensitive comparison).

3) Raw Headers are the Source of Truth

Previously, the `Interceptor` and `RequestOverrider` mostly keep track of
headers as a plain object and the array of raw headers was created by looping
that object. This was the cause for inconsistencies with the final result of
the raw headers. The problem with that approach is that converting raw headers
to an object is a lossy process, so going backwards makes it impossible to
guarantee the correct results.

This change reverses that logic and now the `Interceptor` and `RequestOverrider`
maintain the header data in raw arrays. All additions to headers are only added
to raw headers. The plain headers object is never mutated directly, and instead
is [re]created from the raw headers as needed.
gr2m pushed a commit that referenced this issue Sep 5, 2019
…ity. (#1564)

* Response headers to more closely match Node.

Updates the header handling in the `Interceptor` and `RequestOverrider` with
the intention of mimicking the native behavior of `http.IncomingMessage.rawHeaders`.
> The raw request/response headers list exactly as they were received.

There are three fundamental changes in this changeset:

1) Header Input Type

Previously, headers could be provided to:
- `Scope.defaultReplyHeaders` as a plain object
- `Interceptor.reply(status, body, headers)` as a plain object or an array of raw headers
- `Interceptor.reply(() => [status, body, headers]` as a plain object

Now, all three allow consistent inputs where the headers can be provided as a
plain object, an array of raw headers, or a `Map`.

2) Duplicate Headers Folding

This change deviates from the suggested guidelines laid out in #1553 because
those guidelines didn't properly handle duplicate headers, especially when
some are defined as defaults.

This change was modeled to duplicate [Node's implementation](https://github.com/nodejs/node/blob/908292cf1f551c614a733d858528ffb13fb3a524/lib/_http_incoming.js#L245)
([relevant docs](https://nodejs.org/api/http.html#http_message_headers)).
It specifically lays out how duplicate headers are handled depending on the field name.

In the case of default headers, they are not included on the `Response`
(not even in the raw headers) if the field name exists in the reply headers
(using a case-insensitive comparison).

3) Raw Headers are the Source of Truth

Previously, the `Interceptor` and `RequestOverrider` mostly keep track of
headers as a plain object and the array of raw headers was created by looping
that object. This was the cause for inconsistencies with the final result of
the raw headers. The problem with that approach is that converting raw headers
to an object is a lossy process, so going backwards makes it impossible to
guarantee the correct results.

This change reverses that logic and now the `Interceptor` and `RequestOverrider`
maintain the header data in raw arrays. All additions to headers are only added
to raw headers. The plain headers object is never mutated directly, and instead
is [re]created from the raw headers as needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants