Path: blob/main/contrib/llvm-project/lldb/source/Host/common/JSONTransport.cpp
213799 views
//===-- JSONTransport.cpp -------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Host/JSONTransport.h"9#include "lldb/Utility/IOObject.h"10#include "lldb/Utility/LLDBLog.h"11#include "lldb/Utility/Log.h"12#include "lldb/Utility/SelectHelper.h"13#include "lldb/Utility/Status.h"14#include "lldb/lldb-forward.h"15#include "llvm/ADT/StringExtras.h"16#include "llvm/ADT/StringRef.h"17#include "llvm/Support/Error.h"18#include "llvm/Support/raw_ostream.h"19#include <optional>20#include <string>21#include <utility>2223using namespace llvm;24using namespace lldb;25using namespace lldb_private;2627/// ReadFull attempts to read the specified number of bytes. If EOF is28/// encountered, an empty string is returned.29static Expected<std::string>30ReadFull(IOObject &descriptor, size_t length,31std::optional<std::chrono::microseconds> timeout = std::nullopt) {32if (!descriptor.IsValid())33return llvm::make_error<TransportInvalidError>();3435bool timeout_supported = true;36// FIXME: SelectHelper does not work with NativeFile on Win32.37#if _WIN3238timeout_supported = descriptor.GetFdType() == IOObject::eFDTypeSocket;39#endif4041if (timeout && timeout_supported) {42SelectHelper sh;43sh.SetTimeout(*timeout);44sh.FDSetRead(45reinterpret_cast<lldb::socket_t>(descriptor.GetWaitableHandle()));46Status status = sh.Select();47if (status.Fail()) {48// Convert timeouts into a specific error.49if (status.GetType() == lldb::eErrorTypePOSIX &&50status.GetError() == ETIMEDOUT)51return make_error<TransportTimeoutError>();52return status.takeError();53}54}5556std::string data;57data.resize(length);58Status status = descriptor.Read(data.data(), length);59if (status.Fail())60return status.takeError();6162// Read returns '' on EOF.63if (length == 0)64return make_error<TransportEOFError>();6566// Return the actual number of bytes read.67return data.substr(0, length);68}6970static Expected<std::string>71ReadUntil(IOObject &descriptor, StringRef delimiter,72std::optional<std::chrono::microseconds> timeout = std::nullopt) {73std::string buffer;74buffer.reserve(delimiter.size() + 1);75while (!llvm::StringRef(buffer).ends_with(delimiter)) {76Expected<std::string> next =77ReadFull(descriptor, buffer.empty() ? delimiter.size() : 1, timeout);78if (auto Err = next.takeError())79return std::move(Err);80buffer += *next;81}82return buffer.substr(0, buffer.size() - delimiter.size());83}8485JSONTransport::JSONTransport(IOObjectSP input, IOObjectSP output)86: m_input(std::move(input)), m_output(std::move(output)) {}8788void JSONTransport::Log(llvm::StringRef message) {89LLDB_LOG(GetLog(LLDBLog::Host), "{0}", message);90}9192Expected<std::string>93HTTPDelimitedJSONTransport::ReadImpl(const std::chrono::microseconds &timeout) {94if (!m_input || !m_input->IsValid())95return llvm::make_error<TransportInvalidError>();9697IOObject *input = m_input.get();98Expected<std::string> message_header =99ReadFull(*input, kHeaderContentLength.size(), timeout);100if (!message_header)101return message_header.takeError();102if (*message_header != kHeaderContentLength)103return createStringError(formatv("expected '{0}' and got '{1}'",104kHeaderContentLength, *message_header)105.str());106107Expected<std::string> raw_length = ReadUntil(*input, kHeaderSeparator);108if (!raw_length)109return handleErrors(raw_length.takeError(),110[&](const TransportEOFError &E) -> llvm::Error {111return createStringError(112"unexpected EOF while reading header separator");113});114115size_t length;116if (!to_integer(*raw_length, length))117return createStringError(118formatv("invalid content length {0}", *raw_length).str());119120Expected<std::string> raw_json = ReadFull(*input, length);121if (!raw_json)122return handleErrors(123raw_json.takeError(), [&](const TransportEOFError &E) -> llvm::Error {124return createStringError("unexpected EOF while reading JSON");125});126127Log(llvm::formatv("--> {0}", *raw_json).str());128129return raw_json;130}131132Error HTTPDelimitedJSONTransport::WriteImpl(const std::string &message) {133if (!m_output || !m_output->IsValid())134return llvm::make_error<TransportInvalidError>();135136Log(llvm::formatv("<-- {0}", message).str());137138std::string Output;139raw_string_ostream OS(Output);140OS << kHeaderContentLength << message.length() << kHeaderSeparator << message;141size_t num_bytes = Output.size();142return m_output->Write(Output.data(), num_bytes).takeError();143}144145Expected<std::string>146JSONRPCTransport::ReadImpl(const std::chrono::microseconds &timeout) {147if (!m_input || !m_input->IsValid())148return make_error<TransportInvalidError>();149150IOObject *input = m_input.get();151Expected<std::string> raw_json =152ReadUntil(*input, kMessageSeparator, timeout);153if (!raw_json)154return raw_json.takeError();155156Log(llvm::formatv("--> {0}", *raw_json).str());157158return *raw_json;159}160161Error JSONRPCTransport::WriteImpl(const std::string &message) {162if (!m_output || !m_output->IsValid())163return llvm::make_error<TransportInvalidError>();164165Log(llvm::formatv("<-- {0}", message).str());166167std::string Output;168llvm::raw_string_ostream OS(Output);169OS << message << kMessageSeparator;170size_t num_bytes = Output.size();171return m_output->Write(Output.data(), num_bytes).takeError();172}173174char TransportEOFError::ID;175char TransportTimeoutError::ID;176char TransportInvalidError::ID;177178179