OLD | NEW |
(Empty) | |
| 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 <cstdarg> |
| 6 #include <cstdio> |
| 7 #include <cstdlib> |
| 8 #include <cstring> |
| 9 |
| 10 #include "ppapi/c/ppb_console.h" |
| 11 #include "ppapi/cpp/extensions/dev/socket_dev.h" |
| 12 #include "ppapi/cpp/instance.h" |
| 13 #include "ppapi/cpp/module.h" |
| 14 #include "ppapi/cpp/var.h" |
| 15 #include "ppapi/cpp/var_array_buffer.h" |
| 16 #include "ppapi/tests/test_utils.h" |
| 17 |
| 18 using namespace pp; |
| 19 using namespace pp::ext; |
| 20 |
| 21 namespace { |
| 22 |
| 23 const char* const kSendContents = "0100000005320000005hello"; |
| 24 const char* const kReceiveContentsPrefix = "0100000005320000005"; |
| 25 const size_t kReceiveContentsSuffixSize = 11; |
| 26 |
| 27 } // namespace |
| 28 |
| 29 class MyInstance : public Instance { |
| 30 public: |
| 31 explicit MyInstance(PP_Instance instance) |
| 32 : Instance(instance), |
| 33 socket_(InstanceHandle(instance)), |
| 34 console_interface_(NULL), |
| 35 socket_interface_(NULL), |
| 36 port_(0) { |
| 37 } |
| 38 virtual ~MyInstance() { |
| 39 } |
| 40 |
| 41 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { |
| 42 console_interface_ = static_cast<const PPB_Console*>( |
| 43 Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE)); |
| 44 |
| 45 // TODO(yzshen): Remove this one once the cpp wrapper supports all socket |
| 46 // functions. |
| 47 socket_interface_ = static_cast<const PPB_Ext_Socket_Dev*>( |
| 48 Module::Get()->GetBrowserInterface(PPB_EXT_SOCKET_DEV_INTERFACE)); |
| 49 if (!console_interface_ || !socket_interface_) |
| 50 return false; |
| 51 |
| 52 PostMessage(Var("ready")); |
| 53 return true; |
| 54 } |
| 55 |
| 56 virtual void HandleMessage(const pp::Var& message_data) { |
| 57 std::string output; |
| 58 do { |
| 59 if (!message_data.is_string()) { |
| 60 output = "Invalid control message."; |
| 61 break; |
| 62 } |
| 63 |
| 64 std::string control_message = message_data.AsString(); |
| 65 std::vector<std::string> parts; |
| 66 size_t pos = 0; |
| 67 size_t next_match = 0; |
| 68 while (pos < control_message.size()) { |
| 69 next_match = control_message.find(':', pos); |
| 70 if (next_match == std::string::npos) |
| 71 next_match = control_message.size(); |
| 72 parts.push_back(control_message.substr(pos, next_match - pos)); |
| 73 pos = next_match + 1; |
| 74 } |
| 75 |
| 76 if (parts.size() != 3) { |
| 77 output = "Invalid protocol/address/port input."; |
| 78 break; |
| 79 } |
| 80 |
| 81 test_type_ = parts[0]; |
| 82 address_ = parts[1]; |
| 83 port_ = atoi(parts[2].c_str()); |
| 84 Log(PP_LOGLEVEL_LOG, "Running tests, protocol %s, server %s:%d", |
| 85 test_type_.c_str(), address_.c_str(), port_); |
| 86 |
| 87 if (test_type_ == "tcp_server") { |
| 88 output = TestServerSocket(); |
| 89 } else { |
| 90 output = TestClientSocket(); |
| 91 } |
| 92 } while (false); |
| 93 |
| 94 NotifyTestDone(output); |
| 95 } |
| 96 |
| 97 private: |
| 98 std::string TestServerSocket() { |
| 99 int32_t socket_id = 0; |
| 100 { |
| 101 TestCompletionCallbackWithOutput<socket::CreateInfo_Dev> |
| 102 callback(pp_instance()); |
| 103 callback.WaitForResult(socket_.Create( |
| 104 socket::SocketType_Dev(socket::SocketType_Dev::TCP), |
| 105 Optional<socket::CreateOptions_Dev>(), callback.GetCallback())); |
| 106 if (callback.result() != PP_OK) |
| 107 return "Create(): failed."; |
| 108 socket_id = callback.output().socket_id(); |
| 109 if (socket_id <= 0) |
| 110 return "Create(): invalid socket ID."; |
| 111 } |
| 112 |
| 113 { |
| 114 TestCompletionCallback callback(pp_instance()); |
| 115 PP_Var output = PP_MakeUndefined(); |
| 116 callback.WaitForResult(socket_interface_->Listen( |
| 117 pp_instance(), Var(socket_id).pp_var(), Var(address_).pp_var(), |
| 118 Var(port_).pp_var(), PP_MakeUndefined(), &output, |
| 119 callback.GetCallback().pp_completion_callback())); |
| 120 if (callback.result() != PP_OK) |
| 121 return "Listen(): failed."; |
| 122 Var output_var(PASS_REF, output); |
| 123 if (output_var.AsInt() != 0) |
| 124 return "Listen(): failed."; |
| 125 } |
| 126 |
| 127 int32_t client_socket_id = 0; |
| 128 { |
| 129 TestCompletionCallbackWithOutput<socket::CreateInfo_Dev> |
| 130 callback(pp_instance()); |
| 131 callback.WaitForResult(socket_.Create( |
| 132 socket::SocketType_Dev(socket::SocketType_Dev::TCP), |
| 133 Optional<socket::CreateOptions_Dev>(), callback.GetCallback())); |
| 134 if (callback.result() != PP_OK) |
| 135 return "Create(): failed."; |
| 136 client_socket_id = callback.output().socket_id(); |
| 137 if (client_socket_id <= 0) |
| 138 return "Create(): invalid socket ID."; |
| 139 } |
| 140 |
| 141 { |
| 142 TestCompletionCallback callback(pp_instance()); |
| 143 PP_Var output = PP_MakeUndefined(); |
| 144 callback.WaitForResult(socket_interface_->Connect( |
| 145 pp_instance(), Var(client_socket_id).pp_var(), Var(address_).pp_var(), |
| 146 Var(port_).pp_var(), &output, |
| 147 callback.GetCallback().pp_completion_callback())); |
| 148 if (callback.result() != PP_OK) |
| 149 return "Connect(): failed."; |
| 150 Var output_var(PASS_REF, output); |
| 151 if (output_var.AsInt() != 0) |
| 152 return "Connect(): failed."; |
| 153 } |
| 154 |
| 155 int32_t accepted_socket_id = 0; |
| 156 { |
| 157 TestCompletionCallbackWithOutput<socket::AcceptInfo_Dev> |
| 158 callback(pp_instance()); |
| 159 callback.WaitForResult(socket_.Accept(socket_id, callback.GetCallback())); |
| 160 if (callback.result() != PP_OK) |
| 161 return "Accept(): failed."; |
| 162 socket::AcceptInfo_Dev accept_info = callback.output(); |
| 163 if (accept_info.result_code() != 0 || !accept_info.socket_id().IsSet()) |
| 164 return "Accept(): failed."; |
| 165 accepted_socket_id = *accept_info.socket_id(); |
| 166 } |
| 167 |
| 168 size_t bytes_written = 0; |
| 169 { |
| 170 TestCompletionCallbackWithOutput<socket::WriteInfo_Dev> |
| 171 callback(pp_instance()); |
| 172 VarArrayBuffer array_buffer = ConvertToArrayBuffer(kSendContents); |
| 173 callback.WaitForResult(socket_.Write(client_socket_id, array_buffer, |
| 174 callback.GetCallback())); |
| 175 if (callback.result() != PP_OK) |
| 176 return "Write(): failed."; |
| 177 socket::WriteInfo_Dev write_info = callback.output(); |
| 178 bytes_written = static_cast<size_t>(write_info.bytes_written()); |
| 179 if (bytes_written <= 0) |
| 180 return "Write(): did not write any bytes."; |
| 181 } |
| 182 |
| 183 { |
| 184 TestCompletionCallbackWithOutput<socket::ReadInfo_Dev> |
| 185 callback(pp_instance()); |
| 186 callback.WaitForResult(socket_.Read( |
| 187 accepted_socket_id, Optional<int32_t>(), callback.GetCallback())); |
| 188 if (callback.result() != PP_OK) |
| 189 return "Read(): failed."; |
| 190 |
| 191 std::string data_string = ConvertFromArrayBuffer( |
| 192 &callback.output().data()); |
| 193 if (data_string.compare(0, std::string::npos, kSendContents, |
| 194 bytes_written) != 0) { |
| 195 return "Read(): Received data does not match."; |
| 196 } |
| 197 } |
| 198 |
| 199 socket_.Destroy(client_socket_id); |
| 200 socket_.Destroy(accepted_socket_id); |
| 201 socket_.Destroy(socket_id); |
| 202 return std::string(); |
| 203 } |
| 204 |
| 205 std::string TestClientSocket() { |
| 206 socket::SocketType_Dev socket_type; |
| 207 if (!socket_type.Populate(Var(test_type_).pp_var())) |
| 208 return "Invalid socket type."; |
| 209 |
| 210 int32_t socket_id = 0; |
| 211 { |
| 212 TestCompletionCallbackWithOutput<socket::CreateInfo_Dev> |
| 213 callback(pp_instance()); |
| 214 callback.WaitForResult(socket_.Create( |
| 215 socket_type, Optional<socket::CreateOptions_Dev>(), |
| 216 callback.GetCallback())); |
| 217 if (callback.result() != PP_OK) |
| 218 return "Create(): failed."; |
| 219 socket_id = callback.output().socket_id(); |
| 220 if (socket_id <= 0) |
| 221 return "Create(): invalid socket ID."; |
| 222 } |
| 223 |
| 224 { |
| 225 TestCompletionCallbackWithOutput<socket::SocketInfo_Dev> |
| 226 callback(pp_instance()); |
| 227 callback.WaitForResult(socket_.GetInfo(socket_id, |
| 228 callback.GetCallback())); |
| 229 if (callback.result() != PP_OK) |
| 230 return "GetInfo(): failed."; |
| 231 |
| 232 socket::SocketInfo_Dev socket_info = callback.output(); |
| 233 if (socket_info.socket_type().value != socket_type.value) |
| 234 return "GetInfo(): inconsistent socket type."; |
| 235 if (socket_info.connected()) |
| 236 return "GetInfo(): socket should not be connected."; |
| 237 if (socket_info.peer_address().IsSet() || socket_info.peer_port().IsSet()) |
| 238 return "GetInfo(): unconnected socket should not have peer."; |
| 239 if (socket_info.local_address().IsSet() || |
| 240 socket_info.local_port().IsSet()) { |
| 241 return "GetInfo(): unconnected socket should not have local binding."; |
| 242 } |
| 243 } |
| 244 |
| 245 { |
| 246 if (socket_type.value == socket::SocketType_Dev::TCP) { |
| 247 TestCompletionCallback callback(pp_instance()); |
| 248 PP_Var output = PP_MakeUndefined(); |
| 249 callback.WaitForResult(socket_interface_->Connect( |
| 250 pp_instance(), Var(socket_id).pp_var(), Var(address_).pp_var(), |
| 251 Var(port_).pp_var(), &output, |
| 252 callback.GetCallback().pp_completion_callback())); |
| 253 if (callback.result() != PP_OK) |
| 254 return "Connect(): failed."; |
| 255 Var output_var(PASS_REF, output); |
| 256 if (output_var.AsInt() != 0) |
| 257 return "Connect(): failed."; |
| 258 } else { |
| 259 TestCompletionCallback callback(pp_instance()); |
| 260 PP_Var output = PP_MakeUndefined(); |
| 261 callback.WaitForResult(socket_interface_->Bind( |
| 262 pp_instance(), Var(socket_id).pp_var(), Var("0.0.0.0").pp_var(), |
| 263 Var(0).pp_var(), &output, |
| 264 callback.GetCallback().pp_completion_callback())); |
| 265 if (callback.result() != PP_OK) |
| 266 return "Bind(): failed."; |
| 267 Var output_var(PASS_REF, output); |
| 268 if (output_var.AsInt() != 0) |
| 269 return "Bind(): failed."; |
| 270 } |
| 271 } |
| 272 |
| 273 { |
| 274 TestCompletionCallbackWithOutput<socket::SocketInfo_Dev> |
| 275 callback(pp_instance()); |
| 276 callback.WaitForResult(socket_.GetInfo(socket_id, |
| 277 callback.GetCallback())); |
| 278 if (callback.result() != PP_OK) |
| 279 return "GetInfo(): failed."; |
| 280 |
| 281 socket::SocketInfo_Dev socket_info = callback.output(); |
| 282 if (socket_info.socket_type().value != socket_type.value) |
| 283 return "GetInfo(): inconsistent socket type."; |
| 284 if (!socket_info.local_address().IsSet() || |
| 285 !socket_info.local_port().IsSet()) { |
| 286 return "GetInfo(): bound socket should have local address and port."; |
| 287 } |
| 288 if (socket_type.value == socket::SocketType_Dev::TCP) { |
| 289 if (!socket_info.connected()) |
| 290 return "GetInfo(): TCP socket should be connected."; |
| 291 if (!socket_info.peer_address().IsSet() || |
| 292 !socket_info.peer_port().IsSet()) { |
| 293 return "GetInfo(): connected TCP socket should have peer address and " |
| 294 "port"; |
| 295 } |
| 296 if (*socket_info.peer_address() != "127.0.0.1" || |
| 297 *socket_info.peer_port() != port_) { |
| 298 return "GetInfo(): peer address and port should match the listening " |
| 299 "server."; |
| 300 } |
| 301 } else { |
| 302 if (socket_info.connected()) |
| 303 return "GetInfo(): UDP socket should not be connected."; |
| 304 if (socket_info.peer_address().IsSet() || |
| 305 socket_info.peer_port().IsSet()) { |
| 306 return "GetInfo(): unconnected UDP socket should not have peer " |
| 307 "address or port."; |
| 308 } |
| 309 } |
| 310 } |
| 311 |
| 312 { |
| 313 TestCompletionCallback callback(pp_instance()); |
| 314 PP_Var output = PP_MakeUndefined(); |
| 315 callback.WaitForResult(socket_interface_->SetNoDelay( |
| 316 pp_instance(), Var(socket_id).pp_var(), Var(true).pp_var(), |
| 317 &output, callback.GetCallback().pp_completion_callback())); |
| 318 if (callback.result() != PP_OK) |
| 319 return "SetNoDelay(): failed."; |
| 320 Var output_var(PASS_REF, output); |
| 321 if (socket_type.value == socket::SocketType_Dev::TCP) { |
| 322 if (!output_var.AsBool()) |
| 323 return "SetNoDelay(): failed for TCP."; |
| 324 } else { |
| 325 if (output_var.AsBool()) |
| 326 return "SetNoDelay(): did not fail for UDP."; |
| 327 } |
| 328 } |
| 329 |
| 330 { |
| 331 TestCompletionCallback callback(pp_instance()); |
| 332 PP_Var output = PP_MakeUndefined(); |
| 333 callback.WaitForResult(socket_interface_->SetKeepAlive( |
| 334 pp_instance(), Var(socket_id).pp_var(), Var(true).pp_var(), |
| 335 Var(1000).pp_var(), &output, |
| 336 callback.GetCallback().pp_completion_callback())); |
| 337 if (callback.result() != PP_OK) |
| 338 return "SetKeepAlive(): failed."; |
| 339 Var output_var(PASS_REF, output); |
| 340 if (socket_type.value == socket::SocketType_Dev::TCP) { |
| 341 if (!output_var.AsBool()) |
| 342 return "SetKeepAlive(): failed for TCP."; |
| 343 } else { |
| 344 if (output_var.AsBool()) |
| 345 return "SetKeepAlive(): did not fail for UDP."; |
| 346 } |
| 347 } |
| 348 |
| 349 { |
| 350 VarArrayBuffer input_array_buffer = ConvertToArrayBuffer(kSendContents); |
| 351 size_t bytes_written = 0; |
| 352 int32_t result_code = 0; |
| 353 VarArrayBuffer data; |
| 354 if (socket_type.value == socket::SocketType_Dev::TCP) { |
| 355 TestCompletionCallbackWithOutput<socket::ReadInfo_Dev> |
| 356 read_callback(pp_instance()); |
| 357 int32_t read_result = socket_.Read(socket_id, Optional<int32_t>(), |
| 358 read_callback.GetCallback()); |
| 359 if (read_result != PP_OK_COMPLETIONPENDING) |
| 360 return "Read(): did not wait for data."; |
| 361 |
| 362 TestCompletionCallbackWithOutput<socket::WriteInfo_Dev> |
| 363 write_callback(pp_instance()); |
| 364 write_callback.WaitForResult(socket_.Write( |
| 365 socket_id, input_array_buffer, write_callback.GetCallback())); |
| 366 if (write_callback.result() != PP_OK) |
| 367 return "Write(): failed."; |
| 368 bytes_written = static_cast<size_t>( |
| 369 write_callback.output().bytes_written()); |
| 370 |
| 371 read_callback.WaitForResult(read_result); |
| 372 if (read_callback.result() != PP_OK) |
| 373 return "Read(): failed."; |
| 374 socket::ReadInfo_Dev read_info = read_callback.output(); |
| 375 result_code = read_info.result_code(), |
| 376 data = read_info.data(); |
| 377 } else { |
| 378 TestCompletionCallbackWithOutput<socket::RecvFromInfo_Dev> |
| 379 recv_from_callback(pp_instance()); |
| 380 int32_t recv_from_result = socket_.RecvFrom( |
| 381 socket_id, Optional<int32_t>(), recv_from_callback.GetCallback()); |
| 382 if (recv_from_result != PP_OK_COMPLETIONPENDING) |
| 383 return "RecvFrom(): did not wait for data."; |
| 384 |
| 385 TestCompletionCallbackWithOutput<socket::WriteInfo_Dev> |
| 386 send_to_callback(pp_instance()); |
| 387 send_to_callback.WaitForResult(socket_.SendTo( |
| 388 socket_id, input_array_buffer, address_, port_, |
| 389 send_to_callback.GetCallback())); |
| 390 if (send_to_callback.result() != PP_OK) |
| 391 return "SendTo(): failed."; |
| 392 bytes_written = static_cast<size_t>( |
| 393 send_to_callback.output().bytes_written()); |
| 394 |
| 395 recv_from_callback.WaitForResult(recv_from_result); |
| 396 if (recv_from_callback.result() != PP_OK) |
| 397 return "RecvFrom(): failed."; |
| 398 socket::RecvFromInfo_Dev recv_from_info = recv_from_callback.output(); |
| 399 result_code = recv_from_info.result_code(); |
| 400 data = recv_from_info.data(); |
| 401 } |
| 402 |
| 403 if (bytes_written != strlen(kSendContents)) |
| 404 return "SendTo() or Write(): did not send the whole data buffer."; |
| 405 |
| 406 if (result_code > 0 && |
| 407 static_cast<uint32_t>(result_code) != data.ByteLength()) { |
| 408 return "Read() or RecvFrom(): inconsistent result code and byte " |
| 409 "length."; |
| 410 } |
| 411 |
| 412 std::string output_string = ConvertFromArrayBuffer(&data); |
| 413 size_t prefix_len = strlen(kReceiveContentsPrefix); |
| 414 if (output_string.size() != prefix_len + kReceiveContentsSuffixSize || |
| 415 output_string.compare(0, prefix_len, kReceiveContentsPrefix) != 0) { |
| 416 return std::string("Read() or RecvFrom(): mismatch data: ").append( |
| 417 output_string); |
| 418 } |
| 419 } |
| 420 |
| 421 socket_.Destroy(socket_id); |
| 422 return std::string(); |
| 423 } |
| 424 |
| 425 void Log(PP_LogLevel level, const char* format, ...) { |
| 426 va_list args; |
| 427 va_start(args, format); |
| 428 char buf[512]; |
| 429 vsnprintf(buf, sizeof(buf) - 1, format, args); |
| 430 buf[sizeof(buf) - 1] = '\0'; |
| 431 va_end(args); |
| 432 |
| 433 Var value(buf); |
| 434 console_interface_->Log(pp_instance(), level, value.pp_var()); |
| 435 } |
| 436 |
| 437 void NotifyTestDone(const std::string& message) { |
| 438 PostMessage(message); |
| 439 } |
| 440 |
| 441 VarArrayBuffer ConvertToArrayBuffer(const std::string data) { |
| 442 VarArrayBuffer array_buffer(data.size()); |
| 443 memcpy(array_buffer.Map(), data.c_str(), data.size()); |
| 444 array_buffer.Unmap(); |
| 445 return array_buffer; |
| 446 } |
| 447 |
| 448 std::string ConvertFromArrayBuffer(VarArrayBuffer* array_buffer) { |
| 449 std::string result(static_cast<const char*>(array_buffer->Map()), |
| 450 array_buffer->ByteLength()); |
| 451 array_buffer->Unmap(); |
| 452 return result; |
| 453 } |
| 454 |
| 455 socket::Socket_Dev socket_; |
| 456 const PPB_Console* console_interface_; |
| 457 const PPB_Ext_Socket_Dev* socket_interface_; |
| 458 |
| 459 std::string test_type_; |
| 460 std::string address_; |
| 461 int32_t port_; |
| 462 }; |
| 463 |
| 464 class MyModule : public Module { |
| 465 public: |
| 466 MyModule() : Module() {} |
| 467 virtual ~MyModule() {} |
| 468 |
| 469 virtual Instance* CreateInstance(PP_Instance instance) { |
| 470 return new MyInstance(instance); |
| 471 } |
| 472 }; |
| 473 |
| 474 namespace pp { |
| 475 |
| 476 Module* CreateModule() { |
| 477 return new MyModule(); |
| 478 } |
| 479 |
| 480 } // namespace pp |
| 481 |
OLD | NEW |