[nginx] Mail: lingering close.
Maxim Dounin
mdounin at mdounin.ru
Mon Jun 30 22:41:00 UTC 2025
details: http://freenginx.org/hg/nginx/rev/1746cc8754af
branches:
changeset: 9383:1746cc8754af
user: Maxim Dounin <mdounin at mdounin.ru>
date: Tue Jul 01 01:09:56 2025 +0300
description:
Mail: lingering close.
In mail, lingering close is not normally needed, since connections are
usually closed at well known states, and further commands from clients
are not expected. Still, it can be useful, for example, when closing
a connection due to errors.
diffstat:
src/mail/ngx_mail.h | 10 ++
src/mail/ngx_mail_core_module.c | 31 ++++++++
src/mail/ngx_mail_handler.c | 149 ++++++++++++++++++++++++++++++++++++++-
src/mail/ngx_mail_imap_handler.c | 1 +
src/mail/ngx_mail_pop3_handler.c | 2 +
src/mail/ngx_mail_smtp_handler.c | 1 +
6 files changed, 193 insertions(+), 1 deletions(-)
diffs (322 lines):
diff --git a/src/mail/ngx_mail.h b/src/mail/ngx_mail.h
--- a/src/mail/ngx_mail.h
+++ b/src/mail/ngx_mail.h
@@ -115,10 +115,14 @@ typedef struct {
ngx_msec_t timeout;
ngx_msec_t resolver_timeout;
+ ngx_msec_t lingering_time;
+ ngx_msec_t lingering_timeout;
ngx_uint_t max_errors;
ngx_uint_t max_commands;
+ ngx_flag_t lingering_close;
+
size_t limit_rate;
size_t limit_rate_after;
@@ -217,6 +221,7 @@ typedef struct {
unsigned protocol:3;
unsigned blocked:1;
unsigned quit:1;
+ unsigned no_lingering_close:1;
unsigned quoted:1;
unsigned backslash:1;
unsigned no_sync_literal:1;
@@ -254,6 +259,8 @@ typedef struct {
ngx_msec_t limit_last;
off_t limit_excess;
+ ngx_msec_t lingering_time;
+
/* used to parse POP3/IMAP/SMTP command */
ngx_uint_t state;
@@ -337,6 +344,9 @@ typedef struct {
#define NGX_MAIL_PARSE_INVALID_COMMAND 20
+#define NGX_MAIL_LINGERING_BUFFER_SIZE 4096
+
+
typedef void (*ngx_mail_init_session_pt)(ngx_mail_session_t *s,
ngx_connection_t *c);
typedef void (*ngx_mail_init_protocol_pt)(ngx_event_t *rev);
diff --git a/src/mail/ngx_mail_core_module.c b/src/mail/ngx_mail_core_module.c
--- a/src/mail/ngx_mail_core_module.c
+++ b/src/mail/ngx_mail_core_module.c
@@ -113,6 +113,27 @@ static ngx_command_t ngx_mail_core_comm
offsetof(ngx_mail_core_srv_conf_t, limit_rate_after),
NULL },
+ { ngx_string("lingering_close"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_core_srv_conf_t, lingering_close),
+ NULL },
+
+ { ngx_string("lingering_time"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_core_srv_conf_t, lingering_time),
+ NULL },
+
+ { ngx_string("lingering_timeout"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_core_srv_conf_t, lingering_timeout),
+ NULL },
+
ngx_null_command
};
@@ -190,10 +211,14 @@ ngx_mail_core_create_srv_conf(ngx_conf_t
cscf->timeout = NGX_CONF_UNSET_MSEC;
cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;
+ cscf->lingering_time = NGX_CONF_UNSET_MSEC;
+ cscf->lingering_timeout = NGX_CONF_UNSET_MSEC;
cscf->max_errors = NGX_CONF_UNSET_UINT;
cscf->max_commands = NGX_CONF_UNSET_UINT;
+ cscf->lingering_close = NGX_CONF_UNSET;
+
cscf->limit_rate = NGX_CONF_UNSET_SIZE;
cscf->limit_rate_after = NGX_CONF_UNSET_SIZE;
@@ -215,10 +240,16 @@ ngx_mail_core_merge_srv_conf(ngx_conf_t
ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
ngx_conf_merge_msec_value(conf->resolver_timeout, prev->resolver_timeout,
30000);
+ ngx_conf_merge_msec_value(conf->lingering_time, prev->lingering_time,
+ 30000);
+ ngx_conf_merge_msec_value(conf->lingering_timeout, prev->lingering_timeout,
+ 5000);
ngx_conf_merge_uint_value(conf->max_errors, prev->max_errors, 5);
ngx_conf_merge_uint_value(conf->max_commands, prev->max_commands, 1000);
+ ngx_conf_merge_value(conf->lingering_close, prev->lingering_close, 1);
+
ngx_conf_merge_size_value(conf->limit_rate, prev->limit_rate, 0);
ngx_conf_merge_size_value(conf->limit_rate_after, prev->limit_rate_after,
0);
diff --git a/src/mail/ngx_mail_handler.c b/src/mail/ngx_mail_handler.c
--- a/src/mail/ngx_mail_handler.c
+++ b/src/mail/ngx_mail_handler.c
@@ -22,6 +22,10 @@ static ngx_int_t ngx_mail_verify_cert(ng
ngx_connection_t *c);
#endif
+static void ngx_mail_lingering_close(ngx_connection_t *c);
+static void ngx_mail_lingering_close_handler(ngx_event_t *rev);
+static void ngx_mail_empty_handler(ngx_event_t *wev);
+
void
ngx_mail_init_connection(ngx_connection_t *c)
@@ -1108,7 +1112,7 @@ ngx_mail_send(ngx_event_t *wev)
}
if (s->quit) {
- ngx_mail_close_connection(c);
+ ngx_mail_lingering_close(c);
return;
}
@@ -1267,6 +1271,149 @@ ngx_mail_session_internal_server_error(n
}
+static void
+ngx_mail_lingering_close(ngx_connection_t *c)
+{
+ ngx_event_t *rev, *wev;
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ s = c->data;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ if (s->no_lingering_close || !cscf->lingering_close) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (s->lingering_time == 0) {
+ s->lingering_time = ngx_current_msec + cscf->lingering_time;
+ }
+
+#if (NGX_MAIL_SSL)
+ if (c->ssl) {
+ ngx_int_t rc;
+
+ c->ssl->shutdown_without_free = 1;
+
+ rc = ngx_ssl_shutdown(c);
+
+ if (rc == NGX_ERROR) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (rc == NGX_AGAIN) {
+ c->ssl->handler = ngx_mail_lingering_close;
+ return;
+ }
+ }
+#endif
+
+ rev = c->read;
+ rev->handler = ngx_mail_lingering_close_handler;
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ wev = c->write;
+ wev->handler = ngx_mail_empty_handler;
+
+ if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
+ if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+ }
+
+ if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) {
+ ngx_connection_error(c, ngx_socket_errno,
+ ngx_shutdown_socket_n " failed");
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ c->close = 0;
+ ngx_reusable_connection(c, 1);
+
+ ngx_add_timer(rev, cscf->lingering_timeout);
+
+ if (rev->ready) {
+ ngx_mail_lingering_close_handler(rev);
+ }
+}
+
+
+static void
+ngx_mail_lingering_close_handler(ngx_event_t *rev)
+{
+ ssize_t n;
+ ngx_msec_t timer;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+ u_char buffer[NGX_MAIL_LINGERING_BUFFER_SIZE];
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail lingering close handler");
+
+ if (rev->timedout || c->close) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ timer = s->lingering_time - ngx_current_msec;
+ if ((ngx_msec_int_t) timer <= 0) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ do {
+ n = c->recv(c, buffer, NGX_MAIL_LINGERING_BUFFER_SIZE);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "lingering read: %z", n);
+
+ if (n == NGX_AGAIN) {
+ break;
+ }
+
+ if (n == NGX_ERROR || n == 0) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ } while (rev->ready);
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ if (timer > cscf->lingering_timeout) {
+ timer = cscf->lingering_timeout;
+ }
+
+ ngx_add_timer(rev, timer);
+}
+
+
+static void
+ngx_mail_empty_handler(ngx_event_t *wev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail empty handler");
+
+ return;
+}
+
+
void
ngx_mail_close_connection(ngx_connection_t *c)
{
diff --git a/src/mail/ngx_mail_imap_handler.c b/src/mail/ngx_mail_imap_handler.c
--- a/src/mail/ngx_mail_imap_handler.c
+++ b/src/mail/ngx_mail_imap_handler.c
@@ -179,6 +179,7 @@ ngx_mail_imap_auth_state(ngx_event_t *re
case NGX_IMAP_LOGOUT:
s->quit = 1;
+ s->no_lingering_close = 1;
ngx_str_set(&s->text, imap_bye);
break;
diff --git a/src/mail/ngx_mail_pop3_handler.c b/src/mail/ngx_mail_pop3_handler.c
--- a/src/mail/ngx_mail_pop3_handler.c
+++ b/src/mail/ngx_mail_pop3_handler.c
@@ -192,6 +192,7 @@ ngx_mail_pop3_auth_state(ngx_event_t *re
case NGX_POP3_QUIT:
s->quit = 1;
+ s->no_lingering_close = 1;
break;
case NGX_POP3_NOOP:
@@ -222,6 +223,7 @@ ngx_mail_pop3_auth_state(ngx_event_t *re
case NGX_POP3_QUIT:
s->quit = 1;
+ s->no_lingering_close = 1;
break;
case NGX_POP3_NOOP:
diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c
--- a/src/mail/ngx_mail_smtp_handler.c
+++ b/src/mail/ngx_mail_smtp_handler.c
@@ -496,6 +496,7 @@ ngx_mail_smtp_auth_state(ngx_event_t *re
case NGX_SMTP_QUIT:
s->quit = 1;
+ s->no_lingering_close = 1;
ngx_str_set(&s->out, smtp_bye);
break;
More information about the nginx-devel
mailing list