Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
8 changes: 6 additions & 2 deletions packages/fetch/src/body.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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' }

Expand Down
2 changes: 1 addition & 1 deletion packages/fetch/src/body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}

Expand Down
8 changes: 6 additions & 2 deletions packages/node/src/body.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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' }

Expand Down
2 changes: 1 addition & 1 deletion packages/node/src/body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}

Expand Down
5 changes: 0 additions & 5 deletions tests/data-transfer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }] }),
Expand Down
Loading