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
26 changes: 19 additions & 7 deletions src/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Define prototype for lightweight pseudo Request object

import type { IncomingMessage } from 'node:http'
import type { Http2ServerRequest } from 'node:http2'
import { Http2ServerRequest } from 'node:http2'
import { Readable } from 'node:stream'

const newRequestFromIncoming = (
Expand All @@ -11,9 +11,12 @@ const newRequestFromIncoming = (
incoming: IncomingMessage | Http2ServerRequest
): Request => {
const headerRecord: [string, string][] = []
const len = incoming.rawHeaders.length
for (let i = 0; i < len; i += 2) {
headerRecord.push([incoming.rawHeaders[i], incoming.rawHeaders[i + 1]])
const rawHeaders = incoming.rawHeaders
for (let i = 0; i < rawHeaders.length; i += 2) {
const { [i]: key, [i + 1]: value } = rawHeaders
if (key.charCodeAt(0) !== /*:*/ 0x3a) {
headerRecord.push([key, value])
}
}

const init = {
Expand All @@ -34,19 +37,23 @@ const newRequestFromIncoming = (
const getRequestCache = Symbol('getRequestCache')
const requestCache = Symbol('requestCache')
const incomingKey = Symbol('incomingKey')
const urlKey = Symbol('urlKey')

const requestPrototype: Record<string | symbol, any> = {
get method() {
return this[incomingKey].method || 'GET'
},

get url() {
const url = `http://${this[incomingKey].headers.host}${this[incomingKey].url}`
return /\.\./.test(url) ? new URL(url).href : url
return this[urlKey]
},

[getRequestCache]() {
return (this[requestCache] ||= newRequestFromIncoming(this.method, this.url, this[incomingKey]))
return (this[requestCache] ||= newRequestFromIncoming(
this.method,
this[urlKey],
this[incomingKey]
))
},
}
;[
Expand Down Expand Up @@ -81,5 +88,10 @@ Object.setPrototypeOf(requestPrototype, global.Request.prototype)
export const newRequest = (incoming: IncomingMessage | Http2ServerRequest) => {
const req = Object.create(requestPrototype)
req[incomingKey] = incoming
req[urlKey] = new URL(
`http://${incoming instanceof Http2ServerRequest ? incoming.authority : incoming.headers.host}${
incoming.url
}`
).href
return req
}
23 changes: 23 additions & 0 deletions test/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,12 @@ describe('SSL', () => {
describe('HTTP2', () => {
const app = new Hono()
app.get('/', (c) => c.text('Hello! Node!'))
app.get('/headers', (c) => {
// call newRequestFromIncoming
c.req.header('Accept')
return c.text('Hello! Node!')
})
app.get('/url', (c) => c.text(c.req.url))

const server = createAdaptorServer({
fetch: app.fetch,
Expand All @@ -475,6 +481,23 @@ describe('HTTP2', () => {
expect(res.headers['content-type']).toMatch(/text\/plain/)
expect(res.text).toBe('Hello! Node!')
})

it('Should return 200 response - GET /headers', async () => {
// @ts-expect-error: @types/supertest is not updated yet
const res = await request(server, { http2: true }).get('/headers').trustLocalhost()
expect(res.status).toBe(200)
expect(res.headers['content-type']).toMatch(/text\/plain/)
expect(res.text).toBe('Hello! Node!')
})

// Use :authority as the host for the url.
it('Should return 200 response - GET /url', async () => {
// @ts-expect-error: @types/supertest is not updated yet
const res = await request(server, { http2: true }).get('/url').trustLocalhost()
expect(res.status).toBe(200)
expect(res.headers['content-type']).toMatch(/text\/plain/)
expect(new URL(res.text).hostname).toBe('127.0.0.1')
})
})

describe('Hono compression', () => {
Expand Down