blob: eacc32d9d9b7c97255c0df087f68dacde297bc45 [file] [log] [blame]
/*
* Copyright (C) 2019 Sony Interactive Entertainment Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "RemoteInspectorSocketEndpoint.h"
#if ENABLE(REMOTE_INSPECTOR)
#include <wtf/CryptographicallyRandomNumber.h>
#include <wtf/MainThread.h>
#include <wtf/RunLoop.h>
#include <wtf/text/WTFString.h>
namespace Inspector {
RemoteInspectorSocketEndpoint& RemoteInspectorSocketEndpoint::singleton()
{
static NeverDestroyed<RemoteInspectorSocketEndpoint> shared;
return shared;
}
RemoteInspectorSocketEndpoint::RemoteInspectorSocketEndpoint()
{
if (auto sockets = Socket::createPair()) {
m_wakeupSendSocket = sockets->at(0);
m_wakeupReceiveSocket = sockets->at(1);
}
m_workerThread = Thread::create("SocketEndpoint", [this] {
workerThread();
});
}
RemoteInspectorSocketEndpoint::~RemoteInspectorSocketEndpoint()
{
ASSERT(m_workerThread.get() != &Thread::current());
m_shouldAbortWorkerThread = true;
wakeupWorkerThread();
m_workerThread->waitForCompletion();
Socket::close(m_wakeupSendSocket);
Socket::close(m_wakeupReceiveSocket);
for (const auto& connection : m_connections.values())
Socket::close(connection->socket);
for (const auto& connection : m_listeners.values())
Socket::close(connection->socket);
}
void RemoteInspectorSocketEndpoint::wakeupWorkerThread()
{
if (Socket::isValid(m_wakeupSendSocket))
Socket::write(m_wakeupSendSocket, "1", 1);
}
Optional<ConnectionID> RemoteInspectorSocketEndpoint::connectInet(const char* serverAddress, uint16_t serverPort, Client& client)
{
if (auto socket = Socket::connect(serverAddress, serverPort))
return createClient(*socket, client);
return WTF::nullopt;
}
Optional<ConnectionID> RemoteInspectorSocketEndpoint::listenInet(const char* address, uint16_t port, Listener& listener, Client& client)
{
if (auto socket = Socket::listen(address, port))
return createListener(*socket, listener, client);
return WTF::nullopt;
}
bool RemoteInspectorSocketEndpoint::isListening(ConnectionID id)
{
LockHolder lock(m_connectionsLock);
if (m_listeners.contains(id))
return true;
return false;
}
void RemoteInspectorSocketEndpoint::workerThread()
{
PollingDescriptor wakeup = Socket::preparePolling(m_wakeupReceiveSocket);
while (!m_shouldAbortWorkerThread) {
#if USE(GENERIC_EVENT_LOOP) || USE(WINDOWS_EVENT_LOOP)
RunLoop::iterate();
#endif
Vector<PollingDescriptor> pollfds;
Vector<ConnectionID> ids;
{
LockHolder lock(m_connectionsLock);
for (const auto& connection : m_connections) {
pollfds.append(connection.value->poll);
ids.append(connection.key);
}
for (const auto& connection : m_listeners) {
pollfds.append(connection.value->poll);
ids.append(connection.key);
}
}
pollfds.append(wakeup);
if (!Socket::poll(pollfds, -1))
continue;
if (Socket::isReadable(pollfds.last())) {
char wakeMessage;
Socket::read(m_wakeupReceiveSocket, &wakeMessage, sizeof(wakeMessage));
continue;
}
for (size_t i = 0; i < ids.size(); i++) {
auto id = ids[i];
if (Socket::isReadable(pollfds[i])) {
if (isListening(id))
acceptInetSocketIfEnabled(id);
else
recvIfEnabled(id);
} else if (Socket::isWritable(pollfds[i]))
sendIfEnabled(id);
}
}
}
ConnectionID RemoteInspectorSocketEndpoint::generateConnectionID()
{
ASSERT(m_connectionsLock.isLocked());
ConnectionID id;
do {
id = cryptographicallyRandomNumber();
} while (!id || m_connections.contains(id) || m_listeners.contains(id));
return id;
}
std::unique_ptr<RemoteInspectorSocketEndpoint::Connection> RemoteInspectorSocketEndpoint::makeConnection(PlatformSocketType socket, Client& client)
{
ASSERT(m_connectionsLock.isLocked());
Socket::setup(socket);
auto connection = makeUnique<Connection>(client);
connection->id = generateConnectionID();
connection->poll = Socket::preparePolling(socket);
connection->socket = socket;
return connection;
}
Optional<ConnectionID> RemoteInspectorSocketEndpoint::createClient(PlatformSocketType socket, Client& client)
{
if (!Socket::isValid(socket))
return WTF::nullopt;
LockHolder lock(m_connectionsLock);
auto connection = makeConnection(socket, client);
auto id = connection->id;
m_connections.add(id, WTFMove(connection));
wakeupWorkerThread();
return id;
}
Optional<ConnectionID> RemoteInspectorSocketEndpoint::createListener(PlatformSocketType socket, Listener& listener, Client& client)
{
if (!Socket::isValid(socket))
return WTF::nullopt;
LockHolder lock(m_connectionsLock);
Socket::setup(socket);
auto connection = makeConnection(socket, client);
auto id = connection->id;
connection->listener = &listener;
m_listeners.add(id, WTFMove(connection));
wakeupWorkerThread();
return id;
}
void RemoteInspectorSocketEndpoint::invalidateClient(Client& client)
{
LockHolder lock(m_connectionsLock);
m_connections.removeIf([&client](auto& keyValue) {
const auto& connection = keyValue.value;
if (&connection->client != &client)
return false;
Socket::close(connection->socket);
// do not call client.didClose because client is already invalidating phase.
return true;
});
}
void RemoteInspectorSocketEndpoint::invalidateListener(Listener& listener)
{
LockHolder lock(m_connectionsLock);
m_listeners.removeIf([&listener](auto& keyValue) {
const auto& connection = keyValue.value;
if (connection->listener == &listener) {
Socket::close(connection->socket);
return true;
}
return false;
});
}
Optional<uint16_t> RemoteInspectorSocketEndpoint::getPort(ConnectionID id) const
{
LockHolder lock(m_connectionsLock);
if (const auto& connection = m_listeners.get(id))
return Socket::getPort(connection->socket);
if (const auto& connection = m_connections.get(id))
return Socket::getPort(connection->socket);
return WTF::nullopt;
}
void RemoteInspectorSocketEndpoint::recvIfEnabled(ConnectionID id)
{
LockHolder lock(m_connectionsLock);
if (const auto& connection = m_connections.get(id)) {
Vector<uint8_t> recvBuffer(Socket::BufferSize);
if (auto readSize = Socket::read(connection->socket, recvBuffer.data(), recvBuffer.size())) {
if (*readSize > 0) {
recvBuffer.shrink(*readSize);
connection->client.didReceive(id, WTFMove(recvBuffer));
return;
}
}
Socket::close(connection->socket);
m_connections.remove(id);
lock.unlockEarly();
connection->client.didClose(id);
}
}
void RemoteInspectorSocketEndpoint::sendIfEnabled(ConnectionID id)
{
LockHolder lock(m_connectionsLock);
if (const auto& connection = m_connections.get(id)) {
Socket::clearWaitingWritable(connection->poll);
auto& buffer = connection->sendBuffer;
if (buffer.isEmpty())
return;
if (auto writeSize = Socket::write(connection->socket, buffer.data(), std::min(buffer.size(), Socket::BufferSize))) {
auto size = *writeSize;
if (size == buffer.size()) {
buffer.clear();
return;
}
if (size > 0)
buffer.remove(0, size);
}
Socket::markWaitingWritable(connection->poll);
}
}
void RemoteInspectorSocketEndpoint::send(ConnectionID id, const uint8_t* data, size_t size)
{
LockHolder lock(m_connectionsLock);
if (const auto& connection = m_connections.get(id)) {
size_t offset = 0;
if (connection->sendBuffer.isEmpty()) {
// Try to call send() directly if buffer is empty.
if (auto writeSize = Socket::write(connection->socket, data, std::min(size, Socket::BufferSize)))
offset = *writeSize;
// @TODO need to handle closed socket case?
}
// Check all data is sent.
if (offset == size)
return;
// Copy remaining data to send later.
connection->sendBuffer.appendRange(data + offset, data + size);
Socket::markWaitingWritable(connection->poll);
wakeupWorkerThread();
}
}
void RemoteInspectorSocketEndpoint::acceptInetSocketIfEnabled(ConnectionID id)
{
ASSERT(isListening(id));
LockHolder lock(m_connectionsLock);
if (const auto& connection = m_listeners.get(id)) {
if (auto socket = Socket::accept(connection->socket)) {
// Need to unlock before calling createClient as it also attempts to lock.
lock.unlockEarly();
if (auto newID = createClient(*socket, connection->client)) {
if (connection->listener->didAccept(newID.value(), connection->id, Socket::Domain::Network))
return;
m_connections.remove(id);
}
Socket::close(*socket);
}
}
}
} // namespace Inspector
#endif // ENABLE(REMOTE_INSPECTOR)