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