-
-
Notifications
You must be signed in to change notification settings - Fork 579
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proxying compressed response do not work out of the box #3518
Comments
HI @haochenx I think this will be caused by Wrangler automatically compressing the content and adding a |
Hi @yusukebe, Thanks for taking a look and my apologies for the late reply. I can confirm that if the upstream does not use compression, hono will behave as expected. Yet if the upstream uses compression, even if the upstream not being wrangler, proxied response from hono will be corrupted. As I believe most developers will expect hono decompress the response body from the upstream if necessary before sending it to the original requester. |
@haochenx Thanks for the explanation. I haven't investigated it yet, but it seems to be a problem with the Hi @usualoma I'll work on it later, but if you can, can you take a look? |
Thanks for taking another look! I believe my vite dev server is running on bun (version 1.1.3). it's started by running I will try it with latest version of bun later and let you know. If I can reproduce, (it may take a few days, but) I will make a minimal reproducing repo for you to investigate. I am a little busy recently so I cannot promise commitment, but I will try my best to help your investigation. |
|
I see. I'll take a look when I get time. |
Can you share target URLs to reproduce the problem other than |
Sure! I was using our company's homepage for testing: reproduce the bug with
|
btw I just noticed that using one trigger for the buggy behavior might be the presence of
|
I just tried the newest version of bun (namely, |
Thanks. As I said above, it's import { Hono } from 'hono'
import { serve } from '@hono/node-server'
const app = new Hono()
app.use('*', async (c, next) => {
const response = await fetch(new Request('https://kxc.inc/404', c.req.raw))
return new Response(response.body, response)
})
serve(app) |
Not sure whether it matters at this point, but I just extracted relevant code and made a reproduction repository: edit: added https://github.com/haochenx/hono-3518-reproduce/blob/main/src/minimal.ts |
Thanks. I reproduced the problem with only I also changed the upstream to I have to go for today but I might take a look at the root cause when I got time in the coming days. |
I can found that overriding As a result, the following works well: import { Hono } from 'hono'
import { serve } from '@hono/node-server'
const app = new Hono()
app.get('*', async (c) => {
const response = await fetch(
new Request('https://kxc.inc/404', {
compress: true,
...c.req.raw
})
)
return new Response(response.body, response)
})
serve(app) @usualoma any thoughts? |
I don't think this is a import { Hono } from 'hono'
const app = new Hono()
app.get('/', () => fetch('https://example.com/'))
export default app The following code will not produce an error. (The same error suppression will also be applied when using import { Hono } from 'hono'
const app = new Hono()
app.get('/', () =>
fetch('https://example.com/', {
headers: {
'Accept-Encoding': '',
},
})
)
export default app Currently, when using This may not be a problem that hono should solve as a framework. |
if this semantics is guaranteed, which I guess it should be according to web standard, my workaround is probably the proper way to handle this. If so, the best course of action might be to warn developers about this behavior in the document and introduce the workaround (and potentially provide an easier way to perform this workaround, eg by something in the line of adding a option item in the Responses’ constructor) |
Hi @haochenx, thank you for your comment. Yes, I agree with you.
It would be good to have something like middleware or a utility method to make your workarounds possible with simpler descriptions, but it isn't easy to find a good compromise. |
Thank you for the explanation! It's okay to remove the So, I think the following code is simple and good. What do you think of it? import { serve } from '@hono/node-server'
import { Hono } from 'hono'
const app = new Hono()
const target = 'https://kxc.inc/404'
app.get('/', (c) => {
const req = new Request(target, c.req.raw)
req.headers.delete('Accept-Encoding')
return fetch(req)
})
serve(app) If it's fine, we may add the description on the proxy page on the website: https://hono.dev/examples/proxy |
@usualoma @yusukebe Thank you for commenting. I am sorry for being late replying. Regarding deleting the 'Accept-Encoding' header from the request, I am personally against it, as it would make the proxying much less efficient for large compressible payloads (think about giant javascript files where almost every website is having these days..) (I'm yet familiar with hono's library organization and naming conventions, so just a suggestion:) I think providing a utility function that provides functional update semantics for headers on the response object e.g. somewhere under
, we can write the proxying route as import { serve } from '@hono/node-server'
import { Hono } from 'hono'
import { withHeaderDeleted } from 'hono/utils/updaters'
const app = new Hono()
const target = 'https://kxc.inc/404'
app.get('/', async (c) => {
const req = new Request(target, c.req.raw)
req.headers.delete('Accept-Encoding')
const resp = await fetch(req);
return new Response(resq.body, withHeaderDeleted('Content-Encoding', resq));
})
serve(app) which should both function correctly and take advantage of compression. Please let me know what you think. |
Hi @haochenx As you say, I think it would be good if I could receive compressed responses from the backend. If there is demand for support for proxy patterns, I think it would be better to support them at a slightly higher level of abstraction (my preference). // src/utils/proxy.ts
const ignoreHeadersRe = /^content-(?:encoding|length|range)$/i
// TBD: or perhaps simply named `proxy`
export const proxyFetch: typeof fetch = async (request, options) => {
const req = new Request(request, options)
req.headers.delete('accept-encoding') // TBD: there may be cases where you want to explicitly specify
const res = await fetch(req)
return new Response(res.body, {
...res,
headers: [...res.headers.entries()].filter(([k]) => !ignoreHeadersRe.test(k)),
})
} // app.ts
import { Hono } from 'hono'
import { proxyFetch } from 'hono/utils/proxy'
const target = 'https://example.com'
const app = new Hono()
app.get('/', async (c) => proxyFetch(new Request(target, c.req.raw)))
export default app |
Ah! Or, instead of it, making "Proxy Helper" sounds good! import { proxyFetch } from 'hono/proxy' |
I do think adding a proxy helper is quite reasonable, as it's really not that trivial to get proxying work reliably with only |
@usualoma cool! sure, I'm glad to take a look! |
I ran into this — is it possible to just disable the decompression Hono is doing? I'm not sure why that's the default? |
What version of Hono are you using?
4.6.4
What runtime/platform is your app running on?
Local Dev Server (with
@hono/vite-dev-server
) and Cloudflare Workers Local Dev Server (viawrangler dev
)What steps can reproduce the bug?
run
localhost:8787
(e.g. withwrangler dev src/index.ts
)localhost:5173
(e.g. withvite
)Part 1
having the following middleware:
now, attempt to access
localhost:5173/api/hello
(e.g. using httpie command ``) gives the following error:on the vite devserver log, we can see the following output:
note that directly accessing http://localhost:8787/api/hello gives the following result:
Part 2
if the middleware is modified so that the "content-encoding" header is stripped before returning, everything works fine.
e.g. changing the returning line to the following:
gives the expected result.
What is the expected behavior?
manually stripping the "content-encoding" from the proxy'ed response should not be required for the proxying route to work.
What do you see instead?
(explained above)
Additional information
No response
The text was updated successfully, but these errors were encountered: