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 int32_t result = socket_.Create( |
| 104 socket::SocketType_Dev(socket::SocketType_Dev::TCP), |
| 105 Optional<socket::CreateOptions_Dev>(), callback.GetCallback()); |
| 106 if (result == PP_OK_COMPLETIONPENDING) |
| 107 result = callback.WaitForResult(); |
| 108 if (result != PP_OK) |
| 109 return "Create(): failed."; |
| 110 socket_id = callback.output().socket_id(); |
| 111 if (socket_id <= 0) |
| 112 return "Create(): invalid socket ID."; |
| 113 } |
| 114 |
| 115 { |
| 116 TestCompletionCallback callback(pp_instance()); |
| 117 PP_Var output = PP_MakeUndefined(); |
| 118 int32_t result = socket_interface_->Listen( |
| 119 pp_instance(), Var(socket_id).pp_var(), Var(address_).pp_var(), |
| 120 Var(port_).pp_var(), PP_MakeUndefined(), &output, |
| 121 callback.GetCallback().pp_completion_callback()); |
| 122 if (result == PP_OK_COMPLETIONPENDING) |
| 123 result = callback.WaitForResult(); |
| 124 if (result != PP_OK) |
| 125 return "Listen(): failed."; |
| 126 Var output_var(PASS_REF, output); |
| 127 if (output_var.AsInt() != 0) |
| 128 return "Listen(): failed."; |
| 129 } |
| 130 |
| 131 int32_t client_socket_id = 0; |
| 132 { |
| 133 TestCompletionCallbackWithOutput<socket::CreateInfo_Dev> |
| 134 callback(pp_instance()); |
| 135 int32_t result = socket_.Create( |
| 136 socket::SocketType_Dev(socket::SocketType_Dev::TCP), |
| 137 Optional<socket::CreateOptions_Dev>(), callback.GetCallback()); |
| 138 if (result == PP_OK_COMPLETIONPENDING) |
| 139 result = callback.WaitForResult(); |
| 140 if (result != PP_OK) |
| 141 return "Create(): failed."; |
| 142 client_socket_id = callback.output().socket_id(); |
| 143 if (client_socket_id <= 0) |
| 144 return "Create(): invalid socket ID."; |
| 145 } |
| 146 |
| 147 { |
| 148 TestCompletionCallback callback(pp_instance()); |
| 149 PP_Var output = PP_MakeUndefined(); |
| 150 int32_t result = socket_interface_->Connect( |
| 151 pp_instance(), Var(client_socket_id).pp_var(), Var(address_).pp_var(), |
| 152 Var(port_).pp_var(), &output, |
| 153 callback.GetCallback().pp_completion_callback()); |
| 154 if (result == PP_OK_COMPLETIONPENDING) |
| 155 result = callback.WaitForResult(); |
| 156 if (result != PP_OK) |
| 157 return "Connect(): failed."; |
| 158 Var output_var(PASS_REF, output); |
| 159 if (output_var.AsInt() != 0) |
| 160 return "Connect(): failed."; |
| 161 } |
| 162 |
| 163 int32_t accepted_socket_id = 0; |
| 164 { |
| 165 TestCompletionCallbackWithOutput<socket::AcceptInfo_Dev> |
| 166 callback(pp_instance()); |
| 167 int32_t result = socket_.Accept(socket_id, callback.GetCallback()); |
| 168 if (result == PP_OK_COMPLETIONPENDING) |
| 169 result = callback.WaitForResult(); |
| 170 socket::AcceptInfo_Dev accept_info = callback.output(); |
| 171 if (accept_info.result_code() != 0 || !accept_info.socket_id().IsSet()) |
| 172 return "Accept(): failed."; |
| 173 accepted_socket_id = *accept_info.socket_id(); |
| 174 } |
| 175 |
| 176 size_t bytes_written = 0; |
| 177 { |
| 178 TestCompletionCallbackWithOutput<socket::WriteInfo_Dev> |
| 179 callback(pp_instance()); |
| 180 VarArrayBuffer array_buffer = ConvertToArrayBuffer(kSendContents); |
| 181 int32_t result = socket_.Write(client_socket_id, array_buffer, |
| 182 callback.GetCallback()); |
| 183 if (result == PP_OK_COMPLETIONPENDING) |
| 184 result = callback.WaitForResult(); |
| 185 if (result != PP_OK) |
| 186 return "Write(): failed."; |
| 187 socket::WriteInfo_Dev write_info = callback.output(); |
| 188 bytes_written = static_cast<size_t>(write_info.bytes_written()); |
| 189 if (bytes_written <= 0) |
| 190 return "Write(): did not write any bytes."; |
| 191 } |
| 192 |
| 193 { |
| 194 TestCompletionCallbackWithOutput<socket::ReadInfo_Dev> |
| 195 callback(pp_instance()); |
| 196 int32_t result = socket_.Read(accepted_socket_id, Optional<int32_t>(), |
| 197 callback.GetCallback()); |
| 198 if (result == PP_OK_COMPLETIONPENDING) |
| 199 result = callback.WaitForResult(); |
| 200 if (result != PP_OK) |
| 201 return "Read(): failed."; |
| 202 |
| 203 VarArrayBuffer array_buffer(callback.output().data()); |
| 204 std::string data_string = ConvertFromArrayBuffer(&array_buffer); |
| 205 if (data_string.compare(0, std::string::npos, kSendContents, |
| 206 bytes_written) != 0) { |
| 207 return "Read(): Received data does not match."; |
| 208 } |
| 209 } |
| 210 |
| 211 socket_.Destroy(client_socket_id); |
| 212 socket_.Destroy(accepted_socket_id); |
| 213 socket_.Destroy(socket_id); |
| 214 return std::string(); |
| 215 } |
| 216 |
| 217 std::string TestClientSocket() { |
| 218 socket::SocketType_Dev socket_type; |
| 219 if (!socket_type.Populate(Var(test_type_).pp_var())) |
| 220 return "Invalid socket type."; |
| 221 |
| 222 int32_t socket_id = 0; |
| 223 { |
| 224 TestCompletionCallbackWithOutput<socket::CreateInfo_Dev> |
| 225 callback(pp_instance()); |
| 226 int32_t result = socket_.Create(socket_type, |
| 227 Optional<socket::CreateOptions_Dev>(), |
| 228 callback.GetCallback()); |
| 229 if (result == PP_OK_COMPLETIONPENDING) |
| 230 result = callback.WaitForResult(); |
| 231 if (result != PP_OK) |
| 232 return "Create(): failed."; |
| 233 socket_id = callback.output().socket_id(); |
| 234 if (socket_id <= 0) |
| 235 return "Create(): invalid socket ID."; |
| 236 } |
| 237 |
| 238 { |
| 239 TestCompletionCallbackWithOutput<socket::SocketInfo_Dev> |
| 240 callback(pp_instance()); |
| 241 int32_t result = socket_.GetInfo(socket_id, callback.GetCallback()); |
| 242 if (result == PP_OK_COMPLETIONPENDING) |
| 243 result = callback.WaitForResult(); |
| 244 if (result != PP_OK) |
| 245 return "GetInfo(): failed."; |
| 246 |
| 247 socket::SocketInfo_Dev socket_info = callback.output(); |
| 248 if (socket_info.socket_type().value != socket_type.value) |
| 249 return "GetInfo(): inconsistent socket type."; |
| 250 if (socket_info.connected()) |
| 251 return "GetInfo(): socket should not be connected."; |
| 252 if (socket_info.peer_address().IsSet() || socket_info.peer_port().IsSet()) |
| 253 return "GetInfo(): unconnected socket should not have peer."; |
| 254 if (socket_info.local_address().IsSet() || |
| 255 socket_info.local_port().IsSet()) { |
| 256 return "GetInfo(): unconnected socket should not have local binding."; |
| 257 } |
| 258 } |
| 259 |
| 260 { |
| 261 if (socket_type.value == socket::SocketType_Dev::TCP) { |
| 262 TestCompletionCallback callback(pp_instance()); |
| 263 PP_Var output = PP_MakeUndefined(); |
| 264 int32_t result = socket_interface_->Connect( |
| 265 pp_instance(), Var(socket_id).pp_var(), Var(address_).pp_var(), |
| 266 Var(port_).pp_var(), &output, |
| 267 callback.GetCallback().pp_completion_callback()); |
| 268 if (result == PP_OK_COMPLETIONPENDING) |
| 269 result = callback.WaitForResult(); |
| 270 if (result != PP_OK) |
| 271 return "Connect(): failed."; |
| 272 Var output_var(PASS_REF, output); |
| 273 if (output_var.AsInt() != 0) |
| 274 return "Connect(): failed."; |
| 275 } else { |
| 276 TestCompletionCallback callback(pp_instance()); |
| 277 PP_Var output = PP_MakeUndefined(); |
| 278 int32_t result = socket_interface_->Bind( |
| 279 pp_instance(), Var(socket_id).pp_var(), Var("0.0.0.0").pp_var(), |
| 280 Var(0).pp_var(), &output, |
| 281 callback.GetCallback().pp_completion_callback()); |
| 282 if (result == PP_OK_COMPLETIONPENDING) |
| 283 result = callback.WaitForResult(); |
| 284 if (result != PP_OK) |
| 285 return "Bind(): failed."; |
| 286 Var output_var(PASS_REF, output); |
| 287 if (output_var.AsInt() != 0) |
| 288 return "Bind(): failed."; |
| 289 } |
| 290 } |
| 291 |
| 292 { |
| 293 TestCompletionCallbackWithOutput<socket::SocketInfo_Dev> |
| 294 callback(pp_instance()); |
| 295 int32_t result = socket_.GetInfo(socket_id, callback.GetCallback()); |
| 296 if (result == PP_OK_COMPLETIONPENDING) |
| 297 result = callback.WaitForResult(); |
| 298 if (result != PP_OK) |
| 299 return "GetInfo(): failed."; |
| 300 |
| 301 socket::SocketInfo_Dev socket_info = callback.output(); |
| 302 if (socket_info.socket_type().value != socket_type.value) |
| 303 return "GetInfo(): inconsistent socket type."; |
| 304 if (!socket_info.local_address().IsSet() || |
| 305 !socket_info.local_port().IsSet()) { |
| 306 return "GetInfo(): bound socket should have local address and port."; |
| 307 } |
| 308 if (socket_type.value == socket::SocketType_Dev::TCP) { |
| 309 if (!socket_info.connected()) |
| 310 return "GetInfo(): TCP socket should be connected."; |
| 311 if (!socket_info.peer_address().IsSet() || |
| 312 !socket_info.peer_port().IsSet()) { |
| 313 return "GetInfo(): connected TCP socket should have peer address and " |
| 314 "port"; |
| 315 } |
| 316 if (*socket_info.peer_address() != "127.0.0.1" || |
| 317 *socket_info.peer_port() != port_) { |
| 318 return "GetInfo(): peer address and port should match the listening " |
| 319 "server."; |
| 320 } |
| 321 } else { |
| 322 if (socket_info.connected()) |
| 323 return "GetInfo(): UDP socket should not be connected."; |
| 324 if (socket_info.peer_address().IsSet() || |
| 325 socket_info.peer_port().IsSet()) { |
| 326 return "GetInfo(): unconnected UDP socket should not have peer " |
| 327 "address or port."; |
| 328 } |
| 329 } |
| 330 } |
| 331 |
| 332 { |
| 333 TestCompletionCallback callback(pp_instance()); |
| 334 PP_Var output = PP_MakeUndefined(); |
| 335 int32_t result = socket_interface_->SetNoDelay( |
| 336 pp_instance(), Var(socket_id).pp_var(), Var(true).pp_var(), |
| 337 &output, callback.GetCallback().pp_completion_callback()); |
| 338 if (result == PP_OK_COMPLETIONPENDING) |
| 339 result = callback.WaitForResult(); |
| 340 if (result != PP_OK) |
| 341 return "SetNoDelay(): failed."; |
| 342 Var output_var(PASS_REF, output); |
| 343 if (socket_type.value == socket::SocketType_Dev::TCP) { |
| 344 if (!output_var.AsBool()) |
| 345 return "SetNoDelay(): failed for TCP."; |
| 346 } else { |
| 347 if (output_var.AsBool()) |
| 348 return "SetNoDelay(): did not fail for UDP."; |
| 349 } |
| 350 } |
| 351 |
| 352 { |
| 353 TestCompletionCallback callback(pp_instance()); |
| 354 PP_Var output = PP_MakeUndefined(); |
| 355 int32_t result = socket_interface_->SetKeepAlive( |
| 356 pp_instance(), Var(socket_id).pp_var(), Var(true).pp_var(), |
| 357 Var(1000).pp_var(), &output, |
| 358 callback.GetCallback().pp_completion_callback()); |
| 359 if (result == PP_OK_COMPLETIONPENDING) |
| 360 result = callback.WaitForResult(); |
| 361 if (result != PP_OK) |
| 362 return "SetKeepAlive(): failed."; |
| 363 Var output_var(PASS_REF, output); |
| 364 if (socket_type.value == socket::SocketType_Dev::TCP) { |
| 365 if (!output_var.AsBool()) |
| 366 return "SetKeepAlive(): failed for TCP."; |
| 367 } else { |
| 368 if (output_var.AsBool()) |
| 369 return "SetKeepAlive(): did not fail for UDP."; |
| 370 } |
| 371 } |
| 372 |
| 373 { |
| 374 VarArrayBuffer input_array_buffer = ConvertToArrayBuffer(kSendContents); |
| 375 size_t bytes_written = 0; |
| 376 int32_t result_code = 0; |
| 377 Var data; |
| 378 if (socket_type.value == socket::SocketType_Dev::TCP) { |
| 379 TestCompletionCallbackWithOutput<socket::ReadInfo_Dev> |
| 380 read_callback(pp_instance()); |
| 381 int32_t result = socket_.Read(socket_id, Optional<int32_t>(), |
| 382 read_callback.GetCallback()); |
| 383 if (result != PP_OK_COMPLETIONPENDING) |
| 384 return "Read(): did not wait for data."; |
| 385 |
| 386 TestCompletionCallbackWithOutput<socket::WriteInfo_Dev> |
| 387 write_callback(pp_instance()); |
| 388 result = socket_.Write(socket_id, input_array_buffer, |
| 389 write_callback.GetCallback()); |
| 390 if (result == PP_OK_COMPLETIONPENDING) |
| 391 result = write_callback.WaitForResult(); |
| 392 if (result != PP_OK) |
| 393 return "Write(): failed."; |
| 394 bytes_written = static_cast<size_t>( |
| 395 write_callback.output().bytes_written()); |
| 396 |
| 397 result = read_callback.WaitForResult(); |
| 398 if (result != PP_OK) |
| 399 return "Read(): failed."; |
| 400 socket::ReadInfo_Dev read_info = read_callback.output(); |
| 401 result_code = read_info.result_code(), |
| 402 data = read_info.data(); |
| 403 } else { |
| 404 TestCompletionCallbackWithOutput<socket::RecvFromInfo_Dev> |
| 405 recv_from_callback(pp_instance()); |
| 406 int32_t result = socket_.RecvFrom(socket_id, Optional<int32_t>(), |
| 407 recv_from_callback.GetCallback()); |
| 408 if (result != PP_OK_COMPLETIONPENDING) |
| 409 return "RecvFrom(): did not wait for data."; |
| 410 |
| 411 TestCompletionCallbackWithOutput<socket::WriteInfo_Dev> |
| 412 send_to_callback(pp_instance()); |
| 413 result = socket_.SendTo(socket_id, input_array_buffer, address_, port_, |
| 414 send_to_callback.GetCallback()); |
| 415 if (result == PP_OK_COMPLETIONPENDING) |
| 416 result = send_to_callback.WaitForResult(); |
| 417 if (result != PP_OK) |
| 418 return "SendTo(): failed."; |
| 419 bytes_written = static_cast<size_t>( |
| 420 send_to_callback.output().bytes_written()); |
| 421 |
| 422 result = recv_from_callback.WaitForResult(); |
| 423 if (result != PP_OK) |
| 424 return "RecvFrom(): failed."; |
| 425 socket::RecvFromInfo_Dev recv_from_info = recv_from_callback.output(); |
| 426 result_code = recv_from_info.result_code(); |
| 427 data = recv_from_info.data(); |
| 428 } |
| 429 |
| 430 if (bytes_written != strlen(kSendContents)) |
| 431 return "SendTo() or Write(): did not send the whole data buffer."; |
| 432 |
| 433 VarArrayBuffer output_array_buffer(data); |
| 434 if (result_code > 0 && |
| 435 static_cast<uint32_t>(result_code) != |
| 436 output_array_buffer.ByteLength()) { |
| 437 return "Read() or RecvFrom(): inconsistent result code and byte " |
| 438 "length."; |
| 439 } |
| 440 |
| 441 std::string output_string = ConvertFromArrayBuffer( |
| 442 &output_array_buffer); |
| 443 size_t prefix_len = strlen(kReceiveContentsPrefix); |
| 444 if (output_string.size() != prefix_len + kReceiveContentsSuffixSize || |
| 445 output_string.compare(0, prefix_len, kReceiveContentsPrefix) != 0) { |
| 446 return std::string("Read() or RecvFrom(): mismatch data: ").append( |
| 447 output_string); |
| 448 } |
| 449 } |
| 450 |
| 451 socket_.Destroy(socket_id); |
| 452 return std::string(); |
| 453 } |
| 454 |
| 455 void Log(PP_LogLevel level, const char* format, ...) { |
| 456 va_list args; |
| 457 va_start(args, format); |
| 458 char buf[512]; |
| 459 vsnprintf(buf, sizeof(buf) - 1, format, args); |
| 460 buf[sizeof(buf) - 1] = '\0'; |
| 461 va_end(args); |
| 462 |
| 463 Var value(buf); |
| 464 console_interface_->Log(pp_instance(), level, value.pp_var()); |
| 465 } |
| 466 |
| 467 void NotifyTestDone(const std::string& message) { |
| 468 PostMessage(message); |
| 469 } |
| 470 |
| 471 VarArrayBuffer ConvertToArrayBuffer(const std::string data) { |
| 472 VarArrayBuffer array_buffer(data.size()); |
| 473 memcpy(array_buffer.Map(), data.c_str(), data.size()); |
| 474 array_buffer.Unmap(); |
| 475 return array_buffer; |
| 476 } |
| 477 |
| 478 std::string ConvertFromArrayBuffer(VarArrayBuffer* array_buffer) { |
| 479 std::string result(static_cast<const char*>(array_buffer->Map()), |
| 480 array_buffer->ByteLength()); |
| 481 array_buffer->Unmap(); |
| 482 return result; |
| 483 } |
| 484 |
| 485 socket::Socket_Dev socket_; |
| 486 const PPB_Console* console_interface_; |
| 487 const PPB_Ext_Socket_Dev* socket_interface_; |
| 488 |
| 489 std::string test_type_; |
| 490 std::string address_; |
| 491 int32_t port_; |
| 492 }; |
| 493 |
| 494 class MyModule : public Module { |
| 495 public: |
| 496 MyModule() : Module() {} |
| 497 virtual ~MyModule() {} |
| 498 |
| 499 virtual Instance* CreateInstance(PP_Instance instance) { |
| 500 return new MyInstance(instance); |
| 501 } |
| 502 }; |
| 503 |
| 504 namespace pp { |
| 505 |
| 506 Module* CreateModule() { |
| 507 return new MyModule(); |
| 508 } |
| 509 |
| 510 } // namespace pp |
| 511 |
OLD | NEW |