Home | History | Annotate | Download | only in nacl_io_socket_test
      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"
     21 static 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.
     26 static 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 
     36 void 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 
     65 void 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 
     83 void 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 
    105 void 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 
    123 void 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 
    153 void 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 
    168 void EchoServer::TryRead() {
    169   pp::CompletionCallback callback =
    170       callback_factory_.NewCallback(&EchoServer::OnReadCompletion);
    171   incoming_socket_.Read(receive_buffer_, kBufferSize, callback);
    172 }
    173 
    174 void EchoServer::TryAccept() {
    175   pp::CompletionCallbackWithOutput<pp::TCPSocket> callback =
    176       callback_factory_.NewCallbackWithOutput(
    177           &EchoServer::OnAcceptCompletion);
    178   listening_socket_.Accept(callback);
    179 }
    180