root/ppapi/tests/extensions/socket/test_socket.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. port_
  2. Init
  3. HandleMessage
  4. TestServerSocket
  5. TestMulticast
  6. TestClientSocket
  7. Log
  8. NotifyTestDone
  9. ConvertToArrayBuffer
  10. ConvertFromArrayBuffer
  11. CreateInstance
  12. CreateModule

// Copyright (c) 2013 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 <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include "ppapi/c/ppb_console.h"
#include "ppapi/cpp/extensions/dev/socket_dev.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/var_array_buffer.h"
#include "ppapi/tests/test_utils.h"

using namespace pp;
using namespace pp::ext;

namespace {

const char* const kSendContents = "0100000005320000005hello";
const char* const kReceiveContentsPrefix = "0100000005320000005";
const size_t kReceiveContentsSuffixSize = 11;

const char* const kMulticastAddress = "237.132.100.133";
const int32_t kMulticastPort = 11103;
const char* const kMulticastMessage = "hello world!";

}  // namespace

class MyInstance : public Instance {
 public:
  explicit MyInstance(PP_Instance instance)
      : Instance(instance),
        socket_(InstanceHandle(instance)),
        console_interface_(NULL),
        port_(0) {
  }
  virtual ~MyInstance() {
  }

  virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
    console_interface_ = static_cast<const PPB_Console*>(
        Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE));

    if (!console_interface_)
      return false;

    PostMessage(Var("ready"));
    return true;
  }

  virtual void HandleMessage(const pp::Var& message_data) {
    std::string output;
    do {
      if (!message_data.is_string()) {
        output = "Invalid control message.";
        break;
      }

      std::string control_message = message_data.AsString();
      std::vector<std::string> parts;
      size_t pos = 0;
      size_t next_match = 0;
      while (pos < control_message.size()) {
        next_match = control_message.find(':', pos);
        if (next_match == std::string::npos)
          next_match = control_message.size();
        parts.push_back(control_message.substr(pos, next_match - pos));
        pos = next_match + 1;
      }

      if (parts.size() != 3) {
        output = "Invalid protocol/address/port input.";
        break;
      }

      test_type_ = parts[0];
      address_ = parts[1];
      port_ = atoi(parts[2].c_str());
      Log(PP_LOGLEVEL_LOG, "Running tests, protocol %s, server %s:%d",
          test_type_.c_str(), address_.c_str(), port_);

      if (test_type_ == "tcp_server") {
        output = TestServerSocket();
      } else if (test_type_ == "multicast") {
        output = TestMulticast();
      } else {
        output = TestClientSocket();
      }
    } while (false);

    NotifyTestDone(output);
  }

 private:
  std::string TestServerSocket() {
    int32_t socket_id = 0;
    {
      TestExtCompletionCallbackWithOutput<socket::CreateInfo_Dev>
          callback(pp_instance());
      callback.WaitForResult(socket_.Create(
          socket::SocketType_Dev(socket::SocketType_Dev::TCP),
          Optional<socket::CreateOptions_Dev>(), callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "Create(): failed.";
      socket_id = callback.output().socket_id();
      if (socket_id <= 0)
        return "Create(): invalid socket ID.";
    }

    {
      TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
      callback.WaitForResult(socket_.Listen(
          socket_id, address_, port_, Optional<int32_t>(),
          callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "Listen(): failed.";
      if (callback.output() != 0)
        return "Listen(): failed.";
    }

    int32_t client_socket_id = 0;
    {
      TestExtCompletionCallbackWithOutput<socket::CreateInfo_Dev>
          callback(pp_instance());
      callback.WaitForResult(socket_.Create(
          socket::SocketType_Dev(socket::SocketType_Dev::TCP),
          Optional<socket::CreateOptions_Dev>(), callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "Create(): failed.";
      client_socket_id = callback.output().socket_id();
      if (client_socket_id <= 0)
        return "Create(): invalid socket ID.";
    }

    {
      TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
      callback.WaitForResult(socket_.Connect(
          client_socket_id, address_, port_, callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "Connect(): failed.";
      if (callback.output() != 0)
        return "Connect(): failed.";
    }

    int32_t accepted_socket_id = 0;
    {
      TestExtCompletionCallbackWithOutput<socket::AcceptInfo_Dev>
          callback(pp_instance());
      callback.WaitForResult(socket_.Accept(socket_id, callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "Accept(): failed.";
      socket::AcceptInfo_Dev accept_info = callback.output();
      if (accept_info.result_code() != 0 || !accept_info.socket_id().IsSet())
        return "Accept(): failed.";
      accepted_socket_id = *accept_info.socket_id();
    }

    size_t bytes_written = 0;
    {
      TestExtCompletionCallbackWithOutput<socket::WriteInfo_Dev>
          callback(pp_instance());
      VarArrayBuffer array_buffer = ConvertToArrayBuffer(kSendContents);
      callback.WaitForResult(socket_.Write(client_socket_id, array_buffer,
                                           callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "Write(): failed.";
      socket::WriteInfo_Dev write_info = callback.output();
      bytes_written = static_cast<size_t>(write_info.bytes_written());
      if (bytes_written <= 0)
        return "Write(): did not write any bytes.";
    }

    {
      TestExtCompletionCallbackWithOutput<socket::ReadInfo_Dev>
          callback(pp_instance());
      callback.WaitForResult(socket_.Read(
          accepted_socket_id, Optional<int32_t>(), callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "Read(): failed.";

      std::string data_string = ConvertFromArrayBuffer(
          &callback.output().data());
      if (data_string.compare(0, std::string::npos, kSendContents,
                              bytes_written) != 0) {
        return "Read(): Received data does not match.";
      }
    }

    socket_.Destroy(client_socket_id);
    socket_.Destroy(accepted_socket_id);
    socket_.Destroy(socket_id);
    return std::string();
  }

  std::string TestMulticast() {
    int32_t socket_id = 0;
    {
      TestExtCompletionCallbackWithOutput<socket::CreateInfo_Dev>
          callback(pp_instance());
      callback.WaitForResult(socket_.Create(
          socket::SocketType_Dev::UDP, Optional<socket::CreateOptions_Dev>(),
          callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "Create(): failed.";
      socket_id = callback.output().socket_id();
      if (socket_id <= 0)
        return "Create(): invalid socket ID.";
    }

    {
      TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
      callback.WaitForResult(socket_.SetMulticastTimeToLive(
          socket_id, 0, callback.GetCallback()));
      if (callback.result() != PP_OK || callback.output() != 0)
        return "SetMulticastTimeToLive(): failed.";
    }

    {
      TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
      callback.WaitForResult(socket_.SetMulticastTimeToLive(
          socket_id, -3, callback.GetCallback()));
      if (callback.result() == PP_OK)
        return "SetMulticastTimeToLive(): succeeded unexpectedly.";
      if (callback.output() != -4)
        return "SetMulticastTimeToLive(): returned unexpected result.";
    }

    {
      TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
      callback.WaitForResult(socket_.SetMulticastLoopbackMode(
          socket_id, false, callback.GetCallback()));
      if (callback.result() != PP_OK || callback.output() != 0)
        return "SetMulticastLoopbackMode(): failed.";
    }

    {
      TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
      callback.WaitForResult(socket_.SetMulticastLoopbackMode(
          socket_id, true, callback.GetCallback()));
      if (callback.result() != PP_OK || callback.output() != 0)
        return "SetMulticastLoopbackMode(): failed.";
    }

    socket_.Destroy(socket_id);
    socket_id = 0;

    int32_t server_socket_id = 0;
    {
      TestExtCompletionCallbackWithOutput<socket::CreateInfo_Dev>
          callback(pp_instance());
      callback.WaitForResult(socket_.Create(
          socket::SocketType_Dev::UDP, Optional<socket::CreateOptions_Dev>(),
          callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "Create(): failed.";
      server_socket_id = callback.output().socket_id();
      if (server_socket_id <= 0)
        return "Create(): invalid socket ID.";
    }

    {
      TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
      callback.WaitForResult(socket_.Bind(
          server_socket_id, "0.0.0.0", kMulticastPort, callback.GetCallback()));
      if (callback.result() != PP_OK || callback.output() != 0)
        return "Bind(): failed";
    }

    {
      TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
      callback.WaitForResult(socket_.JoinGroup(
          server_socket_id, kMulticastAddress, callback.GetCallback()));
      if (callback.result() != PP_OK || callback.output() != 0)
        return "JoinGroup(): failed.";
    }

    {
      TestExtCompletionCallbackWithOutput<std::vector<std::string> >
          callback(pp_instance());
      callback.WaitForResult(socket_.GetJoinedGroups(
          server_socket_id, callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "GetJoinedGroups(): failed.";
      std::vector<std::string> groups = callback.output();
      if (groups.size() != 1 || groups[0] != kMulticastAddress) {
        return "GetJoinedGroups(): the returned groups didn't match those "
               "joined.";
      }
    }

    int32_t client_socket_id = 0;
    {
      TestExtCompletionCallbackWithOutput<socket::CreateInfo_Dev>
          callback(pp_instance());
      callback.WaitForResult(socket_.Create(
          socket::SocketType_Dev::UDP, Optional<socket::CreateOptions_Dev>(),
          callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "Create(): failed.";
      client_socket_id = callback.output().socket_id();
      if (client_socket_id <= 0)
        return "Create(): invalid socket ID.";
    }

    {
      TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
      callback.WaitForResult(socket_.SetMulticastTimeToLive(
          client_socket_id, 0, callback.GetCallback()));
      if (callback.result() != PP_OK || callback.output() != 0)
        return "SetMulticastTimeToLive(): failed.";
    }

    {
      TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
      callback.WaitForResult(socket_.Connect(
          client_socket_id, kMulticastAddress, kMulticastPort,
          callback.GetCallback()));
      if (callback.result() != PP_OK || callback.output() != 0)
        return "Connnect(): failed.";
    }

    {
      VarArrayBuffer input_array_buffer =
          ConvertToArrayBuffer(kMulticastMessage);
      size_t bytes_written = 0;
      int32_t result_code = 0;
      VarArrayBuffer data;

      TestExtCompletionCallbackWithOutput<socket::RecvFromInfo_Dev>
          recv_from_callback(pp_instance());
      int32_t recv_from_result = socket_.RecvFrom(
          server_socket_id, 1024, recv_from_callback.GetCallback());
      if (recv_from_result != PP_OK_COMPLETIONPENDING)
        return "RecvFrom(): did not wait for data.";

      TestExtCompletionCallbackWithOutput<socket::WriteInfo_Dev>
          write_callback(pp_instance());
      write_callback.WaitForResult(socket_.Write(
          client_socket_id, input_array_buffer, write_callback.GetCallback()));
      if (write_callback.result() != PP_OK)
        return "Write(): failed.";
      bytes_written = static_cast<size_t>(
          write_callback.output().bytes_written());

      recv_from_callback.WaitForResult(recv_from_result);
      if (recv_from_callback.result() != PP_OK)
        return "RecvFrom(): failed.";
      socket::RecvFromInfo_Dev recv_from_info = recv_from_callback.output();
      result_code = recv_from_info.result_code();
      data = recv_from_info.data();

      if (bytes_written != strlen(kMulticastMessage))
        return "Write(): did not send the whole data buffer.";

      if (result_code > 0 &&
          static_cast<uint32_t>(result_code) != data.ByteLength()) {
        return "RecvFrom(): inconsistent result code and byte length.";
      }

      std::string output_string = ConvertFromArrayBuffer(&data);
      if (output_string != kMulticastMessage) {
        return std::string("RecvFrom(): mismatched data: ").append(
            output_string);
      }
    }

    {
      TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
      callback.WaitForResult(socket_.LeaveGroup(
          server_socket_id, kMulticastAddress, callback.GetCallback()));
      if (callback.result() != PP_OK || callback.output() != 0)
        return "LeaveGroup(): failed.";
    }

    socket_.Destroy(server_socket_id);
    socket_.Destroy(client_socket_id);
    return std::string();
  }

  std::string TestClientSocket() {
    socket::SocketType_Dev socket_type;
    if (!socket_type.Populate(Var(test_type_).pp_var()))
      return "Invalid socket type.";

    int32_t socket_id = 0;
    {
      TestExtCompletionCallbackWithOutput<socket::CreateInfo_Dev>
          callback(pp_instance());
      callback.WaitForResult(socket_.Create(
          socket_type, Optional<socket::CreateOptions_Dev>(),
          callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "Create(): failed.";
      socket_id = callback.output().socket_id();
      if (socket_id <= 0)
        return "Create(): invalid socket ID.";
    }

    {
      TestExtCompletionCallbackWithOutput<socket::SocketInfo_Dev>
          callback(pp_instance());
      callback.WaitForResult(socket_.GetInfo(socket_id,
                                             callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "GetInfo(): failed.";

      socket::SocketInfo_Dev socket_info = callback.output();
      if (socket_info.socket_type().value != socket_type.value)
        return "GetInfo(): inconsistent socket type.";
      if (socket_info.connected())
        return "GetInfo(): socket should not be connected.";
      if (socket_info.peer_address().IsSet() || socket_info.peer_port().IsSet())
        return "GetInfo(): unconnected socket should not have peer.";
      if (socket_info.local_address().IsSet() ||
          socket_info.local_port().IsSet()) {
        return "GetInfo(): unconnected socket should not have local binding.";
      }
    }

    {
      if (socket_type.value == socket::SocketType_Dev::TCP) {
        TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
        callback.WaitForResult(socket_.Connect(
            socket_id, address_, port_, callback.GetCallback()));
        if (callback.result() != PP_OK)
          return "Connect(): failed.";
        if (callback.output() != 0)
          return "Connect(): failed.";
      } else {
        TestExtCompletionCallbackWithOutput<int32_t> callback(pp_instance());
        callback.WaitForResult(socket_.Bind(
            socket_id, "0.0.0.0", 0, callback.GetCallback()));
        if (callback.result() != PP_OK)
          return "Bind(): failed.";
        if (callback.output() != 0)
          return "Bind(): failed.";
      }
    }

    {
      TestExtCompletionCallbackWithOutput<socket::SocketInfo_Dev>
          callback(pp_instance());
      callback.WaitForResult(socket_.GetInfo(socket_id,
                                             callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "GetInfo(): failed.";

      socket::SocketInfo_Dev socket_info = callback.output();
      if (socket_info.socket_type().value != socket_type.value)
        return "GetInfo(): inconsistent socket type.";
      if (!socket_info.local_address().IsSet() ||
          !socket_info.local_port().IsSet()) {
        return "GetInfo(): bound socket should have local address and port.";
      }
      if (socket_type.value == socket::SocketType_Dev::TCP) {
        if (!socket_info.connected())
          return "GetInfo(): TCP socket should be connected.";
        if (!socket_info.peer_address().IsSet() ||
            !socket_info.peer_port().IsSet()) {
          return "GetInfo(): connected TCP socket should have peer address and "
                 "port";
        }
        if (*socket_info.peer_address() != "127.0.0.1" ||
            *socket_info.peer_port() != port_) {
          return "GetInfo(): peer address and port should match the listening "
                 "server.";
        }
      } else {
        if (socket_info.connected())
          return "GetInfo(): UDP socket should not be connected.";
        if (socket_info.peer_address().IsSet() ||
            socket_info.peer_port().IsSet()) {
          return "GetInfo(): unconnected UDP socket should not have peer "
                 "address or port.";
        }
      }
    }

    {
      TestExtCompletionCallbackWithOutput<bool> callback(pp_instance());
      callback.WaitForResult(socket_.SetNoDelay(
          socket_id, true, callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "SetNoDelay(): failed.";
      if (socket_type.value == socket::SocketType_Dev::TCP) {
        if (!callback.output())
          return "SetNoDelay(): failed for TCP.";
      } else {
        if (callback.output())
          return "SetNoDelay(): did not fail for UDP.";
      }
    }

    {
      TestExtCompletionCallbackWithOutput<bool> callback(pp_instance());
      callback.WaitForResult(socket_.SetKeepAlive(
          socket_id, true, 1000, callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "SetKeepAlive(): failed.";
      if (socket_type.value == socket::SocketType_Dev::TCP) {
        if (!callback.output())
          return "SetKeepAlive(): failed for TCP.";
      } else {
        if (callback.output())
          return "SetKeepAlive(): did not fail for UDP.";
      }
    }

    {
      VarArrayBuffer input_array_buffer = ConvertToArrayBuffer(kSendContents);
      size_t bytes_written = 0;
      int32_t result_code = 0;
      VarArrayBuffer data;
      if (socket_type.value == socket::SocketType_Dev::TCP) {
        TestExtCompletionCallbackWithOutput<socket::ReadInfo_Dev>
            read_callback(pp_instance());
        int32_t read_result = socket_.Read(socket_id, Optional<int32_t>(),
                                           read_callback.GetCallback());
        if (read_result != PP_OK_COMPLETIONPENDING)
          return "Read(): did not wait for data.";

        TestExtCompletionCallbackWithOutput<socket::WriteInfo_Dev>
            write_callback(pp_instance());
        write_callback.WaitForResult(socket_.Write(
            socket_id, input_array_buffer, write_callback.GetCallback()));
        if (write_callback.result() != PP_OK)
          return "Write(): failed.";
        bytes_written = static_cast<size_t>(
            write_callback.output().bytes_written());

        read_callback.WaitForResult(read_result);
        if (read_callback.result() != PP_OK)
          return "Read(): failed.";
        socket::ReadInfo_Dev read_info = read_callback.output();
        result_code = read_info.result_code(),
        data = read_info.data();
      } else {
        TestExtCompletionCallbackWithOutput<socket::RecvFromInfo_Dev>
            recv_from_callback(pp_instance());
        int32_t recv_from_result = socket_.RecvFrom(
            socket_id, Optional<int32_t>(), recv_from_callback.GetCallback());
        if (recv_from_result != PP_OK_COMPLETIONPENDING)
          return "RecvFrom(): did not wait for data.";

        TestExtCompletionCallbackWithOutput<socket::WriteInfo_Dev>
            send_to_callback(pp_instance());
        send_to_callback.WaitForResult(socket_.SendTo(
            socket_id, input_array_buffer, address_, port_,
            send_to_callback.GetCallback()));
        if (send_to_callback.result() != PP_OK)
          return "SendTo(): failed.";
        bytes_written = static_cast<size_t>(
            send_to_callback.output().bytes_written());

        recv_from_callback.WaitForResult(recv_from_result);
        if (recv_from_callback.result() != PP_OK)
          return "RecvFrom(): failed.";
        socket::RecvFromInfo_Dev recv_from_info = recv_from_callback.output();
        result_code = recv_from_info.result_code();
        data = recv_from_info.data();
      }

      if (bytes_written != strlen(kSendContents))
        return "SendTo() or Write(): did not send the whole data buffer.";

      if (result_code > 0 &&
          static_cast<uint32_t>(result_code) != data.ByteLength()) {
        return "Read() or RecvFrom(): inconsistent result code and byte "
               "length.";
      }

      std::string output_string = ConvertFromArrayBuffer(&data);
      size_t prefix_len = strlen(kReceiveContentsPrefix);
      if (output_string.size() != prefix_len + kReceiveContentsSuffixSize ||
          output_string.compare(0, prefix_len, kReceiveContentsPrefix) != 0) {
        return std::string("Read() or RecvFrom(): mismatched data: ").append(
            output_string);
      }
    }

    {
      TestExtCompletionCallbackWithOutput<
          std::vector<socket::NetworkInterface_Dev> > callback(pp_instance());
      callback.WaitForResult(socket_.GetNetworkList(callback.GetCallback()));
      if (callback.result() != PP_OK)
        return "GetNetworkList(): failed.";
      if (callback.output().empty())
        return "GetNetworkList(): returned an empty list.";
    }

    socket_.Destroy(socket_id);
    return std::string();
  }

  void Log(PP_LogLevel level, const char* format, ...) {
    va_list args;
    va_start(args, format);
    char buf[512];
    vsnprintf(buf, sizeof(buf) - 1, format, args);
    buf[sizeof(buf) - 1] = '\0';
    va_end(args);

    Var value(buf);
    console_interface_->Log(pp_instance(), level, value.pp_var());
  }

  void NotifyTestDone(const std::string& message) {
    PostMessage(message);
  }

  VarArrayBuffer ConvertToArrayBuffer(const std::string data) {
    VarArrayBuffer array_buffer(data.size());
    memcpy(array_buffer.Map(), data.c_str(), data.size());
    array_buffer.Unmap();
    return array_buffer;
  }

  std::string ConvertFromArrayBuffer(VarArrayBuffer* array_buffer) {
    std::string result(static_cast<const char*>(array_buffer->Map()),
                       array_buffer->ByteLength());
    array_buffer->Unmap();
    return result;
  }

  socket::Socket_Dev socket_;
  const PPB_Console* console_interface_;

  std::string test_type_;
  std::string address_;
  int32_t port_;
};

class MyModule : public Module {
 public:
  MyModule() : Module() {}
  virtual ~MyModule() {}

  virtual Instance* CreateInstance(PP_Instance instance) {
    return new MyInstance(instance);
  }
};

namespace pp {

Module* CreateModule() {
  return new MyModule();
}

}  // namespace pp


/* [<][>][^][v][top][bottom][index][help] */