@mavogel/awscdk-rootmail 0.1.14 → 0.1.16
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/.jsii +10 -10
- package/lib/rootmail.js +1 -1
- package/lib/ses-receive.js +1 -1
- package/node_modules/@aws-sdk/checksums/package.json +3 -3
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-cjs/index.js +23 -1
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-es/models/CloudWatchLogsServiceException.js +1 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-es/models/errors.js +21 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/models/CloudWatchLogsServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/models/errors.d.ts +21 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/ts3.4/models/CloudWatchLogsServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/ts3.4/models/errors.d.ts +21 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/package.json +4 -4
- package/node_modules/@aws-sdk/client-route-53/dist-cjs/index.js +71 -1
- package/node_modules/@aws-sdk/client-route-53/dist-es/models/Route53ServiceException.js +1 -0
- package/node_modules/@aws-sdk/client-route-53/dist-es/models/errors.js +69 -0
- package/node_modules/@aws-sdk/client-route-53/dist-types/models/Route53ServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-route-53/dist-types/models/errors.d.ts +69 -0
- package/node_modules/@aws-sdk/client-route-53/dist-types/ts3.4/models/Route53ServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-route-53/dist-types/ts3.4/models/errors.d.ts +69 -0
- package/node_modules/@aws-sdk/client-route-53/package.json +4 -4
- package/node_modules/@aws-sdk/client-s3/dist-cjs/index.js +23 -1
- package/node_modules/@aws-sdk/client-s3/dist-es/models/S3ServiceException.js +1 -0
- package/node_modules/@aws-sdk/client-s3/dist-es/models/errors.js +21 -0
- package/node_modules/@aws-sdk/client-s3/dist-types/models/S3ServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-s3/dist-types/models/errors.d.ts +21 -0
- package/node_modules/@aws-sdk/client-s3/dist-types/ts3.4/models/S3ServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-s3/dist-types/ts3.4/models/errors.d.ts +21 -0
- package/node_modules/@aws-sdk/client-s3/package.json +8 -8
- package/node_modules/@aws-sdk/client-ses/dist-cjs/index.js +36 -1
- package/node_modules/@aws-sdk/client-ses/dist-es/models/SESServiceException.js +1 -0
- package/node_modules/@aws-sdk/client-ses/dist-es/models/errors.js +34 -0
- package/node_modules/@aws-sdk/client-ses/dist-types/models/SESServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-ses/dist-types/models/errors.d.ts +34 -0
- package/node_modules/@aws-sdk/client-ses/dist-types/ts3.4/models/SESServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-ses/dist-types/ts3.4/models/errors.d.ts +34 -0
- package/node_modules/@aws-sdk/client-ses/package.json +4 -4
- package/node_modules/@aws-sdk/client-ssm/dist-cjs/index.js +142 -1
- package/node_modules/@aws-sdk/client-ssm/dist-es/models/SSMServiceException.js +1 -0
- package/node_modules/@aws-sdk/client-ssm/dist-es/models/errors.js +140 -0
- package/node_modules/@aws-sdk/client-ssm/dist-types/models/SSMServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-ssm/dist-types/models/errors.d.ts +140 -0
- package/node_modules/@aws-sdk/client-ssm/dist-types/ts3.4/models/SSMServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-ssm/dist-types/ts3.4/models/errors.d.ts +140 -0
- package/node_modules/@aws-sdk/client-ssm/package.json +4 -4
- package/node_modules/@aws-sdk/core/package.json +3 -3
- package/node_modules/@aws-sdk/credential-provider-env/package.json +3 -3
- package/node_modules/@aws-sdk/credential-provider-http/package.json +3 -3
- package/node_modules/@aws-sdk/credential-provider-ini/package.json +10 -10
- package/node_modules/@aws-sdk/credential-provider-login/package.json +4 -4
- package/node_modules/@aws-sdk/credential-provider-node/package.json +8 -8
- package/node_modules/@aws-sdk/credential-provider-process/package.json +3 -3
- package/node_modules/@aws-sdk/credential-provider-sso/package.json +5 -5
- package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +4 -4
- package/node_modules/@aws-sdk/middleware-sdk-s3/package.json +4 -4
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +11 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +6 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +6 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +13 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +9 -1
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/models/CognitoIdentityServiceException.js +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/models/errors.js +9 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/models/SigninServiceException.js +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/models/errors.js +4 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/models/SSOServiceException.js +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/models/errors.js +4 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/models/SSOOIDCServiceException.js +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/models/errors.js +11 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/models/STSServiceException.js +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/models/errors.js +7 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/cognito-identity/models/CognitoIdentityServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/cognito-identity/models/errors.d.ts +9 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/signin/models/SigninServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/signin/models/errors.d.ts +4 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso/models/SSOServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso/models/errors.d.ts +4 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso-oidc/models/SSOOIDCServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso-oidc/models/errors.d.ts +11 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sts/models/STSServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sts/models/errors.d.ts +7 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/cognito-identity/models/CognitoIdentityServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/cognito-identity/models/errors.d.ts +9 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/signin/models/SigninServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/signin/models/errors.d.ts +4 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sso/models/SSOServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sso/models/errors.d.ts +4 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sso-oidc/models/SSOOIDCServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sso-oidc/models/errors.d.ts +11 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sts/models/STSServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sts/models/errors.d.ts +7 -0
- package/node_modules/@aws-sdk/nested-clients/package.json +4 -4
- package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +2 -2
- package/node_modules/@aws-sdk/token-providers/package.json +4 -4
- package/node_modules/@smithy/core/dist-cjs/submodules/client/index.js +33 -11
- package/node_modules/@smithy/core/dist-cjs/submodules/endpoints/index.browser.js +2 -1
- package/node_modules/@smithy/core/dist-cjs/submodules/endpoints/index.js +2 -1
- package/node_modules/@smithy/core/dist-cjs/submodules/schema/index.js +3 -0
- package/node_modules/@smithy/core/dist-es/submodules/client/smithy-client/exceptions.js +33 -11
- package/node_modules/@smithy/core/dist-es/submodules/endpoints/util-endpoints/cache/EndpointCache.js +2 -1
- package/node_modules/@smithy/core/dist-es/submodules/schema/TypeRegistry.js +3 -0
- package/node_modules/@smithy/core/dist-types/submodules/client/smithy-client/exceptions.d.ts +1 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/client/smithy-client/exceptions.d.ts +1 -0
- package/node_modules/@smithy/core/package.json +1 -1
- package/node_modules/@smithy/signature-v4/dist-cjs/index.js +4 -1
- package/node_modules/@smithy/signature-v4/dist-es/getCanonicalHeaders.js +4 -1
- package/node_modules/@smithy/signature-v4/package.json +3 -3
- package/node_modules/mailparser/.release-please-manifest.json +1 -1
- package/node_modules/mailparser/CHANGELOG.md +7 -0
- package/node_modules/mailparser/package.json +2 -2
- package/node_modules/nodemailer/CHANGELOG.md +27 -0
- package/node_modules/nodemailer/dist/cjs/base64/index.d.ts +3 -0
- package/node_modules/nodemailer/dist/cjs/base64/index.js +80 -41
- package/node_modules/nodemailer/dist/cjs/dkim/index.js +43 -29
- package/node_modules/nodemailer/dist/cjs/dkim/message-parser.js +2 -2
- package/node_modules/nodemailer/dist/cjs/errors.d.ts +17 -0
- package/node_modules/nodemailer/dist/cjs/errors.js +12 -1
- package/node_modules/nodemailer/dist/cjs/fetch/index.js +23 -3
- package/node_modules/nodemailer/dist/cjs/mailer/index.js +23 -7
- package/node_modules/nodemailer/dist/cjs/mailer/mail-message.d.ts +5 -0
- package/node_modules/nodemailer/dist/cjs/mailer/mail-message.js +20 -0
- package/node_modules/nodemailer/dist/cjs/mime-node/index.d.ts +1 -1
- package/node_modules/nodemailer/dist/cjs/mime-node/index.js +165 -105
- package/node_modules/nodemailer/dist/cjs/nodemailer.js +12 -9
- package/node_modules/nodemailer/dist/cjs/package-info.d.ts +1 -1
- package/node_modules/nodemailer/dist/cjs/package-info.js +1 -1
- package/node_modules/nodemailer/dist/cjs/qp/index.d.ts +2 -0
- package/node_modules/nodemailer/dist/cjs/qp/index.js +103 -33
- package/node_modules/nodemailer/dist/cjs/sendmail-transport/index.js +54 -14
- package/node_modules/nodemailer/dist/cjs/shared/index.d.ts +32 -1
- package/node_modules/nodemailer/dist/cjs/shared/index.js +212 -115
- package/node_modules/nodemailer/dist/cjs/smtp-connection/http-proxy-client.d.ts +3 -1
- package/node_modules/nodemailer/dist/cjs/smtp-connection/http-proxy-client.js +88 -68
- package/node_modules/nodemailer/dist/cjs/smtp-connection/index.d.ts +7 -1
- package/node_modules/nodemailer/dist/cjs/smtp-connection/index.js +348 -136
- package/node_modules/nodemailer/dist/cjs/smtp-pool/index.d.ts +3 -0
- package/node_modules/nodemailer/dist/cjs/smtp-pool/index.js +7 -1
- package/node_modules/nodemailer/dist/cjs/smtp-pool/pool-resource.js +106 -17
- package/node_modules/nodemailer/dist/cjs/smtp-transport/index.js +8 -2
- package/node_modules/nodemailer/dist/esm/base64/index.d.ts +3 -0
- package/node_modules/nodemailer/dist/esm/base64/index.js +80 -41
- package/node_modules/nodemailer/dist/esm/dkim/index.js +43 -29
- package/node_modules/nodemailer/dist/esm/dkim/message-parser.js +2 -2
- package/node_modules/nodemailer/dist/esm/errors.d.ts +17 -0
- package/node_modules/nodemailer/dist/esm/errors.js +10 -0
- package/node_modules/nodemailer/dist/esm/fetch/index.js +23 -3
- package/node_modules/nodemailer/dist/esm/mailer/index.js +23 -7
- package/node_modules/nodemailer/dist/esm/mailer/mail-message.d.ts +5 -0
- package/node_modules/nodemailer/dist/esm/mailer/mail-message.js +20 -0
- package/node_modules/nodemailer/dist/esm/mime-node/index.d.ts +1 -1
- package/node_modules/nodemailer/dist/esm/mime-node/index.js +166 -106
- package/node_modules/nodemailer/dist/esm/nodemailer.js +12 -9
- package/node_modules/nodemailer/dist/esm/package-info.d.ts +1 -1
- package/node_modules/nodemailer/dist/esm/package-info.js +1 -1
- package/node_modules/nodemailer/dist/esm/qp/index.d.ts +2 -0
- package/node_modules/nodemailer/dist/esm/qp/index.js +103 -33
- package/node_modules/nodemailer/dist/esm/sendmail-transport/index.js +54 -14
- package/node_modules/nodemailer/dist/esm/shared/index.d.ts +32 -1
- package/node_modules/nodemailer/dist/esm/shared/index.js +207 -115
- package/node_modules/nodemailer/dist/esm/smtp-connection/http-proxy-client.d.ts +3 -1
- package/node_modules/nodemailer/dist/esm/smtp-connection/http-proxy-client.js +88 -68
- package/node_modules/nodemailer/dist/esm/smtp-connection/index.d.ts +7 -1
- package/node_modules/nodemailer/dist/esm/smtp-connection/index.js +348 -136
- package/node_modules/nodemailer/dist/esm/smtp-pool/index.d.ts +3 -0
- package/node_modules/nodemailer/dist/esm/smtp-pool/index.js +7 -1
- package/node_modules/nodemailer/dist/esm/smtp-pool/pool-resource.js +106 -17
- package/node_modules/nodemailer/dist/esm/smtp-transport/index.js +8 -2
- package/node_modules/nodemailer/package.json +1 -1
- package/node_modules/uuid/package.json +17 -17
- package/package.json +9 -9
|
@@ -19,6 +19,8 @@ export interface SMTPPoolOptions extends SMTPTransportOptions {
|
|
|
19
19
|
rateDelta?: number | undefined;
|
|
20
20
|
/** How many times a message is requeued when its connection closes while sending, defaults to 5, a negative value means unlimited */
|
|
21
21
|
maxRequeues?: number | undefined;
|
|
22
|
+
/** Time in milliseconds a connection may stay idle before it is closed, defaults to 4 minutes, 0 keeps it open until the server closes it */
|
|
23
|
+
idleTimeout?: number | undefined;
|
|
22
24
|
}
|
|
23
25
|
/**
|
|
24
26
|
* The pool options once the constructor has applied the defaults
|
|
@@ -27,6 +29,7 @@ export type SMTPPoolResolvedOptions = SMTPPoolOptions & {
|
|
|
27
29
|
maxConnections: number;
|
|
28
30
|
maxMessages: number;
|
|
29
31
|
maxRequeues: number;
|
|
32
|
+
idleTimeout: number;
|
|
30
33
|
};
|
|
31
34
|
/**
|
|
32
35
|
* Result of a message sent through the pool, same as for the SMTP transport
|
|
@@ -80,6 +80,9 @@ class SMTPPool extends node_events_1.EventEmitter {
|
|
|
80
80
|
this.options.maxMessages = this.options.maxMessages || 100;
|
|
81
81
|
// a default bound, a server that closes every connection before the greeting would otherwise be retried forever
|
|
82
82
|
this.options.maxRequeues = typeof this.options.maxRequeues === 'number' ? this.options.maxRequeues : 5;
|
|
83
|
+
// below the 5 minutes RFC 5321 asks servers to wait at least, the connection is closed by us
|
|
84
|
+
// and not by the server in the middle of handing it a message
|
|
85
|
+
this.options.idleTimeout = typeof this.options.idleTimeout === 'number' ? this.options.idleTimeout : 4 * 60 * 1000;
|
|
83
86
|
this.logger = shared.getLogger(this.options, {
|
|
84
87
|
component: this.options.component || 'smtp-pool'
|
|
85
88
|
});
|
|
@@ -486,7 +489,9 @@ class SMTPPool extends node_events_1.EventEmitter {
|
|
|
486
489
|
// the error paths hand over the error alone
|
|
487
490
|
const done = callback;
|
|
488
491
|
const auth = new pool_resource_js_1.default(this).auth;
|
|
489
|
-
|
|
492
|
+
// the proxy handshake, if any, counts against connectionTimeout as well
|
|
493
|
+
const connectStartedAt = Date.now();
|
|
494
|
+
this.getSocket(Object.assign({}, this.options, { connectStartedAt }), (err, socketOptions) => {
|
|
490
495
|
if (err) {
|
|
491
496
|
return done(err);
|
|
492
497
|
}
|
|
@@ -503,6 +508,7 @@ class SMTPPool extends node_events_1.EventEmitter {
|
|
|
503
508
|
options = Object.assign(shared.assign(false, options), socketOptions);
|
|
504
509
|
}
|
|
505
510
|
const connection = new index_js_1.default(options);
|
|
511
|
+
connection._connectStartedAt = connectStartedAt;
|
|
506
512
|
let returned = false;
|
|
507
513
|
connection.once('error', err => {
|
|
508
514
|
if (returned) {
|
|
@@ -90,6 +90,8 @@ class PoolResource extends node_events_1.EventEmitter {
|
|
|
90
90
|
this.messages = 0;
|
|
91
91
|
this.available = true;
|
|
92
92
|
this._failed = false;
|
|
93
|
+
this._sending = false;
|
|
94
|
+
this._idleTimer = false;
|
|
93
95
|
}
|
|
94
96
|
/**
|
|
95
97
|
* Emits 'error' for the first failure only. A dead resource can report the same failure more
|
|
@@ -109,7 +111,9 @@ class PoolResource extends node_events_1.EventEmitter {
|
|
|
109
111
|
* @param callback Callback function to run once the connection is established or failed
|
|
110
112
|
*/
|
|
111
113
|
connect(callback) {
|
|
112
|
-
|
|
114
|
+
// the proxy handshake, if any, counts against connectionTimeout as well
|
|
115
|
+
const connectStartedAt = Date.now();
|
|
116
|
+
this.pool.getSocket(Object.assign({}, this.options, { connectStartedAt }), (err, socketOptions) => {
|
|
113
117
|
if (err) {
|
|
114
118
|
// nothing was connected, so no 'close' event is coming that would free the
|
|
115
119
|
// slot this resource holds in the pool, report the failure the way a failed
|
|
@@ -131,7 +135,21 @@ class PoolResource extends node_events_1.EventEmitter {
|
|
|
131
135
|
options = Object.assign((0, index_js_2.assign)(false, options), socketOptions);
|
|
132
136
|
}
|
|
133
137
|
this.connection = new index_js_1.default(options);
|
|
134
|
-
this.connection.
|
|
138
|
+
this.connection._connectStartedAt = connectStartedAt;
|
|
139
|
+
this.connection.on('error', (err) => {
|
|
140
|
+
if (this._sending) {
|
|
141
|
+
// the send callback gets the same error and decides what it means for the message
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (this._connected && errors.isTransientError(err)) {
|
|
145
|
+
// the server ended a connection that had nothing in flight, usually after it
|
|
146
|
+
// was idle for a while. The 'end' that follows closes this resource
|
|
147
|
+
this.logger.info({
|
|
148
|
+
tnx: 'pool',
|
|
149
|
+
cid: this.id
|
|
150
|
+
}, 'Connection #%s was closed by the server: %s', this.id, err.message);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
135
153
|
this._fail(err);
|
|
136
154
|
if (returned) {
|
|
137
155
|
return;
|
|
@@ -209,9 +227,39 @@ class PoolResource extends node_events_1.EventEmitter {
|
|
|
209
227
|
if (mail.data.requireTLSExtensionEnabled) {
|
|
210
228
|
envelope.requireTLSExtensionEnabled = mail.data.requireTLSExtensionEnabled;
|
|
211
229
|
}
|
|
212
|
-
this.
|
|
230
|
+
this._sending = true;
|
|
231
|
+
// a connection that sent messages before may have been dropped by the server in between
|
|
232
|
+
const reused = this.messages > 0;
|
|
233
|
+
const messageStream = mail.message.createReadStream();
|
|
234
|
+
this.connection.send(envelope, messageStream, (err, info) => {
|
|
235
|
+
this._sending = false;
|
|
213
236
|
this.messages++;
|
|
214
237
|
if (err) {
|
|
238
|
+
if (reused && messageStream.readableDidRead === false && errors.isTransientError(err)) {
|
|
239
|
+
// Not a byte of the message was sent, so it can go out over another
|
|
240
|
+
// connection. The pool requeues the message when this resource closes
|
|
241
|
+
this.logger.info({
|
|
242
|
+
tnx: 'pool',
|
|
243
|
+
cid: this.id,
|
|
244
|
+
messageId
|
|
245
|
+
}, 'Connection #%s was closed by the server before message %s was sent: %s', this.id, messageId, err.message);
|
|
246
|
+
this.connection.close();
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if ((err.code === errors.EENVELOPE || err.code === errors.EMESSAGE) &&
|
|
250
|
+
err.responseCode !== 421 &&
|
|
251
|
+
!this.connection._destroyed) {
|
|
252
|
+
// The server refused this message, the connection itself is fine. Reset the
|
|
253
|
+
// session and keep using it instead of opening a new one
|
|
254
|
+
this.connection.reset(resetErr => {
|
|
255
|
+
if (resetErr) {
|
|
256
|
+
this.connection.close();
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
this._release();
|
|
260
|
+
});
|
|
261
|
+
return callback(err);
|
|
262
|
+
}
|
|
215
263
|
this.connection.close();
|
|
216
264
|
this._fail(err);
|
|
217
265
|
return callback(err);
|
|
@@ -221,28 +269,69 @@ class PoolResource extends node_events_1.EventEmitter {
|
|
|
221
269
|
to: envelope.to
|
|
222
270
|
};
|
|
223
271
|
info.messageId = messageId;
|
|
224
|
-
setImmediate(() =>
|
|
225
|
-
if (this.messages >= this.options.maxMessages) {
|
|
226
|
-
const err = new Error('Resource exhausted');
|
|
227
|
-
err.code = errors.EMAXLIMIT;
|
|
228
|
-
this.connection.close();
|
|
229
|
-
this._fail(err);
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
this.pool._checkRateLimit(() => {
|
|
233
|
-
this.available = true;
|
|
234
|
-
this.emit('available');
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
});
|
|
272
|
+
setImmediate(() => this._release());
|
|
238
273
|
callback(null, info);
|
|
239
274
|
});
|
|
240
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Makes the connection available for the next message, or closes it once it has sent
|
|
278
|
+
* maxMessages messages
|
|
279
|
+
*
|
|
280
|
+
* @internal
|
|
281
|
+
*/
|
|
282
|
+
_release() {
|
|
283
|
+
if (this.messages >= this.options.maxMessages) {
|
|
284
|
+
const err = new Error('Resource exhausted');
|
|
285
|
+
err.code = errors.EMAXLIMIT;
|
|
286
|
+
this.connection.close();
|
|
287
|
+
this._fail(err);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
this.pool._checkRateLimit(() => {
|
|
291
|
+
this.available = true;
|
|
292
|
+
this._startIdleTimer();
|
|
293
|
+
this.emit('available');
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
/** @internal */
|
|
297
|
+
_startIdleTimer() {
|
|
298
|
+
if (!this.options.idleTimeout || this.options.idleTimeout < 0) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
// one timer per connection, restarted every time the connection becomes available. A
|
|
302
|
+
// connection that is busy when it fires is simply not closed
|
|
303
|
+
if (this._idleTimer && typeof this._idleTimer.refresh === 'function') {
|
|
304
|
+
this._idleTimer.refresh();
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
clearTimeout(this._idleTimer);
|
|
308
|
+
this._idleTimer = setTimeout(() => {
|
|
309
|
+
if (!this.available) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
this.logger.debug({
|
|
313
|
+
tnx: 'pool',
|
|
314
|
+
cid: this.id
|
|
315
|
+
}, 'Closing connection #%s after it was idle for %sms', this.id, this.options.idleTimeout);
|
|
316
|
+
// not handed another message while it says goodbye
|
|
317
|
+
this.available = false;
|
|
318
|
+
this.connection.quit();
|
|
319
|
+
}, this.options.idleTimeout);
|
|
320
|
+
if (typeof this._idleTimer.unref === 'function') {
|
|
321
|
+
this._idleTimer.unref();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/** @internal */
|
|
325
|
+
_stopIdleTimer() {
|
|
326
|
+
clearTimeout(this._idleTimer);
|
|
327
|
+
this._idleTimer = false;
|
|
328
|
+
}
|
|
241
329
|
/**
|
|
242
330
|
* Closes the connection
|
|
243
331
|
*/
|
|
244
332
|
close() {
|
|
245
333
|
this._connected = false;
|
|
334
|
+
this._stopIdleTimer();
|
|
246
335
|
if (this.auth && this.auth.oauth2) {
|
|
247
336
|
this.auth.oauth2.removeAllListeners();
|
|
248
337
|
}
|
|
@@ -142,7 +142,9 @@ class SMTPTransport extends node_events_1.EventEmitter {
|
|
|
142
142
|
* @param callback Callback function
|
|
143
143
|
*/
|
|
144
144
|
send(mail, callback) {
|
|
145
|
-
|
|
145
|
+
// the proxy handshake, if any, counts against connectionTimeout as well
|
|
146
|
+
const connectStartedAt = Date.now();
|
|
147
|
+
this.getSocket(Object.assign({}, this.options, { connectStartedAt }), (err, socketOptions) => {
|
|
146
148
|
if (err) {
|
|
147
149
|
return callback(err);
|
|
148
150
|
}
|
|
@@ -161,6 +163,7 @@ class SMTPTransport extends node_events_1.EventEmitter {
|
|
|
161
163
|
options = Object.assign(shared.assign(false, options), socketOptions);
|
|
162
164
|
}
|
|
163
165
|
const connection = new index_js_1.default(options);
|
|
166
|
+
connection._connectStartedAt = connectStartedAt;
|
|
164
167
|
let perCallAuth;
|
|
165
168
|
const cleanupPerCallAuth = () => {
|
|
166
169
|
if (perCallAuth && perCallAuth !== this.auth && perCallAuth.oauth2) {
|
|
@@ -266,7 +269,9 @@ class SMTPTransport extends node_events_1.EventEmitter {
|
|
|
266
269
|
}
|
|
267
270
|
// the error paths hand over the error alone
|
|
268
271
|
const done = callback;
|
|
269
|
-
|
|
272
|
+
// the proxy handshake, if any, counts against connectionTimeout as well
|
|
273
|
+
const connectStartedAt = Date.now();
|
|
274
|
+
this.getSocket(Object.assign({}, this.options, { connectStartedAt }), (err, socketOptions) => {
|
|
270
275
|
if (err) {
|
|
271
276
|
return done(err);
|
|
272
277
|
}
|
|
@@ -283,6 +288,7 @@ class SMTPTransport extends node_events_1.EventEmitter {
|
|
|
283
288
|
options = Object.assign(shared.assign(false, options), socketOptions);
|
|
284
289
|
}
|
|
285
290
|
const connection = new index_js_1.default(options);
|
|
291
|
+
connection._connectStartedAt = connectStartedAt;
|
|
286
292
|
let returned = false;
|
|
287
293
|
let perCallAuth;
|
|
288
294
|
const cleanupPerCallAuth = () => {
|
|
@@ -24,6 +24,9 @@ export interface EncoderOptions {
|
|
|
24
24
|
/**
|
|
25
25
|
* Creates a transform stream for encoding data to base64 encoding
|
|
26
26
|
*
|
|
27
|
+
* The output is the same as `wrap(encode(input), lineLength)` no matter how the input is split
|
|
28
|
+
* into chunks: every line but the last one ends with a line break, the last one does not
|
|
29
|
+
*
|
|
27
30
|
* @constructor
|
|
28
31
|
* @param options Stream options
|
|
29
32
|
* @param [options.lineLength=76] Maximum length for lines, set to false to disable wrapping
|
|
@@ -11,6 +11,47 @@ export function encode(buffer) {
|
|
|
11
11
|
}
|
|
12
12
|
return buffer.toString('base64');
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Turns a line length option into a whole number of characters, the default for anything unusable
|
|
16
|
+
*/
|
|
17
|
+
function normalizeLineLength(lineLength) {
|
|
18
|
+
const length = Math.floor(Number(lineLength));
|
|
19
|
+
return Number.isFinite(length) && length >= 1 ? length : 76;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Splits the bytes of `src` into lines of `lineLength` bytes, each followed by a line break. With
|
|
23
|
+
* `final` set the last line, which may be shorter, gets no line break; otherwise only complete
|
|
24
|
+
* lines are taken and the rest is left for the caller
|
|
25
|
+
*
|
|
26
|
+
* @param src Bytes to wrap
|
|
27
|
+
* @param lineLength Line length
|
|
28
|
+
* @param final Whether `src` ends the output
|
|
29
|
+
* @returns The wrapped bytes and the number of trailing bytes not taken
|
|
30
|
+
*/
|
|
31
|
+
function wrapBuffer(src, lineLength, final) {
|
|
32
|
+
const lines = Math.ceil(src.length / lineLength);
|
|
33
|
+
// the last line waits for more data unless this is the end: whether it gets a line break
|
|
34
|
+
// depends on whether anything follows it
|
|
35
|
+
const complete = Math.max(lines - 1, 0);
|
|
36
|
+
let rest = src.length - complete * lineLength;
|
|
37
|
+
const output = Buffer.allocUnsafe(complete * (lineLength + 2) + (final ? rest : 0));
|
|
38
|
+
let to = 0;
|
|
39
|
+
for (let from = 0; from < complete * lineLength; from += lineLength) {
|
|
40
|
+
src.copy(output, to, from, from + lineLength);
|
|
41
|
+
to += lineLength;
|
|
42
|
+
output[to++] = 0x0d;
|
|
43
|
+
output[to++] = 0x0a;
|
|
44
|
+
}
|
|
45
|
+
if (final) {
|
|
46
|
+
to += src.copy(output, to, complete * lineLength);
|
|
47
|
+
rest = 0;
|
|
48
|
+
}
|
|
49
|
+
if (to !== output.length) {
|
|
50
|
+
// never hand out bytes of the unfilled allocation
|
|
51
|
+
throw new Error('Unexpected wrapped length');
|
|
52
|
+
}
|
|
53
|
+
return { output, rest };
|
|
54
|
+
}
|
|
14
55
|
/**
|
|
15
56
|
* Adds soft line breaks to a base64 string
|
|
16
57
|
*
|
|
@@ -39,6 +80,9 @@ export function wrap(str, lineLength) {
|
|
|
39
80
|
/**
|
|
40
81
|
* Creates a transform stream for encoding data to base64 encoding
|
|
41
82
|
*
|
|
83
|
+
* The output is the same as `wrap(encode(input), lineLength)` no matter how the input is split
|
|
84
|
+
* into chunks: every line but the last one ends with a line break, the last one does not
|
|
85
|
+
*
|
|
42
86
|
* @constructor
|
|
43
87
|
* @param options Stream options
|
|
44
88
|
* @param [options.lineLength=76] Maximum length for lines, set to false to disable wrapping
|
|
@@ -48,66 +92,61 @@ export class Encoder extends Transform {
|
|
|
48
92
|
super();
|
|
49
93
|
this.options = options || {};
|
|
50
94
|
if (this.options.lineLength !== false) {
|
|
51
|
-
this.options.lineLength = this.options.lineLength
|
|
95
|
+
this.options.lineLength = normalizeLineLength(this.options.lineLength);
|
|
52
96
|
}
|
|
53
97
|
this._curLine = '';
|
|
54
98
|
this._remainingBytes = false;
|
|
55
99
|
this.inputBytes = 0;
|
|
56
100
|
this.outputBytes = 0;
|
|
57
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Emits the encoded characters `b64` that follow the current line, keeping what can not be
|
|
104
|
+
* emitted yet as the new current line
|
|
105
|
+
*
|
|
106
|
+
* @internal
|
|
107
|
+
*/
|
|
108
|
+
_emit(b64, final) {
|
|
109
|
+
const src = Buffer.from(this._curLine + b64, 'latin1');
|
|
110
|
+
if (!src.length) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
let output = src;
|
|
114
|
+
if (this.options.lineLength) {
|
|
115
|
+
const wrapped = wrapBuffer(src, this.options.lineLength, final);
|
|
116
|
+
output = wrapped.output;
|
|
117
|
+
this._curLine = wrapped.rest ? src.toString('latin1', src.length - wrapped.rest) : '';
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
this._curLine = '';
|
|
121
|
+
}
|
|
122
|
+
if (output.length) {
|
|
123
|
+
this.outputBytes += output.length;
|
|
124
|
+
this.push(output);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
58
127
|
/** @internal */
|
|
59
128
|
_transform(chunk, encoding, done) {
|
|
60
129
|
let buf = encoding !== 'buffer' ? Buffer.from(chunk, encoding) : chunk;
|
|
61
130
|
if (!buf || !buf.length) {
|
|
62
|
-
|
|
63
|
-
return;
|
|
131
|
+
return done();
|
|
64
132
|
}
|
|
65
133
|
this.inputBytes += buf.length;
|
|
66
|
-
if (this._remainingBytes
|
|
134
|
+
if (this._remainingBytes) {
|
|
67
135
|
buf = Buffer.concat([this._remainingBytes, buf], this._remainingBytes.length + buf.length);
|
|
68
136
|
this._remainingBytes = false;
|
|
69
137
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
else {
|
|
75
|
-
this._remainingBytes = false;
|
|
76
|
-
}
|
|
77
|
-
let b64 = this._curLine + encode(buf);
|
|
78
|
-
if (this.options.lineLength) {
|
|
79
|
-
b64 = wrap(b64, this.options.lineLength);
|
|
80
|
-
// remove last line as it is still most probably incomplete
|
|
81
|
-
const lastLF = b64.lastIndexOf('\n');
|
|
82
|
-
if (lastLF < 0) {
|
|
83
|
-
this._curLine = b64;
|
|
84
|
-
b64 = '';
|
|
85
|
-
}
|
|
86
|
-
else if (lastLF === b64.length - 1) {
|
|
87
|
-
this._curLine = '';
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
this._curLine = b64.substring(lastLF + 1);
|
|
91
|
-
b64 = b64.substring(0, lastLF + 1);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
if (b64) {
|
|
95
|
-
this.outputBytes += b64.length;
|
|
96
|
-
this.push(Buffer.from(b64, 'ascii'));
|
|
138
|
+
const extra = buf.length % 3;
|
|
139
|
+
if (extra) {
|
|
140
|
+
this._remainingBytes = buf.subarray(buf.length - extra);
|
|
141
|
+
buf = buf.subarray(0, buf.length - extra);
|
|
97
142
|
}
|
|
98
|
-
|
|
143
|
+
this._emit(encode(buf), false);
|
|
144
|
+
done();
|
|
99
145
|
}
|
|
100
146
|
/** @internal */
|
|
101
147
|
_flush(done) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
if (this._curLine) {
|
|
106
|
-
this._curLine = wrap(this._curLine, this.options.lineLength);
|
|
107
|
-
this.outputBytes += this._curLine.length;
|
|
108
|
-
this.push(Buffer.from(this._curLine, 'ascii'));
|
|
109
|
-
this._curLine = '';
|
|
110
|
-
}
|
|
148
|
+
this._emit(this._remainingBytes ? encode(this._remainingBytes) : '', true);
|
|
149
|
+
this._remainingBytes = false;
|
|
111
150
|
done();
|
|
112
151
|
}
|
|
113
152
|
}
|
|
@@ -32,51 +32,67 @@ class DKIMSigner {
|
|
|
32
32
|
this.output = output;
|
|
33
33
|
this.output.usingCache = false;
|
|
34
34
|
this.hasErrored = false;
|
|
35
|
-
this.input.on('error', err =>
|
|
35
|
+
this.input.on('error', err => this.fail(err));
|
|
36
|
+
// A consumer that goes away before the signed message was read in full destroys the
|
|
37
|
+
// output. Stop reading the input and drop the cache file instead of leaving them open
|
|
38
|
+
this.output.once('close', () => {
|
|
39
|
+
if (this.output.writableFinished) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
36
42
|
this.hasErrored = true;
|
|
43
|
+
this.input.unpipe();
|
|
44
|
+
this.input.destroy();
|
|
37
45
|
this.cleanup();
|
|
38
|
-
output.emit('error', err);
|
|
39
46
|
});
|
|
40
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Ends the output with an error and releases the cache file
|
|
50
|
+
*/
|
|
51
|
+
fail(err) {
|
|
52
|
+
this.hasErrored = true;
|
|
53
|
+
this.cleanup();
|
|
54
|
+
this.output.destroy(err);
|
|
55
|
+
}
|
|
41
56
|
cleanup() {
|
|
42
57
|
if (!this.cache || !this.cachePath) {
|
|
43
58
|
return;
|
|
44
59
|
}
|
|
60
|
+
const cache = this.cache;
|
|
61
|
+
this.cache = false;
|
|
62
|
+
cache.destroy();
|
|
45
63
|
fs.unlink(this.cachePath, () => false);
|
|
46
64
|
}
|
|
47
65
|
createReadCache() {
|
|
48
66
|
// pipe remainings to cache file
|
|
49
67
|
this.cache = fs.createReadStream(this.cachePath);
|
|
50
|
-
this.cache.once('error', err =>
|
|
51
|
-
this.cleanup();
|
|
52
|
-
this.output.emit('error', err);
|
|
53
|
-
});
|
|
68
|
+
this.cache.once('error', err => this.fail(err));
|
|
54
69
|
this.cache.once('close', () => {
|
|
55
70
|
this.cleanup();
|
|
56
71
|
});
|
|
57
72
|
this.cache.pipe(this.output);
|
|
58
73
|
}
|
|
59
74
|
sendNextChunk() {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
75
|
+
while (!this.hasErrored) {
|
|
76
|
+
if (this.readPos >= this.chunks.length) {
|
|
77
|
+
if (!this.cache) {
|
|
78
|
+
this.output.end();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
return this.createReadCache();
|
|
82
|
+
}
|
|
83
|
+
const chunk = this.chunks[this.readPos++];
|
|
84
|
+
if (this.output.write(chunk) === false) {
|
|
85
|
+
this.output.once('drain', () => {
|
|
86
|
+
this.sendNextChunk();
|
|
87
|
+
});
|
|
66
88
|
return;
|
|
67
89
|
}
|
|
68
|
-
return this.createReadCache();
|
|
69
|
-
}
|
|
70
|
-
const chunk = this.chunks[this.readPos++];
|
|
71
|
-
if (this.output.write(chunk) === false) {
|
|
72
|
-
this.output.once('drain', () => {
|
|
73
|
-
this.sendNextChunk();
|
|
74
|
-
});
|
|
75
|
-
return;
|
|
76
90
|
}
|
|
77
|
-
setImmediate(() => this.sendNextChunk());
|
|
78
91
|
}
|
|
79
92
|
sendSignedOutput() {
|
|
93
|
+
if (this.hasErrored) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
80
96
|
let keyPos = 0;
|
|
81
97
|
const signNextKey = () => {
|
|
82
98
|
if (keyPos >= this.keys.length) {
|
|
@@ -96,9 +112,7 @@ class DKIMSigner {
|
|
|
96
112
|
});
|
|
97
113
|
}
|
|
98
114
|
catch (err) {
|
|
99
|
-
this.
|
|
100
|
-
this.cleanup();
|
|
101
|
-
this.output.emit('error', err);
|
|
115
|
+
this.fail(err);
|
|
102
116
|
return;
|
|
103
117
|
}
|
|
104
118
|
if (dkimField) {
|
|
@@ -117,7 +131,6 @@ class DKIMSigner {
|
|
|
117
131
|
// pipe remainings to cache file
|
|
118
132
|
this.cache = fs.createWriteStream(this.cachePath);
|
|
119
133
|
this.cache.once('error', err => {
|
|
120
|
-
this.cleanup();
|
|
121
134
|
// drain input
|
|
122
135
|
this.relaxedBody.unpipe(this.cache);
|
|
123
136
|
this.relaxedBody.on('readable', () => {
|
|
@@ -125,11 +138,12 @@ class DKIMSigner {
|
|
|
125
138
|
// do nothing
|
|
126
139
|
}
|
|
127
140
|
});
|
|
128
|
-
this.
|
|
129
|
-
// emit error
|
|
130
|
-
this.output.emit('error', err);
|
|
141
|
+
this.fail(err);
|
|
131
142
|
});
|
|
132
143
|
this.cache.once('close', () => {
|
|
144
|
+
if (this.hasErrored) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
133
147
|
this.sendSignedOutput();
|
|
134
148
|
});
|
|
135
149
|
this.relaxedBody.removeAllListeners('readable');
|
|
@@ -206,7 +220,7 @@ class DKIM {
|
|
|
206
220
|
catch (_E) {
|
|
207
221
|
// the body hash is created here, an unknown hashAlgo throws inside this timer
|
|
208
222
|
// where nothing else could catch it
|
|
209
|
-
|
|
223
|
+
signer.fail(sign.unsupportedHashAlgoError(signer.hashAlgo));
|
|
210
224
|
return;
|
|
211
225
|
}
|
|
212
226
|
if (writeValue) {
|
|
@@ -105,7 +105,7 @@ class MessageParser extends Transform {
|
|
|
105
105
|
const chunk = data.slice(headerPos);
|
|
106
106
|
this.bodySize += chunk.length;
|
|
107
107
|
// this would be the first chunk of data sent downstream
|
|
108
|
-
|
|
108
|
+
this.push(chunk);
|
|
109
109
|
}
|
|
110
110
|
return false;
|
|
111
111
|
}
|
|
@@ -134,7 +134,7 @@ class MessageParser extends Transform {
|
|
|
134
134
|
this.bodySize += chunk.length;
|
|
135
135
|
this.push(chunk);
|
|
136
136
|
}
|
|
137
|
-
|
|
137
|
+
callback();
|
|
138
138
|
}
|
|
139
139
|
/** @internal */
|
|
140
140
|
_flush(callback) {
|
|
@@ -61,6 +61,19 @@ export declare const EPROXY = "EPROXY";
|
|
|
61
61
|
export declare const EFILEACCESS = "EFILEACCESS";
|
|
62
62
|
export declare const EURLACCESS = "EURLACCESS";
|
|
63
63
|
export declare const EFETCH = "EFETCH";
|
|
64
|
+
/**
|
|
65
|
+
* Tells whether the error ended the connection rather than rejected what was sent over it
|
|
66
|
+
*/
|
|
67
|
+
export declare const isTransientError: (err: {
|
|
68
|
+
code?: string | undefined;
|
|
69
|
+
responseCode?: number | undefined;
|
|
70
|
+
}) => boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Code Node.js sets on an error when its permission model (`--permission`) denies an
|
|
73
|
+
* operation. It is not replaced with the Nodemailer code of the failing step (`ESOCKET`,
|
|
74
|
+
* `EDNS`, `EFETCH`, ...), so a missing grant such as `--allow-net` stays recognizable.
|
|
75
|
+
*/
|
|
76
|
+
export declare const ERR_ACCESS_DENIED = "ERR_ACCESS_DENIED";
|
|
64
77
|
/**
|
|
65
78
|
* An Error together with the properties Nodemailer attaches to the errors it
|
|
66
79
|
* hands to callers. Every property is optional, the set that is present
|
|
@@ -84,6 +97,10 @@ export interface NodemailerError extends NodeJS.ErrnoException {
|
|
|
84
97
|
rejected?: string[] | undefined;
|
|
85
98
|
/** Per-recipient errors for the rejected addresses */
|
|
86
99
|
rejectedErrors?: NodemailerError[] | undefined;
|
|
100
|
+
/** The code the error had before Nodemailer set its own, such as ECONNREFUSED for an ESOCKET error */
|
|
101
|
+
originalCode?: string | undefined;
|
|
102
|
+
/** Which wait an ETIMEDOUT error ended: connecting, the greeting, a TLS upgrade or an idle socket */
|
|
103
|
+
timeoutType?: 'CONNECT_TIMEOUT' | 'GREETING_TIMEOUT' | 'UPGRADE_TIMEOUT' | 'SOCKET_TIMEOUT' | undefined;
|
|
87
104
|
}
|
|
88
105
|
/**
|
|
89
106
|
* Node style callback: called with an error, or with null and the result
|
|
@@ -65,3 +65,13 @@ export const EPROXY = 'EPROXY';
|
|
|
65
65
|
export const EFILEACCESS = 'EFILEACCESS';
|
|
66
66
|
export const EURLACCESS = 'EURLACCESS';
|
|
67
67
|
export const EFETCH = 'EFETCH';
|
|
68
|
+
/**
|
|
69
|
+
* Tells whether the error ended the connection rather than rejected what was sent over it
|
|
70
|
+
*/
|
|
71
|
+
export const isTransientError = (err) => err.responseCode === 421 || err.code === ECONNECTION || err.code === ESOCKET || err.code === ETIMEDOUT;
|
|
72
|
+
/**
|
|
73
|
+
* Code Node.js sets on an error when its permission model (`--permission`) denies an
|
|
74
|
+
* operation. It is not replaced with the Nodemailer code of the failing step (`ESOCKET`,
|
|
75
|
+
* `EDNS`, `EFETCH`, ...), so a missing grant such as `--allow-net` stays recognizable.
|
|
76
|
+
*/
|
|
77
|
+
export const ERR_ACCESS_DENIED = 'ERR_ACCESS_DENIED';
|
|
@@ -43,6 +43,17 @@ const TLS_OPTION_KEYS = [
|
|
|
43
43
|
'sessionIdContext',
|
|
44
44
|
'sigalgs'
|
|
45
45
|
];
|
|
46
|
+
/**
|
|
47
|
+
* Marks an error of the request as a fetch error, unless it is a permission model
|
|
48
|
+
* denial, which keeps its own code (see ERR_ACCESS_DENIED)
|
|
49
|
+
*
|
|
50
|
+
* @param err Error to mark
|
|
51
|
+
*/
|
|
52
|
+
function setFetchCode(err) {
|
|
53
|
+
if (err.code !== errors.ERR_ACCESS_DENIED) {
|
|
54
|
+
err.code = errors.EFETCH;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
46
57
|
/**
|
|
47
58
|
* Resolves a URL only if it is one this module is willing to request.
|
|
48
59
|
*
|
|
@@ -139,7 +150,7 @@ function nmfetch(url, options) {
|
|
|
139
150
|
return;
|
|
140
151
|
}
|
|
141
152
|
finished = true;
|
|
142
|
-
err
|
|
153
|
+
setFetchCode(err);
|
|
143
154
|
err.sourceUrl = url;
|
|
144
155
|
fetchRes.emit('error', err);
|
|
145
156
|
});
|
|
@@ -212,7 +223,7 @@ function nmfetch(url, options) {
|
|
|
212
223
|
catch (E) {
|
|
213
224
|
finished = true;
|
|
214
225
|
setImmediate(() => {
|
|
215
|
-
E
|
|
226
|
+
setFetchCode(E);
|
|
216
227
|
E.sourceUrl = url;
|
|
217
228
|
fetchRes.emit('error', E);
|
|
218
229
|
});
|
|
@@ -224,7 +235,7 @@ function nmfetch(url, options) {
|
|
|
224
235
|
return;
|
|
225
236
|
}
|
|
226
237
|
finished = true;
|
|
227
|
-
err
|
|
238
|
+
setFetchCode(err);
|
|
228
239
|
err.sourceUrl = sourceUrl;
|
|
229
240
|
fetchRes.emit('error', err);
|
|
230
241
|
req.abort();
|
|
@@ -234,6 +245,15 @@ function nmfetch(url, options) {
|
|
|
234
245
|
req.setTimeout(timeout, () => fail(new Error('Request Timeout')));
|
|
235
246
|
}
|
|
236
247
|
req.on('error', (err) => fail(err));
|
|
248
|
+
// a consumer that destroys the response stream before it ended does not want the rest of
|
|
249
|
+
// the body, release the request now instead of when the timeout fires
|
|
250
|
+
fetchRes.once('close', () => {
|
|
251
|
+
if (finished || fetchRes.readableEnded) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
finished = true;
|
|
255
|
+
req.destroy();
|
|
256
|
+
});
|
|
237
257
|
req.on('response', res => {
|
|
238
258
|
let inflate;
|
|
239
259
|
if (finished) {
|