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..b631b91c0a7b9aa4385e9916d5ed05d4f269bb92 100644 |
--- a/net/socket/ssl_client_socket_nss.cc |
+++ b/net/socket/ssl_client_socket_nss.cc |
@@ -117,12 +117,42 @@ |
#include <dlfcn.h> |
#endif |
+namespace net { |
+ |
+// State machines are easier to debug if you log state transitions. |
+// Enable these if you want to see what's going on. |
+#if 1 |
+#define EnterFunction(x) |
+#define LeaveFunction(x) |
+#define GotoState(s) next_handshake_state_ = s |
+#else |
+#define EnterFunction(x)\ |
+ VLOG(1) << (void *)this << " " << __FUNCTION__ << " enter " << x\ |
+ << "; next_handshake_state " << next_handshake_state_ |
+#define LeaveFunction(x)\ |
+ VLOG(1) << (void *)this << " " << __FUNCTION__ << " leave " << x\ |
+ << "; next_handshake_state " << next_handshake_state_ |
+#define GotoState(s)\ |
+ do {\ |
+ VLOG(1) << (void *)this << " " << __FUNCTION__ << " jump to state " << s;\ |
+ next_handshake_state_ = s;\ |
+ } while (0) |
+#endif |
+ |
+namespace { |
+ |
// SSL plaintext fragments are shorter than 16KB. Although the record layer |
// overhead is allowed to be 2K + 5 bytes, in practice the overhead is much |
// smaller than 1KB. So a 17KB buffer should be large enough to hold an |
// entire SSL record. |
-static const int kRecvBufferSize = 17 * 1024; |
-static const int kSendBufferSize = 17 * 1024; |
+const int kRecvBufferSize = 17 * 1024; |
+const int kSendBufferSize = 17 * 1024; |
+ |
+// Used by SSLClientSocketNSS::Core to indicate there is no read result |
+// obtained by a previous operation waiting to be returned to the caller. |
+// This constant can be any non-negative/non-zero value (eg: it does not |
+// overlap with any value of the net::Error range, including net::OK). |
+const int kNoPendingReadResult = 1; |
#if defined(OS_WIN) |
// CERT_OCSP_RESPONSE_PROP_ID is only implemented on Vista+, but it can be |
@@ -130,7 +160,7 @@ static const int kSendBufferSize = 17 * 1024; |
// sending the OCSP response if it supports the extension, for the subset of |
// XP clients who will request it but be unable to use it, but this is an |
// acceptable trade-off for simplicity of implementation. |
-static bool IsOCSPStaplingSupported() { |
+bool IsOCSPStaplingSupported() { |
return true; |
} |
#elif defined(USE_NSS) |
@@ -169,47 +199,23 @@ class RuntimeLibNSSFunctionPointers { |
cache_ocsp_response_from_side_channel_; |
}; |
-static CacheOCSPResponseFromSideChannelFunction |
+CacheOCSPResponseFromSideChannelFunction |
GetCacheOCSPResponseFromSideChannelFunction() { |
return RuntimeLibNSSFunctionPointers::GetInstance() |
->GetCacheOCSPResponseFromSideChannelFunction(); |
} |
-static bool IsOCSPStaplingSupported() { |
+bool IsOCSPStaplingSupported() { |
return GetCacheOCSPResponseFromSideChannelFunction() != NULL; |
} |
#else |
// TODO(agl): Figure out if we can plumb the OCSP response into Mac's system |
// certificate validation functions. |
-static bool IsOCSPStaplingSupported() { |
+bool IsOCSPStaplingSupported() { |
return false; |
} |
#endif |
-namespace net { |
- |
-// State machines are easier to debug if you log state transitions. |
-// Enable these if you want to see what's going on. |
-#if 1 |
-#define EnterFunction(x) |
-#define LeaveFunction(x) |
-#define GotoState(s) next_handshake_state_ = s |
-#else |
-#define EnterFunction(x)\ |
- VLOG(1) << (void *)this << " " << __FUNCTION__ << " enter " << x\ |
- << "; next_handshake_state " << next_handshake_state_ |
-#define LeaveFunction(x)\ |
- VLOG(1) << (void *)this << " " << __FUNCTION__ << " leave " << x\ |
- << "; next_handshake_state " << next_handshake_state_ |
-#define GotoState(s)\ |
- do {\ |
- VLOG(1) << (void *)this << " " << __FUNCTION__ << " jump to state " << s;\ |
- next_handshake_state_ = s;\ |
- } while (0) |
-#endif |
- |
-namespace { |
- |
class FreeCERTCertificate { |
public: |
inline void operator()(CERTCertificate* x) const { |
@@ -837,6 +843,17 @@ 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 fill the caller's buffer with |
+ // as much data as possible, without blocking. |
+ // If DoPayloadRead() encounters an error after having read some data, stores |
+ // the results to return on the *next* call to DoPayloadRead(). A value of |
+ // kNoPendingReadResult indicates there is no pending result, otherwise 0 |
+ // indicates EOF and < 0 indicates an error. |
+ int pending_read_result_; |
+ // Contains the previously observed NSS error. Only valid when |
+ // pending_read_result_ != kNoPendingReadResult. |
+ PRErrorCode pending_read_nss_error_; |
+ |
// The certificate chain, in DER form, that is expected to be received from |
// the server. |
std::vector<std::string> predicted_certs_; |
@@ -912,6 +929,8 @@ SSLClientSocketNSS::Core::Core( |
ssl_config_(ssl_config), |
nss_fd_(NULL), |
nss_bufs_(NULL), |
+ pending_read_result_(kNoPendingReadResult), |
+ pending_read_nss_error_(0), |
next_handshake_state_(STATE_NONE), |
channel_id_xtn_negotiated_(false), |
channel_id_needed_(false), |
@@ -1945,43 +1964,115 @@ 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 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_ != kNoPendingReadResult) { |
+ rv = pending_read_result_; |
+ PRErrorCode prerr = pending_read_nss_error_; |
+ pending_read_result_ = kNoPendingReadResult; |
+ pending_read_nss_error_ = 0; |
+ |
+ 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_))); |
+ } else { |
+ 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. In the current NSS implementation, PR_Read will return |
+ // exactly one SSL application data record's worth of data per invocation. |
+ // The record size is dictated by the server, and may be noticeably smaller |
+ // than the caller's buffer. This may be as little as a single byte, if the |
+ // server is performing 1/n-1 record splitting. |
+ // |
+ // 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 that result |
+ // should be deferred until the next call to DoPayloadRead(). Otherwise, if no |
+ // data was read, it's safe to return the error or EOF immediately. |
+ 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); |
int amount_in_read_buffer = memio_GetReadableBufferSize(nss_bufs_); |
- PostOrRunCallback( |
- FROM_HERE, |
- base::Bind(&Core::OnNSSBufferUpdated, this, amount_in_read_buffer)); |
+ 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))); |
- return rv; |
+ 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. |
+ // |
+ // If no data was read, |*next_result| will point to the return value of |
+ // this function. If at least some data was read, |*next_result| will point |
+ // to |pending_read_error_|, to be returned in a future call to |
+ // DoPayloadRead() (e.g.: after the current data is handled). |
+ int* next_result = &rv; |
+ if (total_bytes_read > 0) { |
+ pending_read_result_ = rv; |
+ rv = total_bytes_read; |
+ next_result = &pending_read_result_; |
+ } |
+ |
+ if (client_auth_cert_needed_) { |
+ *next_result = ERR_SSL_CLIENT_AUTH_CERT_NEEDED; |
+ pending_read_nss_error_ = 0; |
+ } else if (*next_result < 0) { |
+ // If *next_result == 0, then that indicates EOF, and no special error |
+ // handling is needed. |
+ pending_read_nss_error_ = PR_GetError(); |
+ *next_result = HandleNSSError(pending_read_nss_error_, false); |
+ if (rv > 0 && *next_result == ERR_IO_PENDING) { |
+ // If at least some data was read from PR_Read(), do not treat |
+ // insufficient data as an error to return in the next call to |
+ // DoPayloadRead() - instead, let the call fall through to check |
+ // PR_Read() again. This is because DoTransportIO() may complete |
+ // in between the next call to DoPayloadRead(), and thus it is |
+ // important to check PR_Read() on subsequent invocations to see |
+ // if a complete record may now be read. |
+ pending_read_nss_error_ = 0; |
+ pending_read_result_ = kNoPendingReadResult; |
+ } |
+ } |
} |
+ |
+ DCHECK_NE(ERR_IO_PENDING, pending_read_result_); |
+ |
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_nss_error_))); |
+ pending_read_nss_error_ = 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; |
} |