Chromium Code Reviews| Index: net/socket/ssl_client_socket_openssl.cc |
| diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc |
| index d6345f71501d2acc3e20384943045d90ebdfa846..548c349529621f0cfdd84ff4f83eb011718e4f44 100644 |
| --- a/net/socket/ssl_client_socket_openssl.cc |
| +++ b/net/socket/ssl_client_socket_openssl.cc |
| @@ -34,6 +34,33 @@ namespace net { |
| namespace { |
| +// This IOBuffer version is to be used with OpenSSL's non-copying interface, |
| +// to guarantee that the BIO object remains alive as long as the socket keeps |
| +// a reference to it's internal data buffer. |
| +// |
| +// |bio_buffer| must be retrieved from |bio| using the non-copying interface |
| +// BIO_nread0() or BIO_nwrite0(). The reference count of |bio| is increased to |
| +// make sure |bio| is not free'd if it is referenced from elsewhere. |
| +class WrappedBIOAndIOBuffer : public IOBuffer { |
| + public: |
| + |
| + explicit WrappedBIOAndIOBuffer(const char* bio_buffer, BIO* bio) |
|
Ryan Sleevi
2014/04/30 21:33:32
nit: delete newline on 46
|
| + : IOBuffer(const_cast<char*>(bio_buffer)), bio_(bio) { |
| + CHECK(bio); |
| + |
| + CRYPTO_add(&bio_->references, 1, CRYPTO_LOCK_BIO); |
| + } |
| + |
| + protected: |
| + virtual ~WrappedBIOAndIOBuffer() { |
| + data_ = NULL; |
| + // Will count down the reference count, and free if the count reaches zero. |
| + BIO_free_all(bio_); |
| + } |
| + |
| + BIO* bio_; |
| +}; |
| + |
| // Enable this to see logging for state machine state transitions. |
| #if 0 |
| #define GotoState(s) do { DVLOG(2) << (void *)this << " " << __FUNCTION__ << \ |
| @@ -1277,10 +1304,23 @@ int SSLClientSocketOpenSSL::BufferSend(void) { |
| size_t max_read = BIO_ctrl_pending(transport_bio_); |
| if (!max_read) |
| return 0; // Nothing pending in the OpenSSL write BIO. |
|
Ryan Sleevi
2014/04/30 21:33:32
Is this still necessary, given that BIO_nread0 wit
haavardm
2014/05/01 16:55:19
The problem with letting 0 fall through is that ca
Ryan Sleevi
2014/05/08 22:59:28
Yeah, I suppose the alternative would be to reset
|
| - send_buffer_ = new DrainableIOBuffer(new IOBuffer(max_read), max_read); |
| - int read_bytes = BIO_read(transport_bio_, send_buffer_->data(), max_read); |
| - DCHECK_GT(read_bytes, 0); |
| - CHECK_EQ(static_cast<int>(max_read), read_bytes); |
| + |
| + char* bio_transport_buf; |
| + // Get the internal buffer and number of bytes available for non-copying |
| + // read. |
| + // BIO_ctrl_pending() is still called above. Calling BIO_nread0() when |
| + // there is no data triggers a call to BIO_nread() to read one byte. This |
| + // has side effect that the retry flag is set. |
| + int available_read_bytes = BIO_nread0(transport_bio_, &bio_transport_buf); |
| + // BIO_nread0() might report less than BIO_ctrl_pending() due to the buffer |
| + // offset and no ring buffer wrap-around for non-copying interface. |
| + CHECK_GE(static_cast<int>(max_read), available_read_bytes); |
| + if (available_read_bytes <= 0) |
| + return 0; |
| + |
| + send_buffer_ = new DrainableIOBuffer( |
|
Ryan Sleevi
2014/04/30 21:33:32
Huh. I'm not really sure why we use a DrainableIOB
haavardm
2014/05/01 16:55:19
I believe the drainable buffer is needed to handle
Ryan Sleevi
2014/05/08 22:59:28
Yeah, but why create a DrainableIOBuffer (and have
haavardm
2014/05/09 07:23:41
Yes, I had a look into NSS yesterday and saw how y
|
| + new WrappedBIOAndIOBuffer(bio_transport_buf, transport_bio_), |
| + available_read_bytes); |
| } |
| int rv = transport_->socket()->Write( |
| @@ -1322,10 +1362,20 @@ int SSLClientSocketOpenSSL::BufferRecv(void) { |
| if (!max_write) |
| return ERR_IO_PENDING; |
| - recv_buffer_ = new IOBuffer(max_write); |
| + char* bio_transport_buf; |
| + // Get the internal buffer and number of bytes available for non-copying |
| + // write. |
| + int available_write_bytes = BIO_nwrite0(transport_bio_, &bio_transport_buf); |
| + // BIO_nwrite0() might report less than BIO_ctrl_get_write_guarantee() due to |
| + // the buffer offset and no ring buffer wrap-around for non-copying interface. |
| + DCHECK_GE(static_cast<int>(max_write), available_write_bytes); |
| + if (available_write_bytes <= 0) |
| + return ERR_IO_PENDING; |
| + |
| + recv_buffer_ = new WrappedBIOAndIOBuffer(bio_transport_buf, transport_bio_); |
| int rv = transport_->socket()->Read( |
| recv_buffer_.get(), |
| - max_write, |
| + available_write_bytes, |
| base::Bind(&SSLClientSocketOpenSSL::BufferRecvComplete, |
| base::Unretained(this))); |
| if (rv == ERR_IO_PENDING) { |
| @@ -1367,6 +1417,11 @@ void SSLClientSocketOpenSSL::TransportWriteComplete(int result) { |
| send_buffer_ = NULL; |
| } else { |
| DCHECK(send_buffer_.get()); |
| + char* bio_transport_buf; |
| + // Advance the buffer index. |
| + int ret = BIO_nread(transport_bio_, &bio_transport_buf, result); |
| + CHECK_EQ(ret, result); |
| + CHECK_EQ(bio_transport_buf, send_buffer_->data()); |
| send_buffer_->DidConsume(result); |
| DCHECK_GE(send_buffer_->BytesRemaining(), 0); |
| if (send_buffer_->BytesRemaining() <= 0) |
| @@ -1390,8 +1445,11 @@ int SSLClientSocketOpenSSL::TransportReadComplete(int result) { |
| // the CHECK. http://crbug.com/335557. |
| result = transport_write_error_; |
| } else { |
| - DCHECK(recv_buffer_.get()); |
| - int ret = BIO_write(transport_bio_, recv_buffer_->data(), result); |
| + char* bio_transport_buf; |
| + // Set the amount of data read into buffer. |
| + int ret = BIO_nwrite(transport_bio_, &bio_transport_buf, result); |
| + CHECK_EQ(ret, result); |
| + CHECK_EQ(bio_transport_buf, recv_buffer_->data()); |
| // A write into a memory BIO should always succeed. |
| // Force values on the stack for http://crbug.com/335557 |
| base::debug::Alias(&result); |