[PATCH 2 of 2] Mail: lingering close
Maxim Dounin
mdounin at mdounin.ru
Thu Jun 26 20:53:08 UTC 2025
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1750964100 -10800
# Thu Jun 26 21:55:00 2025 +0300
# Node ID f7e9eb427a1457102b2c9a43371622924361026d
# Parent 38d84f18b138ae4989d071fcb5cabda2f846b219
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.
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