blob: 1810ab4657f7f058b1c91540e63e91b76691e0ca [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/virtualization/tests/socket.h"
#include <lib/zx/time.h>
#include <src/lib/fxl/logging.h>
#include <array>
static constexpr size_t kSerialBufferSize = 1024;
ZxSocket::ZxSocket(zx::socket socket) : socket_(std::move(socket)) {}
zx_status_t ZxSocket::Send(zx::time deadline, const std::string& message) {
const char* data = message.data();
size_t len = message.size();
while (true) {
// Wait until the socket is writable, is closed, or timeout occurs.
//
// Note "wait_one" returns ZX_OK if already signalled, even if the
// deadline has passed.
zx_status_t status;
zx_signals_t pending = 0;
status = socket_.wait_one(ZX_SOCKET_WRITABLE | ZX_SOCKET_PEER_CLOSED, deadline, &pending);
if (status != ZX_OK) {
return status;
}
if (pending & ZX_SOCKET_PEER_CLOSED) {
return ZX_ERR_PEER_CLOSED;
}
if (!(pending & ZX_SOCKET_WRITABLE)) {
continue;
}
// Write out next chunk of bytes.
size_t actual;
status = socket_.write(0, data, len, &actual);
if (status == ZX_ERR_SHOULD_WAIT) {
continue;
}
if (status != ZX_OK) {
return status;
}
if (actual == len) {
return ZX_OK;
}
data += actual;
len -= actual;
}
}
zx_status_t ZxSocket::Receive(zx::time deadline, std::string* result) {
FXL_CHECK(result != nullptr);
while (true) {
zx_status_t status;
// Wait until the socket is readable, is closed, or timeout occurs.
//
// Note "wait_one" returns ZX_OK if already signalled, even if the
// deadline has passed.
zx_signals_t pending = 0;
status = socket_.wait_one(ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED, deadline, &pending);
if (status != ZX_OK) {
return status;
}
if (pending & ZX_SOCKET_PEER_CLOSED) {
return ZX_ERR_PEER_CLOSED;
}
// Read a chunk of data from the socket.
std::array<char, kSerialBufferSize> buffer;
size_t actual;
status = socket_.read(0, buffer.data(), buffer.size(), &actual);
if (status == ZX_ERR_SHOULD_WAIT) {
// Retry.
continue;
}
if (status != ZX_OK) {
return status;
}
result->clear();
result->append(buffer.data(), actual);
return ZX_OK;
}
}
zx_status_t DrainSocket(SocketInterface* socket, std::string* result) {
std::string drained_data;
zx_status_t last_status;
// Keep fetching data until we hit an error.
do {
std::string buff;
// Perform a non-blocking receive by setting deadline
// to ZX_TIME_INFINITE_PAST.
last_status = socket->Receive(zx::time(ZX_TIME_INFINITE_PAST), &buff);
drained_data.append(buff);
} while (last_status == ZX_OK);
// If we had a timeout, it means that no data was waiting. We still
// consider this a successful drain.
if (last_status == ZX_ERR_TIMED_OUT) {
last_status = ZX_OK;
}
// If we received no data and got an error (other than "ZX_ERR_TIMED_OUT",
// which we handle above), return the error. Otherwise, return success.
bool data_fetched = !drained_data.empty();
if (result != nullptr) {
*result = std::move(drained_data);
}
if (!data_fetched) {
return last_status;
}
return ZX_OK;
}