Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Host/common/JSONTransport.cpp
213799 views
1
//===-- JSONTransport.cpp -------------------------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "lldb/Host/JSONTransport.h"
10
#include "lldb/Utility/IOObject.h"
11
#include "lldb/Utility/LLDBLog.h"
12
#include "lldb/Utility/Log.h"
13
#include "lldb/Utility/SelectHelper.h"
14
#include "lldb/Utility/Status.h"
15
#include "lldb/lldb-forward.h"
16
#include "llvm/ADT/StringExtras.h"
17
#include "llvm/ADT/StringRef.h"
18
#include "llvm/Support/Error.h"
19
#include "llvm/Support/raw_ostream.h"
20
#include <optional>
21
#include <string>
22
#include <utility>
23
24
using namespace llvm;
25
using namespace lldb;
26
using namespace lldb_private;
27
28
/// ReadFull attempts to read the specified number of bytes. If EOF is
29
/// encountered, an empty string is returned.
30
static Expected<std::string>
31
ReadFull(IOObject &descriptor, size_t length,
32
std::optional<std::chrono::microseconds> timeout = std::nullopt) {
33
if (!descriptor.IsValid())
34
return llvm::make_error<TransportInvalidError>();
35
36
bool timeout_supported = true;
37
// FIXME: SelectHelper does not work with NativeFile on Win32.
38
#if _WIN32
39
timeout_supported = descriptor.GetFdType() == IOObject::eFDTypeSocket;
40
#endif
41
42
if (timeout && timeout_supported) {
43
SelectHelper sh;
44
sh.SetTimeout(*timeout);
45
sh.FDSetRead(
46
reinterpret_cast<lldb::socket_t>(descriptor.GetWaitableHandle()));
47
Status status = sh.Select();
48
if (status.Fail()) {
49
// Convert timeouts into a specific error.
50
if (status.GetType() == lldb::eErrorTypePOSIX &&
51
status.GetError() == ETIMEDOUT)
52
return make_error<TransportTimeoutError>();
53
return status.takeError();
54
}
55
}
56
57
std::string data;
58
data.resize(length);
59
Status status = descriptor.Read(data.data(), length);
60
if (status.Fail())
61
return status.takeError();
62
63
// Read returns '' on EOF.
64
if (length == 0)
65
return make_error<TransportEOFError>();
66
67
// Return the actual number of bytes read.
68
return data.substr(0, length);
69
}
70
71
static Expected<std::string>
72
ReadUntil(IOObject &descriptor, StringRef delimiter,
73
std::optional<std::chrono::microseconds> timeout = std::nullopt) {
74
std::string buffer;
75
buffer.reserve(delimiter.size() + 1);
76
while (!llvm::StringRef(buffer).ends_with(delimiter)) {
77
Expected<std::string> next =
78
ReadFull(descriptor, buffer.empty() ? delimiter.size() : 1, timeout);
79
if (auto Err = next.takeError())
80
return std::move(Err);
81
buffer += *next;
82
}
83
return buffer.substr(0, buffer.size() - delimiter.size());
84
}
85
86
JSONTransport::JSONTransport(IOObjectSP input, IOObjectSP output)
87
: m_input(std::move(input)), m_output(std::move(output)) {}
88
89
void JSONTransport::Log(llvm::StringRef message) {
90
LLDB_LOG(GetLog(LLDBLog::Host), "{0}", message);
91
}
92
93
Expected<std::string>
94
HTTPDelimitedJSONTransport::ReadImpl(const std::chrono::microseconds &timeout) {
95
if (!m_input || !m_input->IsValid())
96
return llvm::make_error<TransportInvalidError>();
97
98
IOObject *input = m_input.get();
99
Expected<std::string> message_header =
100
ReadFull(*input, kHeaderContentLength.size(), timeout);
101
if (!message_header)
102
return message_header.takeError();
103
if (*message_header != kHeaderContentLength)
104
return createStringError(formatv("expected '{0}' and got '{1}'",
105
kHeaderContentLength, *message_header)
106
.str());
107
108
Expected<std::string> raw_length = ReadUntil(*input, kHeaderSeparator);
109
if (!raw_length)
110
return handleErrors(raw_length.takeError(),
111
[&](const TransportEOFError &E) -> llvm::Error {
112
return createStringError(
113
"unexpected EOF while reading header separator");
114
});
115
116
size_t length;
117
if (!to_integer(*raw_length, length))
118
return createStringError(
119
formatv("invalid content length {0}", *raw_length).str());
120
121
Expected<std::string> raw_json = ReadFull(*input, length);
122
if (!raw_json)
123
return handleErrors(
124
raw_json.takeError(), [&](const TransportEOFError &E) -> llvm::Error {
125
return createStringError("unexpected EOF while reading JSON");
126
});
127
128
Log(llvm::formatv("--> {0}", *raw_json).str());
129
130
return raw_json;
131
}
132
133
Error HTTPDelimitedJSONTransport::WriteImpl(const std::string &message) {
134
if (!m_output || !m_output->IsValid())
135
return llvm::make_error<TransportInvalidError>();
136
137
Log(llvm::formatv("<-- {0}", message).str());
138
139
std::string Output;
140
raw_string_ostream OS(Output);
141
OS << kHeaderContentLength << message.length() << kHeaderSeparator << message;
142
size_t num_bytes = Output.size();
143
return m_output->Write(Output.data(), num_bytes).takeError();
144
}
145
146
Expected<std::string>
147
JSONRPCTransport::ReadImpl(const std::chrono::microseconds &timeout) {
148
if (!m_input || !m_input->IsValid())
149
return make_error<TransportInvalidError>();
150
151
IOObject *input = m_input.get();
152
Expected<std::string> raw_json =
153
ReadUntil(*input, kMessageSeparator, timeout);
154
if (!raw_json)
155
return raw_json.takeError();
156
157
Log(llvm::formatv("--> {0}", *raw_json).str());
158
159
return *raw_json;
160
}
161
162
Error JSONRPCTransport::WriteImpl(const std::string &message) {
163
if (!m_output || !m_output->IsValid())
164
return llvm::make_error<TransportInvalidError>();
165
166
Log(llvm::formatv("<-- {0}", message).str());
167
168
std::string Output;
169
llvm::raw_string_ostream OS(Output);
170
OS << message << kMessageSeparator;
171
size_t num_bytes = Output.size();
172
return m_output->Write(Output.data(), num_bytes).takeError();
173
}
174
175
char TransportEOFError::ID;
176
char TransportTimeoutError::ID;
177
char TransportInvalidError::ID;
178
179