Skip to content

Commit 2f24692

Browse files
authored
test: reverse-proxy logging with ipfs-host.local:proxy only (#655)
1 parent 9d79c74 commit 2f24692

File tree

2 files changed

+102
-85
lines changed

2 files changed

+102
-85
lines changed

test-e2e/ipfs-gateway.js

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
*/
55
import { mkdir } from 'node:fs/promises'
66
import { tmpdir } from 'node:os'
7-
import { dirname, join, relative } from 'node:path'
7+
import { dirname, relative } from 'node:path'
88
import { cwd } from 'node:process'
99
import { fileURLToPath } from 'node:url'
1010
import { logger } from '@libp2p/logger'
1111
import { $, execa } from 'execa'
1212
import { path } from 'kubo'
13+
import { createReverseProxy } from './reverse-proxy.js'
1314

1415
const log = logger('ipfs-host.local')
1516
const daemonLog = logger('ipfs-host.local:kubo')
@@ -93,22 +94,15 @@ await new Promise((resolve, reject) => {
9394
}, 5000)
9495
})
9596

96-
// @ts-expect-error - overwriting type of NodeJS.ProcessEnv
97-
execaOptions.env = {
98-
...execaOptions.env,
99-
TARGET_HOST: 'localhost',
100-
BACKEND_PORT: gatewayPort.toString(),
101-
PROXY_PORT: process.env.PROXY_PORT ?? '3334',
102-
SUBDOMAIN: `${cid.trim()}.ipfs`,
103-
DISABLE_TRY_FILES: 'true',
104-
X_FORWARDED_HOST: 'ipfs-host.local',
105-
DEBUG: process.env.DEBUG ?? 'reverse-proxy*,reverse-proxy*:trace'
106-
}
107-
const reverseProxy = execa('node', [`${join(__dirname, 'reverse-proxy.js')}`], execaOptions)
97+
// Create and start the reverse proxy
98+
const proxyPort = Number(process.env.PROXY_PORT ?? '3334')
10899

