diff --git a/packages/core/README.md b/packages/core/README.md index f194557..37544aa 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -228,7 +228,9 @@ The `standard-server` header closes this gap. It carries the sender's `StandardB Adapters set the header automatically for the ambiguous body types: a `Blob` or `File` body is sent with `standard-server: file`, and a `ReadableStream` body with `standard-server: octet-stream`, alongside the usual content headers. A header you set yourself always wins, and assigning an empty array removes it entirely. -Other body types get their usual `content-type` (`application/json`, `application/x-www-form-urlencoded`, `text/event-stream`, or none when empty) unless you set one. If yours would be read as another body type, such as JSON sent as `application/problem+json`, adapters add the matching `standard-server` header; otherwise they clear it. `FormData` always gets its generated multipart `content-type`, which carries the boundary. +Other body types get their usual `content-type` (`application/json`, `application/x-www-form-urlencoded`, or `text/event-stream`) unless you set one. If yours would be read as another body type, such as JSON sent as `application/problem+json`, adapters add the matching `standard-server` header; otherwise they clear it. `FormData` always gets its generated multipart `content-type`, which carries the boundary. + +An empty body is always sent without `content-type`. To send an empty response with your own headers, such as for `HEAD`, use an already-closed `ReadableStream`. The header is optional: when it is absent or invalid, the receiver falls back to content-header inference, so plain HTTP clients work as-is. Just set the header yourself whenever the content type alone could be misread: diff --git a/packages/fetch/src/body.test.ts b/packages/fetch/src/body.test.ts index 33690ec..ad3e807 100644 --- a/packages/fetch/src/body.test.ts +++ b/packages/fetch/src/body.test.ts @@ -316,7 +316,12 @@ describe('toFetchBody', () => { } it('undefined', () => { - const [body, headers] = toFetchBody(undefined, baseHeaders, {}) + // a given content-type and hint are dropped + const [body, headers] = toFetchBody( + undefined, + { ...baseHeaders, 'content-type': 'text/html', 'standard-server': 'file' }, + {}, + ) expect(body).toBe(undefined) expect(headers).toEqual({ @@ -554,7 +559,6 @@ describe('toFetchBody', () => { ['url-search-params as text', new URLSearchParams('a=b'), 'text/plain', 'url-search-params'], ['event stream as event-stream', (async function* () {})(), 'text/event-stream', undefined], ['event stream as text', (async function* () {})(), 'text/plain', 'event-stream'], - ['undefined as html', undefined, 'text/html', 'none'], ])('keeps a given content-type and replaces a user hint: %s', (_, body, contentType, hint) => { const headers = { 'content-type': contentType, 'standard-server': 'file' } diff --git a/packages/fetch/src/body.ts b/packages/fetch/src/body.ts index c02868e..bf7fd6a 100644 --- a/packages/fetch/src/body.ts +++ b/packages/fetch/src/body.ts @@ -146,7 +146,7 @@ export function toFetchBody( headers['content-length'] = undefined if (body === undefined) { - headers['standard-server'] = getRequiredStandardBodyHint('none', headers) + headers['content-type'] = undefined return [undefined, headers] } diff --git a/packages/node/src/body.test.ts b/packages/node/src/body.test.ts index 694cbef..ae54d67 100644 --- a/packages/node/src/body.test.ts +++ b/packages/node/src/body.test.ts @@ -591,7 +591,12 @@ describe('toNodeHttpBody', () => { } it('undefined', async () => { - const [body, headers] = toNodeHttpBody(undefined, baseHeaders, {}) + // a given content-type and hint are dropped + const [body, headers] = toNodeHttpBody( + undefined, + { ...baseHeaders, 'content-type': 'text/html', 'standard-server': 'file' }, + {}, + ) expect(body).toBe(undefined) expect(headers).toEqual({ @@ -948,7 +953,6 @@ describe('toNodeHttpBody', () => { ['url-search-params as text', new URLSearchParams('a=b'), 'text/plain', 'url-search-params'], ['event stream as event-stream', (async function* () {})(), 'text/event-stream', undefined], ['event stream as text', (async function* () {})(), 'text/plain', 'event-stream'], - ['undefined as html', undefined, 'text/html', 'none'], ])('keeps a given content-type and replaces a user hint: %s', (_, body, contentType, hint) => { const headers = { 'content-type': contentType, 'standard-server': 'file' } diff --git a/packages/node/src/body.ts b/packages/node/src/body.ts index 638616c..04008e2 100644 --- a/packages/node/src/body.ts +++ b/packages/node/src/body.ts @@ -147,7 +147,7 @@ export function toNodeHttpBody( headers['content-length'] = undefined if (body === undefined) { - headers['standard-server'] = getRequiredStandardBodyHint('none', headers) + headers['content-type'] = undefined return [undefined, headers] } diff --git a/tests/data-transfer.test.ts b/tests/data-transfer.test.ts index 62e9742..db4e0dc 100644 --- a/tests/data-transfer.test.ts +++ b/tests/data-transfer.test.ts @@ -198,11 +198,6 @@ describe.each([ await expect(reader.read()).resolves.toEqual({ done: true }) }, }, - { - name: 'undefined as text/html', - createBody: () => undefined, - contentType: 'text/html', - }, { name: 'json-object as application/problem+json', createBody: () => ({ a: 1, b: [2, 3, { c: 4 }] }),