diff --git a/package.json b/package.json index a02feccaf3..24c3039fe2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hono", - "version": "4.11.7", + "version": "4.11.8", "description": "Web framework built on Web Standards", "main": "dist/cjs/index.js", "type": "module", diff --git a/src/jsx/context.ts b/src/jsx/context.ts index 882beb7970..eed1388c87 100644 --- a/src/jsx/context.ts +++ b/src/jsx/context.ts @@ -24,13 +24,17 @@ export const createContext = (defaultValue: T): Context => { : props.children ).toString() : '' - } finally { + } catch (e) { values.pop() + throw e } if (string instanceof Promise) { - return string.then((resString) => raw(resString, (resString as HtmlEscapedString).callbacks)) + return string + .finally(() => values.pop()) + .then((resString) => raw(resString, (resString as HtmlEscapedString).callbacks)) } else { + values.pop() return raw(string) } }) as Context diff --git a/src/jsx/index.test.tsx b/src/jsx/index.test.tsx index bb72e8bb9b..a830cd1827 100644 --- a/src/jsx/index.test.tsx +++ b/src/jsx/index.test.tsx @@ -1022,6 +1022,44 @@ d.replaceWith(c.content) expect(nextRequest.toString()).toBe('light') }) }) + + describe('async with html helper', () => { + it('should preserve context when using await before html helper', async () => { + // Regression test for https://github.com/honojs/hono/issues/4582 + // Context was being popped before async children resolved + const AsyncParentWithHtml = async (props: { children?: any }) => { + await new Promise((r) => setTimeout(r, 10)) + return html`
${props.children}
` + } + + const template = ( + + + + + + ) + expect((await template.toString()).toString()).toBe('
dark
') + }) + + it('should preserve nested context when using await before html helper', async () => { + const AsyncParentWithHtml = async (props: { children?: any }) => { + await new Promise((r) => setTimeout(r, 10)) + return html`
${props.children}
` + } + + const template = ( + + + + + + + + ) + expect((await template.toString()).toString()).toBe('
black
') + }) + }) }) describe('version', () => { diff --git a/src/middleware/bearer-auth/index.test.ts b/src/middleware/bearer-auth/index.test.ts index c9f579c999..2dfa97e9c0 100644 --- a/src/middleware/bearer-auth/index.test.ts +++ b/src/middleware/bearer-auth/index.test.ts @@ -450,6 +450,18 @@ describe('Bearer Auth by Middleware', () => { expect(res.headers.get('x-custom')).toBe('foo') }) + it.each([['bearer'], ['BEARER'], ['BeArEr']])( + 'Should authorize - prefix is case-insensitive: %s', + async (prefix) => { + const req = new Request('http://localhost/auth/a') + req.headers.set('Authorization', `${prefix} ${token}`) + const res = await app.request(req) + expect(res).not.toBeNull() + expect(res.status).toBe(200) + expect(handlerExecuted).toBeTruthy() + } + ) + it('Should not authorize - no authorization header', async () => { const req = new Request('http://localhost/auth/a') const res = await app.request(req) @@ -481,6 +493,15 @@ describe('Bearer Auth by Middleware', () => { expect(res.headers.get('x-custom')).toBeNull() }) + it('Should not authorize - token is case-sensitive', async () => { + const req = new Request('http://localhost/auth/a') + req.headers.set('Authorization', `Bearer ${token.toUpperCase()}`) + const res = await app.request(req) + expect(res).not.toBeNull() + expect(res.status).toBe(401) + expect(await res.text()).toBe('Unauthorized') + }) + it('Should authorize', async () => { const req = new Request('http://localhost/authBot/a') req.headers.set('Authorization', 'Bot abcdefg12345-._~+/=') diff --git a/src/middleware/bearer-auth/index.ts b/src/middleware/bearer-auth/index.ts index 3ecd2e3be2..122ccc55ef 100644 --- a/src/middleware/bearer-auth/index.ts +++ b/src/middleware/bearer-auth/index.ts @@ -113,7 +113,7 @@ export const bearerAuth = (options: BearerAuthOptions): MiddlewareHandler => { const realm = options.realm?.replace(/"/g, '\\"') const prefixRegexStr = options.prefix === '' ? '' : `${options.prefix} +` - const regexp = new RegExp(`^${prefixRegexStr}(${TOKEN_STRINGS}) *$`) + const regexp = new RegExp(`^${prefixRegexStr}(${TOKEN_STRINGS}) *$`, 'i') const wwwAuthenticatePrefix = options.prefix === '' ? '' : `${options.prefix} ` const throwHTTPException = async (