Home | History | Annotate | Download | only in socket
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <sstream>
      8 
      9 #include "echo_server.h"
     10 
     11 #include "ppapi/cpp/host_resolver.h"
     12 #include "ppapi/cpp/instance.h"
     13 #include "ppapi/cpp/module.h"
     14 #include "ppapi/cpp/tcp_socket.h"
     15 #include "ppapi/cpp/udp_socket.h"
     16 #include "ppapi/cpp/var.h"
     17 #include "ppapi/utility/completion_callback_factory.h"
     18 
     19 #ifdef WIN32
     20 #undef PostMessage
     21 // Allow 'this' in initializer list
     22 #pragma warning(disable : 4355)
     23 #endif
     24 
     25 class ExampleInstance : public pp::Instance {
     26  public:
     27   explicit ExampleInstance(PP_Instance instance)
     28     : pp::Instance(instance),
     29       callback_factory_(this),
     30       send_outstanding_(false),
     31       echo_server_(NULL) {}
     32 
     33   virtual ~ExampleInstance() {
     34     delete echo_server_;
     35   }
     36 
     37   virtual void HandleMessage(const pp::Var& var_message);
     38 
     39  private:
     40   bool IsConnected();
     41   bool IsUDP();
     42 
     43   void Connect(const std::string& host, bool tcp);
     44   void Close();
     45   void Send(const std::string& message);
     46   void Receive();
     47 
     48   void OnConnectCompletion(int32_t result);
     49   void OnResolveCompletion(int32_t result);
     50   void OnReceiveCompletion(int32_t result);
     51   void OnReceiveFromCompletion(int32_t result, pp::NetAddress source);
     52   void OnSendCompletion(int32_t result);
     53 
     54   pp::CompletionCallbackFactory<ExampleInstance> callback_factory_;
     55   pp::TCPSocket tcp_socket_;
     56   pp::UDPSocket udp_socket_;
     57   pp::HostResolver resolver_;
     58   pp::NetAddress remote_host_;
     59 
     60   char receive_buffer_[kBufferSize];
     61   bool send_outstanding_;
     62   EchoServer* echo_server_;
     63 };
     64 
     65 #define MSG_CREATE_TCP 't'
     66 #define MSG_CREATE_UDP 'u'
     67 #define MSG_SEND 's'
     68 #define MSG_CLOSE 'c'
     69 #define MSG_LISTEN 'l'
     70 
     71 void ExampleInstance::HandleMessage(const pp::Var& var_message) {
     72   if (!var_message.is_string())
     73     return;
     74   std::string message = var_message.AsString();
     75   // This message must contain a command character followed by ';' and
     76   // arguments like "X;arguments".
     77   if (message.length() < 2 || message[1] != ';')
     78     return;
     79   switch (message[0]) {
     80     case MSG_CREATE_UDP:
     81       // The command 'b' requests to create a UDP connection the
     82       // specified HOST.
     83       // HOST is passed as an argument like "t;HOST".
     84       Connect(message.substr(2), false);
     85       break;
     86     case MSG_CREATE_TCP:
     87       // The command 'o' requests to connect to the specified HOST.
     88       // HOST is passed as an argument like "u;HOST".
     89       Connect(message.substr(2), true);
     90       break;
     91     case MSG_CLOSE:
     92       // The command 'c' requests to close without any argument like "c;"
     93       Close();
     94       break;
     95     case MSG_LISTEN:
     96       {
     97         // The command 'l' starts a listening socket (server).
     98         int port = atoi(message.substr(2).c_str());
     99         echo_server_ = new EchoServer(this, port);
    100         break;
    101       }
    102     case MSG_SEND:
    103       // The command 't' requests to send a message as a text frame. The
    104       // message passed as an argument like "t;message".
    105       Send(message.substr(2));
    106       break;
    107     default:
    108       std::ostringstream status;
    109       status << "Unhandled message from JavaScript: " << message;
    110       PostMessage(status.str());
    111       break;
    112   }
    113 }
    114 
    115 bool ExampleInstance::IsConnected() {
    116   if (!tcp_socket_.is_null())
    117     return true;
    118   if (!udp_socket_.is_null())
    119     return true;
    120 
    121   return false;
    122 }
    123 
    124 bool ExampleInstance::IsUDP() {
    125   return !udp_socket_.is_null();
    126 }
    127 
    128 void ExampleInstance::Connect(const std::string& host, bool tcp) {
    129   if (IsConnected()) {
    130     PostMessage("Already connected.");
    131     return;
    132   }
    133 
    134   if (tcp) {
    135     if (!pp::TCPSocket::IsAvailable()) {
    136       PostMessage("TCPSocket not available");
    137       return;
    138     }
    139 
    140     tcp_socket_ = pp::TCPSocket(this);
    141     if (tcp_socket_.is_null()) {
    142       PostMessage("Error creating TCPSocket.");
    143       return;
    144     }
    145   } else {
    146     if (!pp::UDPSocket::IsAvailable()) {
    147       PostMessage("UDPSocket not available");
    148       return;
    149     }
    150 
    151     udp_socket_ = pp::UDPSocket(this);
    152     if (udp_socket_.is_null()) {
    153       PostMessage("Error creating UDPSocket.");
    154       return;
    155     }
    156   }
    157 
    158   if (!pp::HostResolver::IsAvailable()) {
    159     PostMessage("HostResolver not available");
    160     return;
    161   }
    162 
    163   resolver_ = pp::HostResolver(this);
    164   if (resolver_.is_null()) {
    165     PostMessage("Error creating HostResolver.");
    166     return;
    167   }
    168 
    169   int port = 80;
    170   std::string hostname = host;
    171   size_t pos = host.rfind(':');
    172   if (pos != std::string::npos) {
    173     hostname = host.substr(0, pos);
    174     port = atoi(host.substr(pos+1).c_str());
    175   }
    176 
    177   pp::CompletionCallback callback =
    178       callback_factory_.NewCallback(&ExampleInstance::OnResolveCompletion);
    179   PP_HostResolver_Hint hint = { PP_NETADDRESS_FAMILY_UNSPECIFIED, 0 };
    180   resolver_.Resolve(hostname.c_str(), port, hint, callback);
    181   PostMessage("Resolving ...");
    182 }
    183 
    184 void ExampleInstance::OnResolveCompletion(int32_t result) {
    185   if (result != PP_OK) {
    186     PostMessage("Resolve failed.");
    187     return;
    188   }
    189 
    190   pp::NetAddress addr = resolver_.GetNetAddress(0);
    191   PostMessage(std::string("Resolved: ") +
    192               addr.DescribeAsString(true).AsString());
    193 
    194   pp::CompletionCallback callback =
    195       callback_factory_.NewCallback(&ExampleInstance::OnConnectCompletion);
    196 
    197   if (IsUDP()) {
    198     PostMessage("Binding ...");
    199     remote_host_ = addr;
    200     PP_NetAddress_IPv4 ipv4_addr = { 0, { 0 } };
    201     udp_socket_.Bind(pp::NetAddress(this, ipv4_addr), callback);
    202   } else {
    203     PostMessage("Connecting ...");
    204     tcp_socket_.Connect(addr, callback);
    205   }
    206 }
    207 
    208 void ExampleInstance::Close() {
    209   if (!IsConnected()) {
    210     PostMessage("Not connected.");
    211     return;
    212   }
    213 
    214   if (tcp_socket_.is_null()) {
    215     udp_socket_.Close();
    216     udp_socket_ = pp::UDPSocket();
    217   } else {
    218     tcp_socket_.Close();
    219     tcp_socket_ = pp::TCPSocket();
    220   }
    221 
    222   PostMessage("Closed connection.");
    223 }
    224 
    225 void ExampleInstance::Send(const std::string& message) {
    226   if (!IsConnected()) {
    227     PostMessage("Not connected.");
    228     return;
    229   }
    230 
    231   if (send_outstanding_) {
    232     PostMessage("Already sending.");
    233     return;
    234   }
    235 
    236   uint32_t size = message.size();
    237   const char* data = message.c_str();
    238   pp::CompletionCallback callback =
    239       callback_factory_.NewCallback(&ExampleInstance::OnSendCompletion);
    240   int32_t result;
    241   if (IsUDP())
    242      result = udp_socket_.SendTo(data, size, remote_host_, callback);
    243   else
    244      result = tcp_socket_.Write(data, size, callback);
    245   std::ostringstream status;
    246   if (result < 0) {
    247     if (result == PP_OK_COMPLETIONPENDING) {
    248       status << "Sending bytes: " << size;
    249       PostMessage(status.str());
    250       send_outstanding_ = true;
    251     } else {
    252       status << "Send returned error: " << result;
    253       PostMessage(status.str());
    254     }
    255   } else {
    256     status << "Sent bytes synchronously: " << result;
    257     PostMessage(status.str());
    258   }
    259 }
    260 
    261 void ExampleInstance::Receive() {
    262   memset(receive_buffer_, 0, kBufferSize);
    263   if (IsUDP()) {
    264     pp::CompletionCallbackWithOutput<pp::NetAddress> callback =
    265         callback_factory_.NewCallbackWithOutput(
    266             &ExampleInstance::OnReceiveFromCompletion);
    267     udp_socket_.RecvFrom(receive_buffer_, kBufferSize, callback);
    268   } else {
    269     pp::CompletionCallback callback =
    270         callback_factory_.NewCallback(&ExampleInstance::OnReceiveCompletion);
    271     tcp_socket_.Read(receive_buffer_, kBufferSize, callback);
    272   }
    273 }
    274 
    275 void ExampleInstance::OnConnectCompletion(int32_t result) {
    276   if (result != PP_OK) {
    277     std::ostringstream status;
    278     status << "Connection failed: " << result;
    279     PostMessage(status.str());
    280     return;
    281   }
    282 
    283   if (IsUDP()) {
    284     pp::NetAddress addr = udp_socket_.GetBoundAddress();
    285     PostMessage(std::string("Bound to: ") +
    286                 addr.DescribeAsString(true).AsString());
    287   } else {
    288     PostMessage("Connected");
    289   }
    290 
    291   Receive();
    292 }
    293 
    294 void ExampleInstance::OnReceiveFromCompletion(int32_t result,
    295                                               pp::NetAddress source) {
    296   OnReceiveCompletion(result);
    297 }
    298 
    299 void ExampleInstance::OnReceiveCompletion(int32_t result) {
    300   if (result < 0) {
    301     std::ostringstream status;
    302     status << "Receive failed with: " << result;
    303     PostMessage(status.str());
    304     return;
    305   }
    306 
    307   PostMessage(std::string("Received: ") + std::string(receive_buffer_, result));
    308   Receive();
    309 }
    310 
    311 void ExampleInstance::OnSendCompletion(int32_t result) {
    312   std::ostringstream status;
    313   if (result < 0) {
    314     status << "Send failed with: " << result;
    315   } else {
    316     status << "Sent bytes: " << result;
    317   }
    318   send_outstanding_ = false;
    319   PostMessage(status.str());
    320 }
    321 
    322 // The ExampleModule provides an implementation of pp::Module that creates
    323 // ExampleInstance objects when invoked.
    324 class ExampleModule : public pp::Module {
    325  public:
    326   ExampleModule() : pp::Module() {}
    327   virtual ~ExampleModule() {}
    328 
    329   virtual pp::Instance* CreateInstance(PP_Instance instance) {
    330     return new ExampleInstance(instance);
    331   }
    332 };
    333 
    334 // Implement the required pp::CreateModule function that creates our specific
    335 // kind of Module.
    336 namespace pp {
    337 Module* CreateModule() { return new ExampleModule(); }
    338 }  // namespace pp
    339