-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathhttp_transport.js
More file actions
108 lines (95 loc) · 4.22 KB
/
Copy pathhttp_transport.js
File metadata and controls
108 lines (95 loc) · 4.22 KB
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
const http = require('http');
const https = require('https');
let storage = (f) => f();
module.exports.sleep = function (ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
};
module.exports.setStorage = function (new_storage) {
storage = new_storage;
}
// Optional observer invoked with each agent response's raw headers
// (Node's flat [name, value, name, value, ...] array). Lets the host tracer
// read response-only headers (e.g. Datadog-Container-Tags-Hash) that are not
// otherwise surfaced through the wasm response body. Never throws into the
// transport: a misbehaving observer must not break trace delivery.
//
// The observer runs synchronously on the response 'end' event, so it must be
// non-blocking and return quickly — long-running synchronous work here would
// stall the event loop.
let responseHeaderObserver = null;
module.exports.setResponseHeaderObserver = function (new_observer) {
responseHeaderObserver = new_observer;
}
module.exports.httpRequest = function (host, port, isHttps, socketPath, head_ptr, head_len, body_ptr, body_len, wasm_memory) {
// A non-empty socketPath routes over a Unix domain socket (or Windows named
// pipe) instead of TCP. Sockets are always plaintext HTTP/1.1, so https is
// ignored in that mode.
const useSocket = typeof socketPath === 'string' && socketPath.length > 0;
const transport = useSocket ? http : (isHttps ? https : http);
function isDetachedBufferError(err) {
return err instanceof TypeError && /detached/i.test(err.message);
}
function attempt() {
return new Promise((resolve, reject) => {
storage(() => {
// wasm_memory.buffer is replaced each time WebAssembly.Memory grows, so
// the views must be recreated on every attempt against the current buffer.
const headView = new Uint8Array(wasm_memory.buffer, head_ptr, head_len);
const bodyView = new Uint8Array(wasm_memory.buffer, body_ptr, body_len);
// host/port (or socketPath) drive connection selection; method/path/
// headers are placeholders because we replace the rendered head below.
const requestOptions = useSocket
? { socketPath, method: 'POST', path: '/' }
: { host, port, method: 'POST', path: '/' };
const req = transport.request(requestOptions, (res) => {
const chunks = [];
res.on('data', (chunk) => chunks.push(chunk));
res.on('end', () => {
const body = Buffer.concat(chunks)
if (responseHeaderObserver !== null) {
try {
responseHeaderObserver(res.rawHeaders);
} catch (err) {
// Only read `err.message` (a string) rather than stringifying an
// arbitrary thrown value, so a hostile/throwing toString on the
// error can't turn the log line into its own failure path.
process.stderr.write("responseHeaderObserver error: " + (err && err.message) + "\n");
}
}
resolve([
res.statusCode,
res.rawHeaders,
// Copy the exact body bytes. `body` is a Buffer from Buffer.concat,
// which for small payloads is a view into Node's shared pool, so
// `body.buffer` is the whole pool — slicing by offset/length (via
// the Uint8Array(typedArray) copy ctor) is required to avoid
// handing the Rust side unrelated pooled memory.
new Uint8Array(body),
]);
});
});
req.on('error', reject);
// Bypass Node's headers: the Rust side has already produced the full
// request head in HTTP/1.1 wire format. Setting _header before write()
// makes write/end skip _implicitHeader and _send prepends our bytes.
try {
req._header = Buffer.from(headView);
req.write(bodyView);
req.end();
} catch (err) {
reject(err);
}
})
});
}
function attemptWithRetry() {
return attempt().catch((err) => {
process.stderr.write("httpRequest error: " + err + "\n")
if (isDetachedBufferError(err)) {
return attemptWithRetry();
}
throw err;
});
}
return attemptWithRetry();
};