109-
reverseProxy.stdout?.on('data', (data) => {
110-
proxyLog(data.toString())
111-
})
112-
reverseProxy.stderr?.on('data', (data) => {
113-
proxyLog.trace(data.toString())
100+
createReverseProxy({
101+
targetHost: 'localhost',
102+
backendPort: gatewayPort,
103+
proxyPort,
104+
subdomain: `${cid.trim()}.ipfs`,
105+
disableTryFiles: true,
106+
xForwardedHost: 'ipfs-host.local',
107+
log: proxyLog
114108
})

test-e2e/reverse-proxy.js

Lines changed: 90 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,114 @@
11
/* eslint-disable no-console */
22
import { request, createServer } from 'node:http'
3+
import { pathToFileURL } from 'node:url'
34
import { logger } from '@libp2p/logger'
45

5-
const log = logger('reverse-proxy')
6-
7-
const TARGET_HOST = process.env.TARGET_HOST ?? 'localhost'
8-
const backendPort = Number(process.env.BACKEND_PORT ?? 3000)
9-
const proxyPort = Number(process.env.PROXY_PORT ?? 3333)
10-
const subdomain = process.env.SUBDOMAIN
11-
const prefixPath = process.env.PREFIX_PATH
12-
const disableTryFiles = process.env.DISABLE_TRY_FILES === 'true'
13-
const X_FORWARDED_HOST = process.env.X_FORWARDED_HOST
14-
156
const setCommonHeaders = (res) => {
167
res.setHeader('Access-Control-Allow-Origin', '*')
178
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Range, User-Agent, X-Requested-With')
189
res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS')
1910
}
2011

21-
const makeRequest = (options, req, res, attemptRootFallback = false) => {
22-
options.headers.Host = TARGET_HOST
23-
const clientIp = req.connection.remoteAddress
24-
options.headers['X-Forwarded-For'] = clientIp
12+
/**
13+
* Creates and starts a reverse proxy server
14+
*
15+
* @param {object} options - Configuration options
16+
* @param {string} [options.targetHost] - Target host to proxy to (defaults to process.env.TARGET_HOST or 'localhost')
17+
* @param {number} [options.backendPort] - Port of the backend service (defaults to process.env.BACKEND_PORT or 3000)
18+
* @param {number} [options.proxyPort] - Port for the proxy to listen on (defaults to process.env.PROXY_PORT or 3333)
19+
* @param {string} [options.subdomain] - Subdomain to use (defaults to process.env.SUBDOMAIN)
20+
* @param {string} [options.prefixPath] - Path prefix to add to requests (defaults to process.env.PREFIX_PATH)
21+
* @param {boolean} [options.disableTryFiles] - Whether to disable try_files behavior (defaults to process.env.DISABLE_TRY_FILES === 'true')
22+
* @param {string} [options.xForwardedHost] - Value for X-Forwarded-Host header (defaults to process.env.X_FORWARDED_HOST)
23+
* @param {object} [options.log] - Logger instance to use (defaults to logger('reverse-proxy'))
24+
* @returns {object} The HTTP server instance
25+
*/
26+
export function createReverseProxy ({
27+
targetHost = process.env.TARGET_HOST ?? 'localhost',
28+
backendPort = Number(process.env.BACKEND_PORT ?? 3000),
29+
proxyPort = Number(process.env.PROXY_PORT ?? 3333),
30+
subdomain = process.env.SUBDOMAIN,
31+
prefixPath = process.env.PREFIX_PATH,
32+
disableTryFiles = process.env.DISABLE_TRY_FILES === 'true',
33+
xForwardedHost = process.env.X_FORWARDED_HOST,
34+
log = logger('reverse-proxy')
35+
} = {}) {
36+
const makeRequest = (options, req, res, attemptRootFallback = false) => {
37+
options.headers.Host = targetHost
38+
const clientIp = req.connection.remoteAddress
39+
options.headers['X-Forwarded-For'] = clientIp
2540

26-
// override path to include prefixPath if set
27-
if (prefixPath != null) {
28-
options.path = `${prefixPath}${options.path}`
29-
}
30-
if (subdomain != null) {
31-
options.headers.Host = `${subdomain}.${TARGET_HOST}`
32-
}
33-
if (X_FORWARDED_HOST != null) {
34-
options.headers['X-Forwarded-Host'] = X_FORWARDED_HOST
35-
}
41+
// override path to include prefixPath if set
42+
if (prefixPath != null) {
43+
options.path = `${prefixPath}${options.path}`
44+
}
45+
if (subdomain != null) {
46+
options.headers.Host = `${subdomain}.${targetHost}`
47+
}
48+
if (xForwardedHost != null) {
49+
options.headers['X-Forwarded-Host'] = xForwardedHost
50+
}
3651

37-
// log where we're making the request to
38-
log('Proxying request from %s:%s to %s:%s%s', req.headers.host, req.url, options.headers.Host, options.port, options.path)
52+
// log where we're making the request to
53+
log('Proxying request from %s:%s to %s:%s%s', req.headers.host, req.url, options.headers.Host, options.port, options.path)
3954

40-
const proxyReq = request(options, proxyRes => {
41-
if (!disableTryFiles && proxyRes.statusCode === 404) { // poor mans attempt to implement nginx style try_files
42-
if (!attemptRootFallback) {
43-
// Split the path and pop the last segment
44-
const pathSegments = options.path.split('/')
45-
const lastSegment = pathSegments.pop() || ''
55+
const proxyReq = request(options, proxyRes => {
56+
if (!disableTryFiles && proxyRes.statusCode === 404) { // poor mans attempt to implement nginx style try_files
57+
if (!attemptRootFallback) {
58+
// Split the path and pop the last segment
59+
const pathSegments = options.path.split('/')
60+
const lastSegment = pathSegments.pop() || ''
4661

47-
// Attempt to request the last segment at the root
48-
makeRequest({ ...options, path: `/${lastSegment}` }, req, res, true)
62+
// Attempt to request the last segment at the root
63+
makeRequest({ ...options, path: `/${lastSegment}` }, req, res, true)
64+
} else {
65+
// If already attempted a root fallback, serve index.html
66+
makeRequest({ ...options, path: '/index.html' }, req, res)
67+
}
4968
} else {
50-
// If already attempted a root fallback, serve index.html
51-
makeRequest({ ...options, path: '/index.html' }, req, res)
69+
setCommonHeaders(res)
70+
res.writeHead(proxyRes.statusCode, proxyRes.headers)
71+
proxyRes.pipe(res, { end: true })
5272
}
53-
} else {
73+
})
74+
75+
req.pipe(proxyReq, { end: true })
76+
77+
proxyReq.on('error', (e) => {
78+
log.error(`Problem with request: ${e.message}`)
79+
setCommonHeaders(res)
80+
res.writeHead(500)
81+
res.end(`Internal Server Error: ${e.message}`)
82+
})
83+
}
84+
85+
const proxyServer = createServer((req, res) => {
86+
if (req.method === 'OPTIONS') {
5487
setCommonHeaders(res)
55-
res.writeHead(proxyRes.statusCode, proxyRes.headers)
56-
proxyRes.pipe(res, { end: true })
88+
res.writeHead(200)
89+
res.end()
90+
return
5791
}
58-
})
5992

60-
req.pipe(proxyReq, { end: true })
93+
const options = {
94+
hostname: targetHost,
95+
port: backendPort,
96+
path: req.url,
97+
method: req.method,
98+
headers: { ...req.headers }
99+
}
61100

62-
proxyReq.on('error', (e) => {
63-
log.error(`Problem with request: ${e.message}`)
64-
setCommonHeaders(res)
65-
res.writeHead(500)
66-
res.end(`Internal Server Error: ${e.message}`)
101+
makeRequest(options, req, res)
67102
})
68-
}
69103

70-
const proxyServer = createServer((req, res) => {
71-
if (req.method === 'OPTIONS') {
72-
setCommonHeaders(res)
73-
res.writeHead(200)
74-
res.end()
75-
return
76-
}
77-
78-
const options = {
79-
hostname: TARGET_HOST,
80-
port: backendPort,
81-
path: req.url,
82-
method: req.method,
83-
headers: { ...req.headers }
84-
}
104+
proxyServer.listen(proxyPort, () => {
105+
log(`Proxy server listening on port ${proxyPort}`)
106+
})
85107

86-
makeRequest(options, req, res)
87-
})
108+
return proxyServer
109+
}
88110

89-
proxyServer.listen(proxyPort, () => {
90-
log(`Proxy server listening on port ${proxyPort}`)
91-
})
111+
// Run main function if this file is being executed directly
112+
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
113+
createReverseProxy()
114+
}

0 commit comments

Comments
 (0)