Skip to content

Commit a80a688

Browse files
committed
fix(http2): allow GET and HEAD request bodies over h2
Assisted-by: OpenAI:gpt-5.4 Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com>
1 parent ec60a7c commit a80a688

File tree

2 files changed

+77
-1
lines changed

2 files changed

+77
-1
lines changed

lib/dispatcher/client-h2.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ function writeH2 (client, request) {
658658
}
659659

660660
// TODO(metcoder95): add support for sending trailers
661-
const shouldEndStream = method === 'GET' || method === 'HEAD' || body === null
661+
const shouldEndStream = body === null
662662
if (expectContinue) {
663663
headers[HTTP2_HEADER_EXPECT] = '100-continue'
664664
stream = session.request(headers, { endStream: shouldEndStream, signal })

test/http2-body.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict'
22

3+
const assert = require('node:assert')
34
const { tspl } = require('@matteo.collina/tspl')
45
const { test, after } = require('node:test')
56
const { createSecureServer } = require('node:http2')
@@ -207,6 +208,81 @@ test('Should handle h2 request with body (stream)', async t => {
207208
await t.completed
208209
})
209210

211+
test('Should handle h2 GET and HEAD requests with body', async () => {
212+
const server = createSecureServer(await pem.generate({ opts: { keySize: 2048 } }))
213+
const expectedBodies = {
214+
GET: 'hello from get',
215+
HEAD: 'hello from head'
216+
}
217+
const requestBodies = new Map()
218+
219+
server.on('stream', async (stream, headers) => {
220+
const method = headers[':method']
221+
const chunks = []
222+
223+
assert.ok(method === 'GET' || method === 'HEAD')
224+
225+
for await (const chunk of stream) {
226+
chunks.push(chunk)
227+
}
228+
229+
requestBodies.set(method, Buffer.concat(chunks).toString('utf-8'))
230+
231+
stream.respond({
232+
'content-type': 'text/plain; charset=utf-8',
233+
'x-custom-h2': method,
234+
':status': 200
235+
})
236+
237+
if (method === 'HEAD') {
238+
stream.end()
239+
} else {
240+
stream.end('hello h2!')
241+
}
242+
})
243+
244+
after(() => server.close())
245+
await once(server.listen(0), 'listening')
246+
247+
const client = new Client(`https://localhost:${server.address().port}`, {
248+
connect: {
249+
rejectUnauthorized: false
250+
},
251+
allowH2: true
252+
})
253+
after(() => client.close())
254+
255+
const getResponse = await client.request({
256+
path: '/',
257+
method: 'GET',
258+
headers: {
259+
'content-type': 'text/plain'
260+
},
261+
body: expectedBodies.GET
262+
})
263+
264+
assert.strictEqual(getResponse.statusCode, 200)
265+
assert.strictEqual(getResponse.headers['content-type'], 'text/plain; charset=utf-8')
266+
assert.strictEqual(getResponse.headers['x-custom-h2'], 'GET')
267+
assert.strictEqual(await getResponse.body.text(), 'hello h2!')
268+
269+
const headResponse = await client.request({
270+
path: '/',
271+
method: 'HEAD',
272+
headers: {
273+
'content-type': 'text/plain'
274+
},
275+
body: expectedBodies.HEAD
276+
})
277+
278+
assert.strictEqual(headResponse.statusCode, 200)
279+
assert.strictEqual(headResponse.headers['content-type'], 'text/plain; charset=utf-8')
280+
assert.strictEqual(headResponse.headers['x-custom-h2'], 'HEAD')
281+
assert.strictEqual(await headResponse.body.text(), '')
282+
assert.strictEqual(requestBodies.get('GET'), expectedBodies.GET)
283+
assert.strictEqual(requestBodies.get('HEAD'), expectedBodies.HEAD)
284+
})
285+
210286
test('Should handle h2 request with body (iterable)', async t => {
211287
t = tspl(t, { plan: 8 })
212288

0 commit comments

Comments
 (0)