// 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 "ppapi/tests/test_tcp_socket_private.h" #include #include "ppapi/cpp/private/tcp_socket_private.h" #include "ppapi/tests/testing_instance.h" #include "ppapi/tests/test_utils.h" namespace { // Validates the first line of an HTTP response. bool ValidateHttpResponse(const std::string& s) { // Just check that it begins with "HTTP/" and ends with a "\r\n". return s.size() >= 5 && s.substr(0, 5) == "HTTP/" && s.substr(s.size() - 2) == "\r\n"; } } // namespace REGISTER_TEST_CASE(TCPSocketPrivate); TestTCPSocketPrivate::TestTCPSocketPrivate(TestingInstance* instance) : TestCase(instance) { } bool TestTCPSocketPrivate::Init() { if (!pp::TCPSocketPrivate::IsAvailable()) return false; // We need something to connect to, so we connect to the HTTP server whence we // came. Grab the host and port. if (!EnsureRunningOverHTTP()) return false; if (!GetLocalHostPort(instance_->pp_instance(), &host_, &port_)) return false; // Get the port for the SSL server. ssl_port_ = instance_->ssl_server_port(); return true; } void TestTCPSocketPrivate::RunTests(const std::string& filter) { RUN_CALLBACK_TEST(TestTCPSocketPrivate, Basic, filter); RUN_CALLBACK_TEST(TestTCPSocketPrivate, ReadWrite, filter); RUN_CALLBACK_TEST(TestTCPSocketPrivate, ReadWriteSSL, filter); RUN_CALLBACK_TEST(TestTCPSocketPrivate, ConnectAddress, filter); RUN_CALLBACK_TEST(TestTCPSocketPrivate, SetOption, filter); } std::string TestTCPSocketPrivate::TestBasic() { pp::TCPSocketPrivate socket(instance_); TestCompletionCallback cb(instance_->pp_instance(), callback_type()); cb.WaitForResult(socket.Connect(host_.c_str(), port_, cb.GetCallback())); CHECK_CALLBACK_BEHAVIOR(cb); ASSERT_EQ(PP_OK, cb.result()); PP_NetAddress_Private unused; // TODO(viettrungluu): check the values somehow. ASSERT_TRUE(socket.GetLocalAddress(&unused)); ASSERT_TRUE(socket.GetRemoteAddress(&unused)); socket.Disconnect(); PASS(); } std::string TestTCPSocketPrivate::TestReadWrite() { pp::TCPSocketPrivate socket(instance_); TestCompletionCallback cb(instance_->pp_instance(), callback_type()); cb.WaitForResult(socket.Connect(host_.c_str(), port_, cb.GetCallback())); CHECK_CALLBACK_BEHAVIOR(cb); ASSERT_EQ(PP_OK, cb.result()); ASSERT_EQ(PP_OK, WriteStringToSocket(&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_TRUE(ValidateHttpResponse(s)); socket.Disconnect(); PASS(); } std::string TestTCPSocketPrivate::TestReadWriteSSL() { pp::TCPSocketPrivate socket(instance_); TestCompletionCallback cb(instance_->pp_instance(), callback_type()); cb.WaitForResult(socket.Connect(host_.c_str(), ssl_port_, cb.GetCallback())); CHECK_CALLBACK_BEHAVIOR(cb); ASSERT_EQ(PP_OK, cb.result()); cb.WaitForResult( socket.SSLHandshake(host_.c_str(), ssl_port_, cb.GetCallback())); CHECK_CALLBACK_BEHAVIOR(cb); ASSERT_EQ(PP_OK, cb.result()); ASSERT_EQ(PP_OK, WriteStringToSocket(&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_TRUE(ValidateHttpResponse(s)); socket.Disconnect(); PASS(); } std::string TestTCPSocketPrivate::TestConnectAddress() { PP_NetAddress_Private address; // First, bring up a connection and grab the address. { pp::TCPSocketPrivate socket(instance_); TestCompletionCallback cb(instance_->pp_instance(), callback_type()); cb.WaitForResult(socket.Connect(host_.c_str(), port_, cb.GetCallback())); CHECK_CALLBACK_BEHAVIOR(cb); ASSERT_EQ(PP_OK, cb.result()); ASSERT_TRUE(socket.GetRemoteAddress(&address)); // Omit the |Disconnect()| here to make sure we don't crash if we just let // the resource be destroyed. } // Connect to that address. pp::TCPSocketPrivate socket(instance_); TestCompletionCallback cb(instance_->pp_instance(), callback_type()); cb.WaitForResult(socket.ConnectWithNetAddress(&address, cb.GetCallback())); CHECK_CALLBACK_BEHAVIOR(cb); ASSERT_EQ(PP_OK, cb.result()); // Make sure we can read/write to it properly (see |TestReadWrite()|). ASSERT_EQ(PP_OK, WriteStringToSocket(&socket, "GET / HTTP/1.0\r\n\r\n")); std::string s; ASSERT_EQ(PP_OK, ReadFirstLineFromSocket(&socket, &s)); ASSERT_TRUE(ValidateHttpResponse(s)); socket.Disconnect(); PASS(); } std::string TestTCPSocketPrivate::TestSetOption() { pp::TCPSocketPrivate socket(instance_); TestCompletionCallback cb(instance_->pp_instance(), callback_type()); cb.WaitForResult( socket.SetOption(PP_TCPSOCKETOPTION_NO_DELAY, true, cb.GetCallback())); CHECK_CALLBACK_BEHAVIOR(cb); ASSERT_EQ(PP_ERROR_FAILED, cb.result()); cb.WaitForResult(socket.Connect(host_.c_str(), port_, cb.GetCallback())); CHECK_CALLBACK_BEHAVIOR(cb); ASSERT_EQ(PP_OK, cb.result()); cb.WaitForResult( socket.SetOption(PP_TCPSOCKETOPTION_NO_DELAY, true, cb.GetCallback())); CHECK_CALLBACK_BEHAVIOR(cb); ASSERT_EQ(PP_OK, cb.result()); cb.WaitForResult( socket.SetOption(PP_TCPSOCKETOPTION_INVALID, true, cb.GetCallback())); CHECK_CALLBACK_BEHAVIOR(cb); ASSERT_EQ(PP_ERROR_BADARGUMENT, cb.result()); socket.Disconnect(); PASS(); } int32_t TestTCPSocketPrivate::ReadFirstLineFromSocket( pp::TCPSocketPrivate* socket, std::string* s) { char buffer[10000]; s->clear(); // Make sure we don't just hang if |Read()| spews. while (s->size() < 1000000) { 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. s->reserve(s->size() + cb.result()); for (int32_t i = 0; i < cb.result(); i++) { s->push_back(buffer[i]); if (buffer[i] == '\n') return PP_OK; } } return PP_ERROR_FAILED; } int32_t TestTCPSocketPrivate::WriteStringToSocket(pp::TCPSocketPrivate* 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; written += cb.result(); } if (written != s.size()) return PP_ERROR_FAILED; return PP_OK; }