-
Notifications
You must be signed in to change notification settings - Fork 224
/
_mock_apm_server.js
126 lines (116 loc) · 3.88 KB
/
_mock_apm_server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/*
* Copyright Elasticsearch B.V. and other contributors where applicable.
* Licensed under the BSD 2-Clause License; you may not use this file except in
* compliance with the BSD 2-Clause License.
*/
// A mock APM server to use in tests.
// It also has an option to attempt to behave like the Elastic Lambda extension.
//
// Usage:
// const server = new MockAPMServer(opts)
// server.start(function (serverUrl) {
// // Test code using `serverUrl`...
// // - Events received on the intake API will be on `server.events`.
// // - Raw request data is on `server.requests`.
// // - Use `server.clear()` to clear `server.events` and `server.requests`
// // for re-use of the mock server in multiple test cases.
// // - Call `server.close()` when done.
// })
const http = require('http');
const { URL } = require('url');
const zlib = require('zlib');
class MockAPMServer {
/**
* @param {object} opts
* - {string} opts.apmServerVersion - The version to report in the `GET /`
* response body. Defaults to "8.0.0".
* - {boolean} opts.mockLambdaExtension - Default false. If enabled then
* this will add some behaviour expected of APM Lambda extension, e.g.
* responding to the `POST /register/transaction` endpoint.
*/
constructor(opts) {
opts = opts || {};
this.clear();
this.serverUrl = null; // set in .start()
this._apmServerVersion = opts.apmServerVersion || '8.0.0';
this._mockLambdaExtension = !!opts.mockLambdaExtension;
this._http = http.createServer(this._onRequest.bind(this));
}
clear() {
this.events = [];
this.requests = [];
}
_onRequest(req, res) {
var parsedUrl = new URL(req.url, this.serverUrl);
var instream = req;
if (req.headers['content-encoding'] === 'gzip') {
instream = req.pipe(zlib.createGunzip());
} else {
instream.setEncoding('utf8');
}
let body = '';
instream.on('data', (chunk) => {
body += chunk;
});
instream.on('end', () => {
let resBody = '';
if (req.method === 'GET' && parsedUrl.pathname === '/') {
// https://www.elastic.co/guide/en/apm/server/current/server-info.html#server-info-endpoint
res.writeHead(200);
resBody = JSON.stringify({
build_date: '2021-09-16T02:05:39Z',
build_sha: 'a183f675ecd03fca4a897cbe85fda3511bc3ca43',
version: this._apmServerVersion,
});
} else if (parsedUrl.pathname === '/config/v1/agents') {
// Central config mocking.
res.writeHead(200);
resBody = '{}';
} else if (
req.method === 'POST' &&
parsedUrl.pathname === '/intake/v2/events'
) {
body
.split(/\n/g) // parse each line
.filter((line) => line.trim()) // ... if it is non-empty
.forEach((line) => {
this.events.push(JSON.parse(line)); // ... append to this.events
});
resBody = '{}';
res.writeHead(202);
} else if (
this._mockLambdaExtension &&
req.method === 'POST' &&
parsedUrl.pathname === '/register/transaction'
) {
// See `func handleTransactionRegistration` in apm-aws-lambda.git.
// This mock doesn't handle the various checks there. It only handles
// the status code, so the APM agent will continue to register
// transactions.
res.writeHead(200);
} else {
res.writeHead(404);
}
this.requests.push({
method: req.method,
url: req.url,
headers: req.headers,
body,
});
res.end(resBody);
});
}
// Start listening and callback with `cb(serverUrl)`.
start(cb) {
return this._http.listen(() => {
this.serverUrl = `http://localhost:${this._http.address().port}`;
cb(this.serverUrl);
});
}
close() {
return this._http.close();
}
}
module.exports = {
MockAPMServer,
};