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 25class 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 71void 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 115bool 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 124bool ExampleInstance::IsUDP() { 125 return !udp_socket_.is_null(); 126} 127 128void 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 184void 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 208void 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 225void 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 261void 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 275void 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 294void ExampleInstance::OnReceiveFromCompletion(int32_t result, 295 pp::NetAddress source) { 296 OnReceiveCompletion(result); 297} 298 299void 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 311void 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. 324class 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. 336namespace pp { 337Module* CreateModule() { return new ExampleModule(); } 338} // namespace pp 339