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,219 @@ 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
+ // Called by transport to send outgoing messages.
66
+ virtual void Event (const Evt &) = 0;
67
+ virtual void Request (const Req &) = 0;
68
+ virtual void Response (const Resp &) = 0;
69
+
70
+ // / Implemented to handle incoming messages. (See Run() below).
71
+ class MessageHandler {
72
+ public:
73
+ virtual ~MessageHandler () = default ;
74
+ virtual void OnEvent (const Evt &) = 0;
75
+ virtual void OnRequest (const Req &) = 0;
76
+ virtual void OnResponse (const Resp &) = 0;
77
+ };
78
+
79
+ // / Called by server or client to receive messages from the connection.
80
+ // / The transport should in turn invoke the handler to process messages.
81
+ // / The MainLoop is used to handle reading from the incoming connection and
82
+ // / will run until the loop is terminated.
83
+ virtual llvm::Error Run (MainLoop &, MessageHandler &) = 0;
62
84
63
- void log (llvm::raw_ostream &OS) const override ;
64
- std::error_code convertToErrorCode () const override ;
85
+ template <typename ... Ts> inline auto Logv (const char *Fmt, Ts &&...Vals) {
86
+ Log (llvm::formatv (Fmt, std::forward<Ts>(Vals)...).str ());
87
+ }
88
+ virtual void Log (llvm::StringRef message) = 0;
65
89
};
66
90
67
- // / A transport class that uses JSON for communication.
68
- class JSONTransport {
91
+ // / A JSONTransport will encode and decode messages using JSON.
92
+ template <typename Req, typename Resp, typename Evt>
93
+ class JSONTransport : public Transport <Req, Resp, Evt> {
69
94
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);
95
+ using Transport<Req, Resp, Evt>::Transport;
96
+
97
+ JSONTransport (lldb::IOObjectSP in, lldb::IOObjectSP out)
98
+ : m_in(in), m_out(out) {}
99
+
100
+ void Event (const Evt &evt) override { Write (evt); }
101
+ void Request (const Req &req) override { Write (req); }
102
+ void Response (const Resp &resp) override { Write (resp); }
103
+
104
+ // / Run registers the transport with the given MainLoop and handles any
105
+ // / incoming messages using the given MessageHandler.
106
+ llvm::Error
107
+ Run (MainLoop &loop,
108
+ typename Transport<Req, Resp, Evt>::MessageHandler &handler) override {
109
+ llvm::Error error = llvm::Error::success ();
110
+ Status status;
111
+ auto read_handle = loop.RegisterReadObject (
112
+ m_in,
113
+ std::bind (&JSONTransport::OnRead, this , &error, std::placeholders::_1,
114
+ std::ref (handler)),
115
+ status);
116
+ if (status.Fail ()) {
117
+ // This error is only set if the read object handler is invoked, mark it
118
+ // as consumed if registration of the handler failed.
119
+ llvm::consumeError (std::move (error));
120
+ return status.takeError ();
121
+ }
122
+
123
+ status = loop.Run ();
124
+ if (status.Fail ())
125
+ return status.takeError ();
126
+ return error;
87
127
}
88
128
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
- }
129
+ // / Public for testing purposes, otherwise this should be an implementation
130
+ // / detail.
131
+ static constexpr size_t kReadBufferSize = 1024 ;
136
132
137
133
protected:
138
- template <typename ... Ts> inline auto Logv (const char *Fmt, Ts &&...Vals) {
139
- Log (llvm::formatv (Fmt, std::forward<Ts>(Vals)...).str ());
134
+ virtual llvm::Expected<std::vector<std::string>> Parse () = 0;
135
+ virtual std::string Encode (const llvm::json::Value &message) = 0;
136
+ void Write (const llvm::json::Value &message) {
137
+ this ->Logv (" <-- {0}" , message);
138
+ std::string output = Encode (message);
139
+ size_t bytes_written = output.size ();
140
+ Status status = m_out->Write (output.data (), bytes_written);
141
+ if (status.Fail ()) {
142
+ this ->Logv (" writing failed: {0}" , status.AsCString ());
143
+ }
140
144
}
141
- virtual void Log (llvm::StringRef message);
142
145
143
- virtual llvm::Error WriteImpl (const std::string &message) = 0;
144
- virtual llvm::Expected<std::vector<std::string>> Parse () = 0;
146
+ llvm::SmallString<kReadBufferSize > m_buffer;
145
147
146
- static constexpr size_t kReadBufferSize = 1024 ;
148
+ private:
149
+ void OnRead (llvm::Error *err, MainLoopBase &loop,
150
+ typename Transport<Req, Resp, Evt>::MessageHandler &handler) {
151
+ llvm::ErrorAsOutParameter ErrAsOutParam (err);
152
+ char buf[kReadBufferSize ];
153
+ size_t num_bytes = sizeof (buf);
154
+ if (Status status = m_in->Read (buf, num_bytes); status.Fail ()) {
155
+ *err = status.takeError ();
156
+ loop.RequestTermination ();
157
+ return ;
158
+ }
159
+
160
+ if (num_bytes)
161
+ m_buffer.append (llvm::StringRef (buf, num_bytes));
162
+
163
+ // If the buffer has contents, try parsing any pending messages.
164
+ if (!m_buffer.empty ()) {
165
+ llvm::Expected<std::vector<std::string>> raw_messages = Parse ();
166
+ if (llvm::Error error = raw_messages.takeError ()) {
167
+ *err = std::move (error);
168
+ loop.RequestTermination ();
169
+ return ;
170
+ }
171
+
172
+ for (const auto &raw_message : *raw_messages) {
173
+ auto message =
174
+ llvm::json::parse<typename Transport<Req, Resp, Evt>::Message>(
175
+ raw_message);
176
+ if (!message) {
177
+ *err = message.takeError ();
178
+ loop.RequestTermination ();
179
+ return ;
180
+ }
181
+
182
+ if (Evt *evt = std::get_if<Evt>(&*message)) {
183
+ handler.OnEvent (*evt);
184
+ } else if (Req *req = std::get_if<Req>(&*message)) {
185
+ handler.OnRequest (*req);
186
+ } else if (Resp *resp = std::get_if<Resp>(&*message)) {
187
+ handler.OnResponse (*resp);
188
+ } else {
189
+ llvm_unreachable (" unknown message type" );
190
+ }
191
+ }
192
+ }
193
+
194
+ if (num_bytes == 0 ) {
195
+ // If we're at EOF and we have unhandled contents in the buffer, return an
196
+ // error for the partial message.
197
+ if (m_buffer.empty ())
198
+ *err = llvm::Error::success ();
199
+ else
200
+ *err = llvm::make_error<TransportUnhandledContentsError>(
201
+ std::string (m_buffer));
202
+ loop.RequestTermination ();
203
+ }
204
+ }
147
205
148
- lldb::IOObjectSP m_input;
149
- lldb::IOObjectSP m_output;
150
- llvm::SmallString<kReadBufferSize > m_buffer;
206
+ lldb::IOObjectSP m_in;
207
+ lldb::IOObjectSP m_out;
151
208
};
152
209
153
210
// / A transport class for JSON with a HTTP header.
154
- class HTTPDelimitedJSONTransport : public JSONTransport {
211
+ template <typename Req, typename Resp, typename Evt>
212
+ class HTTPDelimitedJSONTransport : public JSONTransport <Req, Resp, Evt> {
155
213
public:
156
- HTTPDelimitedJSONTransport (lldb::IOObjectSP input, lldb::IOObjectSP output)
157
- : JSONTransport(input, output) {}
158
- virtual ~HTTPDelimitedJSONTransport () = default ;
214
+ using JSONTransport<Req, Resp, Evt>::JSONTransport;
159
215
160
216
protected:
161
- llvm::Error WriteImpl (const std::string &message) override ;
162
- llvm::Expected<std::vector<std::string>> Parse () override ;
217
+ // / Encodes messages based on
218
+ // / https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol
219
+ std::string Encode (const llvm::json::Value &message) override {
220
+ std::string output;
221
+ std::string raw_message = llvm::formatv (" {0}" , message).str ();
222
+ llvm::raw_string_ostream OS (output);
223
+ OS << kHeaderContentLength << kHeaderFieldSeparator << ' '
224
+ << std::to_string (raw_message.size ()) << kEndOfHeader << raw_message;
225
+ return output;
226
+ }
227
+
228
+ // / Parses messages based on
229
+ // / https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol
230
+ llvm::Expected<std::vector<std::string>> Parse () override {
231
+ std::vector<std::string> messages;
232
+ llvm::StringRef buffer = this ->m_buffer ;
233
+ while (buffer.contains (kEndOfHeader )) {
234
+ auto [headers, rest] = buffer.split (kEndOfHeader );
235
+ size_t content_length = 0 ;
236
+ // HTTP Headers are formatted like `<field-name> ':' [<field-value>]`.
237
+ for (const auto &header : llvm::split (headers, kHeaderSeparator )) {
238
+ auto [key, value] = header.split (kHeaderFieldSeparator );
239
+ // 'Content-Length' is the only meaningful key at the moment. Others are
240
+ // ignored.
241
+ if (!key.equals_insensitive (kHeaderContentLength ))
242
+ continue ;
243
+
244
+ value = value.trim ();
245
+ if (!llvm::to_integer (value, content_length, 10 ))
246
+ return llvm::createStringError (std::errc::invalid_argument,
247
+ " invalid content length: %s" ,
248
+ value.str ().c_str ());
249
+ }
250
+
251
+ // Check if we have enough data.
252
+ if (content_length > rest.size ())
253
+ break ;
254
+
255
+ llvm::StringRef body = rest.take_front (content_length);
256
+ buffer = rest.drop_front (content_length);
257
+ messages.emplace_back (body.str ());
258
+ this ->Logv (" --> {0}" , body);
259
+ }
260
+
261
+ // Store the remainder of the buffer for the next read callback.
262
+ this ->m_buffer = buffer.str ();
263
+
264
+ return std::move (messages);
265
+ }
163
266
164
267
static constexpr llvm::StringLiteral kHeaderContentLength = " Content-Length" ;
165
268
static constexpr llvm::StringLiteral kHeaderFieldSeparator = " :" ;
@@ -168,15 +271,31 @@ class HTTPDelimitedJSONTransport : public JSONTransport {
168
271
};
169
272
170
273
// / A transport class for JSON RPC.
171
- class JSONRPCTransport : public JSONTransport {
274
+ template <typename Req, typename Resp, typename Evt>
275
+ class JSONRPCTransport : public JSONTransport <Req, Resp, Evt> {
172
276
public:
173
- JSONRPCTransport (lldb::IOObjectSP input, lldb::IOObjectSP output)
174
- : JSONTransport(input, output) {}
175
- virtual ~JSONRPCTransport () = default ;
277
+ using JSONTransport<Req, Resp, Evt>::JSONTransport;
176
278
177
279
protected:
178
- llvm::Error WriteImpl (const std::string &message) override ;
179
- llvm::Expected<std::vector<std::string>> Parse () override ;
280
+ std::string Encode (const llvm::json::Value &message) override {
281
+ return llvm::formatv (" {0}{1}" , message, kMessageSeparator ).str ();
282
+ }
283
+
284
+ llvm::Expected<std::vector<std::string>> Parse () override {
285
+ std::vector<std::string> messages;
286
+ llvm::StringRef buf = this ->m_buffer ;
287
+ while (buf.contains (kMessageSeparator )) {
288
+ auto [raw_json, rest] = buf.split (kMessageSeparator );
289
+ buf = rest;
290
+ messages.emplace_back (raw_json.str ());
291
+ this ->Logv (" --> {0}" , raw_json);
292
+ }
293
+
294
+ // Store the remainder of the buffer for the next read callback.
295
+ this ->m_buffer = buf.str ();
296
+
297
+ return messages;
298
+ }
180
299
181
300
static constexpr llvm::StringLiteral kMessageSeparator = " \n " ;
182
301
};
0 commit comments