diff options
author | yzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-20 15:29:13 +0000 |
---|---|---|
committer | yzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-20 15:29:13 +0000 |
commit | 92576792823018d937fc58e74267b0d30663e459 (patch) | |
tree | a80543169bea29b7fee7346387a1a0e1d6b34090 /ppapi/tests | |
parent | 5603a6cf5fb7197f509b894e45bdc733e720b28a (diff) | |
download | chromium_src-92576792823018d937fc58e74267b0d30663e459.zip chromium_src-92576792823018d937fc58e74267b0d30663e459.tar.gz chromium_src-92576792823018d937fc58e74267b0d30663e459.tar.bz2 |
PPB_TCPSocket: add support for TCP server socket operations.
BUG=262601
TEST=new tests in test_tcp_socket.
Review URL: https://chromiumcodereview.appspot.com/24195004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@224383 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ppapi/tests')
-rw-r--r-- | ppapi/tests/test_tcp_socket.cc | 297 | ||||
-rw-r--r-- | ppapi/tests/test_tcp_socket.h | 13 | ||||
-rw-r--r-- | ppapi/tests/test_utils.cc | 47 | ||||
-rw-r--r-- | ppapi/tests/test_utils.h | 5 |
4 files changed, 318 insertions, 44 deletions
diff --git a/ppapi/tests/test_tcp_socket.cc b/ppapi/tests/test_tcp_socket.cc index a4c56eb..90f6913 100644 --- a/ppapi/tests/test_tcp_socket.cc +++ b/ppapi/tests/test_tcp_socket.cc @@ -4,6 +4,9 @@ #include "ppapi/tests/test_tcp_socket.h" +#include <vector> + +#include "ppapi/cpp/message_loop.h" #include "ppapi/cpp/tcp_socket.h" #include "ppapi/tests/test_utils.h" #include "ppapi/tests/testing_instance.h" @@ -49,25 +52,58 @@ void TestTCPSocket::RunTests(const std::string& filter) { RUN_CALLBACK_TEST(TestTCPSocket, Connect, filter); RUN_CALLBACK_TEST(TestTCPSocket, ReadWrite, filter); RUN_CALLBACK_TEST(TestTCPSocket, SetOption, filter); + RUN_CALLBACK_TEST(TestTCPSocket, Listen, filter); + RUN_CALLBACK_TEST(TestTCPSocket, Backlog, filter); } std::string TestTCPSocket::TestConnect() { - pp::TCPSocket socket(instance_); - TestCompletionCallback cb(instance_->pp_instance(), callback_type()); + { + // The basic case. + pp::TCPSocket socket(instance_); + TestCompletionCallback cb(instance_->pp_instance(), callback_type()); - cb.WaitForResult(socket.Connect(addr_, cb.GetCallback())); - CHECK_CALLBACK_BEHAVIOR(cb); - ASSERT_EQ(PP_OK, cb.result()); + cb.WaitForResult(socket.Connect(addr_, cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_EQ(PP_OK, cb.result()); + + pp::NetAddress local_addr, remote_addr; + local_addr = socket.GetLocalAddress(); + remote_addr = socket.GetRemoteAddress(); + + ASSERT_NE(0, local_addr.pp_resource()); + ASSERT_NE(0, remote_addr.pp_resource()); + ASSERT_TRUE(EqualNetAddress(addr_, remote_addr)); - pp::NetAddress local_addr, remote_addr; - local_addr = socket.GetLocalAddress(); - remote_addr = socket.GetRemoteAddress(); + socket.Close(); + } + + { + // Connect a bound socket. + pp::TCPSocket socket(instance_); + TestCompletionCallback cb(instance_->pp_instance(), callback_type()); + + pp::NetAddress any_port_address; + ASSERT_SUBTEST_SUCCESS(GetAddressToBind(&any_port_address)); + + cb.WaitForResult(socket.Bind(any_port_address, cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_EQ(PP_OK, cb.result()); - ASSERT_NE(0, local_addr.pp_resource()); - ASSERT_NE(0, remote_addr.pp_resource()); - ASSERT_TRUE(EqualNetAddress(addr_, remote_addr)); + cb.WaitForResult(socket.Connect(addr_, cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_EQ(PP_OK, cb.result()); - socket.Close(); + pp::NetAddress local_addr, remote_addr; + local_addr = socket.GetLocalAddress(); + remote_addr = socket.GetRemoteAddress(); + + ASSERT_NE(0, local_addr.pp_resource()); + ASSERT_NE(0, remote_addr.pp_resource()); + ASSERT_TRUE(EqualNetAddress(addr_, remote_addr)); + ASSERT_NE(0u, GetPort(local_addr)); + + socket.Close(); + } PASS(); } @@ -80,11 +116,11 @@ std::string TestTCPSocket::TestReadWrite() { CHECK_CALLBACK_BEHAVIOR(cb); ASSERT_EQ(PP_OK, cb.result()); - ASSERT_EQ(PP_OK, WriteStringToSocket(&socket, "GET / HTTP/1.0\r\n\r\n")); + ASSERT_SUBTEST_SUCCESS(WriteToSocket(&socket, "GET / HTTP/1.0\r\n\r\n")); // Read up to the first \n and check that it looks like valid HTTP response. std::string s; - ASSERT_EQ(PP_OK, ReadFirstLineFromSocket(&socket, &s)); + ASSERT_SUBTEST_SUCCESS(ReadFirstLineFromSocket(&socket, &s)); ASSERT_TRUE(ValidateHttpResponse(s)); PASS(); @@ -95,6 +131,7 @@ std::string TestTCPSocket::TestSetOption() { TestCompletionCallback cb_1(instance_->pp_instance(), callback_type()); TestCompletionCallback cb_2(instance_->pp_instance(), callback_type()); TestCompletionCallback cb_3(instance_->pp_instance(), callback_type()); + TestCompletionCallback cb_4(instance_->pp_instance(), callback_type()); // These options cannot be set before the socket is connected. int32_t result_1 = socket.SetOption(PP_TCPSOCKET_OPTION_NO_DELAY, @@ -104,6 +141,10 @@ std::string TestTCPSocket::TestSetOption() { int32_t result_3 = socket.SetOption(PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE, 512, cb_3.GetCallback()); + // This option can only be set before the socket is bound. + int32_t result_4 = socket.SetOption(PP_TCPSOCKET_OPTION_ADDRESS_REUSE, + true, cb_4.GetCallback()); + cb_1.WaitForResult(result_1); CHECK_CALLBACK_BEHAVIOR(cb_1); ASSERT_EQ(PP_ERROR_FAILED, cb_1.result()); @@ -116,6 +157,10 @@ std::string TestTCPSocket::TestSetOption() { CHECK_CALLBACK_BEHAVIOR(cb_3); ASSERT_EQ(PP_ERROR_FAILED, cb_3.result()); + cb_4.WaitForResult(result_4); + CHECK_CALLBACK_BEHAVIOR(cb_4); + ASSERT_EQ(PP_OK, cb_4.result()); + cb_1.WaitForResult(socket.Connect(addr_, cb_1.GetCallback())); CHECK_CALLBACK_BEHAVIOR(cb_1); ASSERT_EQ(PP_OK, cb_1.result()); @@ -126,6 +171,8 @@ std::string TestTCPSocket::TestSetOption() { 512, cb_2.GetCallback()); result_3 = socket.SetOption(PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE, 1024, cb_3.GetCallback()); + result_4 = socket.SetOption(PP_TCPSOCKET_OPTION_ADDRESS_REUSE, + false, cb_4.GetCallback()); cb_1.WaitForResult(result_1); CHECK_CALLBACK_BEHAVIOR(cb_1); @@ -139,53 +186,221 @@ std::string TestTCPSocket::TestSetOption() { CHECK_CALLBACK_BEHAVIOR(cb_3); ASSERT_EQ(PP_OK, cb_3.result()); + cb_4.WaitForResult(result_4); + CHECK_CALLBACK_BEHAVIOR(cb_4); + ASSERT_EQ(PP_ERROR_FAILED, cb_4.result()); + + PASS(); +} + +std::string TestTCPSocket::TestListen() { + static const int kBacklog = 2; + + pp::TCPSocket server_socket(instance_); + ASSERT_SUBTEST_SUCCESS(StartListen(&server_socket, kBacklog)); + + // We can't use a blocking callback for Accept, because it will wait forever + // for the client to connect, since the client connects after. + TestCompletionCallbackWithOutput<pp::TCPSocket> + accept_callback(instance_->pp_instance(), PP_REQUIRED); + // We need to make sure there's a message loop to run accept_callback on. + pp::MessageLoop current_thread_loop(pp::MessageLoop::GetCurrent()); + if (current_thread_loop.is_null() && testing_interface_->IsOutOfProcess()) { + current_thread_loop = pp::MessageLoop(instance_); + current_thread_loop.AttachToCurrentThread(); + } + + int32_t accept_rv = server_socket.Accept(accept_callback.GetCallback()); + + pp::TCPSocket client_socket; + TestCompletionCallback callback(instance_->pp_instance(), callback_type()); + do { + client_socket = pp::TCPSocket(instance_); + + callback.WaitForResult(client_socket.Connect( + server_socket.GetLocalAddress(), callback.GetCallback())); + } while (callback.result() != PP_OK); + + pp::NetAddress client_local_addr = client_socket.GetLocalAddress(); + pp::NetAddress client_remote_addr = client_socket.GetRemoteAddress(); + ASSERT_FALSE(client_local_addr.is_null()); + ASSERT_FALSE(client_remote_addr.is_null()); + + accept_callback.WaitForResult(accept_rv); + CHECK_CALLBACK_BEHAVIOR(accept_callback); + ASSERT_EQ(PP_OK, accept_callback.result()); + + pp::TCPSocket accepted_socket(accept_callback.output()); + pp::NetAddress accepted_local_addr = accepted_socket.GetLocalAddress(); + pp::NetAddress accepted_remote_addr = accepted_socket.GetRemoteAddress(); + ASSERT_FALSE(accepted_local_addr.is_null()); + ASSERT_FALSE(accepted_remote_addr.is_null()); + + ASSERT_TRUE(EqualNetAddress(client_local_addr, accepted_remote_addr)); + + const char kSentByte = 'a'; + ASSERT_SUBTEST_SUCCESS(WriteToSocket(&client_socket, + std::string(1, kSentByte))); + + char received_byte; + ASSERT_SUBTEST_SUCCESS(ReadFromSocket(&accepted_socket, + &received_byte, + sizeof(received_byte))); + ASSERT_EQ(kSentByte, received_byte); + + accepted_socket.Close(); + client_socket.Close(); + server_socket.Close(); + PASS(); } -int32_t TestTCPSocket::ReadFirstLineFromSocket(pp::TCPSocket* socket, - std::string* s) { +std::string TestTCPSocket::TestBacklog() { + static const size_t kBacklog = 5; + + pp::TCPSocket server_socket(instance_); + ASSERT_SUBTEST_SUCCESS(StartListen(&server_socket, 2 * kBacklog)); + + std::vector<pp::TCPSocket*> client_sockets(kBacklog); + std::vector<TestCompletionCallback*> connect_callbacks(kBacklog); + std::vector<int32_t> connect_rv(kBacklog); + pp::NetAddress address = server_socket.GetLocalAddress(); + for (size_t i = 0; i < kBacklog; ++i) { + client_sockets[i] = new pp::TCPSocket(instance_); + connect_callbacks[i] = new TestCompletionCallback(instance_->pp_instance(), + callback_type()); + connect_rv[i] = client_sockets[i]->Connect( + address, connect_callbacks[i]->GetCallback()); + } + + std::vector<pp::TCPSocket*> accepted_sockets(kBacklog); + for (size_t i = 0; i < kBacklog; ++i) { + TestCompletionCallbackWithOutput<pp::TCPSocket> callback( + instance_->pp_instance(), callback_type()); + callback.WaitForResult(server_socket.Accept(callback.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(callback); + ASSERT_EQ(PP_OK, callback.result()); + + accepted_sockets[i] = new pp::TCPSocket(callback.output()); + ASSERT_FALSE(accepted_sockets[i]->is_null()); + } + + for (size_t i = 0; i < kBacklog; ++i) { + connect_callbacks[i]->WaitForResult(connect_rv[i]); + CHECK_CALLBACK_BEHAVIOR(*connect_callbacks[i]); + ASSERT_EQ(PP_OK, connect_callbacks[i]->result()); + } + + for (size_t i = 0; i < kBacklog; ++i) { + const char byte = 'a' + i; + ASSERT_SUBTEST_SUCCESS(WriteToSocket(client_sockets[i], + std::string(1, byte))); + } + + bool byte_received[kBacklog] = {}; + for (size_t i = 0; i < kBacklog; ++i) { + char byte; + ASSERT_SUBTEST_SUCCESS(ReadFromSocket( + accepted_sockets[i], &byte, sizeof(byte))); + const size_t index = byte - 'a'; + ASSERT_GE(index, 0u); + ASSERT_LT(index, kBacklog); + ASSERT_FALSE(byte_received[index]); + byte_received[index] = true; + } + + for (size_t i = 0; i < kBacklog; ++i) { + ASSERT_TRUE(byte_received[i]); + + delete client_sockets[i]; + delete connect_callbacks[i]; + delete accepted_sockets[i]; + } + + PASS(); +} + +std::string TestTCPSocket::ReadFirstLineFromSocket(pp::TCPSocket* socket, + std::string* s) { char buffer[1000]; s->clear(); // Make sure we don't just hang if |Read()| spews. while (s->size() < 10000) { TestCompletionCallback cb(instance_->pp_instance(), callback_type()); - int32_t rv = socket->Read(buffer, sizeof(buffer), cb.GetCallback()); - if (callback_type() == PP_REQUIRED && rv != PP_OK_COMPLETIONPENDING) - return PP_ERROR_FAILED; - cb.WaitForResult(rv); - if (cb.result() < 0) - return cb.result(); - if (cb.result() == 0) - return PP_ERROR_FAILED; // Didn't get a \n-terminated line. + cb.WaitForResult(socket->Read(buffer, sizeof(buffer), cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_GT(cb.result(), 0); s->reserve(s->size() + cb.result()); - for (int32_t i = 0; i < cb.result(); i++) { + for (int32_t i = 0; i < cb.result(); ++i) { s->push_back(buffer[i]); if (buffer[i] == '\n') - return PP_OK; + PASS(); } } - return PP_ERROR_FAILED; + PASS(); +} + +std::string TestTCPSocket::ReadFromSocket(pp::TCPSocket* socket, + char* buffer, + size_t num_bytes) { + while (num_bytes > 0) { + TestCompletionCallback callback(instance_->pp_instance(), callback_type()); + callback.WaitForResult( + socket->Read(buffer, num_bytes, callback.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(callback); + ASSERT_GT(callback.result(), 0); + buffer += callback.result(); + num_bytes -= callback.result(); + } + ASSERT_EQ(0u, num_bytes); + PASS(); } -int32_t TestTCPSocket::WriteStringToSocket(pp::TCPSocket* socket, - const std::string& s) { +std::string TestTCPSocket::WriteToSocket(pp::TCPSocket* socket, + const std::string& s) { const char* buffer = s.data(); size_t written = 0; while (written < s.size()) { TestCompletionCallback cb(instance_->pp_instance(), callback_type()); - int32_t rv = socket->Write(buffer + written, s.size() - written, - cb.GetCallback()); - if (callback_type() == PP_REQUIRED && rv != PP_OK_COMPLETIONPENDING) - return PP_ERROR_FAILED; - cb.WaitForResult(rv); - if (cb.result() < 0) - return cb.result(); - if (cb.result() == 0) - return PP_ERROR_FAILED; + cb.WaitForResult( + socket->Write(buffer + written, s.size() - written, cb.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(cb); + ASSERT_GT(cb.result(), 0); written += cb.result(); } - if (written != s.size()) - return PP_ERROR_FAILED; - return PP_OK; + ASSERT_EQ(written, s.size()); + PASS(); } + +std::string TestTCPSocket::GetAddressToBind(pp::NetAddress* address) { + pp::TCPSocket socket(instance_); + TestCompletionCallback callback(instance_->pp_instance(), callback_type()); + callback.WaitForResult(socket.Connect(addr_, callback.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(callback); + ASSERT_EQ(PP_OK, callback.result()); + + ASSERT_TRUE(ReplacePort(instance_->pp_instance(), socket.GetLocalAddress(), 0, + address)); + ASSERT_FALSE(address->is_null()); + PASS(); +} + +std::string TestTCPSocket::StartListen(pp::TCPSocket* socket, int32_t backlog) { + pp::NetAddress any_port_address; + ASSERT_SUBTEST_SUCCESS(GetAddressToBind(&any_port_address)); + + TestCompletionCallback callback(instance_->pp_instance(), callback_type()); + callback.WaitForResult( + socket->Bind(any_port_address, callback.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(callback); + ASSERT_EQ(PP_OK, callback.result()); + + callback.WaitForResult( + socket->Listen(backlog, callback.GetCallback())); + CHECK_CALLBACK_BEHAVIOR(callback); + ASSERT_EQ(PP_OK, callback.result()); + + PASS(); +} + diff --git a/ppapi/tests/test_tcp_socket.h b/ppapi/tests/test_tcp_socket.h index 44c7321..136dc46 100644 --- a/ppapi/tests/test_tcp_socket.h +++ b/ppapi/tests/test_tcp_socket.h @@ -27,9 +27,16 @@ class TestTCPSocket: public TestCase { std::string TestConnect(); std::string TestReadWrite(); std::string TestSetOption(); - - int32_t ReadFirstLineFromSocket(pp::TCPSocket* socket, std::string* s); - int32_t WriteStringToSocket(pp::TCPSocket* socket, const std::string& s); + std::string TestListen(); + std::string TestBacklog(); + + std::string ReadFirstLineFromSocket(pp::TCPSocket* socket, std::string* s); + std::string ReadFromSocket(pp::TCPSocket* socket, + char* buffer, + size_t num_bytes); + std::string WriteToSocket(pp::TCPSocket* socket, const std::string& s); + std::string GetAddressToBind(pp::NetAddress* address); + std::string StartListen(pp::TCPSocket* socket, int32_t backlog); pp::NetAddress addr_; }; diff --git a/ppapi/tests/test_utils.cc b/ppapi/tests/test_utils.cc index f9fd7b6..eecbe7a 100644 --- a/ppapi/tests/test_utils.cc +++ b/ppapi/tests/test_utils.cc @@ -179,6 +179,53 @@ bool ResolveHost(PP_Instance instance, } } +bool ReplacePort(PP_Instance instance, + const pp::NetAddress& input_addr, + uint16_t port, + pp::NetAddress* output_addr) { + switch (input_addr.GetFamily()) { + case PP_NETADDRESS_FAMILY_IPV4: { + PP_NetAddress_IPv4 ipv4_addr; + if (!input_addr.DescribeAsIPv4Address(&ipv4_addr)) + return false; + ipv4_addr.port = ConvertToNetEndian16(port); + *output_addr = pp::NetAddress(pp::InstanceHandle(instance), ipv4_addr); + return true; + } + case PP_NETADDRESS_FAMILY_IPV6: { + PP_NetAddress_IPv6 ipv6_addr; + if (!input_addr.DescribeAsIPv6Address(&ipv6_addr)) + return false; + ipv6_addr.port = ConvertToNetEndian16(port); + *output_addr = pp::NetAddress(pp::InstanceHandle(instance), ipv6_addr); + return true; + } + default: { + return false; + } + } +} + +uint16_t GetPort(const pp::NetAddress& addr) { + switch (addr.GetFamily()) { + case PP_NETADDRESS_FAMILY_IPV4: { + PP_NetAddress_IPv4 ipv4_addr; + if (!addr.DescribeAsIPv4Address(&ipv4_addr)) + return 0; + return ConvertFromNetEndian16(ipv4_addr.port); + } + case PP_NETADDRESS_FAMILY_IPV6: { + PP_NetAddress_IPv6 ipv6_addr; + if (!addr.DescribeAsIPv6Address(&ipv6_addr)) + return 0; + return ConvertFromNetEndian16(ipv6_addr.port); + } + default: { + return 0; + } + } +} + void NestedEvent::Wait() { PP_DCHECK(pp::Module::Get()->core()->IsMainThread()); // Don't allow nesting more than once; it doesn't work with the code as-is, diff --git a/ppapi/tests/test_utils.h b/ppapi/tests/test_utils.h index 357a1c6..4621c355 100644 --- a/ppapi/tests/test_utils.h +++ b/ppapi/tests/test_utils.h @@ -34,6 +34,11 @@ bool ResolveHost(PP_Instance instance, const std::string& host, uint16_t port, pp::NetAddress* addr); +bool ReplacePort(PP_Instance instance, + const pp::NetAddress& input_addr, + uint16_t port, + pp::NetAddress* output_addr); +uint16_t GetPort(const pp::NetAddress& addr); // NestedEvent allows you to run a nested MessageLoop and wait for a particular // event to complete. For example, you can use it to wait for a callback on a |