Skip to content

Commit

Permalink
fix: remove content headers for unsupported HTTP methods (#204)
Browse files Browse the repository at this point in the history
  • Loading branch information
samialdury authored May 11, 2023
1 parent ccdd079 commit cdcac35
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 39 deletions.
10 changes: 5 additions & 5 deletions src/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { fastifyHttpProxy } from '@fastify/http-proxy'
import { fastify, type FastifyInstance } from 'fastify'
import { fastify, type FastifyInstance, type FastifyRequest } from 'fastify'

import { config } from './config.js'
import { rewriteRequestHeaders } from './http/headers.js'
Expand Down Expand Up @@ -35,11 +35,11 @@ export async function bootstrap(): Promise<FastifyInstance> {
http2: false,
replyOptions: {
rewriteRequestHeaders: (request, headers) => {
return rewriteRequestHeaders(
return rewriteRequestHeaders({
request: request as FastifyRequest,
headers,
request.ip,
targetHost.replace('https://', '')
)
host: targetHost.replace('https://', ''),
})
},
},
})
Expand Down
102 changes: 73 additions & 29 deletions src/http/headers.test.ts
Original file line number Diff line number Diff line change
@@ -1,72 +1,116 @@
import type { FastifyRequest, HTTPMethods } from 'fastify'

import { config, initConfig } from '../config.js'

import { rewriteRequestHeaders } from './headers.js'

describe('HTTP server', () => {
const ip = '192.168.1.1'
const host = 'example.com'

function getMockRequest(method?: HTTPMethods): FastifyRequest {
return {
ip,
...(method && { method }),
} as FastifyRequest
}

beforeEach(() => {
initConfig()
})

it('Should keep existing headers', () => {
const ip = '192.168.1.1'
const originalHeaders = {
referer: 'https://www.test.com',
}

const headers = rewriteRequestHeaders(
{
referer: 'https://www.test.com',
},
ip,
'example.com'
)
const headers = rewriteRequestHeaders({
headers: originalHeaders,
request: getMockRequest(),
host,
})

expect(headers).toEqual({
referer: 'https://www.test.com',
...originalHeaders,
'x-forwarded-for': ip,
'x-forwarded-by': config.appName,
'x-forwarded-host': 'example.com',
host: 'example.com',
host,
})
})

it('Should delete forbidden headers', () => {
const ip = '192.168.1.1'
it('Should delete forbidden HTTP2 headers', () => {
const originalHeaders = {
referer: 'https://www.test.com',
}

const headers = rewriteRequestHeaders(
{
referer: 'https://www.test.com',
const headers = rewriteRequestHeaders({
headers: {
...originalHeaders,
connection: 'keep-alive',
upgrade: 'websocket',
},
ip,
'example.com'
)
request: getMockRequest(),
host,
})

expect(headers).toEqual({
referer: 'https://www.test.com',
...originalHeaders,
'x-forwarded-for': ip,
'x-forwarded-by': config.appName,
'x-forwarded-host': 'example.com',
host: 'example.com',
host,
})
})

it('Should respect x-forwarded-for', () => {
const ip = '192.168.1.1'
const originalHeaders = {
referer: 'https://www.test.com',
}

const headers = rewriteRequestHeaders(
{
referer: 'https://www.test.com',
const headers = rewriteRequestHeaders({
headers: {
...originalHeaders,
'x-forwarded-for': '127.0.0.1',
},
ip,
'example.com'
)
request: getMockRequest(),
host,
})

expect(headers).toEqual({
referer: 'https://www.test.com',
...originalHeaders,
'x-forwarded-for': '127.0.0.1',
'x-forwarded-by': config.appName,
'x-forwarded-host': 'example.com',
host: 'example.com',
host,
})
})

describe('Should remove content headers if method is not PUT, PATCH or POST', () => {
for (const method of ['GET', 'HEAD', 'OPTIONS', 'DELETE']) {
it(`Should remove content headers if method is ${method}`, () => {
const originalHeaders = {
referer: 'https://www.test.com',
'x-forwarded-for': '127.0.0.1',
'content-type': 'application/json',
'content-length': '123',
'transfer-encoding': 'chunked',
}

const headers = rewriteRequestHeaders({
headers: originalHeaders,
request: { ip, method } as FastifyRequest,
host,
})

expect(headers).toEqual({
referer: originalHeaders.referer,
'x-forwarded-for': '127.0.0.1',
'x-forwarded-by': config.appName,
'x-forwarded-host': 'example.com',
host,
})
})
}
})
})
27 changes: 22 additions & 5 deletions src/http/headers.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
import type { IncomingHttpHeaders } from 'node:http'

import type { FastifyRequest } from 'fastify'

import { config } from '../config.js'

export function rewriteRequestHeaders(
headers: IncomingHttpHeaders,
ip: string,
export function rewriteRequestHeaders({
headers,
request,
host,
}: {
headers: IncomingHttpHeaders
request: FastifyRequest
host: string
): IncomingHttpHeaders {
}): IncomingHttpHeaders {
const filteredHeaders: IncomingHttpHeaders = {
...headers,
'x-forwarded-for': headers['x-forwarded-for'] ?? ip,
'x-forwarded-for': headers['x-forwarded-for'] ?? request.ip,
'x-forwarded-host': host,
'x-forwarded-by': config.appName,
host,
}

const expectsPayload =
request.method === 'PUT' ||
request.method === 'POST' ||
request.method === 'PATCH'

if (!expectsPayload) {
delete filteredHeaders['content-length']
delete filteredHeaders['content-type']
delete filteredHeaders['transfer-encoding']
}

// {@link https://github.com/smartlook/smartlook-relay-proxy/pull/106}
delete filteredHeaders.connection
delete filteredHeaders.upgrade
Expand Down

0 comments on commit cdcac35

Please sign in to comment.