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