hono icon indicating copy to clipboard operation
hono copied to clipboard

Modify Req for Proxy scenario

Open draco2003 opened this issue 1 year ago • 12 comments

What is the feature you are proposing?

In this example it takes the request and passes it to the url: https://hono.dev/guides/examples#proxy

In order to make the proxy functionality more useful, it would be helpful to be able to modify or replace the req via the Middleware in order to add headers, santize/transform the url, etc..

draco2003 avatar Sep 05 '23 19:09 draco2003

Example from Compute@Edge documenation for Adding Geo Information to the request to the downstream:

function handler(event) {
  let clientGeo = event.client.geo

  event.request.headers.set("client-geo-continent", clientGeo.continent)
  event.request.headers.set("client-geo-country", clientGeo.country_code)
  event.request.headers.set("client-geo-latitude", clientGeo.latitude)
  event.request.headers.set("client-geo-longitude", clientGeo.longitude)

  return fetch(event.request, { backend: "origin_0" })
}

addEventListener("fetch", (event) => event.respondWith(handler(event)))

Source: https://developer.fastly.com/solutions/examples/decorating-origin-requests-with-geoip/

draco2003 avatar Sep 05 '23 21:09 draco2003

I'm able to do it like this:

   const newRequest = new Request(url,c.req.raw);
                    newRequest.headers.set('X-Connecting-IP',`${REALIP}`)
                    const res = await fetch(newRequest)
                    const newResponse = new Response(res.body, res)
                    newResponse.headers.set('X-Custom-Router', 'Hono')
                return newResponse
                })

Not sure if that's the best way to do it though...

alex8bitw avatar Sep 11 '23 09:09 alex8bitw

@draco2003

I think you can do almost all things with a @alex8bitw 's method. We don't have to make a helper for proxies.

yusukebe avatar Sep 21 '23 21:09 yusukebe

hi @yusukebe .. I'm trying @alex8bitw's method but c.req.raw is empty for me always. I'm on 3.6

eitelkrauss avatar Sep 22 '23 19:09 eitelkrauss

@eitelkrauss

I don't understand why c.req.raw is empty, but you can try the following code:

import { Hono } from 'hono'

const app = new Hono()

app.get('/', async (c) => {
  const newRequest = new Request('http://example.com')
  const res = await fetch(newRequest)
  const newResponse = new Response(res.body, res)
  newResponse.headers.set('X-Message', 'Hello!')
  return newResponse
})

export default app

yusukebe avatar Sep 22 '23 21:09 yusukebe

@yusukebe this works when you have no params, but what would be the best way to include the raw request object like @alex8bitw?

I know ideally you would setup a proxy before reaching the hono server, but I'm still curious how could I implement this in a hono handler?

eitelkrauss avatar Sep 27 '23 15:09 eitelkrauss

@eitelkrauss are you just trying to proxy everything?

The above is only half the solution then. You also need to send through the queries/params. I believe c.req.raw will send through POST requests, @yusukebe please confirm?

For the queries, I use app.all('/*',.....

Then you need to do something like this:

                const path:string = c.req.path
                const query = c.req.url.split("?")
                var url
                if (query[1])  {
                  url = new URL(path + "?" + query[1])
                } else {
                  url = new URL(path)
                }

In order to retain the query path part. Then you can do the rest of your code..

The above is only if you need to mess with path for the proxying. If you don't you can just do url = c.req.url. The above allows you to substitute path for something else.

alex8bitw avatar Sep 28 '23 05:09 alex8bitw

Yes @alex8bitw is right.

yusukebe avatar Sep 28 '23 08:09 yusukebe

Got it ... thought you could just use the same request object somehow.. thanks! @alex8bitw @yusukebe

eitelkrauss avatar Sep 29 '23 16:09 eitelkrauss

hi @yusukebe .. I'm trying @alex8bitw's method but c.req.raw is empty for me always. I'm on 3.6

hey, new user of hono here. I am trying to build a simple proxy but c.req.raw is also empty for me. no matter whether it's a unit test or an integration test via cf workers + vote.

psteinroe avatar Apr 16 '24 14:04 psteinroe

Hi @psteinroe , what are you trying to do? Hono works really well for proxying. I'm on 3.12.8 currently. I think the gist is, you create a new request with the c.req.raw object. Here's an example for CF workers:

const newRequest = new Request(url,c.req.raw);
newRequest.headers.set('X-Forwarded-For',`${REALIP}`)
const res = await fetch(newRequest,{
  cf: {
    cacheTtl: CACHE_TIME,
    cacheEverything: true,
    cacheTags: [ 'worker', `${HONO_TAG}` ],
    cacheKey: url.hostname + url.pathname,
  }})
  const newResponse = new Response(res.body, res)
  newResponse.headers.set('X-Custom-Router', `${HONO_TAG}`)

  return newResponse

The above modifies the connections by adding CF caching. Just wrap that in an app.all() with the proper query parsing you want to do (as shown a few messages above).

alex8bitw avatar Apr 17 '24 07:04 alex8bitw

Hi @psteinroe , what are you trying to do? Hono works really well for proxying. I'm on 3.12.8 currently. I think the gist is, you create a new request with the c.req.raw object. Here's an example for CF workers:

const newRequest = new Request(url,c.req.raw);
newRequest.headers.set('X-Forwarded-For',`${REALIP}`)
const res = await fetch(newRequest,{
  cf: {
    cacheTtl: CACHE_TIME,
    cacheEverything: true,
    cacheTags: [ 'worker', `${HONO_TAG}` ],
    cacheKey: url.hostname + url.pathname,
  }})
  const newResponse = new Response(res.body, res)
  newResponse.headers.set('X-Custom-Router', `${HONO_TAG}`)

  return newResponse

The above modifies the connections by adding CF caching. Just wrap that in an app.all() with the proper query parsing you want to do (as shown a few messages above).

ahh thanks, that works!! I was passing c.req.raw directly to fetch, but creating a new Request first does the trick.

psteinroe avatar Apr 17 '24 07:04 psteinroe