Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(394)

Unified Diff: net/socket/ssl_client_socket_nss.cc

Issue 12025040: When reading from an SSL socket, attempt to fully fill the caller's buffer (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Initialization order Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | net/socket/ssl_client_socket_openssl.h » ('j') | net/socket/ssl_client_socket_openssl.cc » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/socket/ssl_client_socket_nss.cc
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index 27d31b96cfa3a3a3d7f416d4caec7782a557962e..76ebb66c2a5e5bde369d52a88dddbc1e384d55d2 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -837,6 +837,15 @@ class SSLClientSocketNSS::Core : public base::RefCountedThreadSafe<Core> {
// Buffers for the network end of the SSL state machine
memio_Private* nss_bufs_;
+ // Used by DoPayloadRead() when attempting to completely fill the caller's
+ // buffer.
wtc 2013/02/13 22:06:51 The goal should be: when data has been received, f
+ // If DoPayloadRead() encounters an error after having read some data, stores
+ // the results to return on the *next* call to DoPayloadRead(). A value > 0
+ // indicates there is no pending result (as 0 indicates EOF, < 0 indicates
+ // error).
+ int pending_read_result_;
+ PRErrorCode pending_read_prerror_;
wtc 2013/02/13 22:06:51 prerror => nss_error Document that this member is
+
// The certificate chain, in DER form, that is expected to be received from
// the server.
std::vector<std::string> predicted_certs_;
@@ -912,6 +921,8 @@ SSLClientSocketNSS::Core::Core(
ssl_config_(ssl_config),
nss_fd_(NULL),
nss_bufs_(NULL),
+ pending_read_result_(1),
+ pending_read_prerror_(0),
next_handshake_state_(STATE_NONE),
channel_id_xtn_negotiated_(false),
channel_id_needed_(false),
@@ -1945,43 +1956,90 @@ int SSLClientSocketNSS::Core::DoPayloadRead() {
DCHECK(user_read_buf_);
DCHECK_GT(user_read_buf_len_, 0);
- int rv = PR_Read(nss_fd_, user_read_buf_->data(), user_read_buf_len_);
- // Update NSSTaskRunner status because PR_Read may consume |nss_bufs_|, then
- // following |amount_in_read_buffer| may be changed here.
- int amount_in_read_buffer = memio_GetReadableBufferSize(nss_bufs_);
wtc 2013/02/13 22:06:51 Just wanted to verify that you meant to delete thi
Ryan Sleevi 2013/02/13 22:55:48 Nope. Rebase bug. Good catch.
- PostOrRunCallback(
- FROM_HERE,
- base::Bind(&Core::OnNSSBufferUpdated, this, amount_in_read_buffer));
-
- if (client_auth_cert_needed_) {
- // We don't need to invalidate the non-client-authenticated SSL session
- // because the server will renegotiate anyway.
- rv = ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
- PostOrRunCallback(
- FROM_HERE,
- base::Bind(&AddLogEventWithCallback, weak_net_log_,
- NetLog::TYPE_SSL_READ_ERROR,
- CreateNetLogSSLErrorCallback(rv, 0)));
+ int rv;
+ // If a previous greedy read resulted in an error that was not consumed (eg:
+ // due to the caller having read some data successfully), then return that
+ // pending error now.
+ if (pending_read_result_ <= 0) {
+ rv = pending_read_result_;
+ PRErrorCode prerr = pending_read_prerror_;
+ pending_read_result_ = 1;
+ pending_read_prerror_ = 0;
+
+ if (rv != ERR_IO_PENDING) {
wtc 2013/02/13 22:06:51 We should add && rv != 0. A graceful connection cl
+ PostOrRunCallback(
+ FROM_HERE,
+ base::Bind(&AddLogEventWithCallback, weak_net_log_,
+ NetLog::TYPE_SSL_READ_ERROR,
+ CreateNetLogSSLErrorCallback(rv, prerr)));
+ }
return rv;
}
+
+ // Perform a greedy read, attempting to read as much as the caller has
+ // requested. Normally, PR_Read will return exactly one SSL application data
wtc 2013/02/13 22:06:51 Please add "in the current NSS implementation". We
Ryan Sleevi 2013/02/13 22:55:48 I'm not sure I agree, for reasons I explained prev
wtc 2013/02/15 23:30:04 The techniques you're using here (calling PR_Read
+ // record's worth of data per invocation. The record size is dictated by the
+ // server, and may be noticably smaller than the caller's buffer.
+ //
+ // However, this greedy read may result in renegotiations/re-handshakes
+ // happening or may lead to some data being read, followed by an EOF (such as
+ // a TLS close-notify). If at least some data was read, then the error should
+ // be deferred until the next call to DoPayloadRead(). Otherwise, the error
+ // should be returned immediately.
wtc 2013/02/13 22:06:51 I believe that if renegotiation happens, PR_Read w
Ryan Sleevi 2013/02/13 22:55:48 My focus was in highlighting that SSL is not alway
+ int total_bytes_read = 0;
+ do {
+ rv = PR_Read(nss_fd_, user_read_buf_->data() + total_bytes_read,
+ user_read_buf_len_ - total_bytes_read);
+ if (rv > 0)
+ total_bytes_read += rv;
+ } while (total_bytes_read < user_read_buf_len_ && rv > 0);
+
+ if (total_bytes_read == user_read_buf_len_) {
+ // The caller's entire request was satisfied without error. No further
+ // processing needed.
+ rv = total_bytes_read;
+ } else {
+ // Otherwise, an error occurred (rv <= 0). The error needs to be handled
+ // immediately, while the NSPR/NSS errors are still available in
+ // thread-local storage. However, the handled/remapped error code should
+ // only be returned if no application data was already read; if it was, the
+ // error code should be deferred until the next call of DoPayloadRead.
+ int* next_result = &rv;
wtc 2013/02/13 22:06:51 Just a commentary: the use of next_result makes th
+ if (total_bytes_read > 0) {
+ pending_read_result_ = rv;
Ryan Hamilton 2013/02/13 17:23:18 same comment about using a const kNoPendingReadRes
+ rv = total_bytes_read;
+ next_result = &pending_read_result_;
+ }
+
+ if (client_auth_cert_needed_) {
+ *next_result = ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
wtc 2013/02/13 22:06:51 We should set pending_read_prerror_ to 0 in this c
+ } else if (*next_result < 0) {
+ // If *next_result == 0, then that indicates EOF, and no special error
+ // handling is needed.
+ pending_read_prerror_ = PR_GetError();
+ if (pending_read_prerror_ == PR_WOULD_BLOCK_ERROR) {
+ *next_result = ERR_IO_PENDING;
+ pending_read_prerror_ = 0;
wtc 2013/02/13 22:06:51 It seems OK for pending_read_prerror_ to be PR_WOU
Ryan Sleevi 2013/02/13 22:55:48 I'm not sure I fully follow, but what I understand
+ } else {
+ *next_result = HandleNSSError(pending_read_prerror_, false);
+ }
+ }
+ }
+
if (rv >= 0) {
PostOrRunCallback(
FROM_HERE,
base::Bind(&LogByteTransferEvent, weak_net_log_,
NetLog::TYPE_SSL_SOCKET_BYTES_RECEIVED, rv,
scoped_refptr<IOBuffer>(user_read_buf_)));
- return rv;
+ } else if (rv != ERR_IO_PENDING) {
+ PostOrRunCallback(
+ FROM_HERE,
+ base::Bind(&AddLogEventWithCallback, weak_net_log_,
+ NetLog::TYPE_SSL_READ_ERROR,
+ CreateNetLogSSLErrorCallback(rv, pending_read_prerror_)));
+ pending_read_prerror_ = 0;
}
- PRErrorCode prerr = PR_GetError();
- if (prerr == PR_WOULD_BLOCK_ERROR)
- return ERR_IO_PENDING;
-
- rv = HandleNSSError(prerr, false);
- PostOrRunCallback(
- FROM_HERE,
- base::Bind(&AddLogEventWithCallback, weak_net_log_,
- NetLog::TYPE_SSL_READ_ERROR,
- CreateNetLogSSLErrorCallback(rv, prerr)));
return rv;
}
« no previous file with comments | « no previous file | net/socket/ssl_client_socket_openssl.h » ('j') | net/socket/ssl_client_socket_openssl.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698