13
13
#ifndef LLDB_HOST_JSONTRANSPORT_H
14
14
#define LLDB_HOST_JSONTRANSPORT_H
15
15
16
+ #include " lldb/Host/MainLoop.h"
16
17
#include " lldb/Host/MainLoopBase.h"
17
18
#include " lldb/Utility/IOObject.h"
18
19
#include " lldb/Utility/Status.h"
19
20
#include " lldb/lldb-forward.h"
21
+ #include " llvm/ADT/StringExtras.h"
20
22
#include " llvm/ADT/StringRef.h"
21
23
#include " llvm/Support/Error.h"
24
+ #include " llvm/Support/ErrorHandling.h"
22
25
#include " llvm/Support/FormatVariadic.h"
23
26
#include " llvm/Support/JSON.h"
27
+ #include " llvm/Support/raw_ostream.h"
24
28
#include < string>
25
29
#include < system_error>
30
+ #include < variant>
26
31
#include < vector>
27
32
28
33
namespace lldb_private {
29
34
30
- class TransportEOFError : public llvm ::ErrorInfo<TransportEOFError> {
31
- public:
32
- static char ID;
33
-
34
- TransportEOFError () = default ;
35
- void log (llvm::raw_ostream &OS) const override ;
36
- std::error_code convertToErrorCode () const override ;
37
- };
38
-
39
35
class TransportUnhandledContentsError
40
36
: public llvm::ErrorInfo<TransportUnhandledContentsError> {
41
37
public:
@@ -54,112 +50,214 @@ class TransportUnhandledContentsError
54
50
std::string m_unhandled_contents;
55
51
};
56
52
57
- class TransportInvalidError : public llvm ::ErrorInfo<TransportInvalidError> {
53
+ // / A transport is responsible for maintaining the connection to a client
54
+ // / application, and reading/writing structured messages to it.
55
+ // /
56
+ // / Transports have limited thread safety requirements:
57
+ // / - Messages will not be sent concurrently.
58
+ // / - Messages MAY be sent while Run() is reading, or its callback is active.
59
+ template <typename Req, typename Resp, typename Evt> class Transport {
58
60
public:
59
- static char ID;
60
-
61
- TransportInvalidError () = default ;
61
+ using Message = std::variant<Req, Resp, Evt>;
62
+
63
+ virtual ~Transport () = default ;
64
+
65
+ // / Sends an event, a message that does not require a response.
66
+ virtual llvm::Error Send (const Evt &) = 0;
67
+ // / Sends a request, a message that expects a response.
68
+ virtual llvm::Error Send (const Req &) = 0;
69
+ // / Sends a response to a specific request.
70
+ virtual llvm::Error Send (const Resp &) = 0;
71
+
72
+ // / Implemented to handle incoming messages. (See Run() below).
73
+ class MessageHandler {
74
+ public:
75
+ virtual ~MessageHandler () = default ;
76
+ // / Called when an event is received.
77
+ virtual void Received (const Evt &) = 0;
78
+ // / Called when a request is received.
79
+ virtual void Received (const Req &) = 0;
80
+ // / Called when a response is received.
81
+ virtual void Received (const Resp &) = 0;
82
+
83
+ // / Called when an error occurs while reading from the transport.
84
+ // /
85
+ // / NOTE: This does *NOT* indicate that a specific request failed, but that
86
+ // / there was an error in the underlying transport.
87
+ virtual void OnError (llvm::Error) = 0;
88
+
89
+ // / Called on EOF or client disconnect.
90
+ virtual void OnClosed () = 0;
91
+ };
92
+
93
+ using MessageHandlerSP = std::shared_ptr<MessageHandler>;
94
+
95
+ // / RegisterMessageHandler registers the Transport with the given MainLoop and
96
+ // / handles any incoming messages using the given MessageHandler.
97
+ // /
98
+ // / If an unexpected error occurs, the MainLoop will be terminated and a log
99
+ // / message will include additional information about the termination reason.
100
+ virtual llvm::Expected<MainLoop::ReadHandleUP>
101
+ RegisterMessageHandler (MainLoop &loop, MessageHandler &handler) = 0 ;
62
102
63
- void log (llvm::raw_ostream &OS) const override ;
64
- std::error_code convertToErrorCode () const override ;
103
+ protected:
104
+ template <typename ... Ts> inline auto Logv (const char *Fmt, Ts &&...Vals) {
105
+ Log (llvm::formatv (Fmt, std::forward<Ts>(Vals)...).str ());
106
+ }
107
+ virtual void Log (llvm::StringRef message) = 0;
65
108
};
66
109
67
- // / A transport class that uses JSON for communication.
68
- class JSONTransport {
110
+ // / A JSONTransport will encode and decode messages using JSON.
111
+ template <typename Req, typename Resp, typename Evt>
112
+ class JSONTransport : public Transport <Req, Resp, Evt> {
69
113
public:
70
- using ReadHandleUP = MainLoopBase::ReadHandleUP;
71
- template <typename T>
72
- using Callback = std::function<void (MainLoopBase &, const llvm::Expected<T>)>;
73
-
74
- JSONTransport (lldb::IOObjectSP input, lldb::IOObjectSP output);
75
- virtual ~JSONTransport () = default ;
76
-
77
- // / Transport is not copyable.
78
- // / @{
79
- JSONTransport (const JSONTransport &rhs) = delete ;
80
- void operator =(const JSONTransport &rhs) = delete ;
81
- // / @}
82
-
83
- // / Writes a message to the output stream.
84
- template <typename T> llvm::Error Write (const T &t) {
85
- const std::string message = llvm::formatv (" {0}" , toJSON (t)).str ();
86
- return WriteImpl (message);
114
+ using Transport<Req, Resp, Evt>::Transport;
115
+ using MessageHandler = typename Transport<Req, Resp, Evt>::MessageHandler;
116
+
117
+ JSONTransport (lldb::IOObjectSP in, lldb::IOObjectSP out)
118
+ : m_in(in), m_out(out) {}
119
+
120
+ llvm::Error Send (const Evt &evt) override { return Write (evt); }
121
+ llvm::Error Send (const Req &req) override { return Write (req); }
122
+ llvm::Error Send (const Resp &resp) override { return Write (resp); }
123
+
124
+ llvm::Expected<MainLoop::ReadHandleUP>
125
+ RegisterMessageHandler (MainLoop &loop, MessageHandler &handler) override {
126
+ Status status;
127
+ MainLoop::ReadHandleUP read_handle = loop.RegisterReadObject (
128
+ m_in,
129
+ std::bind (&JSONTransport::OnRead, this , std::placeholders::_1,
130
+ std::ref (handler)),
131
+ status);
132
+ if (status.Fail ()) {
133
+ return status.takeError ();
134
+ }
135
+ return read_handle;
87
136
}
88
137
89
- // / Registers the transport with the MainLoop.
90
- template <typename T>
91
- llvm::Expected<ReadHandleUP> RegisterReadObject (MainLoopBase &loop,
92
- Callback<T> read_cb) {
93
- Status error;
94
- ReadHandleUP handle = loop.RegisterReadObject (
95
- m_input,
96
- [read_cb, this ](MainLoopBase &loop) {
97
- char buf[kReadBufferSize ];
98
- size_t num_bytes = sizeof (buf);
99
- if (llvm::Error error = m_input->Read (buf, num_bytes).takeError ()) {
100
- read_cb (loop, std::move (error));
101
- return ;
102
- }
103
- if (num_bytes)
104
- m_buffer.append (std::string (buf, num_bytes));
105
-
106
- // If the buffer has contents, try parsing any pending messages.
107
- if (!m_buffer.empty ()) {
108
- llvm::Expected<std::vector<std::string>> messages = Parse ();
109
- if (llvm::Error error = messages.takeError ()) {
110
- read_cb (loop, std::move (error));
111
- return ;
112
- }
113
-
114
- for (const auto &message : *messages)
115
- if constexpr (std::is_same<T, std::string>::value)
116
- read_cb (loop, message);
117
- else
118
- read_cb (loop, llvm::json::parse<T>(message));
119
- }
120
-
121
- // On EOF, notify the callback after the remaining messages were
122
- // handled.
123
- if (num_bytes == 0 ) {
124
- if (m_buffer.empty ())
125
- read_cb (loop, llvm::make_error<TransportEOFError>());
126
- else
127
- read_cb (loop, llvm::make_error<TransportUnhandledContentsError>(
128
- std::string (m_buffer)));
129
- }
130
- },
131
- error);
132
- if (error.Fail ())
133
- return error.takeError ();
134
- return handle;
135
- }
138
+ // / Public for testing purposes, otherwise this should be an implementation
139
+ // / detail.
140
+ static constexpr size_t kReadBufferSize = 1024 ;
136
141
137
142
protected:
138
- template <typename ... Ts> inline auto Logv (const char *Fmt, Ts &&...Vals) {
139
- Log (llvm::formatv (Fmt, std::forward<Ts>(Vals)...).str ());
143
+ virtual llvm::Expected<std::vector<std::string>> Parse () = 0;
144
+ virtual std::string Encode (const llvm::json::Value &message) = 0;
145
+ llvm::Error Write (const llvm::json::Value &message) {
146
+ this ->Logv (" <-- {0}" , message);
147
+ std::string output = Encode (message);
148
+ size_t bytes_written = output.size ();
149
+ return m_out->Write (output.data (), bytes_written).takeError ();
140
150
}
141
- virtual void Log (llvm::StringRef message);
142
151
143
- virtual llvm::Error WriteImpl (const std::string &message) = 0;
144
- virtual llvm::Expected<std::vector<std::string>> Parse () = 0;
152
+ llvm::SmallString<kReadBufferSize > m_buffer;
145
153
146
- static constexpr size_t kReadBufferSize = 1024 ;
154
+ private:
155
+ void OnRead (MainLoopBase &loop, MessageHandler &handler) {
156
+ char buf[kReadBufferSize ];
157
+ size_t num_bytes = sizeof (buf);
158
+ if (Status status = m_in->Read (buf, num_bytes); status.Fail ()) {
159
+ handler.OnError (status.takeError ());
160
+ return ;
161
+ }
162
+
163
+ if (num_bytes)
164
+ m_buffer.append (llvm::StringRef (buf, num_bytes));
165
+
166
+ // If the buffer has contents, try parsing any pending messages.
167
+ if (!m_buffer.empty ()) {
168
+ llvm::Expected<std::vector<std::string>> raw_messages = Parse ();
169
+ if (llvm::Error error = raw_messages.takeError ()) {
170
+ handler.OnError (std::move (error));
171
+ return ;
172
+ }
173
+
174
+ for (const std::string &raw_message : *raw_messages) {
175
+ llvm::Expected<typename Transport<Req, Resp, Evt>::Message> message =
176
+ llvm::json::parse<typename Transport<Req, Resp, Evt>::Message>(
177
+ raw_message);
178
+ if (!message) {
179
+ handler.OnError (message.takeError ());
180
+ return ;
181
+ }
182
+
183
+ std::visit ([&handler](auto &&msg) { handler.Received (msg); }, *message);
184
+ }
185
+ }
186
+
187
+ // Check if we reached EOF.
188
+ if (num_bytes == 0 ) {
189
+ // EOF reached, but there may still be unhandled contents in the buffer.
190
+ if (!m_buffer.empty ())
191
+ handler.OnError (llvm::make_error<TransportUnhandledContentsError>(
192
+ std::string (m_buffer.str ())));
193
+ handler.OnClosed ();
194
+ }
195
+ }
147
196
148
- lldb::IOObjectSP m_input;
149
- lldb::IOObjectSP m_output;
150
- llvm::SmallString<kReadBufferSize > m_buffer;
197
+ lldb::IOObjectSP m_in;
198
+ lldb::IOObjectSP m_out;
151
199
};
152
200
153
201
// / A transport class for JSON with a HTTP header.
154
- class HTTPDelimitedJSONTransport : public JSONTransport {
202
+ template <typename Req, typename Resp, typename Evt>
203
+ class HTTPDelimitedJSONTransport : public JSONTransport <Req, Resp, Evt> {
155
204
public:
156
- HTTPDelimitedJSONTransport (lldb::IOObjectSP input, lldb::IOObjectSP output)
157
- : JSONTransport(input, output) {}
158
- virtual ~HTTPDelimitedJSONTransport () = default ;
205
+ using JSONTransport<Req, Resp, Evt>::JSONTransport;
159
206
160
207
protected:
161
- llvm::Error WriteImpl (const std::string &message) override ;
162
- llvm::Expected<std::vector<std::string>> Parse () override ;
208
+ // / Encodes messages based on
209
+ // / https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol
210
+ std::string Encode (const llvm::json::Value &message) override {
211
+ std::string output;
212
+ std::string raw_message = llvm::formatv (" {0}" , message).str ();
213
+ llvm::raw_string_ostream OS (output);
214
+ OS << kHeaderContentLength << kHeaderFieldSeparator << ' '
215
+ << std::to_string (raw_message.size ()) << kEndOfHeader << raw_message;
216
+ return output;
217
+ }
218
+
219
+ // / Parses messages based on
220
+ // / https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol
221
+ llvm::Expected<std::vector<std::string>> Parse () override {
222
+ std::vector<std::string> messages;
223
+ llvm::StringRef buffer = this ->m_buffer ;
224
+ while (buffer.contains (kEndOfHeader )) {
225
+ auto [headers, rest] = buffer.split (kEndOfHeader );
226
+ size_t content_length = 0 ;
227
+ // HTTP Headers are formatted like `<field-name> ':' [<field-value>]`.
228
+ for (const llvm::StringRef &header :
229
+ llvm::split (headers, kHeaderSeparator )) {
230
+ auto [key, value] = header.split (kHeaderFieldSeparator );
231
+ // 'Content-Length' is the only meaningful key at the moment. Others are
232
+ // ignored.
233
+ if (!key.equals_insensitive (kHeaderContentLength ))
234
+ continue ;
235
+
236
+ value = value.trim ();
237
+ if (!llvm::to_integer (value, content_length, 10 )) {
238
+ // Clear the buffer to avoid re-parsing this malformed message.
239
+ this ->m_buffer .clear ();
240
+ return llvm::createStringError (std::errc::invalid_argument,
241
+ " invalid content length: %s" ,
242
+ value.str ().c_str ());
243
+ }
244
+ }
245
+
246
+ // Check if we have enough data.
247
+ if (content_length > rest.size ())
248
+ break ;
249
+
250
+ llvm::StringRef body = rest.take_front (content_length);
251
+ buffer = rest.drop_front (content_length);
252
+ messages.emplace_back (body.str ());
253
+ this ->Logv (" --> {0}" , body);
254
+ }
255
+
256
+ // Store the remainder of the buffer for the next read callback.
257
+ this ->m_buffer = buffer.str ();
258
+
259
+ return std::move (messages);
260
+ }
163
261
164
262
static constexpr llvm::StringLiteral kHeaderContentLength = " Content-Length" ;
165
263
static constexpr llvm::StringLiteral kHeaderFieldSeparator = " :" ;
@@ -168,15 +266,31 @@ class HTTPDelimitedJSONTransport : public JSONTransport {
168
266
};
169
267
170
268
// / A transport class for JSON RPC.
171
- class JSONRPCTransport : public JSONTransport {
269
+ template <typename Req, typename Resp, typename Evt>
270
+ class JSONRPCTransport : public JSONTransport <Req, Resp, Evt> {
172
271
public:
173
- JSONRPCTransport (lldb::IOObjectSP input, lldb::IOObjectSP output)
174
- : JSONTransport(input, output) {}
175
- virtual ~JSONRPCTransport () = default ;
272
+ using JSONTransport<Req, Resp, Evt>::JSONTransport;
176
273
177
274
protected:
178
- llvm::Error WriteImpl (const std::string &message) override ;
179
- llvm::Expected<std::vector<std::string>> Parse () override ;
275
+ std::string Encode (const llvm::json::Value &message) override {
276
+ return llvm::formatv (" {0}{1}" , message, kMessageSeparator ).str ();
277
+ }
278
+
279
+ llvm::Expected<std::vector<std::string>> Parse () override {
280
+ std::vector<std::string> messages;
281
+ llvm::StringRef buf = this ->m_buffer ;
282
+ while (buf.contains (kMessageSeparator )) {
283
+ auto [raw_json, rest] = buf.split (kMessageSeparator );
284
+ buf = rest;
285
+ messages.emplace_back (raw_json.str ());
286
+ this ->Logv (" --> {0}" , raw_json);
287
+ }
288
+
289
+ // Store the remainder of the buffer for the next read callback.
290
+ this ->m_buffer = buf.str ();
291
+
292
+ return messages;
293
+ }
180
294
181
295
static constexpr llvm::StringLiteral kMessageSeparator = " \n " ;
182
296
};
0 commit comments