Home | History | Annotate | Download | only in socket
      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"
     20 static 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.
     25 static 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 
     35 void 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 
     64 void 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 
     82 void 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 
     97 void 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 
    115 void 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 
    145 void 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 
    160 void EchoServer::TryRead() {
    161   pp::CompletionCallback callback =
    162       callback_factory_.NewCallback(&EchoServer::OnReadCompletion);
    163   incoming_socket_.Read(receive_buffer_, kBufferSize, callback);
    164 }
    165 
    166 void EchoServer::TryAccept() {
    167   pp::CompletionCallbackWithOutput<pp::TCPSocket> callback =
    168       callback_factory_.NewCallbackWithOutput(
    169           &EchoServer::OnAcceptCompletion);
    170   listening_socket_.Accept(callback);
    171 }
    172