| // 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 <stdio.h> |
| #include <sstream> |
| |
| #include "ppapi/cpp/completion_callback.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/cpp/websocket.h" |
| |
| class WebSocketInstance : public pp::Instance { |
| public: |
| explicit WebSocketInstance(PP_Instance instance) |
| : pp::Instance(instance), websocket_(NULL) {} |
| virtual ~WebSocketInstance() {} |
| virtual void HandleMessage(const pp::Var& var_message); |
| |
| private: |
| bool IsConnected(); |
| |
| void Open(const std::string& url); |
| void Close(); |
| void SendAsBinary(const std::string& message); |
| void SendAsText(const std::string& message); |
| void Receive(); |
| |
| void OnConnectCompletion(int32_t result); |
| void OnCloseCompletion(int32_t result); |
| void OnReceiveCompletion(int32_t result); |
| |
| static void OnConnectCompletionCallback(void* user_data, int32_t result); |
| static void OnCloseCompletionCallback(void* user_data, int32_t result); |
| static void OnReceiveCompletionCallback(void* user_data, int32_t result); |
| |
| pp::WebSocket* websocket_; |
| pp::Var receive_var_; |
| }; |
| |
| #define MAX_TO_CONVERT 8 |
| #define BYTES_PER_CHAR 4 |
| #define TAIL_AND_NUL_SIZE 4 |
| |
| static std::string ArrayToString(pp::VarArrayBuffer& array) { |
| char tmp[MAX_TO_CONVERT * BYTES_PER_CHAR + TAIL_AND_NUL_SIZE]; |
| uint32_t offs = 0; |
| uint8_t* data = static_cast<uint8_t*>(array.Map()); |
| |
| for (offs = 0; offs < array.ByteLength() && offs < MAX_TO_CONVERT; offs++) |
| sprintf(&tmp[offs * BYTES_PER_CHAR], "%02Xh ", data[offs]); |
| |
| sprintf(&tmp[offs * BYTES_PER_CHAR], "..."); |
| array.Unmap(); |
| return std::string(tmp); |
| } |
| |
| void WebSocketInstance::HandleMessage(const pp::Var& var_message) { |
| if (!var_message.is_string()) |
| return; |
| std::string message = var_message.AsString(); |
| // This message must contain a command character followed by ';' and |
| // arguments like "X;arguments". |
| if (message.length() < 2 || message[1] != ';') |
| return; |
| switch (message[0]) { |
| case 'o': |
| // The command 'o' requests to open the specified URL. |
| // URL is passed as an argument like "o;URL". |
| Open(message.substr(2)); |
| break; |
| case 'c': |
| // The command 'c' requests to close without any argument like "c;" |
| Close(); |
| break; |
| case 'b': |
| // The command 'b' requests to send a message as a binary frame. The |
| // message is passed as an argument like "b;message". |
| SendAsBinary(message.substr(2)); |
| break; |
| case 't': |
| // The command 't' requests to send a message as a text frame. The message |
| // is passed as an argument like "t;message". |
| SendAsText(message.substr(2)); |
| break; |
| } |
| } |
| |
| bool WebSocketInstance::IsConnected() { |
| if (!websocket_) |
| return false; |
| if (websocket_->GetReadyState() != PP_WEBSOCKETREADYSTATE_OPEN) |
| return false; |
| return true; |
| } |
| |
| void WebSocketInstance::Open(const std::string& url) { |
| pp::CompletionCallback callback(OnConnectCompletionCallback, this); |
| websocket_ = new pp::WebSocket(this); |
| if (!websocket_) |
| return; |
| websocket_->Connect(pp::Var(url), NULL, 0, callback); |
| PostMessage(pp::Var("connecting...")); |
| } |
| |
| void WebSocketInstance::Close() { |
| if (!IsConnected()) |
| return; |
| pp::CompletionCallback callback(OnCloseCompletionCallback, this); |
| websocket_->Close( |
| PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, pp::Var("bye"), callback); |
| } |
| |
| void WebSocketInstance::SendAsBinary(const std::string& message) { |
| if (!IsConnected()) |
| return; |
| uint32_t size = message.size(); |
| pp::VarArrayBuffer array_buffer(size); |
| char* data = static_cast<char*>(array_buffer.Map()); |
| for (uint32_t i = 0; i < size; ++i) |
| data[i] = message[i]; |
| array_buffer.Unmap(); |
| websocket_->SendMessage(array_buffer); |
| std::string message_text = ArrayToString(array_buffer); |
| PostMessage(pp::Var("send (binary): " + message_text)); |
| } |
| |
| void WebSocketInstance::SendAsText(const std::string& message) { |
| if (!IsConnected()) |
| return; |
| websocket_->SendMessage(pp::Var(message)); |
| PostMessage(pp::Var("send (text): " + message)); |
| } |
| |
| void WebSocketInstance::Receive() { |
| pp::CompletionCallback callback(OnReceiveCompletionCallback, this); |
| // |receive_var_| must be valid until |callback| is invoked. |
| // Just use a member variable. |
| websocket_->ReceiveMessage(&receive_var_, callback); |
| } |
| |
| void WebSocketInstance::OnConnectCompletion(int32_t result) { |
| if (result != PP_OK) { |
| PostMessage(pp::Var("connection failed")); |
| return; |
| } |
| PostMessage(pp::Var("connected")); |
| Receive(); |
| } |
| |
| void WebSocketInstance::OnCloseCompletion(int32_t result) { |
| PostMessage(pp::Var(PP_OK == result ? "closed" : "abnormally closed")); |
| } |
| |
| void WebSocketInstance::OnReceiveCompletion(int32_t result) { |
| if (result == PP_OK) { |
| if (receive_var_.is_array_buffer()) { |
| pp::VarArrayBuffer array_buffer(receive_var_); |
| std::string message_text = ArrayToString(array_buffer); |
| PostMessage("receive (binary): " + message_text); |
| } |
| else { |
| PostMessage("receive (text): " + receive_var_.AsString()); |
| } |
| } |
| Receive(); |
| } |
| |
| void WebSocketInstance::OnConnectCompletionCallback(void* user_data, |
| int32_t result) { |
| WebSocketInstance* instance = static_cast<WebSocketInstance*>(user_data); |
| instance->OnConnectCompletion(result); |
| } |
| |
| void WebSocketInstance::OnCloseCompletionCallback(void* user_data, |
| int32_t result) { |
| WebSocketInstance* instance = static_cast<WebSocketInstance*>(user_data); |
| instance->OnCloseCompletion(result); |
| } |
| |
| void WebSocketInstance::OnReceiveCompletionCallback(void* user_data, |
| int32_t result) { |
| WebSocketInstance* instance = static_cast<WebSocketInstance*>(user_data); |
| instance->OnReceiveCompletion(result); |
| } |
| |
| // The WebSocketModule provides an implementation of pp::Module that creates |
| // WebSocketInstance objects when invoked. |
| class WebSocketModule : public pp::Module { |
| public: |
| WebSocketModule() : pp::Module() {} |
| virtual ~WebSocketModule() {} |
| |
| virtual pp::Instance* CreateInstance(PP_Instance instance) { |
| return new WebSocketInstance(instance); |
| } |
| }; |
| |
| // Implement the required pp::CreateModule function that creates our specific |
| // kind of Module. |
| namespace pp { |
| Module* CreateModule() { return new WebSocketModule(); } |
| } // namespace pp |