Skip to content

Commit acfd2d1

Browse files
authored
fix: use req.originalUrl (express, fastify) (#167)
Use `req.originalUrl`, if available, when converting `req` to ECS fields. This is necessary because Express, and possibly Fastify, will mutate req.url during routing in some cases.
1 parent af2f9e9 commit acfd2d1

File tree

3 files changed

+99
-19
lines changed

3 files changed

+99
-19
lines changed

packages/ecs-helpers/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @elastic/ecs-helpers Changelog
22

3+
## v2.1.1
4+
5+
- fix: Use `req.originalUrl`, if available, when converting `req` to ECS fields.
6+
This is necessary because [Express will mutate req.url during routing](https://expressjs.com/en/4x/api.html#req.originalUrl).
7+
(https://github.com/elastic/ecs-logging-nodejs/issues/114)
8+
39
## v2.1.0
410

511
- Bump the `ecs.version` from "1.6.0" to "8.10.0".

packages/ecs-helpers/lib/http-formatters.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ function formatHttpRequest (ecs, req) {
4545

4646
const {
4747
method,
48-
url,
4948
headers,
5049
hostname,
5150
httpVersion,
@@ -57,6 +56,12 @@ function formatHttpRequest (ecs, req) {
5756
ecs.http.request = ecs.http.request || {}
5857
ecs.http.request.method = method
5958

59+
// Express can strip leading parts of `req.url` during routing, so we must use
60+
// https://expressjs.com/en/4x/api.html#req.originalUrl if available.
61+
// Fastify docs (https://fastify.dev/docs/latest/Reference/Request/) imply
62+
// that it might mutate `req.url` during routing as well.
63+
const url = req.originalUrl || req.url
64+
6065
ecs.url = ecs.url || {}
6166
ecs.url.full = (socket && socket.encrypted ? 'https://' : 'http://') + headers.host + url
6267
const hasQuery = url.indexOf('?')
@@ -78,6 +83,7 @@ function formatHttpRequest (ecs, req) {
7883
if (hostname) {
7984
const [host, port] = hostname.split(':')
8085
ecs.url.domain = host
86+
// istanbul ignore next
8187
if (port) {
8288
ecs.url.port = Number(port)
8389
}

packages/ecs-helpers/test/express.test.js

Lines changed: 86 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,26 @@ const {
2727
formatHttpResponse
2828
} = require('../')
2929

30+
async function makeARequest (url, opts) {
31+
return new Promise((resolve, reject) => {
32+
const clientReq = http.request(url, opts, function (clientRes) {
33+
const chunks = []
34+
clientRes.on('data', function (chunk) {
35+
chunks.push(chunk)
36+
})
37+
clientRes.on('end', function () {
38+
resolve({
39+
statusCode: clientRes.statusCode,
40+
headers: clientRes.headers,
41+
body: chunks.join('')
42+
})
43+
})
44+
})
45+
clientReq.on('error', reject)
46+
clientReq.end()
47+
})
48+
}
49+
3050
test('express res/req serialization', t => {
3151
const app = express()
3252
let server
@@ -78,26 +98,74 @@ test('express res/req serialization', t => {
7898
t.equal(typeof (rec.client.port), 'number')
7999
})
80100

81-
app.listen(0, '127.0.0.1', function () {
82-
server = this
83-
const req = http.get(
84-
`http://127.0.0.1:${server.address().port}/apath?aquery#ahash`,
85-
{
86-
headers: {
87-
'user-agent': 'cool-agent',
88-
connection: 'close',
89-
'x-request-id': 'arequestid'
101+
const aRouter = express.Router()
102+
aRouter.get('/asubpath', (req, res) => {
103+
res.end('hi')
104+
105+
const rec = {}
106+
let rv = formatHttpRequest(rec, req)
107+
t.ok(rv, 'formatHttpRequest processed req')
108+
rv = formatHttpResponse(rec, res)
109+
t.ok(rv, 'formatHttpResponse processed res')
110+
111+
const port = server.address().port
112+
t.same(rec, {
113+
http: {
114+
version: '1.1',
115+
request: {
116+
method: 'GET',
117+
headers: {
118+
host: `127.0.0.1:${port}`,
119+
connection: 'close'
120+
}
121+
},
122+
response: {
123+
status_code: 200,
124+
headers: {
125+
'x-powered-by': 'Express'
126+
}
90127
}
91128
},
92-
function (res) {
93-
res.on('data', function () {})
94-
res.on('end', function () {
95-
server.close(function () {
96-
t.end()
97-
})
98-
})
129+
url: {
130+
full: `http://127.0.0.1:${port}/arouter/asubpath`,
131+
path: '/arouter/asubpath',
132+
domain: '127.0.0.1'
133+
},
134+
client: {
135+
address: '127.0.0.1',
136+
ip: '127.0.0.1',
137+
port: req.socket.remotePort
99138
}
100-
)
101-
req.on('error', t.error)
139+
})
140+
})
141+
app.use('/arouter', aRouter)
142+
143+
app.listen(0, '127.0.0.1', async function () {
144+
server = this
145+
146+
await Promise.all([
147+
makeARequest(
148+
`http://127.0.0.1:${server.address().port}/apath?aquery#ahash`,
149+
{
150+
headers: {
151+
'user-agent': 'cool-agent',
152+
connection: 'close',
153+
'x-request-id': 'arequestid'
154+
}
155+
}
156+
),
157+
makeARequest(
158+
`http://127.0.0.1:${server.address().port}/arouter/asubpath`,
159+
{
160+
headers: {
161+
connection: 'close'
162+
}
163+
}
164+
)
165+
])
166+
167+
server.close(() => {
168+
t.end()
169+
})
102170
})
103171
})

0 commit comments

Comments
 (0)