fastify / fastify-reply-from

fastify plugin to forward the current http request to another server
MIT License
148 stars 76 forks source link

Update Documentation for multipart and reply-from interaction #314

Open eskimoquinn opened 1 year ago

eskimoquinn commented 1 year ago

Prerequisites

🚀 Feature Proposal

I would like to use fastify as a reverse proxy. My current reverse proxy handles multipart form data, so a requirement for my new reverse proxy would need to handle this. I am aware that fastify-reply-from and fastify-multipart plugins are not compatible. Does anyone have any advice on creating a reverse proxy with fastify that handles multipart form data?

I believe the documentation should be updated to indicate if reply-from does not support any multipart file handling or JUST is not compatible with the multipart plugin. This would help end users decide if this plugin is acceptable for the use cases that are needed.

Motivation

Save developers time when choosing plugins

Example

No response

mcollina commented 1 year ago

@fastify/reply-from proxies correctly multipart form data. What you cannot do is to process multipart data inside the proxy.

purpledrgn commented 4 months ago

From looking at the code and experience trying to use it, in the current implementation, when body is present, @fastify/reply-from parses the content-type header and only keeps the base type. For most headers this won't make too big of a difference, but for multipart this means the boundary is dropped completely which results in nonsensical multipart header and will cause errors on the receiver (basically unusable). Consequently breaking forms.

Some frameworks and form plugins for frameworks use multipart even if the form is not some upload form, so this basically means the proxy breaks all the forms, not just specific cases.

I don't think content type should be touched.


For anyone finding this and looking for a solution. A quick fix is simply to remember the content-type before reply-from touches it and then rewrite it back by passing it to reply-from's rewriteHeaders.

Loosly, something like this:

type Locals = {
  originalContentType?: string
}

const proxy = Fastify({ logger: true })

proxy.addContentTypeParser("*", /* ... */)

proxy.addHook("preHandler", async function preHandler(req, reply) {
  let locals: Locals = {
    originalContentType: req.headers["content-type"],
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  ;(req as any).locals = locals

  // ...anything else you want to do
})

proxy.register(replyFrom, {
  base: UPSTREAM,
})

proxy.get("/*", /* ... */)

proxy.post("/*", (req, reply) => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  let locals: Locals = (req as any).locals as Locals

  let extraHeaders: { [header: string]: string } = {
    // ...any other extra headers here
  }

  if (locals.originalContentType != nil) {
    extraHeaders["content-type"] = locals.originalContentType
  }

  reply.from(req.originalUrl, {
    rewriteRequestHeaders: (req, headers) => {
      return {
        ...headers,
        ...extraHeaders,
      }
    },
  })
})