Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(604)

Unified Diff: webrtc/base/physicalsocketserver_unittest.cc

Issue 1616153007: Stay writable after partial socket writes. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Fixed compiler error about signed/unsigned comparison. Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « webrtc/base/physicalsocketserver.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: webrtc/base/physicalsocketserver_unittest.cc
diff --git a/webrtc/base/physicalsocketserver_unittest.cc b/webrtc/base/physicalsocketserver_unittest.cc
index a2fde80b42c951c640e80c1ef9fc28f8f0547176..b6a45c3e77daa39a4d50ac8bb63317bc8d253d51 100644
--- a/webrtc/base/physicalsocketserver_unittest.cc
+++ b/webrtc/base/physicalsocketserver_unittest.cc
@@ -29,8 +29,15 @@ class FakeSocketDispatcher : public SocketDispatcher {
: SocketDispatcher(ss) {
}
+ FakeSocketDispatcher(SOCKET s, PhysicalSocketServer* ss)
+ : SocketDispatcher(s, ss) {
+ }
+
protected:
SOCKET DoAccept(SOCKET socket, sockaddr* addr, socklen_t* addrlen) override;
+ int DoSend(SOCKET socket, const char* buf, int len, int flags) override;
+ int DoSendTo(SOCKET socket, const char* buf, int len, int flags,
+ const struct sockaddr* dest_addr, socklen_t addrlen) override;
};
class FakePhysicalSocketServer : public PhysicalSocketServer {
@@ -59,6 +66,16 @@ class FakePhysicalSocketServer : public PhysicalSocketServer {
}
}
+ AsyncSocket* WrapSocket(SOCKET s) override {
+ SocketDispatcher* dispatcher = new FakeSocketDispatcher(s, this);
+ if (dispatcher->Initialize()) {
+ return dispatcher;
+ } else {
+ delete dispatcher;
+ return nullptr;
+ }
pthatcher1 2016/01/30 00:03:10 Can you flip this around? if (!dispatcher->Initia
joachim 2016/01/31 23:29:07 Done. This was implemented similar to the existing
+ }
+
PhysicalSocketTest* GetTest() const { return test_; }
private:
@@ -71,18 +88,25 @@ class PhysicalSocketTest : public SocketTest {
void SetFailAccept(bool fail) { fail_accept_ = fail; }
bool FailAccept() const { return fail_accept_; }
+ // Maximum size to ::send to a socket. Set to 0 to disable limiting.
pthatcher1 2016/01/30 00:03:10 Can you make "disabled limited" < 0, such as -1.
joachim 2016/01/31 23:29:06 Done.
+ void SetMaximumSendSize(int max_size) { max_send_size_ = max_size; }
+ int MaximumSendSize() const { return max_send_size_; }
pthatcher1 2016/01/30 00:03:10 Please change MaximumSendSize => MaxSendSize in bo
joachim 2016/01/31 23:29:07 Done.
+
protected:
PhysicalSocketTest()
: server_(new FakePhysicalSocketServer(this)),
scope_(server_.get()),
- fail_accept_(false) {
+ fail_accept_(false),
+ max_send_size_(0) {
}
void ConnectInternalAcceptError(const IPAddress& loopback);
+ void PartialWriteStayWritable(const IPAddress& loopback);
pthatcher1 2016/01/30 00:03:10 Can you name this WritableAfterPartialWrite?
joachim 2016/01/31 23:29:06 Done.
rtc::scoped_ptr<FakePhysicalSocketServer> server_;
SocketServerScope scope_;
bool fail_accept_;
+ int max_send_size_;
};
SOCKET FakeSocketDispatcher::DoAccept(SOCKET socket,
@@ -97,6 +121,29 @@ SOCKET FakeSocketDispatcher::DoAccept(SOCKET socket,
return SocketDispatcher::DoAccept(socket, addr, addrlen);
}
+int FakeSocketDispatcher::DoSend(SOCKET socket, const char* buf, int len,
+ int flags) {
+ FakePhysicalSocketServer* ss =
+ static_cast<FakePhysicalSocketServer*>(socketserver());
+ if (ss->GetTest()->MaximumSendSize() > 0) {
+ len = std::min(len, ss->GetTest()->MaximumSendSize());
+ }
+
+ return SocketDispatcher::DoSend(socket, buf, len, flags);
+}
+
+int FakeSocketDispatcher::DoSendTo(SOCKET socket, const char* buf, int len,
+ int flags, const struct sockaddr* dest_addr, socklen_t addrlen) {
+ FakePhysicalSocketServer* ss =
+ static_cast<FakePhysicalSocketServer*>(socketserver());
+ if (ss->GetTest()->MaximumSendSize() > 0) {
+ len = std::min(len, ss->GetTest()->MaximumSendSize());
+ }
+
+ return SocketDispatcher::DoSendTo(socket, buf, len, flags, dest_addr,
+ addrlen);
+}
+
TEST_F(PhysicalSocketTest, TestConnectIPv4) {
SocketTest::TestConnectIPv4();
}
@@ -209,6 +256,146 @@ TEST_F(PhysicalSocketTest, MAYBE_TestConnectAcceptErrorIPv6) {
ConnectInternalAcceptError(kIPv6Loopback);
}
+void PhysicalSocketTest::PartialWriteStayWritable(const IPAddress& loopback) {
+ testing::StreamSink sink;
+ SocketAddress accept_addr;
+
+ // Simulate a really small maximum send size.
+ const int kMaximumSendSize = 128;
pthatcher1 2016/01/30 00:03:10 kMaxSendSize
joachim 2016/01/31 23:29:06 Done.
+ SetMaximumSendSize(kMaximumSendSize);
+
+ // Create test data.
+ const size_t kDataSize = 128 * 1024;
+ scoped_ptr<char[]> send_buffer(new char[kDataSize]);
+ scoped_ptr<char[]> recv_buffer(new char[kDataSize]);
pthatcher1 2016/01/30 00:03:10 It seems like this would benefit from using rtc::B
joachim 2016/01/31 23:29:07 Done. As I used "SocketTest::TcpInternal" as base
+ size_t send_pos = 0, recv_pos = 0;
pthatcher1 2016/01/30 00:03:10 You can replace all instances of recv_pos with rec
joachim 2016/01/31 23:29:06 Done.
+ for (size_t i = 0; i < kDataSize; ++i) {
+ send_buffer[i] = static_cast<char>(i % 256);
+ recv_buffer[i] = 0;
+ }
+
+ // Create client.
+ scoped_ptr<AsyncSocket> client(
+ server_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+ sink.Monitor(client.get());
+
+ // Create server and listen.
+ scoped_ptr<AsyncSocket> server(
+ server_->CreateAsyncSocket(loopback.family(), SOCK_STREAM));
+ sink.Monitor(server.get());
+ EXPECT_EQ(0, server->Bind(SocketAddress(loopback, 0)));
+ EXPECT_EQ(0, server->Listen(5));
+
+ // Attempt connection.
+ EXPECT_EQ(0, client->Connect(server->GetLocalAddress()));
+
+ // Accept connection.
+ EXPECT_TRUE_WAIT((sink.Check(server.get(), testing::SSE_READ)), kTimeout);
+ scoped_ptr<AsyncSocket> accepted(server->Accept(&accept_addr));
+ ASSERT_TRUE(accepted);
+ sink.Monitor(accepted.get());
+
+ // Both sides are now connected.
+ EXPECT_EQ_WAIT(AsyncSocket::CS_CONNECTED, client->GetState(), kTimeout);
+ EXPECT_TRUE(sink.Check(client.get(), testing::SSE_OPEN));
+ EXPECT_EQ(client->GetRemoteAddress(), accepted->GetLocalAddress());
+ EXPECT_EQ(accepted->GetRemoteAddress(), client->GetLocalAddress());
+
+ // Send and receive a bunch of data.
pthatcher1 2016/01/30 00:03:10 Can you move the definition of the send and recv b
joachim 2016/01/31 23:29:06 Done.
+ bool send_waiting_for_writability = false;
pthatcher1 2016/01/30 00:03:10 Why not just "writable", with the reversed meaning
pthatcher1 2016/01/30 00:03:10 Can you call sender = accepted; receiver = clien
joachim 2016/01/31 23:29:06 Done.
joachim 2016/01/31 23:29:06 Done.
+ bool send_expect_success = true;
pthatcher1 2016/01/30 00:03:10 Can you call this send_called = false? That would
joachim 2016/01/31 23:29:07 Done.
+ bool recv_waiting_for_readability = true;
pthatcher1 2016/01/30 00:03:10 Similarly, "readable = false" here.
joachim 2016/01/31 23:29:06 Done.
+ bool recv_expect_success = false;
pthatcher1 2016/01/30 00:03:10 And this "recv_called = false"?
joachim 2016/01/31 23:29:06 Done.
+ int data_in_flight = 0;
+ while (recv_pos < kDataSize) {
+ // Send as much as we can if we've been cleared to send.
pthatcher1 2016/01/30 00:03:09 if we've been cleared to send => while we're clear
joachim 2016/01/31 23:29:06 Done.
+ while (!send_waiting_for_writability && send_pos < kDataSize) {
+ int tosend = static_cast<int>(kDataSize - send_pos);
+ int sent = accepted->Send(send_buffer.get() + send_pos, tosend);
+ if (send_expect_success) {
+ // The first Send() after connecting or getting writability should
+ // succeed and send some data.
+ EXPECT_GT(sent, 0);
+ send_expect_success = false;
+ }
+ if (sent >= 0) {
+ EXPECT_LE(sent, tosend);
+ EXPECT_LE(sent, kMaximumSendSize);
+ send_pos += sent;
+ data_in_flight += sent;
pthatcher1 2016/01/30 00:03:10 I think you can replace all instances of data_in_f
joachim 2016/01/31 23:29:06 Done.
+ }
+ if (sent < tosend) {
+ // Partial data or nothing sent.
+ ASSERT_TRUE(accepted->IsBlocking());
+ send_waiting_for_writability = true;
+ }
+ }
+
+ // Read all the sent data.
+ while (data_in_flight > 0) {
pthatcher1 2016/01/30 00:03:10 Or in this case: while (recv_buffer.size() < sent
joachim 2016/01/31 23:29:06 Done.
+ if (recv_waiting_for_readability) {
+ // Wait until data is available.
+ EXPECT_TRUE_WAIT(sink.Check(client.get(), testing::SSE_READ), kTimeout);
+ recv_waiting_for_readability = false;
+ recv_expect_success = true;
+ }
+
+ // Receive as much as we can get in a single recv call.
+ int rcvd = client->Recv(recv_buffer.get() + recv_pos,
+ kDataSize - recv_pos);
+
+ if (recv_expect_success) {
+ // The first Recv() after getting readability should succeed and receive
+ // some data.
+ EXPECT_GT(rcvd, 0);
+ recv_expect_success = false;
+ }
+ if (rcvd >= 0) {
+ EXPECT_LE(rcvd, data_in_flight);
+ recv_pos += rcvd;
+ data_in_flight -= rcvd;
+ } else {
+ ASSERT_TRUE(client->IsBlocking());
+ recv_waiting_for_readability = true;
+ }
+ }
+
+ // Once all that we've sent has been rcvd, expect to be able to send again.
+ if (send_waiting_for_writability) {
+ EXPECT_TRUE_WAIT(sink.Check(accepted.get(), testing::SSE_WRITE),
+ kTimeout);
+ send_waiting_for_writability = false;
+ send_expect_success = true;
+ }
+ }
+
+ // The received data matches the sent data.
+ EXPECT_EQ(kDataSize, send_pos);
+ EXPECT_EQ(kDataSize, recv_pos);
+ EXPECT_EQ(0, memcmp(recv_buffer.get(), send_buffer.get(), kDataSize));
+
+ // Close down.
+ accepted->Close();
+ EXPECT_EQ_WAIT(AsyncSocket::CS_CLOSED, client->GetState(), kTimeout);
+ EXPECT_TRUE(sink.Check(client.get(), testing::SSE_CLOSE));
+ client->Close();
+}
+
+TEST_F(PhysicalSocketTest, TestPartialWriteStayWritableIPv4) {
+ PartialWriteStayWritable(kIPv4Loopback);
+}
+
+// Crashes on Linux. See webrtc:4923.
+#if defined(WEBRTC_LINUX)
+#define MAYBE_TestPartialWriteStayWritableIPv6 \
+ DISABLED_TestPartialWriteStayWritableIPv6
+#else
+#define MAYBE_TestPartialWriteStayWritableIPv6 TestPartialWriteStayWritableIPv6
+#endif
+TEST_F(PhysicalSocketTest, MAYBE_TestPartialWriteStayWritableIPv6) {
+ PartialWriteStayWritable(kIPv6Loopback);
+}
+
// Crashes on Linux. See webrtc:4923.
#if defined(WEBRTC_LINUX)
#define MAYBE_TestConnectFailIPv6 DISABLED_TestConnectFailIPv6
« no previous file with comments | « webrtc/base/physicalsocketserver.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698