blob: 22ab94f109084ba5bb10ed444a35498fbe3b14da [file] [log] [blame]
// Copyright 2018 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/shared/starboard/net_args.h"
#include <string>
#include <vector>
#include "starboard/common/scoped_ptr.h"
#include "starboard/common/socket.h"
#include "starboard/socket_waiter.h"
#include "starboard/thread.h"
#include "starboard/time.h"
#ifndef NET_ARGS_PORT
#define NET_ARGS_PORT 49355
#endif
// Controls whether using IPv4 or IPv6.
#ifndef NET_ARGS_IP_VERSION
#define NET_ARGS_IP_VERSION kSbSocketAddressTypeIpv4
#endif
namespace starboard {
namespace shared {
namespace starboard {
namespace {
scoped_ptr<Socket> CreateListenSocket() {
scoped_ptr<Socket> socket(
new Socket(NET_ARGS_IP_VERSION, kSbSocketProtocolTcp));
socket->SetReuseAddress(true);
SbSocketAddress sock_addr;
// Ip address will be set to 0.0.0.0 so that it will bind to all sockets.
memset(&sock_addr, 0, sizeof(SbSocketAddress));
sock_addr.type = NET_ARGS_IP_VERSION;
sock_addr.port = NET_ARGS_PORT;
SbSocketError sock_err = socket->Bind(&sock_addr);
const char kErrFmt[] = "Socket error while attempting to bind, error = %d\n";
if (sock_err != kSbSocketOk) {
SbLogRawFormatF(kErrFmt, sock_err);
}
sock_err = socket->Listen();
if (sock_err != kSbSocketOk) {
SbLogRawFormatF(kErrFmt, sock_err);
}
return socket.Pass();
}
void WaitUntilReadableOrConnectionReset(SbSocket sock) {
SbSocketWaiter waiter = SbSocketWaiterCreate();
struct F {
static void WakeUp(SbSocketWaiter waiter, SbSocket, void*, int) {
SbSocketWaiterWakeUp(waiter);
}
};
SbSocketWaiterAdd(waiter,
sock,
NULL,
&F::WakeUp,
kSbSocketWaiterInterestRead,
false); // false means one shot.
SbSocketWaiterWait(waiter);
SbSocketWaiterRemove(waiter, sock);
SbSocketWaiterDestroy(waiter);
}
scoped_ptr<Socket> WaitForClientConnection(Socket* listen_sock,
SbTime timeout) {
SbTimeMonotonic expire_time =
(timeout >= 0) && (timeout < kSbTimeMax)?
SbTimeGetMonotonicNow() + timeout :
kSbTimeMax;
while (true) {
scoped_ptr<Socket> client_connection(listen_sock->Accept());
if (client_connection) {
return client_connection.Pass();
}
if (SbTimeGetMonotonicNow() > expire_time) {
return scoped_ptr<Socket>();
}
SbThreadSleep(kSbTimeMillisecond);
}
}
std::vector<std::string> SplitStringByLines(const std::string& string_buff) {
std::vector<std::string> lines;
std::stringstream ss;
ss << string_buff;
for (std::string line; std::getline(ss, line);) {
if (!line.empty()) {
lines.push_back(line);
}
}
return lines;
}
} // namespace.
// Command line switch useful for determining if NetArgsWaitForConnection()
// should be called.
const char kNetArgsCommandSwitchWait[] = "net_args_wait_for_connection";
std::vector<std::string> NetArgsWaitForPayload(SbTime timeout) {
scoped_ptr<Socket> listen = CreateListenSocket();
scoped_ptr<Socket> client_connection =
WaitForClientConnection(listen.get(), timeout);
if (!client_connection) {
SB_LOG(ERROR) << "Timed out waiting for net args.";
return std::vector<std::string>();
}
std::string str_buff;
while (true) {
char buff[128];
int result = client_connection->ReceiveFrom(buff, sizeof(buff), NULL);
if (result > 0) {
str_buff.append(buff, static_cast<size_t>(result));
continue;
} else if (result == 0) {
// Socket has closed.
break;
} else if (result < 0) { // Handle error condition.
SbSocketError err = client_connection->GetLastError();
client_connection->ClearLastError();
switch (err) {
case kSbSocketOk: {
SB_NOTREACHED() << "Expected error condition when return val "
<< "is < 0.";
continue;
}
case kSbSocketPending: {
WaitUntilReadableOrConnectionReset(client_connection->socket());
continue;
}
default: {
break;
}
}
}
}
return SplitStringByLines(str_buff);
}
} // namespace starboard
} // namespace shared
} // namespace starboard