node_modules: update (#344)

Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com>
This commit is contained in:
Dawid Dziurlaanddawidd6 authored and GitHub committed 2026-10-10 17:27:57 +02:00
1 parent b099186883
commit 6e88c72525
63 files changed
+2863 -1227

No files matched your search

+106 -17
View File
@@ -52,6 +52,8 @@ export default class PoolResource extends EventEmitter {
this.messages = 0;
this.available = true;
this._failed = false;
this._sending = false;
this._idleTimer = false;
}
/**
* Emits 'error' for the first failure only. A dead resource can report the same failure more
@@ -71,7 +73,9 @@ export default class PoolResource extends EventEmitter {
* @param callback Callback function to run once the connection is established or failed
*/
connect(callback) {
this.pool.getSocket(this.options, (err, socketOptions) => {
// the proxy handshake, if any, counts against connectionTimeout as well
const connectStartedAt = Date.now();
this.pool.getSocket(Object.assign({}, this.options, { connectStartedAt }), (err, socketOptions) => {
if (err) {
// nothing was connected, so no 'close' event is coming that would free the
// slot this resource holds in the pool, report the failure the way a failed
@@ -93,7 +97,21 @@ export default class PoolResource extends EventEmitter {
options = Object.assign(assign(false, options), socketOptions);
}
this.connection = new SMTPConnection(options);
this.connection.on('error', err => {
this.connection._connectStartedAt = connectStartedAt;
this.connection.on('error', (err) => {
if (this._sending) {
// the send callback gets the same error and decides what it means for the message
return;
}
if (this._connected && errors.isTransientError(err)) {
// the server ended a connection that had nothing in flight, usually after it
// was idle for a while. The 'end' that follows closes this resource
this.logger.info({
tnx: 'pool',
cid: this.id
}, 'Connection #%s was closed by the server: %s', this.id, err.message);
return;
}
this._fail(err);
if (returned) {
return;
@@ -171,9 +189,39 @@ export default class PoolResource extends EventEmitter {
if (mail.data.requireTLSExtensionEnabled) {
envelope.requireTLSExtensionEnabled = mail.data.requireTLSExtensionEnabled;
}
this.connection.send(envelope, mail.message.createReadStream(), (err, info) => {
this._sending = true;
// a connection that sent messages before may have been dropped by the server in between
const reused = this.messages > 0;
const messageStream = mail.message.createReadStream();
this.connection.send(envelope, messageStream, (err, info) => {
this._sending = false;
this.messages++;
if (err) {
if (reused && messageStream.readableDidRead === false && errors.isTransientError(err)) {
// Not a byte of the message was sent, so it can go out over another
// connection. The pool requeues the message when this resource closes
this.logger.info({
tnx: 'pool',
cid: this.id,
messageId
}, 'Connection #%s was closed by the server before message %s was sent: %s', this.id, messageId, err.message);
this.connection.close();
return;
}
if ((err.code === errors.EENVELOPE || err.code === errors.EMESSAGE) &&
err.responseCode !== 421 &&
!this.connection._destroyed) {
// The server refused this message, the connection itself is fine. Reset the
// session and keep using it instead of opening a new one
this.connection.reset(resetErr => {
if (resetErr) {
this.connection.close();
return;
}
this._release();
});
return callback(err);
}
this.connection.close();
this._fail(err);
return callback(err);
@@ -183,28 +231,69 @@ export default class PoolResource extends EventEmitter {
to: envelope.to
};
info.messageId = messageId;
setImmediate(() => {
if (this.messages >= this.options.maxMessages) {
const err = new Error('Resource exhausted');
err.code = errors.EMAXLIMIT;
this.connection.close();
this._fail(err);
}
else {
this.pool._checkRateLimit(() => {
this.available = true;
this.emit('available');
});
}
});
setImmediate(() => this._release());
callback(null, info);
});
}
/**
* Makes the connection available for the next message, or closes it once it has sent
* maxMessages messages
*
* @internal
*/
_release() {
if (this.messages >= this.options.maxMessages) {
const err = new Error('Resource exhausted');
err.code = errors.EMAXLIMIT;
this.connection.close();
this._fail(err);
return;
}
this.pool._checkRateLimit(() => {
this.available = true;
this._startIdleTimer();
this.emit('available');
});
}
/** @internal */
_startIdleTimer() {
if (!this.options.idleTimeout || this.options.idleTimeout < 0) {
return;
}
// one timer per connection, restarted every time the connection becomes available. A
// connection that is busy when it fires is simply not closed
if (this._idleTimer && typeof this._idleTimer.refresh === 'function') {
this._idleTimer.refresh();
return;
}
clearTimeout(this._idleTimer);
this._idleTimer = setTimeout(() => {
if (!this.available) {
return;
}
this.logger.debug({
tnx: 'pool',
cid: this.id
}, 'Closing connection #%s after it was idle for %sms', this.id, this.options.idleTimeout);
// not handed another message while it says goodbye
this.available = false;
this.connection.quit();
}, this.options.idleTimeout);
if (typeof this._idleTimer.unref === 'function') {
this._idleTimer.unref();
}
}
/** @internal */
_stopIdleTimer() {
clearTimeout(this._idleTimer);
this._idleTimer = false;
}
/**
* Closes the connection
*/
close() {
this._connected = false;
this._stopIdleTimer();
if (this.auth && this.auth.oauth2) {
this.auth.oauth2.removeAllListeners();
}