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 "echo_server.h" 6 7#include <string.h> 8#include <sstream> 9 10#include "ppapi/c/pp_errors.h" 11#include "ppapi/cpp/var.h" 12#include "ppapi/utility/completion_callback_factory.h" 13 14#ifdef WIN32 15#undef PostMessage 16#endif 17 18// Number of connections to queue up on the listening 19// socket before new ones get "Connection Refused" 20static const int kBacklog = 10; 21 22// Implement htons locally. Even though this is provided by 23// nacl_io we don't want to include nacl_io in this simple 24// example. 25static uint16_t Htons(uint16_t hostshort) { 26 uint8_t result_bytes[2]; 27 result_bytes[0] = (uint8_t) ((hostshort >> 8) & 0xFF); 28 result_bytes[1] = (uint8_t) (hostshort & 0xFF); 29 30 uint16_t result; 31 memcpy(&result, result_bytes, 2); 32 return result; 33} 34 35void EchoServer::Start(uint16_t port) { 36 if (!pp::TCPSocket::IsAvailable()) { 37 instance_->PostMessage("TCPSocket not available"); 38 return; 39 } 40 41 listening_socket_ = pp::TCPSocket(instance_); 42 if (listening_socket_.is_null()) { 43 instance_->PostMessage("Error creating TCPSocket."); 44 return; 45 } 46 47 std::ostringstream status; 48 status << "Starting server on port: " << port; 49 instance_->PostMessage(status.str()); 50 51 // Attempt to listen on all interfaces (0.0.0.0) 52 // on the given port number. 53 PP_NetAddress_IPv4 ipv4_addr = { Htons(port), { 0 } }; 54 pp::NetAddress addr(instance_, ipv4_addr); 55 pp::CompletionCallback callback = 56 callback_factory_.NewCallback(&EchoServer::OnBindCompletion); 57 int32_t rtn = listening_socket_.Bind(addr, callback); 58 if (rtn != PP_OK_COMPLETIONPENDING) { 59 instance_->PostMessage("Error binding listening socket."); 60 return; 61 } 62} 63 64void EchoServer::OnBindCompletion(int32_t result) { 65 if (result != PP_OK) { 66 std::ostringstream status; 67 status << "server: Bind failed with: " << result; 68 instance_->PostMessage(status.str()); 69 return; 70 } 71 72 pp::CompletionCallback callback = 73 callback_factory_.NewCallback(&EchoServer::OnListenCompletion); 74 75 int32_t rtn = listening_socket_.Listen(kBacklog, callback); 76 if (rtn != PP_OK_COMPLETIONPENDING) { 77 instance_->PostMessage("server: Error listening on server socket."); 78 return; 79 } 80} 81 82void EchoServer::OnListenCompletion(int32_t result) { 83 std::ostringstream status; 84 if (result != PP_OK) { 85 status << "server: Listen failed with: " << result; 86 instance_->PostMessage(status.str()); 87 return; 88 } 89 90 pp::NetAddress addr = listening_socket_.GetLocalAddress(); 91 status << "server: Listening on: " << addr.DescribeAsString(true).AsString(); 92 instance_->PostMessage(status.str()); 93 94 TryAccept(); 95} 96 97void EchoServer::OnAcceptCompletion(int32_t result, pp::TCPSocket socket) { 98 std::ostringstream status; 99 100 if (result != PP_OK) { 101 status << "server: Accept failed: " << result; 102 instance_->PostMessage(status.str()); 103 return; 104 } 105 106 pp::NetAddress addr = socket.GetLocalAddress(); 107 status << "server: New connection from: "; 108 status << addr.DescribeAsString(true).AsString(); 109 instance_->PostMessage(status.str()); 110 incoming_socket_ = socket; 111 112 TryRead(); 113} 114 115void EchoServer::OnReadCompletion(int32_t result) { 116 std::ostringstream status; 117 if (result <= 0) { 118 if (result == 0) 119 status << "server: client disconnected"; 120 else 121 status << "server: Read failed: " << result; 122 instance_->PostMessage(status.str()); 123 124 // Remove the current incoming socket and try 125 // to accept the next one. 126 incoming_socket_.Close(); 127 incoming_socket_ = pp::TCPSocket(); 128 TryAccept(); 129 return; 130 } 131 132 status << "server: Read " << result << " bytes"; 133 instance_->PostMessage(status.str()); 134 135 // Echo the bytes back to the client 136 pp::CompletionCallback callback = 137 callback_factory_.NewCallback(&EchoServer::OnWriteCompletion); 138 result = incoming_socket_.Write(receive_buffer_, result, callback); 139 if (result != PP_OK_COMPLETIONPENDING) { 140 status << "server: Write failed: " << result; 141 instance_->PostMessage(status.str()); 142 } 143} 144 145void EchoServer::OnWriteCompletion(int32_t result) { 146 std::ostringstream status; 147 if (result < 0) { 148 status << "server: Write failed: " << result; 149 instance_->PostMessage(status.str()); 150 return; 151 } 152 153 status << "server: Wrote " << result << " bytes"; 154 instance_->PostMessage(status.str()); 155 156 // Try and read more bytes from the client 157 TryRead(); 158} 159 160void EchoServer::TryRead() { 161 pp::CompletionCallback callback = 162 callback_factory_.NewCallback(&EchoServer::OnReadCompletion); 163 incoming_socket_.Read(receive_buffer_, kBufferSize, callback); 164} 165 166void EchoServer::TryAccept() { 167 pp::CompletionCallbackWithOutput<pp::TCPSocket> callback = 168 callback_factory_.NewCallbackWithOutput( 169 &EchoServer::OnAcceptCompletion); 170 listening_socket_.Accept(callback); 171} 172