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