Chromium Code Reviews| 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 809dbc5beb28ce36b36e1e76c8ef4141a24eb715..90cc7e524395f2f6ef950c0044d37fe1c843306c 100644 |
| --- a/net/socket/ssl_client_socket_nss.cc |
| +++ b/net/socket/ssl_client_socket_nss.cc |
| @@ -642,6 +642,11 @@ class SSLClientSocketNSS::Core : public base::RefCountedThreadSafe<Core> { |
| int Read(IOBuffer* buf, int buf_len, const CompletionCallback& callback); |
| int Write(IOBuffer* buf, int buf_len, const CompletionCallback& callback); |
| + // Called on the network task runner. |
| + bool IsConnected(); |
| + bool HasPendingAsyncOperation(); |
| + bool HasUnhandledReceivedData(); |
| + |
| private: |
| friend class base::RefCountedThreadSafe<Core>; |
| ~Core(); |
| @@ -652,6 +657,12 @@ class SSLClientSocketNSS::Core : public base::RefCountedThreadSafe<Core> { |
| STATE_GET_DOMAIN_BOUND_CERT_COMPLETE, |
| }; |
| + enum Operation { |
| + OPERATION_READ, |
| + OPERATION_WRITE, |
| + OPERATION_UPDATE, |
| + }; |
| + |
| bool OnNSSTaskRunner() const; |
| bool OnNetworkTaskRunner() const; |
| @@ -740,6 +751,10 @@ class SSLClientSocketNSS::Core : public base::RefCountedThreadSafe<Core> { |
| void DoConnectCallback(int result); |
| void DoReadCallback(int result); |
| void DoWriteCallback(int result); |
| + void UpdateNSSTaskRunnerStatus(int amount_in_read_buffer, |
| + bool closed, |
| + Operation operation, |
| + const base::Closure& callback); |
| // Client channel ID handler. |
| static SECStatus ClientChannelIDHandler( |
| @@ -817,6 +832,12 @@ class SSLClientSocketNSS::Core : public base::RefCountedThreadSafe<Core> { |
| ServerBoundCertService* server_bound_cert_service_; |
| ServerBoundCertService::RequestHandle domain_bound_cert_request_handle_; |
| + // The information about NSS task runner. |
| + int unhandled_buffer_size_; |
| + bool nss_waiting_read_; |
| + bool nss_waiting_write_; |
| + bool nss_is_closed_; |
| + |
| //////////////////////////////////////////////////////////////////////////// |
| // Members that are ONLY accessed on the NSS task runner: |
| //////////////////////////////////////////////////////////////////////////// |
| @@ -896,6 +917,10 @@ SSLClientSocketNSS::Core::Core( |
| transport_(transport), |
| weak_net_log_factory_(net_log), |
| server_bound_cert_service_(server_bound_cert_service), |
| + unhandled_buffer_size_(0), |
| + nss_waiting_read_(false), |
| + nss_waiting_write_(false), |
| + nss_is_closed_(false), |
| host_and_port_(host_and_port), |
| ssl_config_(ssl_config), |
| nss_fd_(NULL), |
| @@ -1094,6 +1119,8 @@ int SSLClientSocketNSS::Core::Read(IOBuffer* buf, int buf_len, |
| FROM_HERE, |
| base::Bind(IgnoreResult(&Core::Read), this, make_scoped_refptr(buf), |
| buf_len, callback)); |
| + if (!posted) |
| + nss_is_closed_ = true; |
|
Ryan Sleevi
2013/01/21 16:33:29
BUG: Wrong thread here
Ryan Sleevi
2013/01/22 02:48:05
Er, this one was right
|
| return posted ? ERR_IO_PENDING : ERR_ABORTED; |
| } |
| @@ -1104,6 +1131,7 @@ int SSLClientSocketNSS::Core::Read(IOBuffer* buf, int buf_len, |
| DCHECK(user_connect_callback_.is_null()); |
| DCHECK(!user_read_buf_); |
| DCHECK(nss_bufs_); |
| + DCHECK(!nss_waiting_read_); |
|
Ryan Sleevi
2013/01/21 16:33:29
BUG: Wrong thread here
Takashi Toyoshima
2013/01/22 14:16:50
Done.
|
| user_read_buf_ = buf; |
| user_read_buf_len_ = buf_len; |
| @@ -1111,6 +1139,7 @@ int SSLClientSocketNSS::Core::Read(IOBuffer* buf, int buf_len, |
| int rv = DoReadLoop(OK); |
| if (rv == ERR_IO_PENDING) { |
| user_read_callback_ = callback; |
| + nss_waiting_read_ = true; |
|
Ryan Sleevi
2013/01/21 16:33:29
BUG: Wrong thread here
Takashi Toyoshima
2013/01/22 14:16:50
Done.
|
| } else { |
| user_read_buf_ = NULL; |
| user_read_buf_len_ = 0; |
| @@ -1119,6 +1148,9 @@ int SSLClientSocketNSS::Core::Read(IOBuffer* buf, int buf_len, |
| PostOrRunCallback(FROM_HERE, base::Bind(callback, rv)); |
| return ERR_IO_PENDING; |
| } |
| + |
| + if (rv <= 0) |
| + nss_is_closed_ = true; |
|
Ryan Sleevi
2013/01/21 16:33:29
BUG: Wrong thread here
Takashi Toyoshima
2013/01/22 14:16:50
Done.
|
| } |
| return rv; |
| @@ -1136,6 +1168,8 @@ int SSLClientSocketNSS::Core::Write(IOBuffer* buf, int buf_len, |
| base::Bind(IgnoreResult(&Core::Write), this, make_scoped_refptr(buf), |
| buf_len, callback)); |
| int rv = posted ? ERR_IO_PENDING : ERR_ABORTED; |
| + if (!posted) |
| + nss_is_closed_ = true; |
|
Ryan Sleevi
2013/01/21 16:33:29
BUG: Wrong thread here
Ryan Sleevi
2013/01/22 02:48:05
as was this one
Takashi Toyoshima
2013/01/22 14:16:50
Done.
|
| return rv; |
| } |
| @@ -1146,6 +1180,7 @@ int SSLClientSocketNSS::Core::Write(IOBuffer* buf, int buf_len, |
| DCHECK(user_connect_callback_.is_null()); |
| DCHECK(!user_write_buf_); |
| DCHECK(nss_bufs_); |
| + DCHECK(!nss_waiting_write_); |
|
Ryan Sleevi
2013/01/21 16:33:29
BUG: Wrong thread here
Takashi Toyoshima
2013/01/22 14:16:50
Done.
|
| user_write_buf_ = buf; |
| user_write_buf_len_ = buf_len; |
| @@ -1153,6 +1188,7 @@ int SSLClientSocketNSS::Core::Write(IOBuffer* buf, int buf_len, |
| int rv = DoWriteLoop(OK); |
| if (rv == ERR_IO_PENDING) { |
| user_write_callback_ = callback; |
| + nss_waiting_write_ = true; |
|
Ryan Sleevi
2013/01/21 16:33:29
BUG: Wrong thread here
Takashi Toyoshima
2013/01/22 14:16:50
Done.
|
| } else { |
| user_write_buf_ = NULL; |
| user_write_buf_len_ = 0; |
| @@ -1161,11 +1197,28 @@ int SSLClientSocketNSS::Core::Write(IOBuffer* buf, int buf_len, |
| PostOrRunCallback(FROM_HERE, base::Bind(callback, rv)); |
| return ERR_IO_PENDING; |
| } |
| + |
| + if (rv < 0) |
| + nss_is_closed_ = true; |
|
Ryan Sleevi
2013/01/21 16:33:29
BUG: Wrong thread here
Takashi Toyoshima
2013/01/22 14:16:50
Done.
|
| } |
| return rv; |
| } |
| +bool SSLClientSocketNSS::Core::IsConnected() { |
| + return !nss_is_closed_; |
| +} |
| + |
| +bool SSLClientSocketNSS::Core::HasPendingAsyncOperation() { |
| + DCHECK(OnNetworkTaskRunner()); |
| + return nss_waiting_read_ || nss_waiting_write_; |
| +} |
| + |
| +bool SSLClientSocketNSS::Core::HasUnhandledReceivedData() { |
| + DCHECK(OnNetworkTaskRunner()); |
| + return unhandled_buffer_size_ != 0; |
| +} |
| + |
| bool SSLClientSocketNSS::Core::OnNSSTaskRunner() const { |
| return nss_task_runner_->RunsTasksOnCurrentThread(); |
| } |
| @@ -2044,6 +2097,17 @@ int SSLClientSocketNSS::Core::DoPayloadRead() { |
| 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_); |
| + base::Closure task = base::Bind( |
| + &Core::UpdateNSSTaskRunnerStatus, |
| + this, |
| + amount_in_read_buffer, |
| + rv <= 0 && rv != ERR_IO_PENDING, |
| + OPERATION_UPDATE, |
| + base::Closure()); |
| + |
| if (client_auth_cert_needed_) { |
| // We don't need to invalidate the non-client-authenticated SSL session |
| // because the server will renegotiate anyway. |
| @@ -2081,7 +2145,21 @@ int SSLClientSocketNSS::Core::DoPayloadWrite() { |
| DCHECK(user_write_buf_); |
| + int old_amount_in_read_buffer = memio_GetReadableBufferSize(nss_bufs_); |
| int rv = PR_Write(nss_fd_, user_write_buf_->data(), user_write_buf_len_); |
| + int new_amount_in_read_buffer = memio_GetReadableBufferSize(nss_bufs_); |
| + // PR_Write could potentially consume the unhandled data in the memio read |
| + // buffer if a renegotiation is in progress. If the buffer is consumed, |
| + // notify the latest buffer size to NetworkRunner. |
| + if (old_amount_in_read_buffer != new_amount_in_read_buffer) { |
| + base::Closure task = base::Bind( |
| + &Core::UpdateNSSTaskRunnerStatus, |
| + this, |
| + new_amount_in_read_buffer, |
| + false, |
| + OPERATION_UPDATE, |
| + base::Closure()); |
| + } |
| if (rv >= 0) { |
| PostOrRunCallback( |
| FROM_HERE, |
| @@ -2287,10 +2365,20 @@ void SSLClientSocketNSS::Core::DoReadCallback(int rv) { |
| user_read_buf_ = NULL; |
| user_read_buf_len_ = 0; |
| - base::Closure c = base::Bind( |
| - base::ResetAndReturn(&user_read_callback_), |
| - rv); |
| - PostOrRunCallback(FROM_HERE, c); |
| + int amount_in_read_buffer = memio_GetReadableBufferSize(nss_bufs_); |
| + // This will invoke the user callback with |rv| as result. |
| + base::Closure user_cb = base::Bind( |
| + base::ResetAndReturn(&user_read_callback_), rv); |
| + // This is used to curry the |amount_int_read_buffer| and |user_cb| back to |
| + // the network task runner. |
| + base::Closure task = base::Bind( |
| + &Core::UpdateNSSTaskRunnerStatus, |
| + this, |
| + amount_in_read_buffer, |
| + rv <= 0, // EOF or errors. |
| + OPERATION_READ, |
| + user_cb); |
| + PostOrRunCallback(FROM_HERE, task); |
| } |
| void SSLClientSocketNSS::Core::DoWriteCallback(int rv) { |
| @@ -2302,10 +2390,45 @@ void SSLClientSocketNSS::Core::DoWriteCallback(int rv) { |
| // up front. |
| user_write_buf_ = NULL; |
| user_write_buf_len_ = 0; |
| - base::Closure c = base::Bind( |
| - base::ResetAndReturn(&user_write_callback_), |
| - rv); |
| - PostOrRunCallback(FROM_HERE, c); |
| + // Update buffer status because DoWriteLoop called DoTransportIO which may |
| + // perform read operations. |
| + int amount_in_read_buffer = memio_GetReadableBufferSize(nss_bufs_); |
| + // This will invoke the user callback with |rv| as result. |
| + base::Closure user_cb = base::Bind( |
| + base::ResetAndReturn(&user_write_callback_), rv); |
| + // This is used to curry the |amount_int_read_buffer| and |user_cb| back to |
| + // the network task runner. |
| + base::Closure task = base::Bind( |
| + &Core::UpdateNSSTaskRunnerStatus, |
| + this, |
| + amount_in_read_buffer, |
| + rv < 0, // Errors. |
| + OPERATION_WRITE, |
| + user_cb); |
| + PostOrRunCallback(FROM_HERE, task); |
| +} |
| + |
| +void SSLClientSocketNSS::Core::UpdateNSSTaskRunnerStatus( |
|
Takashi Toyoshima
2013/01/22 14:16:50
I renamed it as OnNSSTaskRunnerStateUpdated() to f
|
| + int amount_in_read_buffer, |
| + bool closed, |
| + Operation operation, |
| + const base::Closure& callback) { |
| + DCHECK(OnNetworkTaskRunner()); |
| + DCHECK(operation == OPERATION_UPDATE || !callback.is_null()); |
| + DCHECK((operation == OPERATION_READ && nss_waiting_read_) || |
| + (operation == OPERATION_WRITE && nss_waiting_write_) || |
| + operation == OPERATION_UPDATE); |
| + |
| + unhandled_buffer_size_ = amount_in_read_buffer; |
| + if (closed) |
| + nss_is_closed_ = true; |
| + if (operation == OPERATION_READ) |
| + nss_waiting_read_ = false; |
| + else if (operation == OPERATION_WRITE) |
| + nss_waiting_write_ = false; |
| + |
| + if (operation != OPERATION_UPDATE) |
| + callback.Run(); |
| } |
| SECStatus SSLClientSocketNSS::Core::ClientChannelIDHandler( |
| @@ -2922,29 +3045,21 @@ void SSLClientSocketNSS::Disconnect() { |
| } |
| bool SSLClientSocketNSS::IsConnected() const { |
| - // Ideally, we should also check if we have received the close_notify alert |
| - // message from the server, and return false in that case. We're not doing |
| - // that, so this function may return a false positive. Since the upper |
| - // layer (HttpNetworkTransaction) needs to handle a persistent connection |
| - // closed by the server when we send a request anyway, a false positive in |
| - // exchange for simpler code is a good trade-off. |
| EnterFunction(""); |
| - bool ret = completed_handshake_ && transport_->socket()->IsConnected(); |
| + bool ret = completed_handshake_ && |
| + (core_->HasPendingAsyncOperation() || |
| + core_->HasUnhandledReceivedData() || |
| + core_->IsConnected() || |
|
Ryan Sleevi
2013/01/21 16:33:29
This logic change isn't correct. You're short-circ
Takashi Toyoshima
2013/01/22 14:16:50
Thank you.
I was confused on how to handle transpo
|
| + transport_->socket()->IsConnected()); |
| LeaveFunction(""); |
| return ret; |
| } |
| bool SSLClientSocketNSS::IsConnectedAndIdle() const { |
| - // Unlike IsConnected, this method doesn't return a false positive. |
| - // |
| - // Strictly speaking, we should check if we have received the close_notify |
| - // alert message from the server, and return false in that case. Although |
| - // the close_notify alert message means EOF in the SSL layer, it is just |
| - // bytes to the transport layer below, so |
| - // transport_->socket()->IsConnectedAndIdle() returns the desired false |
| - // when we receive close_notify. |
| EnterFunction(""); |
| - bool ret = completed_handshake_ && transport_->socket()->IsConnectedAndIdle(); |
| + bool ret = IsConnected() && |
| + !(core_->IsConnected() && core_->HasUnhandledReceivedData()) && |
| + transport_->socket()->IsConnectedAndIdle(); |
| LeaveFunction(""); |
| return ret; |
| } |