Home | History | Annotate | Download | only in websocket
      1 // Copyright (c) 2012 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 <sstream>
      7 
      8 #include "ppapi/cpp/completion_callback.h"
      9 #include "ppapi/cpp/instance.h"
     10 #include "ppapi/cpp/module.h"
     11 #include "ppapi/cpp/var.h"
     12 #include "ppapi/cpp/var_array_buffer.h"
     13 #include "ppapi/cpp/websocket.h"
     14 
     15 class WebSocketInstance : public pp::Instance {
     16  public:
     17   explicit WebSocketInstance(PP_Instance instance)
     18       : pp::Instance(instance), websocket_(NULL) {}
     19   virtual ~WebSocketInstance() {}
     20   virtual void HandleMessage(const pp::Var& var_message);
     21 
     22  private:
     23   bool IsConnected();
     24 
     25   void Open(const std::string& url);
     26   void Close();
     27   void SendAsBinary(const std::string& message);
     28   void SendAsText(const std::string& message);
     29   void Receive();
     30 
     31   void OnConnectCompletion(int32_t result);
     32   void OnCloseCompletion(int32_t result);
     33   void OnReceiveCompletion(int32_t result);
     34 
     35   static void OnConnectCompletionCallback(void* user_data, int32_t result);
     36   static void OnCloseCompletionCallback(void* user_data, int32_t result);
     37   static void OnReceiveCompletionCallback(void* user_data, int32_t result);
     38 
     39   pp::WebSocket* websocket_;
     40   pp::Var receive_var_;
     41 };
     42 
     43 #define MAX_TO_CONVERT 8
     44 #define BYTES_PER_CHAR 4
     45 #define TAIL_AND_NUL_SIZE 4
     46 
     47 static std::string ArrayToString(pp::VarArrayBuffer& array) {
     48   char tmp[MAX_TO_CONVERT * BYTES_PER_CHAR + TAIL_AND_NUL_SIZE];
     49   uint32_t offs = 0;
     50   uint8_t* data = static_cast<uint8_t*>(array.Map());
     51 
     52   for (offs = 0; offs < array.ByteLength() && offs < MAX_TO_CONVERT; offs++)
     53     sprintf(&tmp[offs * BYTES_PER_CHAR], "%02Xh ", data[offs]);
     54 
     55   sprintf(&tmp[offs * BYTES_PER_CHAR], "...");
     56   array.Unmap();
     57   return std::string(tmp);
     58 }
     59 
     60 void WebSocketInstance::HandleMessage(const pp::Var& var_message) {
     61   if (!var_message.is_string())
     62     return;
     63   std::string message = var_message.AsString();
     64   // This message must contain a command character followed by ';' and
     65   // arguments like "X;arguments".
     66   if (message.length() < 2 || message[1] != ';')
     67     return;
     68   switch (message[0]) {
     69     case 'o':
     70       // The command 'o' requests to open the specified URL.
     71       // URL is passed as an argument like "o;URL".
     72       Open(message.substr(2));
     73       break;
     74     case 'c':
     75       // The command 'c' requests to close without any argument like "c;"
     76       Close();
     77       break;
     78     case 'b':
     79       // The command 'b' requests to send a message as a binary frame. The
     80       // message is passed as an argument like "b;message".
     81       SendAsBinary(message.substr(2));
     82       break;
     83     case 't':
     84       // The command 't' requests to send a message as a text frame. The message
     85       // is passed as an argument like "t;message".
     86       SendAsText(message.substr(2));
     87       break;
     88   }
     89 }
     90 
     91 bool WebSocketInstance::IsConnected() {
     92   if (!websocket_)
     93     return false;
     94   if (websocket_->GetReadyState() != PP_WEBSOCKETREADYSTATE_OPEN)
     95     return false;
     96   return true;
     97 }
     98 
     99 void WebSocketInstance::Open(const std::string& url) {
    100   pp::CompletionCallback callback(OnConnectCompletionCallback, this);
    101   websocket_ = new pp::WebSocket(this);
    102   if (!websocket_)
    103     return;
    104   websocket_->Connect(pp::Var(url), NULL, 0, callback);
    105   PostMessage(pp::Var("connecting..."));
    106 }
    107 
    108 void WebSocketInstance::Close() {
    109   if (!IsConnected())
    110     return;
    111   pp::CompletionCallback callback(OnCloseCompletionCallback, this);
    112   websocket_->Close(
    113       PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, pp::Var("bye"), callback);
    114 }
    115 
    116 void WebSocketInstance::SendAsBinary(const std::string& message) {
    117   if (!IsConnected())
    118     return;
    119   uint32_t size = message.size();
    120   pp::VarArrayBuffer array_buffer(size);
    121   char* data = static_cast<char*>(array_buffer.Map());
    122   for (uint32_t i = 0; i < size; ++i)
    123     data[i] = message[i];
    124   array_buffer.Unmap();
    125   websocket_->SendMessage(array_buffer);
    126   std::string message_text = ArrayToString(array_buffer);
    127   PostMessage(pp::Var("send (binary): " + message_text));
    128 }
    129 
    130 void WebSocketInstance::SendAsText(const std::string& message) {
    131   if (!IsConnected())
    132     return;
    133   websocket_->SendMessage(pp::Var(message));
    134   PostMessage(pp::Var("send (text): " + message));
    135 }
    136 
    137 void WebSocketInstance::Receive() {
    138   pp::CompletionCallback callback(OnReceiveCompletionCallback, this);
    139   // |receive_var_| must be valid until |callback| is invoked.
    140   // Just use a member variable.
    141   websocket_->ReceiveMessage(&receive_var_, callback);
    142 }
    143 
    144 void WebSocketInstance::OnConnectCompletion(int32_t result) {
    145   if (result != PP_OK) {
    146     PostMessage(pp::Var("connection failed"));
    147     return;
    148   }
    149   PostMessage(pp::Var("connected"));
    150   Receive();
    151 }
    152 
    153 void WebSocketInstance::OnCloseCompletion(int32_t result) {
    154   PostMessage(pp::Var(PP_OK == result ? "closed" : "abnormally closed"));
    155 }
    156 
    157 void WebSocketInstance::OnReceiveCompletion(int32_t result) {
    158   if (result == PP_OK) {
    159     if (receive_var_.is_array_buffer()) {
    160       pp::VarArrayBuffer array_buffer(receive_var_);
    161       std::string message_text = ArrayToString(array_buffer);
    162       PostMessage("receive (binary): " + message_text);
    163     }
    164     else {
    165       PostMessage("receive (text): " + receive_var_.AsString());
    166     }
    167   }
    168   Receive();
    169 }
    170 
    171 void WebSocketInstance::OnConnectCompletionCallback(void* user_data,
    172                                                     int32_t result) {
    173   WebSocketInstance* instance = static_cast<WebSocketInstance*>(user_data);
    174   instance->OnConnectCompletion(result);
    175 }
    176 
    177 void WebSocketInstance::OnCloseCompletionCallback(void* user_data,
    178                                                   int32_t result) {
    179   WebSocketInstance* instance = static_cast<WebSocketInstance*>(user_data);
    180   instance->OnCloseCompletion(result);
    181 }
    182 
    183 void WebSocketInstance::OnReceiveCompletionCallback(void* user_data,
    184                                                     int32_t result) {
    185   WebSocketInstance* instance = static_cast<WebSocketInstance*>(user_data);
    186   instance->OnReceiveCompletion(result);
    187 }
    188 
    189 // The WebSocketModule provides an implementation of pp::Module that creates
    190 // WebSocketInstance objects when invoked.
    191 class WebSocketModule : public pp::Module {
    192  public:
    193   WebSocketModule() : pp::Module() {}
    194   virtual ~WebSocketModule() {}
    195 
    196   virtual pp::Instance* CreateInstance(PP_Instance instance) {
    197     return new WebSocketInstance(instance);
    198   }
    199 };
    200 
    201 // Implement the required pp::CreateModule function that creates our specific
    202 // kind of Module.
    203 namespace pp {
    204 Module* CreateModule() { return new WebSocketModule(); }
    205 }  // namespace pp
    206