Skip to content

Commit b974cb7

Browse files
authored
fix(ts-bindings): populate response headers in fetch() (#4691)
# Description of Changes `fetch()` in TypeScript bindings was deserializing the full `HttpResponse` from BSATN (including headers), but then discarding them and always returning `new Headers()`. This adds a `deserializeHeaders()` helper that converts BSATN-deserialized `HttpHeaders` into a web-standard `Headers` object, so response headers are now correctly populated on the returned `SyncResponse`. # API and ABI breaking changes None. Purely additive — no existing code depended on the (always-empty) headers. # Expected complexity level and risk 1 — Single-line behavioral change plus a small helper function. The fix mirrors the existing request-side encoding pattern. # Testing - [x] Unit test: headers survive BSATN serialize/deserialize round-trip - [x] Unit test: empty headers round-trip correctly - [x] Verified `deserializeHeaders` uses the same `textDecoder` (UTF-8) as existing body decoding
1 parent 7d537d3 commit b974cb7

2 files changed

Lines changed: 70 additions & 1 deletion

File tree

crates/bindings-typescript/src/server/http_internal.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ export interface ResponseInit {
2727
const textEncoder = new TextEncoder();
2828
const textDecoder = new TextDecoder('utf-8' /* { fatal: true } */);
2929

30+
function deserializeHeaders(headers: HttpHeaders): Headers {
31+
return new Headers(
32+
headers.entries.map(({ name, value }): [string, string] => [
33+
name,
34+
textDecoder.decode(value),
35+
])
36+
);
37+
}
38+
3039
const makeResponse = Symbol('makeResponse');
3140

3241
// based on deno's type of the same name
@@ -187,7 +196,7 @@ function fetch(url: URL | string, init: RequestOptions = {}) {
187196
url: uri,
188197
status: response.code,
189198
statusText: status(response.code),
190-
headers: new Headers(),
199+
headers: deserializeHeaders(response.headers),
191200
aborted: false,
192201
});
193202
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { describe, expect, test } from 'vitest';
2+
import { BinaryReader, BinaryWriter } from '../src';
3+
import { HttpResponse } from '../src/lib/http_types';
4+
5+
describe('HttpResponse header round-trip', () => {
6+
test('headers survive BSATN serialize/deserialize', () => {
7+
const textEncoder = new TextEncoder();
8+
const textDecoder = new TextDecoder('utf-8');
9+
10+
const original: HttpResponse = {
11+
headers: {
12+
entries: [
13+
{
14+
name: 'content-type',
15+
value: textEncoder.encode('text/event-stream'),
16+
},
17+
{ name: 'x-request-id', value: textEncoder.encode('abc-123') },
18+
],
19+
},
20+
version: { tag: 'Http11' },
21+
code: 200,
22+
};
23+
24+
const writer = new BinaryWriter(256);
25+
HttpResponse.serialize(writer, original);
26+
const buf = writer.getBuffer();
27+
28+
const deserialized = HttpResponse.deserialize(new BinaryReader(buf));
29+
30+
expect(deserialized.code).toBe(200);
31+
expect(deserialized.headers.entries).toHaveLength(2);
32+
33+
expect(deserialized.headers.entries[0].name).toBe('content-type');
34+
expect(textDecoder.decode(deserialized.headers.entries[0].value)).toBe(
35+
'text/event-stream'
36+
);
37+
38+
expect(deserialized.headers.entries[1].name).toBe('x-request-id');
39+
expect(textDecoder.decode(deserialized.headers.entries[1].value)).toBe(
40+
'abc-123'
41+
);
42+
});
43+
44+
test('empty headers round-trip correctly', () => {
45+
const original: HttpResponse = {
46+
headers: { entries: [] },
47+
version: { tag: 'Http11' },
48+
code: 404,
49+
};
50+
51+
const writer = new BinaryWriter(64);
52+
HttpResponse.serialize(writer, original);
53+
const deserialized = HttpResponse.deserialize(
54+
new BinaryReader(writer.getBuffer())
55+
);
56+
57+
expect(deserialized.code).toBe(404);
58+
expect(deserialized.headers.entries).toHaveLength(0);
59+
});
60+
});

0 commit comments

Comments
 (0)