feifeiiong edited a comment on issue #1096:
URL: https://github.com/apache/incubator-brpc/issues/1096#issuecomment-619385096
感谢您的回复!第四点确实是如您所说。关于1,3点,有一些不同的看法:
1. DoRead 的设计确实是读到0即代表约定遇到EOF,但是SSL_read为0时的处理并不完备,以下内容摘自man:
SSL_read return values:
>0
The read operation was successful; the return value is the number of bytes
actually read from the TLS/SSL connection.
0
The read operation was not successful. The reason may either be a clean
shutdown due to a "close notify" alert sent by the peer (in which case the
SSL_RECEIVED_SHUTDOWN flag in the ssl shutdown state is set (see
ssl_shutdown(3), ssl_set_shutdown(3)). It is also possible, that the peer
simply shut down the underlying transport and the shutdown is incomplete. Call
SSL_get_error() with the return value ret to find out, whether an error
occurred or the connection was shut down cleanly ( SSL_ERROR_ZERO_RETURN
SSL_get_error Return Values:
SSL_ERROR_ZERO_RETURN
The TLS/SSL connection has been closed.
SSL_ERROR_SYSCALL
Some I/O error occurred. The OpenSSL error queue may contain more
information on the error. If the error queue is empty (i.e. ERR_get_error()
returns 0), ret can be used to find out more about the error: If ret == 0, an
EOF was observed that violates the protocol. If ret == -1, the underlying BIO
reported an I/O error (for socket I/O on Unix systems, consult errno for
details).
在iobuf中是这样处理的:
```
const int rc = SSL_read(ssl, _block->data + _block->size, read_len);
*ssl_error = SSL_get_error(ssl, rc);
if (rc > 0) {
const IOBuf::BlockRef r = { (uint32_t)_block->size,
(uint32_t)rc, _block };
_push_back_ref(r);
_block->size += rc;
if (_block->full()) {
Block* const saved_next = _block->portal_next;
_block->dec_ref(); // _block may be deleted
_block = saved_next;
}
nr += rc;
} else {
if (rc < 0) {
if (*ssl_error == SSL_ERROR_WANT_READ
|| (*ssl_error == SSL_ERROR_SYSCALL
&& BIO_fd_non_fatal_error(errno) == 1)) {
// Non fatal error, tell caller to read again
*ssl_error = SSL_ERROR_WANT_READ;
} else {
// Other errors are fatal
return rc;
}
}
return (nr > 0 ? nr : rc);
```
在rc == 0时,返回,并置错误码,此处的错误码,有可能是SSL_ERROR_SYSCALL/SSL_ERROR_ZERO_RETURN,
在socket.cpp:
int ssl_error = 0;
ssize_t nr = _read_buf.append_from_SSL_channel(_ssl_session, &ssl_error,
size_hint);
switch (ssl_error) {
case SSL_ERROR_NONE: // `nr' > 0
break;
case SSL_ERROR_WANT_READ:
// Regard this error as EAGAIN
errno = EAGAIN;
break;
case SSL_ERROR_WANT_WRITE:
// Disable renegotiation
errno = EPROTO;
return -1;
default: {
const unsigned long e = ERR_get_error();
if (nr == 0) {
// Socket EOF or SSL session EOF
} else if (e != 0) {
LOG(WARNING) << "Fail to read from ssl_fd=" << fd()
<< ": " << SSLError(e);
errno = ESSL;
} else {
// System error with corresponding errno set
PLOG(WARNING) << "Fail to read from ssl_fd=" << fd();
}
break;
}
如上,以上错误码被default捕获,而此刻nr 有可能!=0 (iobuf.cpp : return (nr > 0 ? nr : rc);),
从而导致该EOF被忽略,导致socket无法关闭。在实际应用中,该问题已经频繁被触发,打印日志类似:
Fail to read from ssl_fd=id:Success
DoWrite有类似的问题,这里就不贴代码了,烦请您看看!
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]