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
14 changes: 14 additions & 0 deletions src/client/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,20 @@ describe('Use custom fetch (app.request) method', () => {
const res = await client.search.$get()
expect(res.ok).toBe(true)
})

it.each([false, 0, '', null])('Should send falsy JSON value: %j', async (json) => {
const app = new Hono().post(
'/json',
validator('json', (value) => value as boolean | number | string | null),
(c) => c.json(c.req.valid('json'))
)
const client = hc<typeof app>('', { fetch: app.request })

const res = await client.json.$post({ json })

expect(res.status).toBe(200)
expect(await res.json()).toBe(json)
})
})

describe('Optional parameters in JSON response', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class ClientRequestImpl {
this.rBody = form
}

if (args.json) {
if (args.json !== undefined) {
this.rBody = JSON.stringify(args.json)
this.cType = 'application/json'
}
Expand Down
11 changes: 11 additions & 0 deletions src/middleware/cors/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,17 @@ describe('CORS by Middleware', () => {
expect(res2.headers.get('Access-Control-Allow-Methods')).toBe('GET,HEAD')
})

it('Does not set allow methods when function returns an empty array', async () => {
for (const allowMethods of [() => [], async () => []]) {
const app = new Hono()
app.use(cors({ allowMethods }))

const res = await app.request('http://localhost/', { method: 'OPTIONS' })

expect(res.headers.get('Access-Control-Allow-Methods')).toBeNull()
}
})

it('Emits the wildcard, not the reflected origin, when credentials is true with wildcard origin', async () => {
const res = await app.request('http://localhost/api10/abc', {
headers: {
Expand Down
31 changes: 19 additions & 12 deletions src/middleware/cors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ export const cors = (options?: CORSOptions): MiddlewareHandler => {
...options,
} satisfies CORSOptions

const exposeHeadersStr = opts.exposeHeaders?.length ? opts.exposeHeaders.join(',') : undefined
const allowHeadersStr = opts.allowHeaders?.length ? opts.allowHeaders.join(',') : undefined

const findAllowOrigin = ((optsOrigin) => {
if (typeof optsOrigin === 'string') {
if (optsOrigin === '*') {
Expand All @@ -85,11 +88,12 @@ export const cors = (options?: CORSOptions): MiddlewareHandler => {

const findAllowMethods = ((optsAllowMethods) => {
if (typeof optsAllowMethods === 'function') {
return optsAllowMethods
return async (origin: string, c: Context) => (await optsAllowMethods(origin, c)).join(',')
} else if (Array.isArray(optsAllowMethods)) {
return () => optsAllowMethods
const methodsStr = optsAllowMethods.join(',')
return () => methodsStr
} else {
return () => []
return () => ''
}
})(opts.allowMethods)

Expand All @@ -107,8 +111,8 @@ export const cors = (options?: CORSOptions): MiddlewareHandler => {
set('Access-Control-Allow-Credentials', 'true')
}

if (opts.exposeHeaders?.length) {
set('Access-Control-Expose-Headers', opts.exposeHeaders.join(','))
if (exposeHeadersStr) {
set('Access-Control-Expose-Headers', exposeHeadersStr)
}

if (c.req.method === 'OPTIONS') {
Expand All @@ -121,19 +125,22 @@ export const cors = (options?: CORSOptions): MiddlewareHandler => {
}

const allowMethods = await findAllowMethods(c.req.header('origin') || '', c)
if (allowMethods.length) {
set('Access-Control-Allow-Methods', allowMethods.join(','))
if (allowMethods) {
set('Access-Control-Allow-Methods', allowMethods)
}

let headers = opts.allowHeaders
if (!headers?.length) {
let headersStr = allowHeadersStr
if (!headersStr) {
const requestHeaders = c.req.header('Access-Control-Request-Headers')
if (requestHeaders) {
headers = requestHeaders.split(',').map((h) => h.trim())
headersStr = requestHeaders
.split(',')
.map((h) => h.trim())
.join(',')
}
}
if (headers?.length) {
set('Access-Control-Allow-Headers', headers.join(','))
if (headersStr) {
set('Access-Control-Allow-Headers', headersStr)
c.res.headers.append('Vary', 'Access-Control-Request-Headers')
}

Expand Down
1 change: 1 addition & 0 deletions src/utils/url.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ describe('url', () => {
'/api/:version/animal',
'/api/:version/animal/:type',
])
expect(checkOptionalParameter('/api/:id{[0-9]?}?')).toEqual(['/api', '/api/:id{[0-9]?}'])
})
})

Expand Down
4 changes: 2 additions & 2 deletions src/utils/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,13 @@ export const checkOptionalParameter = (path: string): string[] | null => {
if (segment !== '' && !/\:/.test(segment)) {
basePath += '/' + segment
} else if (/\:/.test(segment)) {
if (/\?/.test(segment)) {
if (segment.charCodeAt(segment.length - 1) === 63) {
if (results.length === 0 && basePath === '') {
results.push('/')
} else {
results.push(basePath)
}
const optionalSegment = segment.replace('?', '')
const optionalSegment = segment.slice(0, -1)
basePath += '/' + optionalSegment
results.push(basePath)
} else {
Expand Down
Loading