| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "net/socket/socket_test_util.h" | |
| #include <algorithm> | |
| #include <vector> | |
| #include "base/basictypes.h" | |
| #include "base/bind.h" | |
| #include "base/bind_helpers.h" | |
| #include "base/compiler_specific.h" | |
| #include "base/message_loop.h" | |
| #include "base/time.h" | |
| #include "net/base/address_family.h" | |
| #include "net/base/address_list.h" | |
| #include "net/base/auth.h" | |
| #include "net/base/ssl_cert_request_info.h" | |
| #include "net/base/ssl_info.h" | |
| #include "net/http/http_network_session.h" | |
| #include "net/http/http_request_headers.h" | |
| #include "net/http/http_response_headers.h" | |
| #include "net/socket/client_socket_pool_histograms.h" | |
| #include "net/socket/socket.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| // Socket events are easier to debug if you log individual reads and writes. | |
| // Enable these if locally debugging, but they are too noisy for the waterfall. | |
| #if 0 | |
| #define NET_TRACE(level, s) DLOG(level) << s << __FUNCTION__ << "() " | |
| #else | |
| #define NET_TRACE(level, s) EAT_STREAM_PARAMETERS | |
| #endif | |
| namespace net { | |
| namespace { | |
| inline char AsciifyHigh(char x) { | |
| char nybble = static_cast<char>((x >> 4) & 0x0F); | |
| return nybble + ((nybble < 0x0A) ? '0' : 'A' - 10); | |
| } | |
| inline char AsciifyLow(char x) { | |
| char nybble = static_cast<char>((x >> 0) & 0x0F); | |
| return nybble + ((nybble < 0x0A) ? '0' : 'A' - 10); | |
| } | |
| inline char Asciify(char x) { | |
| if ((x < 0) || !isprint(x)) | |
| return '.'; | |
| return x; | |
| } | |
| void DumpData(const char* data, int data_len) { | |
| if (logging::LOG_INFO < logging::GetMinLogLevel()) | |
| return; | |
| DVLOG(1) << "Length: " << data_len; | |
| const char* pfx = "Data: "; | |
| if (!data || (data_len <= 0)) { | |
| DVLOG(1) << pfx << "<None>"; | |
| } else { | |
| int i; | |
| for (i = 0; i <= (data_len - 4); i += 4) { | |
| DVLOG(1) << pfx | |
| << AsciifyHigh(data[i + 0]) << AsciifyLow(data[i + 0]) | |
| << AsciifyHigh(data[i + 1]) << AsciifyLow(data[i + 1]) | |
| << AsciifyHigh(data[i + 2]) << AsciifyLow(data[i + 2]) | |
| << AsciifyHigh(data[i + 3]) << AsciifyLow(data[i + 3]) | |
| << " '" | |
| << Asciify(data[i + 0]) | |
| << Asciify(data[i + 1]) | |
| << Asciify(data[i + 2]) | |
| << Asciify(data[i + 3]) | |
| << "'"; | |
| pfx = " "; | |
| } | |
| // Take care of any 'trailing' bytes, if data_len was not a multiple of 4. | |
| switch (data_len - i) { | |
| case 3: | |
| DVLOG(1) << pfx | |
| << AsciifyHigh(data[i + 0]) << AsciifyLow(data[i + 0]) | |
| << AsciifyHigh(data[i + 1]) << AsciifyLow(data[i + 1]) | |
| << AsciifyHigh(data[i + 2]) << AsciifyLow(data[i + 2]) | |
| << " '" | |
| << Asciify(data[i + 0]) | |
| << Asciify(data[i + 1]) | |
| << Asciify(data[i + 2]) | |
| << " '"; | |
| break; | |
| case 2: | |
| DVLOG(1) << pfx | |
| << AsciifyHigh(data[i + 0]) << AsciifyLow(data[i + 0]) | |
| << AsciifyHigh(data[i + 1]) << AsciifyLow(data[i + 1]) | |
| << " '" | |
| << Asciify(data[i + 0]) | |
| << Asciify(data[i + 1]) | |
| << " '"; | |
| break; | |
| case 1: | |
| DVLOG(1) << pfx | |
| << AsciifyHigh(data[i + 0]) << AsciifyLow(data[i + 0]) | |
| << " '" | |
| << Asciify(data[i + 0]) | |
| << " '"; | |
| break; | |
| } | |
| } | |
| } | |
| template <MockReadWriteType type> | |
| void DumpMockReadWrite(const MockReadWrite<type>& r) { | |
| if (logging::LOG_INFO < logging::GetMinLogLevel()) | |
| return; | |
| DVLOG(1) << "Async: " << (r.mode == ASYNC) | |
| << "\nResult: " << r.result; | |
| DumpData(r.data, r.data_len); | |
| const char* stop = (r.sequence_number & MockRead::STOPLOOP) ? " (STOP)" : ""; | |
| DVLOG(1) << "Stage: " << (r.sequence_number & ~MockRead::STOPLOOP) << stop | |
| << "\nTime: " << r.time_stamp.ToInternalValue(); | |
| } | |
| } // namespace | |
| MockConnect::MockConnect() : mode(ASYNC), result(OK) { | |
| IPAddressNumber ip; | |
| CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); | |
| peer_addr = IPEndPoint(ip, 0); | |
| } | |
| MockConnect::MockConnect(IoMode io_mode, int r) : mode(io_mode), result(r) { | |
| IPAddressNumber ip; | |
| CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); | |
| peer_addr = IPEndPoint(ip, 0); | |
| } | |
| MockConnect::MockConnect(IoMode io_mode, int r, IPEndPoint addr) : | |
| mode(io_mode), | |
| result(r), | |
| peer_addr(addr) { | |
| } | |
| MockConnect::~MockConnect() {} | |
| StaticSocketDataProvider::StaticSocketDataProvider() | |
| : reads_(NULL), | |
| read_index_(0), | |
| read_count_(0), | |
| writes_(NULL), | |
| write_index_(0), | |
| write_count_(0) { | |
| } | |
| StaticSocketDataProvider::StaticSocketDataProvider(MockRead* reads, | |
| size_t reads_count, | |
| MockWrite* writes, | |
| size_t writes_count) | |
| : reads_(reads), | |
| read_index_(0), | |
| read_count_(reads_count), | |
| writes_(writes), | |
| write_index_(0), | |
| write_count_(writes_count) { | |
| } | |
| StaticSocketDataProvider::~StaticSocketDataProvider() {} | |
| const MockRead& StaticSocketDataProvider::PeekRead() const { | |
| DCHECK(!at_read_eof()); | |
| return reads_[read_index_]; | |
| } | |
| const MockWrite& StaticSocketDataProvider::PeekWrite() const { | |
| DCHECK(!at_write_eof()); | |
| return writes_[write_index_]; | |
| } | |
| const MockRead& StaticSocketDataProvider::PeekRead(size_t index) const { | |
| DCHECK_LT(index, read_count_); | |
| return reads_[index]; | |
| } | |
| const MockWrite& StaticSocketDataProvider::PeekWrite(size_t index) const { | |
| DCHECK_LT(index, write_count_); | |
| return writes_[index]; | |
| } | |
| MockRead StaticSocketDataProvider::GetNextRead() { | |
| DCHECK(!at_read_eof()); | |
| reads_[read_index_].time_stamp = base::Time::Now(); | |
| return reads_[read_index_++]; | |
| } | |
| MockWriteResult StaticSocketDataProvider::OnWrite(const std::string& data) { | |
| if (!writes_) { | |
| // Not using mock writes; succeed synchronously. | |
| return MockWriteResult(SYNCHRONOUS, data.length()); | |
| } | |
| DCHECK(!at_write_eof()); | |
| // Check that what we are writing matches the expectation. | |
| // Then give the mocked return value. | |
| MockWrite* w = &writes_[write_index_++]; | |
| w->time_stamp = base::Time::Now(); | |
| int result = w->result; | |
| if (w->data) { | |
| // Note - we can simulate a partial write here. If the expected data | |
| // is a match, but shorter than the write actually written, that is legal. | |
| // Example: | |
| // Application writes "foobarbaz" (9 bytes) | |
| // Expected write was "foo" (3 bytes) | |
| // This is a success, and we return 3 to the application. | |
| std::string expected_data(w->data, w->data_len); | |
| EXPECT_GE(data.length(), expected_data.length()); | |
| std::string actual_data(data.substr(0, w->data_len)); | |
| EXPECT_EQ(expected_data, actual_data); | |
| if (expected_data != actual_data) | |
| return MockWriteResult(SYNCHRONOUS, ERR_UNEXPECTED); | |
| if (result == OK) | |
| result = w->data_len; | |
| } | |
| return MockWriteResult(w->mode, result); | |
| } | |
| void StaticSocketDataProvider::Reset() { | |
| read_index_ = 0; | |
| write_index_ = 0; | |
| } | |
| DynamicSocketDataProvider::DynamicSocketDataProvider() | |
| : short_read_limit_(0), | |
| allow_unconsumed_reads_(false) { | |
| } | |
| DynamicSocketDataProvider::~DynamicSocketDataProvider() {} | |
| MockRead DynamicSocketDataProvider::GetNextRead() { | |
| if (reads_.empty()) | |
| return MockRead(SYNCHRONOUS, ERR_UNEXPECTED); | |
| MockRead result = reads_.front(); | |
| if (short_read_limit_ == 0 || result.data_len <= short_read_limit_) { | |
| reads_.pop_front(); | |
| } else { | |
| result.data_len = short_read_limit_; | |
| reads_.front().data += result.data_len; | |
| reads_.front().data_len -= result.data_len; | |
| } | |
| return result; | |
| } | |
| void DynamicSocketDataProvider::Reset() { | |
| reads_.clear(); | |
| } | |
| void DynamicSocketDataProvider::SimulateRead(const char* data, | |
| const size_t length) { | |
| if (!allow_unconsumed_reads_) { | |
| EXPECT_TRUE(reads_.empty()) << "Unconsumed read: " << reads_.front().data; | |
| } | |
| reads_.push_back(MockRead(ASYNC, data, length)); | |
| } | |
| SSLSocketDataProvider::SSLSocketDataProvider(IoMode mode, int result) | |
| : connect(mode, result), | |
| next_proto_status(SSLClientSocket::kNextProtoUnsupported), | |
| was_npn_negotiated(false), | |
| protocol_negotiated(kProtoUnknown), | |
| client_cert_sent(false), | |
| cert_request_info(NULL), | |
| channel_id_sent(false) { | |
| } | |
| SSLSocketDataProvider::~SSLSocketDataProvider() { | |
| } | |
| void SSLSocketDataProvider::SetNextProto(NextProto proto) { | |
| was_npn_negotiated = true; | |
| next_proto_status = SSLClientSocket::kNextProtoNegotiated; | |
| protocol_negotiated = proto; | |
| next_proto = SSLClientSocket::NextProtoToString(proto); | |
| } | |
| DelayedSocketData::DelayedSocketData( | |
| int write_delay, MockRead* reads, size_t reads_count, | |
| MockWrite* writes, size_t writes_count) | |
| : StaticSocketDataProvider(reads, reads_count, writes, writes_count), | |
| write_delay_(write_delay), | |
| read_in_progress_(false), | |
| ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | |
| DCHECK_GE(write_delay_, 0); | |
| } | |
| DelayedSocketData::DelayedSocketData( | |
| const MockConnect& connect, int write_delay, MockRead* reads, | |
| size_t reads_count, MockWrite* writes, size_t writes_count) | |
| : StaticSocketDataProvider(reads, reads_count, writes, writes_count), | |
| write_delay_(write_delay), | |
| read_in_progress_(false), | |
| ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | |
| DCHECK_GE(write_delay_, 0); | |
| set_connect_data(connect); | |
| } | |
| DelayedSocketData::~DelayedSocketData() { | |
| } | |
| void DelayedSocketData::ForceNextRead() { | |
| DCHECK(read_in_progress_); | |
| write_delay_ = 0; | |
| CompleteRead(); | |
| } | |
| MockRead DelayedSocketData::GetNextRead() { | |
| MockRead out = MockRead(ASYNC, ERR_IO_PENDING); | |
| if (write_delay_ <= 0) | |
| out = StaticSocketDataProvider::GetNextRead(); | |
| read_in_progress_ = (out.result == ERR_IO_PENDING); | |
| return out; | |
| } | |
| MockWriteResult DelayedSocketData::OnWrite(const std::string& data) { | |
| MockWriteResult rv = StaticSocketDataProvider::OnWrite(data); | |
| // Now that our write has completed, we can allow reads to continue. | |
| if (!--write_delay_ && read_in_progress_) | |
| MessageLoop::current()->PostDelayedTask( | |
| FROM_HERE, | |
| base::Bind(&DelayedSocketData::CompleteRead, | |
| weak_factory_.GetWeakPtr()), | |
| base::TimeDelta::FromMilliseconds(100)); | |
| return rv; | |
| } | |
| void DelayedSocketData::Reset() { | |
| set_socket(NULL); | |
| read_in_progress_ = false; | |
| weak_factory_.InvalidateWeakPtrs(); | |
| StaticSocketDataProvider::Reset(); | |
| } | |
| void DelayedSocketData::CompleteRead() { | |
| if (socket() && read_in_progress_) | |
| socket()->OnReadComplete(GetNextRead()); | |
| } | |
| OrderedSocketData::OrderedSocketData( | |
| MockRead* reads, size_t reads_count, MockWrite* writes, size_t writes_count) | |
| : StaticSocketDataProvider(reads, reads_count, writes, writes_count), | |
| sequence_number_(0), loop_stop_stage_(0), | |
| blocked_(false), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | |
| } | |
| OrderedSocketData::OrderedSocketData( | |
| const MockConnect& connect, | |
| MockRead* reads, size_t reads_count, | |
| MockWrite* writes, size_t writes_count) | |
| : StaticSocketDataProvider(reads, reads_count, writes, writes_count), | |
| sequence_number_(0), loop_stop_stage_(0), | |
| blocked_(false), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | |
| set_connect_data(connect); | |
| } | |
| void OrderedSocketData::EndLoop() { | |
| // If we've already stopped the loop, don't do it again until we've advanced | |
| // to the next sequence_number. | |
| NET_TRACE(INFO, " *** ") << "Stage " << sequence_number_ << ": EndLoop()"; | |
| if (loop_stop_stage_ > 0) { | |
| const MockRead& next_read = StaticSocketDataProvider::PeekRead(); | |
| if ((next_read.sequence_number & ~MockRead::STOPLOOP) > | |
| loop_stop_stage_) { | |
| NET_TRACE(INFO, " *** ") << "Stage " << sequence_number_ | |
| << ": Clearing stop index"; | |
| loop_stop_stage_ = 0; | |
| } else { | |
| return; | |
| } | |
| } | |
| // Record the sequence_number at which we stopped the loop. | |
| NET_TRACE(INFO, " *** ") << "Stage " << sequence_number_ | |
| << ": Posting Quit at read " << read_index(); | |
| loop_stop_stage_ = sequence_number_; | |
| } | |
| MockRead OrderedSocketData::GetNextRead() { | |
| weak_factory_.InvalidateWeakPtrs(); | |
| blocked_ = false; | |
| const MockRead& next_read = StaticSocketDataProvider::PeekRead(); | |
| if (next_read.sequence_number & MockRead::STOPLOOP) | |
| EndLoop(); | |
| if ((next_read.sequence_number & ~MockRead::STOPLOOP) <= | |
| sequence_number_++) { | |
| NET_TRACE(INFO, " *** ") << "Stage " << sequence_number_ - 1 | |
| << ": Read " << read_index(); | |
| DumpMockReadWrite(next_read); | |
| blocked_ = (next_read.result == ERR_IO_PENDING); | |
| return StaticSocketDataProvider::GetNextRead(); | |
| } | |
| NET_TRACE(INFO, " *** ") << "Stage " << sequence_number_ - 1 | |
| << ": I/O Pending"; | |
| MockRead result = MockRead(ASYNC, ERR_IO_PENDING); | |
| DumpMockReadWrite(result); | |
| blocked_ = true; | |
| return result; | |
| } | |
| MockWriteResult OrderedSocketData::OnWrite(const std::string& data) { | |
| NET_TRACE(INFO, " *** ") << "Stage " << sequence_number_ | |
| << ": Write " << write_index(); | |
| DumpMockReadWrite(PeekWrite()); | |
| ++sequence_number_; | |
| if (blocked_) { | |
| // TODO(willchan): This 100ms delay seems to work around some weirdness. We | |
| // should probably fix the weirdness. One example is in SpdyStream, | |
| // DoSendRequest() will return ERR_IO_PENDING, and there's a race. If the | |
| // SYN_REPLY causes OnResponseReceived() to get called before | |
| // SpdyStream::ReadResponseHeaders() is called, we hit a NOTREACHED(). | |
| MessageLoop::current()->PostDelayedTask( | |
| FROM_HERE, | |
| base::Bind(&OrderedSocketData::CompleteRead, | |
| weak_factory_.GetWeakPtr()), | |
| base::TimeDelta::FromMilliseconds(100)); | |
| } | |
| return StaticSocketDataProvider::OnWrite(data); | |
| } | |
| void OrderedSocketData::Reset() { | |
| NET_TRACE(INFO, " *** ") << "Stage " | |
| << sequence_number_ << ": Reset()"; | |
| sequence_number_ = 0; | |
| loop_stop_stage_ = 0; | |
| set_socket(NULL); | |
| weak_factory_.InvalidateWeakPtrs(); | |
| StaticSocketDataProvider::Reset(); | |
| } | |
| void OrderedSocketData::CompleteRead() { | |
| if (socket() && blocked_) { | |
| NET_TRACE(INFO, " *** ") << "Stage " << sequence_number_; | |
| socket()->OnReadComplete(GetNextRead()); | |
| } | |
| } | |
| OrderedSocketData::~OrderedSocketData() {} | |
| DeterministicSocketData::DeterministicSocketData(MockRead* reads, | |
| size_t reads_count, MockWrite* writes, size_t writes_count) | |
| : StaticSocketDataProvider(reads, reads_count, writes, writes_count), | |
| sequence_number_(0), | |
| current_read_(), | |
| current_write_(), | |
| stopping_sequence_number_(0), | |
| stopped_(false), | |
| print_debug_(false) { | |
| VerifyCorrectSequenceNumbers(reads, reads_count, writes, writes_count); | |
| } | |
| DeterministicSocketData::~DeterministicSocketData() {} | |
| void DeterministicSocketData::Run() { | |
| SetStopped(false); | |
| int counter = 0; | |
| // Continue to consume data until all data has run out, or the stopped_ flag | |
| // has been set. Consuming data requires two separate operations -- running | |
| // the tasks in the message loop, and explicitly invoking the read/write | |
| // callbacks (simulating network I/O). We check our conditions between each, | |
| // since they can change in either. | |
| while ((!at_write_eof() || !at_read_eof()) && !stopped()) { | |
| if (counter % 2 == 0) | |
| MessageLoop::current()->RunUntilIdle(); | |
| if (counter % 2 == 1) { | |
| InvokeCallbacks(); | |
| } | |
| counter++; | |
| } | |
| // We're done consuming new data, but it is possible there are still some | |
| // pending callbacks which we expect to complete before returning. | |
| while (socket_ && (socket_->write_pending() || socket_->read_pending()) && | |
| !stopped()) { | |
| InvokeCallbacks(); | |
| MessageLoop::current()->RunUntilIdle(); | |
| } | |
| SetStopped(false); | |
| } | |
| void DeterministicSocketData::RunFor(int steps) { | |
| StopAfter(steps); | |
| Run(); | |
| } | |
| void DeterministicSocketData::SetStop(int seq) { | |
| DCHECK_LT(sequence_number_, seq); | |
| stopping_sequence_number_ = seq; | |
| stopped_ = false; | |
| } | |
| void DeterministicSocketData::StopAfter(int seq) { | |
| SetStop(sequence_number_ + seq); | |
| } | |
| MockRead DeterministicSocketData::GetNextRead() { | |
| current_read_ = StaticSocketDataProvider::PeekRead(); | |
| EXPECT_LE(sequence_number_, current_read_.sequence_number); | |
| // Synchronous read while stopped is an error | |
| if (stopped() && current_read_.mode == SYNCHRONOUS) { | |
| LOG(ERROR) << "Unable to perform synchronous IO while stopped"; | |
| return MockRead(SYNCHRONOUS, ERR_UNEXPECTED); | |
| } | |
| // Async read which will be called back in a future step. | |
| if (sequence_number_ < current_read_.sequence_number) { | |
| NET_TRACE(INFO, " *** ") << "Stage " << sequence_number_ | |
| << ": I/O Pending"; | |
| MockRead result = MockRead(SYNCHRONOUS, ERR_IO_PENDING); | |
| if (current_read_.mode == SYNCHRONOUS) { | |
| LOG(ERROR) << "Unable to perform synchronous read: " | |
| << current_read_.sequence_number | |
| << " at stage: " << sequence_number_; | |
| result = MockRead(SYNCHRONOUS, ERR_UNEXPECTED); | |
| } | |
| if (print_debug_) | |
| DumpMockReadWrite(result); | |
| return result; | |
| } | |
| NET_TRACE(INFO, " *** ") << "Stage " << sequence_number_ | |
| << ": Read " << read_index(); | |
| if (print_debug_) | |
| DumpMockReadWrite(current_read_); | |
| // Increment the sequence number if IO is complete | |
| if (current_read_.mode == SYNCHRONOUS) | |
| NextStep(); | |
| DCHECK_NE(ERR_IO_PENDING, current_read_.result); | |
| StaticSocketDataProvider::GetNextRead(); | |
| return current_read_; | |
| } | |
| MockWriteResult DeterministicSocketData::OnWrite(const std::string& data) { | |
| const MockWrite& next_write = StaticSocketDataProvider::PeekWrite(); | |
| current_write_ = next_write; | |
| // Synchronous write while stopped is an error | |
| if (stopped() && next_write.mode == SYNCHRONOUS) { | |
| LOG(ERROR) << "Unable to perform synchronous IO while stopped"; | |
| return MockWriteResult(SYNCHRONOUS, ERR_UNEXPECTED); | |
| } | |
| // Async write which will be called back in a future step. | |
| if (sequence_number_ < next_write.sequence_number) { | |
| NET_TRACE(INFO, " *** ") << "Stage " << sequence_number_ | |
| << ": I/O Pending"; | |
| if (next_write.mode == SYNCHRONOUS) { | |
| LOG(ERROR) << "Unable to perform synchronous write: " | |
| << next_write.sequence_number << " at stage: " << sequence_number_; | |
| return MockWriteResult(SYNCHRONOUS, ERR_UNEXPECTED); | |
| } | |
| } else { | |
| NET_TRACE(INFO, " *** ") << "Stage " << sequence_number_ | |
| << ": Write " << write_index(); | |
| } | |
| if (print_debug_) | |
| DumpMockReadWrite(next_write); | |
| // Move to the next step if I/O is synchronous, since the operation will | |
| // complete when this method returns. | |
| if (next_write.mode == SYNCHRONOUS) | |
| NextStep(); | |
| // This is either a sync write for this step, or an async write. | |
| return StaticSocketDataProvider::OnWrite(data); | |
| } | |
| void DeterministicSocketData::Reset() { | |
| NET_TRACE(INFO, " *** ") << "Stage " | |
| << sequence_number_ << ": Reset()"; | |
| sequence_number_ = 0; | |
| StaticSocketDataProvider::Reset(); | |
| NOTREACHED(); | |
| } | |
| void DeterministicSocketData::InvokeCallbacks() { | |
| if (socket_ && socket_->write_pending() && | |
| (current_write().sequence_number == sequence_number())) { | |
| socket_->CompleteWrite(); | |
| NextStep(); | |
| return; | |
| } | |
| if (socket_ && socket_->read_pending() && | |
| (current_read().sequence_number == sequence_number())) { | |
| socket_->CompleteRead(); | |
| NextStep(); | |
| return; | |
| } | |
| } | |
| void DeterministicSocketData::NextStep() { | |
| // Invariant: Can never move *past* the stopping step. | |
| DCHECK_LT(sequence_number_, stopping_sequence_number_); | |
| sequence_number_++; | |
| if (sequence_number_ == stopping_sequence_number_) | |
| SetStopped(true); | |
| } | |
| void DeterministicSocketData::VerifyCorrectSequenceNumbers( | |
| MockRead* reads, size_t reads_count, | |
| MockWrite* writes, size_t writes_count) { | |
| size_t read = 0; | |
| size_t write = 0; | |
| int expected = 0; | |
| while (read < reads_count || write < writes_count) { | |
| // Check to see that we have a read or write at the expected | |
| // state. | |
| if (read < reads_count && reads[read].sequence_number == expected) { | |
| ++read; | |
| ++expected; | |
| continue; | |
| } | |
| if (write < writes_count && writes[write].sequence_number == expected) { | |
| ++write; | |
| ++expected; | |
| continue; | |
| } | |
| NOTREACHED() << "Missing sequence number: " << expected; | |
| return; | |
| } | |
| DCHECK_EQ(read, reads_count); | |
| DCHECK_EQ(write, writes_count); | |
| } | |
| MockClientSocketFactory::MockClientSocketFactory() {} | |
| MockClientSocketFactory::~MockClientSocketFactory() {} | |
| void MockClientSocketFactory::AddSocketDataProvider( | |
| SocketDataProvider* data) { | |
| mock_data_.Add(data); | |
| } | |
| void MockClientSocketFactory::AddSSLSocketDataProvider( | |
| SSLSocketDataProvider* data) { | |
| mock_ssl_data_.Add(data); | |
| } | |
| void MockClientSocketFactory::ResetNextMockIndexes() { | |
| mock_data_.ResetNextIndex(); | |
| mock_ssl_data_.ResetNextIndex(); | |
| } | |
| DatagramClientSocket* MockClientSocketFactory::CreateDatagramClientSocket( | |
| DatagramSocket::BindType bind_type, | |
| const RandIntCallback& rand_int_cb, | |
| net::NetLog* net_log, | |
| const net::NetLog::Source& source) { | |
| SocketDataProvider* data_provider = mock_data_.GetNext(); | |
| MockUDPClientSocket* socket = new MockUDPClientSocket(data_provider, net_log); | |
| data_provider->set_socket(socket); | |
| return socket; | |
| } | |
| StreamSocket* MockClientSocketFactory::CreateTransportClientSocket( | |
| const AddressList& addresses, | |
| net::NetLog* net_log, | |
| const net::NetLog::Source& source) { | |
| SocketDataProvider* data_provider = mock_data_.GetNext(); | |
| MockTCPClientSocket* socket = | |
| new MockTCPClientSocket(addresses, net_log, data_provider); | |
| data_provider->set_socket(socket); | |
| return socket; | |
| } | |
| SSLClientSocket* MockClientSocketFactory::CreateSSLClientSocket( | |
| ClientSocketHandle* transport_socket, | |
| const HostPortPair& host_and_port, | |
| const SSLConfig& ssl_config, | |
| const SSLClientSocketContext& context) { | |
| MockSSLClientSocket* socket = | |
| new MockSSLClientSocket(transport_socket, host_and_port, ssl_config, | |
| mock_ssl_data_.GetNext()); | |
| return socket; | |
| } | |
| void MockClientSocketFactory::ClearSSLSessionCache() { | |
| } | |
| const char MockClientSocket::kTlsUnique[] = "MOCK_TLSUNIQ"; | |
| MockClientSocket::MockClientSocket(net::NetLog* net_log) | |
| : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), | |
| connected_(false), | |
| net_log_(BoundNetLog::Make(net_log, net::NetLog::SOURCE_NONE)) { | |
| IPAddressNumber ip; | |
| CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); | |
| peer_addr_ = IPEndPoint(ip, 0); | |
| } | |
| bool MockClientSocket::SetReceiveBufferSize(int32 size) { | |
| return true; | |
| } | |
| bool MockClientSocket::SetSendBufferSize(int32 size) { | |
| return true; | |
| } | |
| void MockClientSocket::Disconnect() { | |
| connected_ = false; | |
| } | |
| bool MockClientSocket::IsConnected() const { | |
| return connected_; | |
| } | |
| bool MockClientSocket::IsConnectedAndIdle() const { | |
| return connected_; | |
| } | |
| int MockClientSocket::GetPeerAddress(IPEndPoint* address) const { | |
| *address = peer_addr_; | |
| return OK; | |
| } | |
| int MockClientSocket::GetLocalAddress(IPEndPoint* address) const { | |
| IPAddressNumber ip; | |
| bool rv = ParseIPLiteralToNumber("192.0.2.33", &ip); | |
| CHECK(rv); | |
| *address = IPEndPoint(ip, 123); | |
| return OK; | |
| } | |
| const BoundNetLog& MockClientSocket::NetLog() const { | |
| return net_log_; | |
| } | |
| void MockClientSocket::GetSSLCertRequestInfo( | |
| SSLCertRequestInfo* cert_request_info) { | |
| } | |
| int MockClientSocket::ExportKeyingMaterial(const base::StringPiece& label, | |
| bool has_context, | |
| const base::StringPiece& context, | |
| unsigned char* out, | |
| unsigned int outlen) { | |
| memset(out, 'A', outlen); | |
| return OK; | |
| } | |
| int MockClientSocket::GetTLSUniqueChannelBinding(std::string* out) { | |
| out->assign(MockClientSocket::kTlsUnique); | |
| return OK; | |
| } | |
| ServerBoundCertService* MockClientSocket::GetServerBoundCertService() const { | |
| NOTREACHED(); | |
| return NULL; | |
| } | |
| SSLClientSocket::NextProtoStatus | |
| MockClientSocket::GetNextProto(std::string* proto, std::string* server_protos) { | |
| proto->clear(); | |
| server_protos->clear(); | |
| return SSLClientSocket::kNextProtoUnsupported; | |
| } | |
| MockClientSocket::~MockClientSocket() {} | |
| void MockClientSocket::RunCallbackAsync(const CompletionCallback& callback, | |
| int result) { | |
| MessageLoop::current()->PostTask(FROM_HERE, | |
| base::Bind(&MockClientSocket::RunCallback, weak_factory_.GetWeakPtr(), | |
| callback, result)); | |
| } | |
| void MockClientSocket::RunCallback(const net::CompletionCallback& callback, | |
| int result) { | |
| if (!callback.is_null()) | |
| callback.Run(result); | |
| } | |
| MockTCPClientSocket::MockTCPClientSocket(const AddressList& addresses, | |
| net::NetLog* net_log, | |
| SocketDataProvider* data) | |
| : MockClientSocket(net_log), | |
| addresses_(addresses), | |
| data_(data), | |
| read_offset_(0), | |
| num_bytes_read_(0), | |
| read_data_(SYNCHRONOUS, ERR_UNEXPECTED), | |
| need_read_data_(true), | |
| peer_closed_connection_(false), | |
| pending_buf_(NULL), | |
| pending_buf_len_(0), | |
| was_used_to_convey_data_(false) { | |
| DCHECK(data_); | |
| peer_addr_ = data->connect_data().peer_addr; | |
| data_->Reset(); | |
| } | |
| MockTCPClientSocket::~MockTCPClientSocket() {} | |
| int MockTCPClientSocket::Read(IOBuffer* buf, int buf_len, | |
| const CompletionCallback& callback) { | |
| if (!connected_) | |
| return ERR_UNEXPECTED; | |
| // If the buffer is already in use, a read is already in progress! | |
| DCHECK(pending_buf_ == NULL); | |
| // Store our async IO data. | |
| pending_buf_ = buf; | |
| pending_buf_len_ = buf_len; | |
| pending_callback_ = callback; | |
| if (need_read_data_) { | |
| read_data_ = data_->GetNextRead(); | |
| if (read_data_.result == ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ) { | |
| // This MockRead is just a marker to instruct us to set | |
| // peer_closed_connection_. Skip it and get the next one. | |
| read_data_ = data_->GetNextRead(); | |
| peer_closed_connection_ = true; | |
| } | |
| // ERR_IO_PENDING means that the SocketDataProvider is taking responsibility | |
| // to complete the async IO manually later (via OnReadComplete). | |
| if (read_data_.result == ERR_IO_PENDING) { | |
| // We need to be using async IO in this case. | |
| DCHECK(!callback.is_null()); | |
| return ERR_IO_PENDING; | |
| } | |
| need_read_data_ = false; | |
| } | |
| return CompleteRead(); | |
| } | |
| int MockTCPClientSocket::Write(IOBuffer* buf, int buf_len, | |
| const CompletionCallback& callback) { | |
| DCHECK(buf); | |
| DCHECK_GT(buf_len, 0); | |
| if (!connected_) | |
| return ERR_UNEXPECTED; | |
| std::string data(buf->data(), buf_len); | |
| MockWriteResult write_result = data_->OnWrite(data); | |
| was_used_to_convey_data_ = true; | |
| if (write_result.mode == ASYNC) { | |
| RunCallbackAsync(callback, write_result.result); | |
| return ERR_IO_PENDING; | |
| } | |
| return write_result.result; | |
| } | |
| int MockTCPClientSocket::Connect(const CompletionCallback& callback) { | |
| if (connected_) | |
| return OK; | |
| connected_ = true; | |
| peer_closed_connection_ = false; | |
| if (data_->connect_data().mode == ASYNC) { | |
| RunCallbackAsync(callback, data_->connect_data().result); | |
| return ERR_IO_PENDING; | |
| } | |
| return data_->connect_data().result; | |
| } | |
| void MockTCPClientSocket::Disconnect() { | |
| MockClientSocket::Disconnect(); | |
| pending_callback_.Reset(); | |
| } | |
| bool MockTCPClientSocket::IsConnected() const { | |
| return connected_ && !peer_closed_connection_; | |
| } | |
| bool MockTCPClientSocket::IsConnectedAndIdle() const { | |
| return IsConnected(); | |
| } | |
| int MockTCPClientSocket::GetPeerAddress(IPEndPoint* address) const { | |
| if (addresses_.empty()) | |
| return MockClientSocket::GetPeerAddress(address); | |
| *address = addresses_[0]; | |
| return OK; | |
| } | |
| bool MockTCPClientSocket::WasEverUsed() const { | |
| return was_used_to_convey_data_; | |
| } | |
| bool MockTCPClientSocket::UsingTCPFastOpen() const { | |
| return false; | |
| } | |
| int64 MockTCPClientSocket::NumBytesRead() const { | |
| return num_bytes_read_; | |
| } | |
| base::TimeDelta MockTCPClientSocket::GetConnectTimeMicros() const { | |
| // Dummy value. | |
| static const base::TimeDelta kTestingConnectTimeMicros = | |
| base::TimeDelta::FromMicroseconds(20); | |
| return kTestingConnectTimeMicros; | |
| } | |
| bool MockTCPClientSocket::WasNpnNegotiated() const { | |
| return false; | |
| } | |
| bool MockTCPClientSocket::GetSSLInfo(SSLInfo* ssl_info) { | |
| return false; | |
| } | |
| void MockTCPClientSocket::OnReadComplete(const MockRead& data) { | |
| // There must be a read pending. | |
| DCHECK(pending_buf_); | |
| // You can't complete a read with another ERR_IO_PENDING status code. | |
| DCHECK_NE(ERR_IO_PENDING, data.result); | |
| // Since we've been waiting for data, need_read_data_ should be true. | |
| DCHECK(need_read_data_); | |
| read_data_ = data; | |
| need_read_data_ = false; | |
| // The caller is simulating that this IO completes right now. Don't | |
| // let CompleteRead() schedule a callback. | |
| read_data_.mode = SYNCHRONOUS; | |
| CompletionCallback callback = pending_callback_; | |
| int rv = CompleteRead(); | |
| RunCallback(callback, rv); | |
| } | |
| int MockTCPClientSocket::CompleteRead() { | |
| DCHECK(pending_buf_); | |
| DCHECK(pending_buf_len_ > 0); | |
| was_used_to_convey_data_ = true; | |
| // Save the pending async IO data and reset our |pending_| state. | |
| IOBuffer* buf = pending_buf_; | |
| int buf_len = pending_buf_len_; | |
| CompletionCallback callback = pending_callback_; | |
| pending_buf_ = NULL; | |
| pending_buf_len_ = 0; | |
| pending_callback_.Reset(); | |
| int result = read_data_.result; | |
| DCHECK(result != ERR_IO_PENDING); | |
| if (read_data_.data) { | |
| if (read_data_.data_len - read_offset_ > 0) { | |
| result = std::min(buf_len, read_data_.data_len - read_offset_); | |
| memcpy(buf->data(), read_data_.data + read_offset_, result); | |
| read_offset_ += result; | |
| num_bytes_read_ += result; | |
| if (read_offset_ == read_data_.data_len) { | |
| need_read_data_ = true; | |
| read_offset_ = 0; | |
| } | |
| } else { | |
| result = 0; // EOF | |
| } | |
| } | |
| if (read_data_.mode == ASYNC) { | |
| DCHECK(!callback.is_null()); | |
| RunCallbackAsync(callback, result); | |
| return ERR_IO_PENDING; | |
| } | |
| return result; | |
| } | |
| DeterministicMockTCPClientSocket::DeterministicMockTCPClientSocket( | |
| net::NetLog* net_log, DeterministicSocketData* data) | |
| : MockClientSocket(net_log), | |
| write_pending_(false), | |
| write_result_(0), | |
| read_data_(), | |
| read_buf_(NULL), | |
| read_buf_len_(0), | |
| read_pending_(false), | |
| data_(data), | |
| was_used_to_convey_data_(false) { | |
| peer_addr_ = data->connect_data().peer_addr; | |
| } | |
| DeterministicMockTCPClientSocket::~DeterministicMockTCPClientSocket() {} | |
| void DeterministicMockTCPClientSocket::CompleteWrite() { | |
| was_used_to_convey_data_ = true; | |
| write_pending_ = false; | |
| write_callback_.Run(write_result_); | |
| } | |
| int DeterministicMockTCPClientSocket::CompleteRead() { | |
| DCHECK_GT(read_buf_len_, 0); | |
| DCHECK_LE(read_data_.data_len, read_buf_len_); | |
| DCHECK(read_buf_); | |
| was_used_to_convey_data_ = true; | |
| if (read_data_.result == ERR_IO_PENDING) | |
| read_data_ = data_->GetNextRead(); | |
| DCHECK_NE(ERR_IO_PENDING, read_data_.result); | |
| // If read_data_.mode is ASYNC, we do not need to wait, since this is already | |
| // the callback. Therefore we don't even bother to check it. | |
| int result = read_data_.result; | |
| if (read_data_.data_len > 0) { | |
| DCHECK(read_data_.data); | |
| result = std::min(read_buf_len_, read_data_.data_len); | |
| memcpy(read_buf_->data(), read_data_.data, result); | |
| } | |
| if (read_pending_) { | |
| read_pending_ = false; | |
| read_callback_.Run(result); | |
| } | |
| return result; | |
| } | |
| int DeterministicMockTCPClientSocket::Write( | |
| IOBuffer* buf, int buf_len, const CompletionCallback& callback) { | |
| DCHECK(buf); | |
| DCHECK_GT(buf_len, 0); | |
| if (!connected_) | |
| return ERR_UNEXPECTED; | |
| std::string data(buf->data(), buf_len); | |
| MockWriteResult write_result = data_->OnWrite(data); | |
| if (write_result.mode == ASYNC) { | |
| write_callback_ = callback; | |
| write_result_ = write_result.result; | |
| DCHECK(!write_callback_.is_null()); | |
| write_pending_ = true; | |
| return ERR_IO_PENDING; | |
| } | |
| was_used_to_convey_data_ = true; | |
| write_pending_ = false; | |
| return write_result.result; | |
| } | |
| int DeterministicMockTCPClientSocket::Read( | |
| IOBuffer* buf, int buf_len, const CompletionCallback& callback) { | |
| if (!connected_) | |
| return ERR_UNEXPECTED; | |
| read_data_ = data_->GetNextRead(); | |
| // The buffer should always be big enough to contain all the MockRead data. To | |
| // use small buffers, split the data into multiple MockReads. | |
| DCHECK_LE(read_data_.data_len, buf_len); | |
| read_buf_ = buf; | |
| read_buf_len_ = buf_len; | |
| read_callback_ = callback; | |
| if (read_data_.mode == ASYNC || (read_data_.result == ERR_IO_PENDING)) { | |
| read_pending_ = true; | |
| DCHECK(!read_callback_.is_null()); | |
| return ERR_IO_PENDING; | |
| } | |
| was_used_to_convey_data_ = true; | |
| return CompleteRead(); | |
| } | |
| // TODO(erikchen): Support connect sequencing. | |
| int DeterministicMockTCPClientSocket::Connect( | |
| const CompletionCallback& callback) { | |
| if (connected_) | |
| return OK; | |
| connected_ = true; | |
| if (data_->connect_data().mode == ASYNC) { | |
| RunCallbackAsync(callback, data_->connect_data().result); | |
| return ERR_IO_PENDING; | |
| } | |
| return data_->connect_data().result; | |
| } | |
| void DeterministicMockTCPClientSocket::Disconnect() { | |
| MockClientSocket::Disconnect(); | |
| } | |
| bool DeterministicMockTCPClientSocket::IsConnected() const { | |
| return connected_; | |
| } | |
| bool DeterministicMockTCPClientSocket::IsConnectedAndIdle() const { | |
| return IsConnected(); | |
| } | |
| bool DeterministicMockTCPClientSocket::WasEverUsed() const { | |
| return was_used_to_convey_data_; | |
| } | |
| bool DeterministicMockTCPClientSocket::UsingTCPFastOpen() const { | |
| return false; | |
| } | |
| int64 DeterministicMockTCPClientSocket::NumBytesRead() const { | |
| return -1; | |
| } | |
| base::TimeDelta DeterministicMockTCPClientSocket::GetConnectTimeMicros() const { | |
| return base::TimeDelta::FromMicroseconds(-1); | |
| } | |
| bool DeterministicMockTCPClientSocket::WasNpnNegotiated() const { | |
| return false; | |
| } | |
| bool DeterministicMockTCPClientSocket::GetSSLInfo(SSLInfo* ssl_info) { | |
| return false; | |
| } | |
| void DeterministicMockTCPClientSocket::OnReadComplete(const MockRead& data) {} | |
| // static | |
| void MockSSLClientSocket::ConnectCallback( | |
| MockSSLClientSocket *ssl_client_socket, | |
| const CompletionCallback& callback, | |
| int rv) { | |
| if (rv == OK) | |
| ssl_client_socket->connected_ = true; | |
| callback.Run(rv); | |
| } | |
| MockSSLClientSocket::MockSSLClientSocket( | |
| ClientSocketHandle* transport_socket, | |
| const HostPortPair& host_port_pair, | |
| const SSLConfig& ssl_config, | |
| SSLSocketDataProvider* data) | |
| : MockClientSocket(transport_socket->socket()->NetLog().net_log()), | |
| transport_(transport_socket), | |
| data_(data), | |
| is_npn_state_set_(false), | |
| new_npn_value_(false), | |
| is_protocol_negotiated_set_(false), | |
| protocol_negotiated_(kProtoUnknown) { | |
| DCHECK(data_); | |
| peer_addr_ = data->connect.peer_addr; | |
| } | |
| MockSSLClientSocket::~MockSSLClientSocket() { | |
| Disconnect(); | |
| } | |
| int MockSSLClientSocket::Read(IOBuffer* buf, int buf_len, | |
| const CompletionCallback& callback) { | |
| return transport_->socket()->Read(buf, buf_len, callback); | |
| } | |
| int MockSSLClientSocket::Write(IOBuffer* buf, int buf_len, | |
| const CompletionCallback& callback) { | |
| return transport_->socket()->Write(buf, buf_len, callback); | |
| } | |
| int MockSSLClientSocket::Connect(const CompletionCallback& callback) { | |
| int rv = transport_->socket()->Connect( | |
| base::Bind(&ConnectCallback, base::Unretained(this), callback)); | |
| if (rv == OK) { | |
| if (data_->connect.result == OK) | |
| connected_ = true; | |
| if (data_->connect.mode == ASYNC) { | |
| RunCallbackAsync(callback, data_->connect.result); | |
| return ERR_IO_PENDING; | |
| } | |
| return data_->connect.result; | |
| } | |
| return rv; | |
| } | |
| void MockSSLClientSocket::Disconnect() { | |
| MockClientSocket::Disconnect(); | |
| if (transport_->socket() != NULL) | |
| transport_->socket()->Disconnect(); | |
| } | |
| bool MockSSLClientSocket::IsConnected() const { | |
| return transport_->socket()->IsConnected(); | |
| } | |
| bool MockSSLClientSocket::WasEverUsed() const { | |
| return transport_->socket()->WasEverUsed(); | |
| } | |
| bool MockSSLClientSocket::UsingTCPFastOpen() const { | |
| return transport_->socket()->UsingTCPFastOpen(); | |
| } | |
| int64 MockSSLClientSocket::NumBytesRead() const { | |
| return -1; | |
| } | |
| int MockSSLClientSocket::GetPeerAddress(IPEndPoint* address) const { | |
| return transport_->socket()->GetPeerAddress(address); | |
| } | |
| base::TimeDelta MockSSLClientSocket::GetConnectTimeMicros() const { | |
| return base::TimeDelta::FromMicroseconds(-1); | |
| } | |
| bool MockSSLClientSocket::GetSSLInfo(SSLInfo* ssl_info) { | |
| ssl_info->Reset(); | |
| ssl_info->cert = data_->cert; | |
| ssl_info->client_cert_sent = data_->client_cert_sent; | |
| ssl_info->channel_id_sent = data_->channel_id_sent; | |
| return true; | |
| } | |
| void MockSSLClientSocket::GetSSLCertRequestInfo( | |
| SSLCertRequestInfo* cert_request_info) { | |
| DCHECK(cert_request_info); | |
| if (data_->cert_request_info) { | |
| cert_request_info->host_and_port = | |
| data_->cert_request_info->host_and_port; | |
| cert_request_info->client_certs = data_->cert_request_info->client_certs; | |
| } else { | |
| cert_request_info->Reset(); | |
| } | |
| } | |
| SSLClientSocket::NextProtoStatus MockSSLClientSocket::GetNextProto( | |
| std::string* proto, std::string* server_protos) { | |
| *proto = data_->next_proto; | |
| *server_protos = data_->server_protos; | |
| return data_->next_proto_status; | |
| } | |
| bool MockSSLClientSocket::set_was_npn_negotiated(bool negotiated) { | |
| is_npn_state_set_ = true; | |
| return new_npn_value_ = negotiated; | |
| } | |
| bool MockSSLClientSocket::WasNpnNegotiated() const { | |
| if (is_npn_state_set_) | |
| return new_npn_value_; | |
| return data_->was_npn_negotiated; | |
| } | |
| NextProto MockSSLClientSocket::GetNegotiatedProtocol() const { | |
| if (is_protocol_negotiated_set_) | |
| return protocol_negotiated_; | |
| return data_->protocol_negotiated; | |
| } | |
| void MockSSLClientSocket::set_protocol_negotiated( | |
| NextProto protocol_negotiated) { | |
| is_protocol_negotiated_set_ = true; | |
| protocol_negotiated_ = protocol_negotiated; | |
| } | |
| bool MockSSLClientSocket::WasChannelIDSent() const { | |
| return data_->channel_id_sent; | |
| } | |
| void MockSSLClientSocket::set_channel_id_sent(bool channel_id_sent) { | |
| data_->channel_id_sent = channel_id_sent; | |
| } | |
| ServerBoundCertService* MockSSLClientSocket::GetServerBoundCertService() const { | |
| return data_->server_bound_cert_service; | |
| } | |
| void MockSSLClientSocket::OnReadComplete(const MockRead& data) { | |
| NOTIMPLEMENTED(); | |
| } | |
| MockUDPClientSocket::MockUDPClientSocket(SocketDataProvider* data, | |
| net::NetLog* net_log) | |
| : connected_(false), | |
| data_(data), | |
| read_offset_(0), | |
| read_data_(SYNCHRONOUS, ERR_UNEXPECTED), | |
| need_read_data_(true), | |
| pending_buf_(NULL), | |
| pending_buf_len_(0), | |
| net_log_(BoundNetLog::Make(net_log, net::NetLog::SOURCE_NONE)), | |
| ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { | |
| DCHECK(data_); | |
| data_->Reset(); | |
| } | |
| MockUDPClientSocket::~MockUDPClientSocket() {} | |
| int MockUDPClientSocket::Read(IOBuffer* buf, int buf_len, | |
| const CompletionCallback& callback) { | |
| if (!connected_) | |
| return ERR_UNEXPECTED; | |
| // If the buffer is already in use, a read is already in progress! | |
| DCHECK(pending_buf_ == NULL); | |
| // Store our async IO data. | |
| pending_buf_ = buf; | |
| pending_buf_len_ = buf_len; | |
| pending_callback_ = callback; | |
| if (need_read_data_) { | |
| read_data_ = data_->GetNextRead(); | |
| // ERR_IO_PENDING means that the SocketDataProvider is taking responsibility | |
| // to complete the async IO manually later (via OnReadComplete). | |
| if (read_data_.result == ERR_IO_PENDING) { | |
| // We need to be using async IO in this case. | |
| DCHECK(!callback.is_null()); | |
| return ERR_IO_PENDING; | |
| } | |
| need_read_data_ = false; | |
| } | |
| return CompleteRead(); | |
| } | |
| int MockUDPClientSocket::Write(IOBuffer* buf, int buf_len, | |
| const CompletionCallback& callback) { | |
| DCHECK(buf); | |
| DCHECK_GT(buf_len, 0); | |
| if (!connected_) | |
| return ERR_UNEXPECTED; | |
| std::string data(buf->data(), buf_len); | |
| MockWriteResult write_result = data_->OnWrite(data); | |
| if (write_result.mode == ASYNC) { | |
| RunCallbackAsync(callback, write_result.result); | |
| return ERR_IO_PENDING; | |
| } | |
| return write_result.result; | |
| } | |
| bool MockUDPClientSocket::SetReceiveBufferSize(int32 size) { | |
| return true; | |
| } | |
| bool MockUDPClientSocket::SetSendBufferSize(int32 size) { | |
| return true; | |
| } | |
| void MockUDPClientSocket::Close() { | |
| connected_ = false; | |
| } | |
| int MockUDPClientSocket::GetPeerAddress(IPEndPoint* address) const { | |
| NOTIMPLEMENTED(); | |
| return OK; | |
| } | |
| int MockUDPClientSocket::GetLocalAddress(IPEndPoint* address) const { | |
| NOTIMPLEMENTED(); | |
| return OK; | |
| } | |
| const BoundNetLog& MockUDPClientSocket::NetLog() const { | |
| return net_log_; | |
| } | |
| int MockUDPClientSocket::Connect(const IPEndPoint& address) { | |
| connected_ = true; | |
| return OK; | |
| } | |
| void MockUDPClientSocket::OnReadComplete(const MockRead& data) { | |
| // There must be a read pending. | |
| DCHECK(pending_buf_); | |
| // You can't complete a read with another ERR_IO_PENDING status code. | |
| DCHECK_NE(ERR_IO_PENDING, data.result); | |
| // Since we've been waiting for data, need_read_data_ should be true. | |
| DCHECK(need_read_data_); | |
| read_data_ = data; | |
| need_read_data_ = false; | |
| // The caller is simulating that this IO completes right now. Don't | |
| // let CompleteRead() schedule a callback. | |
| read_data_.mode = SYNCHRONOUS; | |
| net::CompletionCallback callback = pending_callback_; | |
| int rv = CompleteRead(); | |
| RunCallback(callback, rv); | |
| } | |
| int MockUDPClientSocket::CompleteRead() { | |
| DCHECK(pending_buf_); | |
| DCHECK(pending_buf_len_ > 0); | |
| // Save the pending async IO data and reset our |pending_| state. | |
| IOBuffer* buf = pending_buf_; | |
| int buf_len = pending_buf_len_; | |
| CompletionCallback callback = pending_callback_; | |
| pending_buf_ = NULL; | |
| pending_buf_len_ = 0; | |
| pending_callback_.Reset(); | |
| int result = read_data_.result; | |
| DCHECK(result != ERR_IO_PENDING); | |
| if (read_data_.data) { | |
| if (read_data_.data_len - read_offset_ > 0) { | |
| result = std::min(buf_len, read_data_.data_len - read_offset_); | |
| memcpy(buf->data(), read_data_.data + read_offset_, result); | |
| read_offset_ += result; | |
| if (read_offset_ == read_data_.data_len) { | |
| need_read_data_ = true; | |
| read_offset_ = 0; | |
| } | |
| } else { | |
| result = 0; // EOF | |
| } | |
| } | |
| if (read_data_.mode == ASYNC) { | |
| DCHECK(!callback.is_null()); | |
| RunCallbackAsync(callback, result); | |
| return ERR_IO_PENDING; | |
| } | |
| return result; | |
| } | |
| void MockUDPClientSocket::RunCallbackAsync(const CompletionCallback& callback, | |
| int result) { | |
| MessageLoop::current()->PostTask( | |
| FROM_HERE, | |
| base::Bind(&MockUDPClientSocket::RunCallback, weak_factory_.GetWeakPtr(), | |
| callback, result)); | |
| } | |
| void MockUDPClientSocket::RunCallback(const CompletionCallback& callback, | |
| int result) { | |
| if (!callback.is_null()) | |
| callback.Run(result); | |
| } | |
| TestSocketRequest::TestSocketRequest( | |
| std::vector<TestSocketRequest*>* request_order, size_t* completion_count) | |
| : request_order_(request_order), | |
| completion_count_(completion_count), | |
| ALLOW_THIS_IN_INITIALIZER_LIST(callback_( | |
| base::Bind(&TestSocketRequest::OnComplete, base::Unretained(this)))) { | |
| DCHECK(request_order); | |
| DCHECK(completion_count); | |
| } | |
| TestSocketRequest::~TestSocketRequest() { | |
| } | |
| void TestSocketRequest::OnComplete(int result) { | |
| SetResult(result); | |
| (*completion_count_)++; | |
| request_order_->push_back(this); | |
| } | |
| // static | |
| const int ClientSocketPoolTest::kIndexOutOfBounds = -1; | |
| // static | |
| const int ClientSocketPoolTest::kRequestNotFound = -2; | |
| ClientSocketPoolTest::ClientSocketPoolTest() : completion_count_(0) {} | |
| ClientSocketPoolTest::~ClientSocketPoolTest() {} | |
| int ClientSocketPoolTest::GetOrderOfRequest(size_t index) const { | |
| index--; | |
| if (index >= requests_.size()) | |
| return kIndexOutOfBounds; | |
| for (size_t i = 0; i < request_order_.size(); i++) | |
| if (requests_[index] == request_order_[i]) | |
| return i + 1; | |
| return kRequestNotFound; | |
| } | |
| bool ClientSocketPoolTest::ReleaseOneConnection(KeepAlive keep_alive) { | |
| ScopedVector<TestSocketRequest>::iterator i; | |
| for (i = requests_.begin(); i != requests_.end(); ++i) { | |
| if ((*i)->handle()->is_initialized()) { | |
| if (keep_alive == NO_KEEP_ALIVE) | |
| (*i)->handle()->socket()->Disconnect(); | |
| (*i)->handle()->Reset(); | |
| MessageLoop::current()->RunUntilIdle(); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| void ClientSocketPoolTest::ReleaseAllConnections(KeepAlive keep_alive) { | |
| bool released_one; | |
| do { | |
| released_one = ReleaseOneConnection(keep_alive); | |
| } while (released_one); | |
| } | |
| MockTransportClientSocketPool::MockConnectJob::MockConnectJob( | |
| StreamSocket* socket, | |
| ClientSocketHandle* handle, | |
| const CompletionCallback& callback) | |
| : socket_(socket), | |
| handle_(handle), | |
| user_callback_(callback) { | |
| } | |
| MockTransportClientSocketPool::MockConnectJob::~MockConnectJob() {} | |
| int MockTransportClientSocketPool::MockConnectJob::Connect() { | |
| int rv = socket_->Connect(base::Bind(&MockConnectJob::OnConnect, | |
| base::Unretained(this))); | |
| if (rv == OK) { | |
| user_callback_.Reset(); | |
| OnConnect(OK); | |
| } | |
| return rv; | |
| } | |
| bool MockTransportClientSocketPool::MockConnectJob::CancelHandle( | |
| const ClientSocketHandle* handle) { | |
| if (handle != handle_) | |
| return false; | |
| socket_.reset(); | |
| handle_ = NULL; | |
| user_callback_.Reset(); | |
| return true; | |
| } | |
| void MockTransportClientSocketPool::MockConnectJob::OnConnect(int rv) { | |
| if (!socket_.get()) | |
| return; | |
| if (rv == OK) { | |
| handle_->set_socket(socket_.release()); | |
| } else { | |
| socket_.reset(); | |
| } | |
| handle_ = NULL; | |
| if (!user_callback_.is_null()) { | |
| CompletionCallback callback = user_callback_; | |
| user_callback_.Reset(); | |
| callback.Run(rv); | |
| } | |
| } | |
| MockTransportClientSocketPool::MockTransportClientSocketPool( | |
| int max_sockets, | |
| int max_sockets_per_group, | |
| ClientSocketPoolHistograms* histograms, | |
| ClientSocketFactory* socket_factory) | |
| : TransportClientSocketPool(max_sockets, max_sockets_per_group, histograms, | |
| NULL, NULL, NULL), | |
| client_socket_factory_(socket_factory), | |
| release_count_(0), | |
| cancel_count_(0) { | |
| } | |
| MockTransportClientSocketPool::~MockTransportClientSocketPool() {} | |
| int MockTransportClientSocketPool::RequestSocket( | |
| const std::string& group_name, const void* socket_params, | |
| RequestPriority priority, ClientSocketHandle* handle, | |
| const CompletionCallback& callback, const BoundNetLog& net_log) { | |
| StreamSocket* socket = client_socket_factory_->CreateTransportClientSocket( | |
| AddressList(), net_log.net_log(), net::NetLog::Source()); | |
| MockConnectJob* job = new MockConnectJob(socket, handle, callback); | |
| job_list_.push_back(job); | |
| handle->set_pool_id(1); | |
| return job->Connect(); | |
| } | |
| void MockTransportClientSocketPool::CancelRequest(const std::string& group_name, | |
| ClientSocketHandle* handle) { | |
| std::vector<MockConnectJob*>::iterator i; | |
| for (i = job_list_.begin(); i != job_list_.end(); ++i) { | |
| if ((*i)->CancelHandle(handle)) { | |
| cancel_count_++; | |
| break; | |
| } | |
| } | |
| } | |
| void MockTransportClientSocketPool::ReleaseSocket(const std::string& group_name, | |
| StreamSocket* socket, int id) { | |
| EXPECT_EQ(1, id); | |
| release_count_++; | |
| delete socket; | |
| } | |
| DeterministicMockClientSocketFactory::DeterministicMockClientSocketFactory() {} | |
| DeterministicMockClientSocketFactory::~DeterministicMockClientSocketFactory() {} | |
| void DeterministicMockClientSocketFactory::AddSocketDataProvider( | |
| DeterministicSocketData* data) { | |
| mock_data_.Add(data); | |
| } | |
| void DeterministicMockClientSocketFactory::AddSSLSocketDataProvider( | |
| SSLSocketDataProvider* data) { | |
| mock_ssl_data_.Add(data); | |
| } | |
| void DeterministicMockClientSocketFactory::ResetNextMockIndexes() { | |
| mock_data_.ResetNextIndex(); | |
| mock_ssl_data_.ResetNextIndex(); | |
| } | |
| MockSSLClientSocket* DeterministicMockClientSocketFactory:: | |
| GetMockSSLClientSocket(size_t index) const { | |
| DCHECK_LT(index, ssl_client_sockets_.size()); | |
| return ssl_client_sockets_[index]; | |
| } | |
| DatagramClientSocket* | |
| DeterministicMockClientSocketFactory::CreateDatagramClientSocket( | |
| DatagramSocket::BindType bind_type, | |
| const RandIntCallback& rand_int_cb, | |
| net::NetLog* net_log, | |
| const NetLog::Source& source) { | |
| NOTREACHED(); | |
| return NULL; | |
| } | |
| StreamSocket* DeterministicMockClientSocketFactory::CreateTransportClientSocket( | |
| const AddressList& addresses, | |
| net::NetLog* net_log, | |
| const net::NetLog::Source& source) { | |
| DeterministicSocketData* data_provider = mock_data().GetNext(); | |
| DeterministicMockTCPClientSocket* socket = | |
| new DeterministicMockTCPClientSocket(net_log, data_provider); | |
| data_provider->set_socket(socket->AsWeakPtr()); | |
| tcp_client_sockets().push_back(socket); | |
| return socket; | |
| } | |
| SSLClientSocket* DeterministicMockClientSocketFactory::CreateSSLClientSocket( | |
| ClientSocketHandle* transport_socket, | |
| const HostPortPair& host_and_port, | |
| const SSLConfig& ssl_config, | |
| const SSLClientSocketContext& context) { | |
| MockSSLClientSocket* socket = | |
| new MockSSLClientSocket(transport_socket, host_and_port, ssl_config, | |
| mock_ssl_data_.GetNext()); | |
| ssl_client_sockets_.push_back(socket); | |
| return socket; | |
| } | |
| void DeterministicMockClientSocketFactory::ClearSSLSessionCache() { | |
| } | |
| MockSOCKSClientSocketPool::MockSOCKSClientSocketPool( | |
| int max_sockets, | |
| int max_sockets_per_group, | |
| ClientSocketPoolHistograms* histograms, | |
| TransportClientSocketPool* transport_pool) | |
| : SOCKSClientSocketPool(max_sockets, max_sockets_per_group, histograms, | |
| NULL, transport_pool, NULL), | |
| transport_pool_(transport_pool) { | |
| } | |
| MockSOCKSClientSocketPool::~MockSOCKSClientSocketPool() {} | |
| int MockSOCKSClientSocketPool::RequestSocket( | |
| const std::string& group_name, const void* socket_params, | |
| RequestPriority priority, ClientSocketHandle* handle, | |
| const CompletionCallback& callback, const BoundNetLog& net_log) { | |
| return transport_pool_->RequestSocket( | |
| group_name, socket_params, priority, handle, callback, net_log); | |
| } | |
| void MockSOCKSClientSocketPool::CancelRequest( | |
| const std::string& group_name, | |
| ClientSocketHandle* handle) { | |
| return transport_pool_->CancelRequest(group_name, handle); | |
| } | |
| void MockSOCKSClientSocketPool::ReleaseSocket(const std::string& group_name, | |
| StreamSocket* socket, int id) { | |
| return transport_pool_->ReleaseSocket(group_name, socket, id); | |
| } | |
| const char kSOCKS5GreetRequest[] = { 0x05, 0x01, 0x00 }; | |
| const int kSOCKS5GreetRequestLength = arraysize(kSOCKS5GreetRequest); | |
| const char kSOCKS5GreetResponse[] = { 0x05, 0x00 }; | |
| const int kSOCKS5GreetResponseLength = arraysize(kSOCKS5GreetResponse); | |
| const char kSOCKS5OkRequest[] = | |
| { 0x05, 0x01, 0x00, 0x03, 0x04, 'h', 'o', 's', 't', 0x00, 0x50 }; | |
| const int kSOCKS5OkRequestLength = arraysize(kSOCKS5OkRequest); | |
| const char kSOCKS5OkResponse[] = | |
| { 0x05, 0x00, 0x00, 0x01, 127, 0, 0, 1, 0x00, 0x50 }; | |
| const int kSOCKS5OkResponseLength = arraysize(kSOCKS5OkResponse); | |
| } // namespace net |