Skip to content

Commit ecf5633

Browse files
committed
Add support for typed arrays as request body
Fixes #2417
1 parent c367c29 commit ecf5633

File tree

5 files changed

+63
-6
lines changed

5 files changed

+63
-6
lines changed

documentation/2-options.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,11 @@ stream.on('data', console.log);
293293

294294
### `body`
295295

296-
**Type: `string | Buffer | stream.Readable | Generator | AsyncGenerator | Iterable | AsyncIterable | FormData` or [`form-data` instance](https://github.com/form-data/form-data)**
296+
**Type: `string | Buffer | TypedArray | stream.Readable | Generator | AsyncGenerator | Iterable | AsyncIterable | FormData` or [`form-data` instance](https://github.com/form-data/form-data)**
297297

298298
The payload to send.
299299

300-
For `string` and `Buffer` types, the `content-length` header is automatically set if the `content-length` and `transfer-encoding` headers are missing.
300+
For `string`, `Buffer`, and `TypedArray` types (Uint8Array, Uint16Array, etc.), the `content-length` header is automatically set if the `content-length` and `transfer-encoding` headers are missing.
301301

302302
**The `content-length` header is not automatically set when `body` is an instance of [`fs.createReadStream()`](https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options).**
303303

@@ -331,6 +331,21 @@ console.log(data);
331331
//=> 'Hello, world!'
332332
```
333333

334+
You can also use typed arrays (Uint8Array, Uint16Array, etc.) as request body:
335+
336+
```js
337+
import got from 'got';
338+
339+
const uint8Body = new Uint8Array([104, 101, 108, 108, 111]); // 'hello' in ASCII
340+
341+
const {data} = await got.post('https://httpbin.org/anything', {
342+
body: uint8Body
343+
}).json();
344+
345+
console.log(data);
346+
//=> 'hello'
347+
```
348+
334349
You can use `Iterable` and `AsyncIterable` objects as request body, including Web [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream):
335350

336351
```js

source/core/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,17 @@ export default class Request extends Duplex implements RequestEvents<Request> {
10281028

10291029
if (is.nodeStream(body)) {
10301030
body.pipe(currentRequest);
1031+
} else if (is.buffer(body)) {
1032+
// Buffer should be sent directly without conversion
1033+
this._writeRequest(body, undefined, () => {});
1034+
currentRequest.end();
1035+
} else if (is.typedArray(body)) {
1036+
// Typed arrays should be treated like buffers, not iterated over
1037+
// Create a Uint8Array view over the data (Node.js streams accept Uint8Array)
1038+
const typedArray = body as ArrayBufferView;
1039+
const uint8View = new Uint8Array(typedArray.buffer, typedArray.byteOffset, typedArray.byteLength);
1040+
this._writeRequest(uint8View, undefined, () => {});
1041+
currentRequest.end();
10311042
} else if (is.asyncIterable(body) || (is.iterable(body) && !is.string(body) && !isBuffer(body))) {
10321043
(async () => {
10331044
try {

source/core/options.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,7 +1410,7 @@ export default class Options {
14101410
14111411
__Note #4__: This option is not enumerable and will not be merged with the instance defaults.
14121412
1413-
The `content-length` header will be automatically set if `body` is a `string` / `Buffer` / [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) / [`form-data` instance](https://github.com/form-data/form-data), and `content-length` and `transfer-encoding` are not manually set in `options.headers`.
1413+
The `content-length` header will be automatically set if `body` is a `string` / `Buffer` / typed array ([`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array), etc.) / [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) / [`form-data` instance](https://github.com/form-data/form-data), and `content-length` and `transfer-encoding` are not manually set in `options.headers`.
14141414
14151415
Since Got 12, the `content-length` is not automatically set when `body` is a `fs.createReadStream`.
14161416
@@ -1431,12 +1431,12 @@ export default class Options {
14311431
});
14321432
```
14331433
*/
1434-
get body(): string | Buffer | Readable | Generator | AsyncGenerator | Iterable<unknown> | AsyncIterable<unknown> | FormDataLike | undefined {
1434+
get body(): string | Buffer | Readable | Generator | AsyncGenerator | Iterable<unknown> | AsyncIterable<unknown> | FormDataLike | ArrayBufferView | undefined {
14351435
return this._internals.body;
14361436
}
14371437

1438-
set body(value: string | Buffer | Readable | Generator | AsyncGenerator | Iterable<unknown> | AsyncIterable<unknown> | FormDataLike | undefined) {
1439-
assert.any([is.string, is.buffer, is.nodeStream, is.generator, is.asyncGenerator, is.iterable, is.asyncIterable, isFormData, is.undefined], value);
1438+
set body(value: string | Buffer | Readable | Generator | AsyncGenerator | Iterable<unknown> | AsyncIterable<unknown> | FormDataLike | ArrayBufferView | undefined) {
1439+
assert.any([is.string, is.buffer, is.nodeStream, is.generator, is.asyncGenerator, is.iterable, is.asyncIterable, isFormData, is.typedArray, is.undefined], value);
14401440

14411441
if (is.nodeStream(value)) {
14421442
assert.truthy(value.readable);

source/core/utils/get-body-size.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export default async function getBodySize(body: unknown, headers: ClientRequestA
2121
return body.length;
2222
}
2323

24+
if (is.typedArray(body)) {
25+
return (body as ArrayBufferView).byteLength;
26+
}
27+
2428
if (isFormData(body)) {
2529
try {
2630
return await promisify(body.getLength.bind(body))();

test/post.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,33 @@ test('sends Buffers', withServer, async (t, server, got) => {
7676
t.is(body, 'wow');
7777
});
7878

79+
test('sends Uint8Array', withServer, async (t, server, got) => {
80+
server.post('/', defaultEndpoint);
81+
82+
const uint8Body = new Uint8Array([119, 111, 119]); // 'wow' in ASCII
83+
const {body} = await got.post({body: uint8Body});
84+
t.is(body, 'wow');
85+
});
86+
87+
test('sends Uint16Array', withServer, async (t, server, got) => {
88+
server.post('/', defaultEndpoint);
89+
90+
const text = 'hello';
91+
const buffer = Buffer.from(text);
92+
const uint16Body = new Uint16Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / Uint16Array.BYTES_PER_ELEMENT);
93+
const {body} = await got.post({body: uint16Body});
94+
t.is(Buffer.from(uint16Body.buffer, uint16Body.byteOffset, uint16Body.byteLength).toString(), body);
95+
});
96+
97+
test('`content-length` header with Uint8Array body', withServer, async (t, server, got) => {
98+
server.post('/', echoHeaders);
99+
100+
const uint8Body = new Uint8Array([119, 111, 119]);
101+
const {body} = await got.post({body: uint8Body});
102+
const headers = JSON.parse(body);
103+
t.is(headers['content-length'], '3');
104+
});
105+
79106
test('sends Streams', withServer, async (t, server, got) => {
80107
server.post('/', defaultEndpoint);
81108

0 commit comments

Comments
 (0)