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); |