0
0
mirror of https://github.com/honojs/hono.git synced 2024-12-01 10:51:01 +00:00

Merge branch 'main' into next

This commit is contained in:
Yusuke Wada 2023-05-12 16:11:12 +09:00
commit 1bfecc15e2
3 changed files with 85 additions and 28 deletions

View File

@ -9,6 +9,12 @@ describe('AWS Lambda Adapter for Hono', () => {
return c.text('Hello Lambda!') return c.text('Hello Lambda!')
}) })
app.get('/binary', (c) => {
return c.body('Fake Image', 200, {
'Content-Type': 'image/png',
})
})
app.post('/post', async (c) => { app.post('/post', async (c) => {
const body = (await c.req.parseBody()) as { message: string } const body = (await c.req.parseBody()) as { message: string }
return c.text(body.message) return c.text(body.message)
@ -35,8 +41,27 @@ describe('AWS Lambda Adapter for Hono', () => {
const response = await handler(event) const response = await handler(event)
expect(response.statusCode).toBe(200) expect(response.statusCode).toBe(200)
expect(response.body).toBe('SGVsbG8gTGFtYmRhIQ==') expect(response.body).toBe('Hello Lambda!')
expect(response.headers['content-type']).toMatch(/^text\/plain/) expect(response.headers['content-type']).toMatch(/^text\/plain/)
expect(response.isBase64Encoded).toBe(false)
})
it('Should handle a GET request and return a 200 response with binary', async () => {
const event = {
httpMethod: 'GET',
headers: {},
path: '/binary',
body: null,
isBase64Encoded: false,
requestContext: {
domainName: 'example.com',
},
}
const response = await handler(event)
expect(response.statusCode).toBe(200)
expect(response.body).toBe('RmFrZSBJbWFnZQ==')
expect(response.headers['content-type']).toMatch(/^image\/png/)
expect(response.isBase64Encoded).toBe(true) expect(response.isBase64Encoded).toBe(true)
}) })
@ -53,14 +78,14 @@ describe('AWS Lambda Adapter for Hono', () => {
method: 'GET', method: 'GET',
}, },
}, },
}; }
const response = await handler(event); const response = await handler(event)
expect(response.statusCode).toBe(200); expect(response.statusCode).toBe(200)
expect(response.body).toBe('SGVsbG8gTGFtYmRhIQ=='); expect(response.body).toBe('Hello Lambda!')
expect(response.headers['content-type']).toMatch(/^text\/plain/); expect(response.headers['content-type']).toMatch(/^text\/plain/)
expect(response.isBase64Encoded).toBe(true); expect(response.isBase64Encoded).toBe(false)
}); })
it('Should handle a GET request and return a 404 response', async () => { it('Should handle a GET request and return a 404 response', async () => {
const event = { const event = {
@ -96,12 +121,12 @@ describe('AWS Lambda Adapter for Hono', () => {
const response = await handler(event) const response = await handler(event)
expect(response.statusCode).toBe(200) expect(response.statusCode).toBe(200)
expect(response.body).toBe('R29vZCBNb3JuaW5nIExhbWJkYSE=') expect(response.body).toBe('Good Morning Lambda!')
}) })
it('Should handle a POST request and return a 200 response (LambdaFunctionUrlEvent)', async () => { it('Should handle a POST request and return a 200 response (LambdaFunctionUrlEvent)', async () => {
const searchParam = new URLSearchParams(); const searchParam = new URLSearchParams()
searchParam.append('message', 'Good Morning Lambda!'); searchParam.append('message', 'Good Morning Lambda!')
const event = { const event = {
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
@ -116,12 +141,12 @@ describe('AWS Lambda Adapter for Hono', () => {
method: 'POST', method: 'POST',
}, },
}, },
}; }
const response = await handler(event); const response = await handler(event)
expect(response.statusCode).toBe(200); expect(response.statusCode).toBe(200)
expect(response.body).toBe('R29vZCBNb3JuaW5nIExhbWJkYSE='); expect(response.body).toBe('Good Morning Lambda!')
}); })
it('Should handle a request and return a 401 response with Basic auth', async () => { it('Should handle a request and return a 401 response with Basic auth', async () => {
const event = { const event = {
@ -159,6 +184,6 @@ describe('AWS Lambda Adapter for Hono', () => {
const response = await handler(event) const response = await handler(event)
expect(response.statusCode).toBe(200) expect(response.statusCode).toBe(200)
expect(response.body).toBe('R29vZCBOaWdodCBMYW1iZGEh') expect(response.body).toBe('Good Night Lambda!')
}) })
}) })

View File

@ -0,0 +1,15 @@
import { isContentTypeBinary } from './handler'
describe('isContentTypeBinary', () => {
it('Should determine whether it is binary', () => {
expect(isContentTypeBinary('image/png')).toBe(true)
expect(isContentTypeBinary('font/woff2')).toBe(true)
expect(isContentTypeBinary('text/plain')).toBe(false)
expect(isContentTypeBinary('text/plain; charset=UTF-8')).toBe(false)
expect(isContentTypeBinary('text/css')).toBe(false)
expect(isContentTypeBinary('text/javascript')).toBe(false)
expect(isContentTypeBinary('application/json')).toBe(false)
expect(isContentTypeBinary('application/ld+json')).toBe(false)
expect(isContentTypeBinary('application/json; charset=UTF-8')).toBe(false)
})
})

View File

@ -40,9 +40,9 @@ interface LambdaFunctionUrlEvent {
body: string | null body: string | null
isBase64Encoded: boolean isBase64Encoded: boolean
requestContext: { requestContext: {
domainName: string, domainName: string
http: { http: {
method: string, method: string
} }
} }
} }
@ -69,11 +69,16 @@ export const handle = (app: Hono) => {
} }
const createResult = async (res: Response): Promise<APIGatewayProxyResult> => { const createResult = async (res: Response): Promise<APIGatewayProxyResult> => {
const contentType = res.headers.get('content-type')
const isBase64Encoded = contentType && isContentTypeBinary(contentType) ? true : false
const body = isBase64Encoded ? await fromReadableToString(res) : await res.text()
const result: APIGatewayProxyResult = { const result: APIGatewayProxyResult = {
body: await fromReadableToString(res), body: body,
headers: {}, headers: {},
statusCode: res.status, statusCode: res.status,
isBase64Encoded: true, isBase64Encoded,
} }
res.headers.forEach((value, key) => { res.headers.forEach((value, key) => {
@ -83,9 +88,13 @@ const createResult = async (res: Response): Promise<APIGatewayProxyResult> => {
return result return result
} }
const createRequest = (event: APIGatewayProxyEvent | APIGatewayProxyEventV2 | LambdaFunctionUrlEvent) => { const createRequest = (
event: APIGatewayProxyEvent | APIGatewayProxyEventV2 | LambdaFunctionUrlEvent
) => {
const queryString = extractQueryString(event) const queryString = extractQueryString(event)
const urlPath = `https://${event.requestContext.domainName}${isProxyEvent(event) ? event.path : event.rawPath}` const urlPath = `https://${event.requestContext.domainName}${
isProxyEvent(event) ? event.path : event.rawPath
}`
const url = queryString ? `${urlPath}?${queryString}` : urlPath const url = queryString ? `${urlPath}?${queryString}` : urlPath
const headers = new Headers() const headers = new Headers()
@ -96,7 +105,7 @@ const createRequest = (event: APIGatewayProxyEvent | APIGatewayProxyEventV2 | La
const method = 'httpMethod' in event ? event.httpMethod : event.requestContext.http.method const method = 'httpMethod' in event ? event.httpMethod : event.requestContext.http.method
const requestInit: RequestInit = { const requestInit: RequestInit = {
headers, headers,
method method,
} }
if (event.body) { if (event.body) {
@ -106,7 +115,9 @@ const createRequest = (event: APIGatewayProxyEvent | APIGatewayProxyEventV2 | La
return new Request(url, requestInit) return new Request(url, requestInit)
} }
const extractQueryString = (event: APIGatewayProxyEvent | APIGatewayProxyEventV2 | LambdaFunctionUrlEvent) => { const extractQueryString = (
event: APIGatewayProxyEvent | APIGatewayProxyEventV2 | LambdaFunctionUrlEvent
) => {
if (isProxyEvent(event)) { if (isProxyEvent(event)) {
return Object.entries(event.queryStringParameters || {}) return Object.entries(event.queryStringParameters || {})
.filter(([, value]) => value) .filter(([, value]) => value)
@ -142,3 +153,9 @@ const fromReadableToString = async (res: Response) => {
return btoa(string) return btoa(string)
} }
export const isContentTypeBinary = (contentType: string) => {
return !/^(text\/(plain|html|css|javascript|csv).*|application\/(.*json|.*xml).*|image\/svg\+xml)$/.test(
contentType
)
}