diff --git a/include/haproxy/connection-t.h b/include/haproxy/connection-t.h index 835c70b81..88534dc63 100644 --- a/include/haproxy/connection-t.h +++ b/include/haproxy/connection-t.h @@ -329,6 +329,7 @@ enum { CO_RFL_KEEP_RECV = 0x0008, /* Instruct the mux to still wait for read events */ CO_RFL_BUF_NOT_STUCK = 0x0010, /* Buffer is not stuck. Optims are possible during data copy */ CO_RFL_MAY_SPLICE = 0x0020, /* The producer can use the kernel splicing */ + CO_RFL_TRY_HARDER = 0x0040, /* Try to read till READ0 even on short reads */ }; /* flags that can be passed to xprt->snd_buf() and mux->snd_buf() */ diff --git a/src/raw_sock.c b/src/raw_sock.c index 5ef904d74..eebdf8be8 100644 --- a/src/raw_sock.c +++ b/src/raw_sock.c @@ -330,9 +330,11 @@ static size_t raw_sock_to_buf(struct connection *conn, void *xprt_ctx, struct bu goto read0; } - if (!(fdtab[conn->handle.fd].state & FD_LINGER_RISK) || - (cur_poller.flags & HAP_POLL_F_RDHUP)) { - break; + if (!(flags & CO_RFL_TRY_HARDER)) { + if (!(fdtab[conn->handle.fd].state & FD_LINGER_RISK) || + (cur_poller.flags & HAP_POLL_F_RDHUP)) { + break; + } } } count -= ret; @@ -360,6 +362,31 @@ static size_t raw_sock_to_buf(struct connection *conn, void *xprt_ctx, struct bu if (unlikely(conn->flags & CO_FL_WAIT_L4_CONN) && done) conn->flags &= ~CO_FL_WAIT_L4_CONN; + if (unlikely((flags & CO_RFL_TRY_HARDER) && + !(conn->flags & CO_FL_SOCK_RD_SH) && + !count)) { + /* we've read exactly what was being asked for, which is loewr + * than a full buffer, and the caller wants us to really check + * if there's something after. This happens in the context of + * SSL where the lib reads in tiny chunks without offering the + * ability to detect a pending close. Let's just check using + * MSG_PEEK so that we don't pull bytes we shouldn't. + */ + char c; + + ret = recv(conn->handle.fd, &c, 1, MSG_PEEK); + if (ret == 0) { + conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_shutr); + goto read0; + } + else if (ret < 0 && + (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENOTCONN && errno != EINTR)) { + conn_report_term_evt(conn, tevt_loc_fd, fd_tevt_type_rcv_err); + conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH; + conn_set_errno(conn, errno); + } + } + leave: return done;