oracledb 5.4.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1849
- package/LICENSE.txt +8 -222
- package/NOTICE.txt +1 -1
- package/README.md +38 -30
- package/SECURITY.md +38 -0
- package/THIRD_PARTY_LICENSES.txt +1 -1
- package/build/Release/oracledb-6.0.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.0.0-darwin-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.0-js-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.0-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.0.0-linux-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.0-linux-x64.node +0 -0
- package/build/Release/oracledb-6.0.0-linux-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.0-win32-x64.node +0 -0
- package/build/Release/oracledb-6.0.0-win32-x64.node-buildinfo.txt +1 -0
- package/examples/README.md +113 -0
- package/examples/dbconfig.js +100 -0
- package/examples/example.js +156 -0
- package/index.js +26 -0
- package/lib/aqDeqOptions.js +146 -7
- package/lib/aqEnqOptions.js +59 -7
- package/lib/aqMessage.js +118 -7
- package/lib/aqQueue.js +242 -53
- package/lib/connection.js +1324 -465
- package/lib/constants.js +203 -0
- package/lib/dbObject.js +346 -32
- package/lib/errors.js +781 -0
- package/lib/impl/aqDeqOptions.js +197 -0
- package/lib/impl/aqEnqOptions.js +89 -0
- package/lib/impl/aqMessage.js +134 -0
- package/lib/impl/aqQueue.js +81 -0
- package/lib/impl/connection.js +529 -0
- package/lib/impl/dbObject.js +159 -0
- package/lib/impl/index.js +59 -0
- package/lib/impl/lob.js +85 -0
- package/lib/impl/pool.js +195 -0
- package/lib/impl/resultset.js +223 -0
- package/lib/impl/sodaCollection.js +174 -0
- package/lib/impl/sodaDatabase.js +81 -0
- package/lib/impl/sodaDocCursor.js +63 -0
- package/lib/impl/sodaDocument.js +98 -0
- package/lib/impl/sodaOperation.js +109 -0
- package/lib/lob.js +156 -68
- package/lib/oracledb.js +1051 -257
- package/lib/pool.js +654 -465
- package/lib/poolStatistics.js +23 -9
- package/lib/queryStream.js +14 -7
- package/lib/resultset.js +292 -171
- package/lib/settings.js +191 -0
- package/lib/sodaCollection.js +214 -201
- package/lib/sodaDatabase.js +107 -71
- package/lib/sodaDocCursor.js +44 -32
- package/lib/sodaDocument.js +76 -14
- package/lib/sodaOperation.js +131 -118
- package/lib/thin/connection.js +1038 -0
- package/lib/thin/dbObject.js +629 -0
- package/lib/thin/index.js +40 -0
- package/lib/thin/lob.js +188 -0
- package/lib/thin/pool.js +481 -0
- package/lib/thin/protocol/buffer.js +1040 -0
- package/lib/thin/protocol/capabilities.js +116 -0
- package/lib/thin/protocol/constants.js +764 -0
- package/lib/thin/protocol/encryptDecrypt.js +185 -0
- package/lib/thin/protocol/messages/auth.js +348 -0
- package/lib/thin/protocol/messages/base.js +475 -0
- package/lib/thin/protocol/messages/commit.js +56 -0
- package/lib/thin/protocol/messages/dataType.js +382 -0
- package/lib/thin/protocol/messages/execute.js +312 -0
- package/lib/thin/protocol/messages/fetch.js +60 -0
- package/lib/thin/protocol/messages/index.js +53 -0
- package/lib/thin/protocol/messages/lobOp.js +200 -0
- package/lib/thin/protocol/messages/logOff.js +53 -0
- package/lib/thin/protocol/messages/ping.js +56 -0
- package/lib/thin/protocol/messages/protocol.js +78 -0
- package/lib/thin/protocol/messages/rollback.js +56 -0
- package/lib/thin/protocol/messages/sessionRelease.js +60 -0
- package/lib/thin/protocol/messages/withData.js +811 -0
- package/lib/thin/protocol/oson.js +660 -0
- package/lib/thin/protocol/packet.js +520 -0
- package/lib/thin/protocol/protocol.js +202 -0
- package/lib/thin/protocol/utils.js +88 -0
- package/lib/thin/resultSet.js +122 -0
- package/lib/thin/sqlnet/connStrategy.js +175 -0
- package/lib/thin/sqlnet/constants.js +199 -0
- package/lib/thin/sqlnet/ezConnectResolver.js +488 -0
- package/lib/thin/sqlnet/navNodes.js +870 -0
- package/lib/thin/sqlnet/networkSession.js +620 -0
- package/lib/thin/sqlnet/ntTcp.js +525 -0
- package/lib/thin/sqlnet/nvStrToNvPair.js +783 -0
- package/lib/thin/sqlnet/packet.js +405 -0
- package/lib/thin/sqlnet/paramParser.js +205 -0
- package/lib/thin/sqlnet/sessionAtts.js +160 -0
- package/lib/thin/statement.js +301 -0
- package/lib/thin/util.js +289 -0
- package/lib/transformer.js +250 -0
- package/lib/types.js +357 -0
- package/lib/util.js +194 -108
- package/lib/version.js +36 -0
- package/package/README.md +33 -30
- package/package/install.js +89 -64
- package/package/prunebinaries.js +60 -12
- package/package.json +3 -3
- package/build/Release/oracledb-5.4.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-5.4.0-darwin-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-5.4.0-linux-x64.node +0 -0
- package/build/Release/oracledb-5.4.0-linux-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-5.4.0-win32-x64.node +0 -0
- package/build/Release/oracledb-5.4.0-win32-x64.node-buildinfo.txt +0 -1
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
// Copyright (c) 2022, 2023, Oracle and/or its affiliates.
|
|
2
|
+
|
|
3
|
+
//-----------------------------------------------------------------------------
|
|
4
|
+
//
|
|
5
|
+
// This software is dual-licensed to you under the Universal Permissive License
|
|
6
|
+
// (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
|
7
|
+
// 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
|
|
8
|
+
// either license.
|
|
9
|
+
//
|
|
10
|
+
// If you elect to accept the software under the Apache License, Version 2.0,
|
|
11
|
+
// the following applies:
|
|
12
|
+
//
|
|
13
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
14
|
+
// you may not use this file except in compliance with the License.
|
|
15
|
+
// You may obtain a copy of the License at
|
|
16
|
+
//
|
|
17
|
+
// https://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
+
//
|
|
19
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
20
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
+
// See the License for the specific language governing permissions and
|
|
23
|
+
// limitations under the License.
|
|
24
|
+
//
|
|
25
|
+
//-----------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
'use strict';
|
|
28
|
+
|
|
29
|
+
const net = require("net");
|
|
30
|
+
const tls = require("tls");
|
|
31
|
+
const http = require("http");
|
|
32
|
+
const Timers = require('timers');
|
|
33
|
+
const constants = require("./constants.js");
|
|
34
|
+
const errors = require("../../errors.js");
|
|
35
|
+
|
|
36
|
+
const PACKET_HEADER_SIZE = 8;
|
|
37
|
+
const DEFAULT_PORT = 1521;
|
|
38
|
+
const DEFAULT_HTTPS_PROXY_PORT = 80;
|
|
39
|
+
|
|
40
|
+
/* Protocol characteristics */
|
|
41
|
+
const TCPCHA = 1 << 1 | /* ASYNC support */
|
|
42
|
+
1 << 2 | /* Callback support */
|
|
43
|
+
1 << 3 | /* More Data support */
|
|
44
|
+
1 << 8 | /* Read/Write Readiness support */
|
|
45
|
+
1 << 9 | /* Full Duplex support */
|
|
46
|
+
1 << 12; /* SIGPIPE Support */
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Network Transport TCP/TCPS adapter
|
|
50
|
+
* @param {Address} address Destination Address
|
|
51
|
+
* @param {Object} atts Transport Attributes
|
|
52
|
+
*/
|
|
53
|
+
class NTTCP {
|
|
54
|
+
|
|
55
|
+
constructor(atts) {
|
|
56
|
+
this.atts = atts;
|
|
57
|
+
this.cha = TCPCHA;
|
|
58
|
+
this.connected = false;
|
|
59
|
+
this.err = false;
|
|
60
|
+
this.needsDrain = false;
|
|
61
|
+
this.numPacketsSinceLastWait = 0;
|
|
62
|
+
this.secure = false;
|
|
63
|
+
this.largeSDU = false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* DN matching funciton(used with TLS)
|
|
68
|
+
*/
|
|
69
|
+
dnMatch(serverName, cert) {
|
|
70
|
+
if (this.atts.sslServerDNMatch) {
|
|
71
|
+
let toObject = str =>str .split(',').map(x => x.split('=').map(y => y.trim())).reduce((a, x) => {
|
|
72
|
+
a[x[0]] = x[1];
|
|
73
|
+
return a;
|
|
74
|
+
}, {});
|
|
75
|
+
if (this.atts.sslServerCertDN) { /* Full DN Match */
|
|
76
|
+
let obj = toObject(this.atts.sslServerCertDN);
|
|
77
|
+
for (let key in obj) {
|
|
78
|
+
if (obj[key] != cert.subject[key]) {
|
|
79
|
+
return (errors.getErr(errors.ERR_TLS_DNMATCH_FAILURE));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
if (tls.checkServerIdentity(this.hostName, cert) && (!this.originHost || tls.checkServerIdentity(this.originHost, cert))) { /* Hostname match */
|
|
84
|
+
const hostName = this.hostName + " " + (this.originHost ? "or " + this.originHost : "");
|
|
85
|
+
return (errors.getErr(errors.ERR_TLS_HOSTMATCH_FAILURE, hostName));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* TLS connection establishment
|
|
93
|
+
* @returns Promise
|
|
94
|
+
*/
|
|
95
|
+
async tlsConnect(secureContext, connStream) {
|
|
96
|
+
this.stream.removeAllListeners();
|
|
97
|
+
let connectErrCause;
|
|
98
|
+
const tlsOptions = {
|
|
99
|
+
host: this.host,
|
|
100
|
+
socket: connStream,
|
|
101
|
+
rejectUnauthorized: true,
|
|
102
|
+
secureContext: secureContext,
|
|
103
|
+
enableTrace: false,
|
|
104
|
+
checkServerIdentity: this.dnMatch.bind(this)
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
await new Promise((resolve) => {
|
|
108
|
+
this.stream = tls.connect(tlsOptions, () => {
|
|
109
|
+
if (!this.stream.authorized) {
|
|
110
|
+
connectErrCause = "server certificate unauthorized";
|
|
111
|
+
}
|
|
112
|
+
resolve();
|
|
113
|
+
}).on('error', (err) => {
|
|
114
|
+
connectErrCause = err.message;
|
|
115
|
+
resolve();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
if (connectErrCause)
|
|
119
|
+
errors.throwErr(errors.ERR_TLS_AUTH_FAILURE, this.host, this.port, this.atts.connectionId, connectErrCause);
|
|
120
|
+
this.connStream = connStream;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* TCP connection establishment
|
|
125
|
+
* @returns Promise
|
|
126
|
+
*/
|
|
127
|
+
async ntConnect(address) {
|
|
128
|
+
if (!address.port) {
|
|
129
|
+
address.port = DEFAULT_PORT;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let connectErrCause, proxyConnectErrCause, req;
|
|
133
|
+
let httpsProxy = address.httpsProxy || this.atts.httpsProxy;
|
|
134
|
+
let httpsProxyPort = address.httpsProxyPort || this.atts.httpsProxyPort;
|
|
135
|
+
|
|
136
|
+
await new Promise((resolve) => {
|
|
137
|
+
if (httpsProxy) {
|
|
138
|
+
if (!httpsProxyPort) {
|
|
139
|
+
httpsProxyPort = DEFAULT_HTTPS_PROXY_PORT;
|
|
140
|
+
}
|
|
141
|
+
req = http.request({
|
|
142
|
+
host: httpsProxy,
|
|
143
|
+
port: httpsProxyPort,
|
|
144
|
+
method: 'CONNECT',
|
|
145
|
+
path: address.host + ':' + address.port,
|
|
146
|
+
});
|
|
147
|
+
req.once('connect', (res, socket) => {
|
|
148
|
+
if (res.statusCode == 200) {
|
|
149
|
+
this.connected = true;
|
|
150
|
+
this.stream = socket;
|
|
151
|
+
} else {
|
|
152
|
+
proxyConnectErrCause = res.statusCode;
|
|
153
|
+
}
|
|
154
|
+
resolve();
|
|
155
|
+
});
|
|
156
|
+
req.once('error', (err) => {
|
|
157
|
+
proxyConnectErrCause = err.message;
|
|
158
|
+
resolve();
|
|
159
|
+
});
|
|
160
|
+
req.end();
|
|
161
|
+
} else {
|
|
162
|
+
this.stream = net.connect(address.port, address.host, () => {
|
|
163
|
+
this.connected = true;
|
|
164
|
+
resolve();
|
|
165
|
+
});
|
|
166
|
+
this.stream.once('error', (err) => {
|
|
167
|
+
connectErrCause = err.message;
|
|
168
|
+
resolve();
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
if (req)
|
|
173
|
+
req.removeAllListeners();
|
|
174
|
+
if (!this.connected) {
|
|
175
|
+
if (proxyConnectErrCause) {
|
|
176
|
+
errors.throwErr(errors.ERR_PROXY_CONNECTION_FAILURE, httpsProxy, httpsProxyPort, this.atts.connectionId, proxyConnectErrCause);
|
|
177
|
+
} else {
|
|
178
|
+
errors.throwErr(errors.ERR_CONNECTION_INCOMPLETE, this.host, this.port, this.atts.connectionId, connectErrCause);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Network Transport connection establishment
|
|
185
|
+
* @returns Promise
|
|
186
|
+
*/
|
|
187
|
+
async connect(address) { /* Connect function for TCP sockets */
|
|
188
|
+
this.originHost = address.originHost;
|
|
189
|
+
this.host = address.host;
|
|
190
|
+
this.hostName = address.hostname;
|
|
191
|
+
this.port = address.port;
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
await this.ntConnect(address);
|
|
195
|
+
if (this.atts.expireTime || this.atts.enableDCD) { /* Set Keep alives */
|
|
196
|
+
if (this.atts.expireTime) {
|
|
197
|
+
this.stream.setKeepAlive(true, this.atts.expireTime);
|
|
198
|
+
} else {
|
|
199
|
+
this.stream.setKeepAlive(true);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (this.atts.tcpNoDelay) { /* Turn off Nagle's unless explicitly enabled by user */
|
|
203
|
+
this.stream.setNoDelay(true);
|
|
204
|
+
}
|
|
205
|
+
if (address.protocol.toUpperCase() == "TCPS") {
|
|
206
|
+
let secureContext;
|
|
207
|
+
this.secure = true;
|
|
208
|
+
try {
|
|
209
|
+
secureContext = tls.createSecureContext({
|
|
210
|
+
cert : this.atts.wallet,
|
|
211
|
+
key : this.atts.wallet,
|
|
212
|
+
passphrase: this.atts.walletPassword,
|
|
213
|
+
ca : this.atts.wallet,
|
|
214
|
+
});
|
|
215
|
+
} catch (err) {
|
|
216
|
+
errors.throwErr(errors.ERR_TLS_INIT_FAILURE);
|
|
217
|
+
}
|
|
218
|
+
await this.tlsConnect(secureContext, this.stream);
|
|
219
|
+
}
|
|
220
|
+
} finally {
|
|
221
|
+
if (this.stream) {
|
|
222
|
+
this.setupEventHandlers();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Disconnect Network Transoprt
|
|
229
|
+
* @param {int} type
|
|
230
|
+
* @returns Proimise
|
|
231
|
+
*/
|
|
232
|
+
disconnect(type) { /* Disconnect function for TCP sockets */
|
|
233
|
+
if (this.connected && !this.err) {
|
|
234
|
+
if (type == constants.NSFIMM)
|
|
235
|
+
this.stream.destroy();
|
|
236
|
+
else
|
|
237
|
+
this.stream.end();
|
|
238
|
+
}
|
|
239
|
+
this.stream = null;
|
|
240
|
+
this.connected = false;
|
|
241
|
+
this.waiter = null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get the string containing a packet dump.
|
|
246
|
+
* @param {Buffer} buffer containing packet data
|
|
247
|
+
*/
|
|
248
|
+
getPacketDump(buffer) {
|
|
249
|
+
const lines = [];
|
|
250
|
+
for (let i = 0; i < buffer.length; i += 8) {
|
|
251
|
+
const address = i.toString().padStart(4, '0');
|
|
252
|
+
const block = buffer.slice(i, i + 8);
|
|
253
|
+
const hexDumpValues = [];
|
|
254
|
+
const printableValues = [];
|
|
255
|
+
for (let hexByte of block) {
|
|
256
|
+
hexDumpValues.push(hexByte.toString(16).toUpperCase().padStart(2, '0'));
|
|
257
|
+
if (hexByte > 0x20 && hexByte < 0x7f) {
|
|
258
|
+
printableValues.push(String.fromCharCode(hexByte));
|
|
259
|
+
} else {
|
|
260
|
+
printableValues.push(".");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
while (hexDumpValues.length < 8) {
|
|
264
|
+
hexDumpValues.push(" ");
|
|
265
|
+
printableValues.push(" ");
|
|
266
|
+
}
|
|
267
|
+
const hexValuesBlock = hexDumpValues.join(" ");
|
|
268
|
+
const printableBlock = printableValues.join("");
|
|
269
|
+
lines.push(`${address} : ${hexValuesBlock} |${printableBlock}|`);
|
|
270
|
+
}
|
|
271
|
+
return lines.join("\n");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Print the packet to the console.
|
|
276
|
+
* @param {String} operation which was performed
|
|
277
|
+
* @param {Buffer} buffer containing packet data
|
|
278
|
+
*/
|
|
279
|
+
printPacket(operation, buffer) {
|
|
280
|
+
const now = new Date();
|
|
281
|
+
const formattedDate =
|
|
282
|
+
`${now.getFullYear()}-${now.getMonth().toString().padStart(2, '0')}-` +
|
|
283
|
+
`${now.getDay().toString().padStart(2, '0')} ` +
|
|
284
|
+
`${now.getHours().toString().padStart(2, '0')}:` +
|
|
285
|
+
`${now.getMinutes().toString().padStart(2, '0')}:` +
|
|
286
|
+
`${now.getSeconds().toString().padStart(2, '0')}.` +
|
|
287
|
+
`${now.getMilliseconds().toString().padStart(3, '0')}`;
|
|
288
|
+
const packetDump = this.getPacketDump(buffer);
|
|
289
|
+
console.log(`${formattedDate} ${operation}:\n${packetDump}\n`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Check for errors
|
|
294
|
+
*/
|
|
295
|
+
checkErr() {
|
|
296
|
+
if (!this.connected || this.err) {
|
|
297
|
+
let err, newErr;
|
|
298
|
+
if (this.savedErr) {
|
|
299
|
+
err = errors.getErr(errors.ERR_CONNECTION_LOSTCONTACT,
|
|
300
|
+
this.host, this.port, this.atts.connectionId, this.savedErr.message);
|
|
301
|
+
} else {
|
|
302
|
+
err = errors.getErr(errors.ERR_CONNECTION_EOF, this.host, this.port, this.atts.connectionId,);
|
|
303
|
+
}
|
|
304
|
+
/* Wrap around NJS-500 */
|
|
305
|
+
newErr = errors.getErr(errors.ERR_CONNECTION_CLOSED);
|
|
306
|
+
newErr.message = newErr.message + "\n" + err.message;
|
|
307
|
+
throw (newErr);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Transport Send
|
|
313
|
+
* @param {Buffer} buf Buffer to send
|
|
314
|
+
* @returns Promise
|
|
315
|
+
*/
|
|
316
|
+
send(buf) {
|
|
317
|
+
this.checkErr();
|
|
318
|
+
if (process.env.NODE_ORACLEDB_DEBUG_PACKETS)
|
|
319
|
+
this.printPacket("Sending packet", buf);
|
|
320
|
+
const result = this.stream.write(buf, (err) => {
|
|
321
|
+
if (err) {
|
|
322
|
+
this.savedErr = err;
|
|
323
|
+
this.err = true;
|
|
324
|
+
if (this.waiter) {
|
|
325
|
+
this.waiter();
|
|
326
|
+
this.waiter = null;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
if (!result) {
|
|
331
|
+
this.needsDrain = true;
|
|
332
|
+
}
|
|
333
|
+
this.numPacketsSinceLastWait++;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Should writing to the transport be paused? This occurs if draining is
|
|
338
|
+
* required or if the number of packets written since the last pause exceeds
|
|
339
|
+
* 100 (in order to avoid starvation of the event loop during large writes).
|
|
340
|
+
*/
|
|
341
|
+
shouldPauseWrite() {
|
|
342
|
+
return (this.needsDrain || this.numPacketsSinceLastWait >= 100);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Perform a wait -- if draining is required, then until the drain event is
|
|
347
|
+
* emitted or if draining is not required, then a simple setImmediate() that
|
|
348
|
+
* ensures that the event loop is not starved.
|
|
349
|
+
*/
|
|
350
|
+
async pauseWrite() {
|
|
351
|
+
this.checkErr();
|
|
352
|
+
if (this.needsDrain) {
|
|
353
|
+
await new Promise((resolve) => {
|
|
354
|
+
this.waiter = resolve;
|
|
355
|
+
this.stream.once('drain', () => {
|
|
356
|
+
resolve();
|
|
357
|
+
this.waiter = null;
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
this.checkErr();
|
|
361
|
+
} else {
|
|
362
|
+
await new Promise((resolve) => Timers.setImmediate(resolve));
|
|
363
|
+
}
|
|
364
|
+
this.numPacketsSinceLastWait = 0;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Start Async reads
|
|
369
|
+
*/
|
|
370
|
+
startRead() {
|
|
371
|
+
let tempBuf;
|
|
372
|
+
this.packets = [];
|
|
373
|
+
this.stream.on('data', (chunk) => {
|
|
374
|
+
|
|
375
|
+
// append buffer if previous chunk(s) were insufficient for a full packet
|
|
376
|
+
if (tempBuf) {
|
|
377
|
+
tempBuf = Buffer.concat([tempBuf, chunk]);
|
|
378
|
+
} else {
|
|
379
|
+
tempBuf = chunk;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
while (tempBuf.length >= PACKET_HEADER_SIZE) {
|
|
383
|
+
|
|
384
|
+
// determine the length of the packet
|
|
385
|
+
let len;
|
|
386
|
+
if (this.largeSDU) {
|
|
387
|
+
len = tempBuf.readUInt32BE();
|
|
388
|
+
} else {
|
|
389
|
+
len = tempBuf.readUInt16BE();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// not enough for a full packet so wait for more data to arrive
|
|
393
|
+
if (len > tempBuf.length)
|
|
394
|
+
break;
|
|
395
|
+
|
|
396
|
+
// enough for a full packet, extract details from the packet header
|
|
397
|
+
// and pass them along for processing
|
|
398
|
+
const packet = {
|
|
399
|
+
buf: tempBuf.subarray(0, len),
|
|
400
|
+
type: tempBuf[4],
|
|
401
|
+
flags: tempBuf[5]
|
|
402
|
+
};
|
|
403
|
+
this.packets.push(packet);
|
|
404
|
+
if (this.waiter) {
|
|
405
|
+
this.waiter();
|
|
406
|
+
this.waiter = null;
|
|
407
|
+
}
|
|
408
|
+
if (process.env.NODE_ORACLEDB_DEBUG_PACKETS)
|
|
409
|
+
this.printPacket("Receiving packet", packet.buf);
|
|
410
|
+
|
|
411
|
+
// if the packet consumed all of the bytes (most common scenario), then
|
|
412
|
+
// simply clear the temporary buffer; otherwise, retain whatever bytes
|
|
413
|
+
// are unused and see if sufficient data is available for another
|
|
414
|
+
// packet
|
|
415
|
+
if (len === tempBuf.length) {
|
|
416
|
+
tempBuf = null;
|
|
417
|
+
break;
|
|
418
|
+
} else {
|
|
419
|
+
tempBuf = tempBuf.subarray(len);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Synchronous receive
|
|
429
|
+
* @returns a single packet or undefined if no packets are available
|
|
430
|
+
*/
|
|
431
|
+
syncReceive() {
|
|
432
|
+
return this.packets.shift();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Asynchronous receive
|
|
437
|
+
* @returns a single packet
|
|
438
|
+
*/
|
|
439
|
+
async receive() {
|
|
440
|
+
if (this.packets.length === 0) {
|
|
441
|
+
this.checkErr();
|
|
442
|
+
await new Promise((resolve) => {
|
|
443
|
+
this.waiter = resolve;
|
|
444
|
+
this.numPacketsSinceLastWait = 0;
|
|
445
|
+
});
|
|
446
|
+
this.checkErr();
|
|
447
|
+
}
|
|
448
|
+
return this.packets.shift();
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* TLS renegotiate
|
|
453
|
+
* @returns Promise
|
|
454
|
+
*/
|
|
455
|
+
async renegTLS() {
|
|
456
|
+
try {
|
|
457
|
+
this.checkErr();
|
|
458
|
+
let secureContext = tls.createSecureContext({
|
|
459
|
+
cert : this.atts.wallet,
|
|
460
|
+
key : this.atts.wallet,
|
|
461
|
+
passphrase: this.atts.walletPassword,
|
|
462
|
+
ca : this.atts.wallet,
|
|
463
|
+
});
|
|
464
|
+
await this.tlsConnect(secureContext, this.connStream);
|
|
465
|
+
} finally {
|
|
466
|
+
this.setupEventHandlers();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Setup handling of events
|
|
472
|
+
*/
|
|
473
|
+
setupEventHandlers() {
|
|
474
|
+
this.stream.removeAllListeners();
|
|
475
|
+
|
|
476
|
+
this.stream.on('error', (err) => {
|
|
477
|
+
this.savedErr = err;
|
|
478
|
+
this.err = true;
|
|
479
|
+
if (this.waiter) {
|
|
480
|
+
this.waiter();
|
|
481
|
+
this.waiter = null;
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
this.stream.on('end', () => {
|
|
486
|
+
this.err = true;
|
|
487
|
+
if (this.waiter) {
|
|
488
|
+
this.waiter();
|
|
489
|
+
this.waiter = null;
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
this.stream.on('close', () => {
|
|
494
|
+
this.connected = false;
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
this.stream.on('drain', () => {
|
|
498
|
+
this.needsDrain = false;
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Get Transport Attributes
|
|
505
|
+
* @param {int} opcode type of attribute
|
|
506
|
+
* @returns attribute value
|
|
507
|
+
*/
|
|
508
|
+
getOption(opcode) {
|
|
509
|
+
this.checkErr();
|
|
510
|
+
switch (opcode) {
|
|
511
|
+
case constants.NT_MOREDATA: /* More data available to read */
|
|
512
|
+
return (this.packets.length > 0);
|
|
513
|
+
case constants.REMOTEADDR: /* Remote Address */
|
|
514
|
+
{
|
|
515
|
+
let socket = this.secure ? this.connStream : this.stream;
|
|
516
|
+
return (socket.remoteAddress + ":" + socket.remotePort);
|
|
517
|
+
}
|
|
518
|
+
default:
|
|
519
|
+
errors.throwErr(errors.ERR_INTERNAL, "getOption not supported for opcode " + opcode);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
module.exports = NTTCP;
|