| // Copyright (c) 2013 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 "echo_server.h" |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <sstream> |
| |
| #include "ppapi/c/pp_errors.h" |
| #include "ppapi/cpp/var.h" |
| #include "ppapi/utility/completion_callback_factory.h" |
| |
| #ifdef WIN32 |
| #undef PostMessage |
| #endif |
| |
| // Number of connections to queue up on the listening |
| // socket before new ones get "Connection Refused" |
| static const int kBacklog = 10; |
| |
| // Implement htons locally. Even though this is provided by |
| // nacl_io we don't want to include nacl_io in this simple |
| // example. |
| static uint16_t Htons(uint16_t hostshort) { |
| uint8_t result_bytes[2]; |
| result_bytes[0] = (uint8_t) ((hostshort >> 8) & 0xFF); |
| result_bytes[1] = (uint8_t) (hostshort & 0xFF); |
| |
| uint16_t result; |
| memcpy(&result, result_bytes, 2); |
| return result; |
| } |
| |
| void EchoServer::Start(uint16_t port) { |
| if (!pp::TCPSocket::IsAvailable()) { |
| Log("TCPSocket not available"); |
| return; |
| } |
| |
| listening_socket_ = pp::TCPSocket(instance_); |
| if (listening_socket_.is_null()) { |
| Log("Error creating TCPSocket."); |
| return; |
| } |
| |
| std::ostringstream status; |
| status << "Starting server on port: " << port; |
| Log(status.str().c_str()); |
| |
| // Attempt to listen on all interfaces (0.0.0.0) |
| // on the given port number. |
| PP_NetAddress_IPv4 ipv4_addr = { Htons(port), { 0 } }; |
| pp::NetAddress addr(instance_, ipv4_addr); |
| pp::CompletionCallback callback = |
| callback_factory_.NewCallback(&EchoServer::OnBindCompletion); |
| int32_t rtn = listening_socket_.Bind(addr, callback); |
| if (rtn != PP_OK_COMPLETIONPENDING) { |
| Log("Error binding listening socket."); |
| return; |
| } |
| } |
| |
| void EchoServer::OnBindCompletion(int32_t result) { |
| if (result != PP_OK) { |
| std::ostringstream status; |
| status << "server: Bind failed with: " << result; |
| Log(status.str().c_str()); |
| return; |
| } |
| |
| pp::CompletionCallback callback = |
| callback_factory_.NewCallback(&EchoServer::OnListenCompletion); |
| |
| int32_t rtn = listening_socket_.Listen(kBacklog, callback); |
| if (rtn != PP_OK_COMPLETIONPENDING) { |
| Log("server: Error listening on server socket."); |
| return; |
| } |
| } |
| |
| void EchoServer::OnListenCompletion(int32_t result) { |
| std::ostringstream status; |
| if (result != PP_OK) { |
| status << "server: Listen failed with: " << result; |
| Log(status.str().c_str()); |
| return; |
| } |
| |
| pp::NetAddress addr = listening_socket_.GetLocalAddress(); |
| status << "server: Listening on: " << addr.DescribeAsString(true).AsString(); |
| Log(status.str().c_str()); |
| |
| ready_ = true; |
| if (ready_cond_) { |
| pthread_mutex_lock(ready_lock_); |
| pthread_cond_signal(ready_cond_); |
| pthread_mutex_unlock(ready_lock_); |
| } |
| |
| TryAccept(); |
| } |
| |
| void EchoServer::OnAcceptCompletion(int32_t result, pp::TCPSocket socket) { |
| std::ostringstream status; |
| |
| if (result != PP_OK) { |
| status << "server: Accept failed: " << result; |
| Log(status.str().c_str()); |
| return; |
| } |
| |
| pp::NetAddress addr = socket.GetLocalAddress(); |
| status << "server: New connection from: "; |
| status << addr.DescribeAsString(true).AsString(); |
| Log(status.str().c_str()); |
| incoming_socket_ = socket; |
| |
| TryRead(); |
| } |
| |
| void EchoServer::OnReadCompletion(int32_t result) { |
| std::ostringstream status; |
| if (result <= 0) { |
| if (result == 0) |
| status << "server: client disconnected"; |
| else |
| status << "server: Read failed: " << result; |
| Log(status.str().c_str()); |
| |
| // Remove the current incoming socket and try |
| // to accept the next one. |
| incoming_socket_.Close(); |
| incoming_socket_ = pp::TCPSocket(); |
| TryAccept(); |
| return; |
| } |
| |
| status << "server: Read " << result << " bytes"; |
| Log(status.str().c_str()); |
| |
| // Echo the bytes back to the client |
| pp::CompletionCallback callback = |
| callback_factory_.NewCallback(&EchoServer::OnWriteCompletion); |
| result = incoming_socket_.Write(receive_buffer_, result, callback); |
| if (result != PP_OK_COMPLETIONPENDING) { |
| status << "server: Write failed: " << result; |
| Log(status.str().c_str()); |
| } |
| } |
| |
| void EchoServer::OnWriteCompletion(int32_t result) { |
| std::ostringstream status; |
| if (result < 0) { |
| status << "server: Write failed: " << result; |
| Log(status.str().c_str()); |
| return; |
| } |
| |
| status << "server: Wrote " << result << " bytes"; |
| Log(status.str().c_str()); |
| |
| // Try and read more bytes from the client |
| TryRead(); |
| } |
| |
| void EchoServer::TryRead() { |
| pp::CompletionCallback callback = |
| callback_factory_.NewCallback(&EchoServer::OnReadCompletion); |
| incoming_socket_.Read(receive_buffer_, kBufferSize, callback); |
| } |
| |
| void EchoServer::TryAccept() { |
| pp::CompletionCallbackWithOutput<pp::TCPSocket> callback = |
| callback_factory_.NewCallbackWithOutput( |
| &EchoServer::OnAcceptCompletion); |
| listening_socket_.Accept(callback); |
| } |