Welcome! Log In Create A New Profile

Advanced

[nginx] Stream: socket peek in preread phase.

Roman Arutyunyan
March 21, 2024 09:42AM
details: https://hg.nginx.org/nginx/rev/cf890df37bb6
branches:
changeset: 9215:cf890df37bb6
user: Roman Arutyunyan <arut@nginx.com>
date: Wed Dec 13 18:04:55 2023 +0400
description:
Stream: socket peek in preread phase.

Previously, preread buffer was always read out from socket, which made it
impossible to terminate SSL on the connection without introducing additional
SSL BIOs. The following patches will rely on this.

Now, when possible, recv(MSG_PEEK) is used instead, which keeps data in socket.
It's called if SSL is not already terminated and if an egde-triggered event
method is used. For epoll, EPOLLRDHUP support is also required.

diffstat:

src/stream/ngx_stream_core_module.c | 189 +++++++++++++++++++++++++++--------
1 files changed, 146 insertions(+), 43 deletions(-)

diffs (229 lines):

diff -r d6f8a2c31cde -r cf890df37bb6 src/stream/ngx_stream_core_module.c
--- a/src/stream/ngx_stream_core_module.c Thu Mar 21 17:06:21 2024 +0400
+++ b/src/stream/ngx_stream_core_module.c Wed Dec 13 18:04:55 2023 +0400
@@ -10,6 +10,11 @@
#include <ngx_stream.h>


+static ngx_uint_t ngx_stream_preread_can_peek(ngx_connection_t *c);
+static ngx_int_t ngx_stream_preread_peek(ngx_stream_session_t *s,
+ ngx_stream_phase_handler_t *ph);
+static ngx_int_t ngx_stream_preread(ngx_stream_session_t *s,
+ ngx_stream_phase_handler_t *ph);
static ngx_int_t ngx_stream_core_preconfiguration(ngx_conf_t *cf);
static void *ngx_stream_core_create_main_conf(ngx_conf_t *cf);
static char *ngx_stream_core_init_main_conf(ngx_conf_t *cf, void *conf);
@@ -203,8 +208,6 @@ ngx_int_t
ngx_stream_core_preread_phase(ngx_stream_session_t *s,
ngx_stream_phase_handler_t *ph)
{
- size_t size;
- ssize_t n;
ngx_int_t rc;
ngx_connection_t *c;
ngx_stream_core_srv_conf_t *cscf;
@@ -217,56 +220,33 @@ ngx_stream_core_preread_phase(ngx_stream

if (c->read->timedout) {
rc = NGX_STREAM_OK;
+ goto done;
+ }

- } else if (c->read->timer_set) {
- rc = NGX_AGAIN;
+ if (!c->read->timer_set) {
+ rc = ph->handler(s);

- } else {
- rc = ph->handler(s);
+ if (rc != NGX_AGAIN) {
+ goto done;
+ }
}

- while (rc == NGX_AGAIN) {
-
+ if (c->buffer == NULL) {
+ c->buffer = ngx_create_temp_buf(c->pool, cscf->preread_buffer_size);
if (c->buffer == NULL) {
- c->buffer = ngx_create_temp_buf(c->pool, cscf->preread_buffer_size);
- if (c->buffer == NULL) {
- rc = NGX_ERROR;
- break;
- }
+ rc = NGX_ERROR;
+ goto done;
}
-
- size = c->buffer->end - c->buffer->last;
-
- if (size == 0) {
- ngx_log_error(NGX_LOG_ERR, c->log, 0, "preread buffer full");
- rc = NGX_STREAM_BAD_REQUEST;
- break;
- }
+ }

- if (c->read->eof) {
- rc = NGX_STREAM_OK;
- break;
- }
-
- if (!c->read->ready) {
- break;
- }
-
- n = c->recv(c, c->buffer->last, size);
+ if (ngx_stream_preread_can_peek(c)) {
+ rc = ngx_stream_preread_peek(s, ph);

- if (n == NGX_ERROR || n == 0) {
- rc = NGX_STREAM_OK;
- break;
- }
+ } else {
+ rc = ngx_stream_preread(s, ph);
+ }

- if (n == NGX_AGAIN) {
- break;
- }
-
- c->buffer->last += n;
-
- rc = ph->handler(s);
- }
+done:

if (rc == NGX_AGAIN) {
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
@@ -311,6 +291,129 @@ ngx_stream_core_preread_phase(ngx_stream
}


+static ngx_uint_t
+ngx_stream_preread_can_peek(ngx_connection_t *c)
+{
+#if (NGX_STREAM_SSL)
+ if (c->ssl) {
+ return 0;
+ }
+#endif
+
+ if ((ngx_event_flags & NGX_USE_CLEAR_EVENT) == 0) {
+ return 0;
+ }
+
+#if (NGX_HAVE_KQUEUE)
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ return 1;
+ }
+#endif
+
+#if (NGX_HAVE_EPOLLRDHUP)
+ if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ngx_use_epoll_rdhup) {
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+
+
+static ngx_int_t
+ngx_stream_preread_peek(ngx_stream_session_t *s, ngx_stream_phase_handler_t *ph)
+{
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_err_t err;
+ ngx_connection_t *c;
+
+ c = s->connection;
+
+ n = recv(c->fd, (char *) c->buffer->last,
+ c->buffer->end - c->buffer->last, MSG_PEEK);
+
+ err = ngx_socket_errno;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream recv(): %z", n);
+
+ if (n == -1) {
+ if (err == NGX_EAGAIN) {
+ c->read->ready = 0;
+ return NGX_AGAIN;
+ }
+
+ ngx_connection_error(c, err, "recv() failed");
+ return NGX_STREAM_OK;
+ }
+
+ if (n == 0) {
+ return NGX_STREAM_OK;
+ }
+
+ c->buffer->last += n;
+
+ rc = ph->handler(s);
+
+ if (rc != NGX_AGAIN) {
+ c->buffer->last = c->buffer->pos;
+ return rc;
+ }
+
+ if (c->buffer->last == c->buffer->end) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0, "preread buffer full");
+ return NGX_STREAM_BAD_REQUEST;
+ }
+
+ if (c->read->pending_eof) {
+ return NGX_STREAM_OK;
+ }
+
+ c->buffer->last = c->buffer->pos;
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_stream_preread(ngx_stream_session_t *s, ngx_stream_phase_handler_t *ph)
+{
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_connection_t *c;
+
+ c = s->connection;
+
+ while (c->read->ready) {
+
+ n = c->recv(c, c->buffer->last, c->buffer->end - c->buffer->last);
+
+ if (n == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ if (n == NGX_ERROR || n == 0) {
+ return NGX_STREAM_OK;
+ }
+
+ c->buffer->last += n;
+
+ rc = ph->handler(s);
+
+ if (rc != NGX_AGAIN) {
+ return rc;
+ }
+
+ if (c->buffer->last == c->buffer->end) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0, "preread buffer full");
+ return NGX_STREAM_BAD_REQUEST;
+ }
+ }
+
+ return NGX_AGAIN;
+}
+
+
ngx_int_t
ngx_stream_core_content_phase(ngx_stream_session_t *s,
ngx_stream_phase_handler_t *ph)
_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
https://mailman.nginx.org/mailman/listinfo/nginx-devel
Subject Author Views Posted

[nginx] Stream: socket peek in preread phase.

Roman Arutyunyan 89 March 21, 2024 09:42AM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 155
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready