1313#ifndef LLDB_HOST_JSONTRANSPORT_H
1414#define LLDB_HOST_JSONTRANSPORT_H
1515
16+ #include " lldb/Host/MainLoop.h"
1617#include " lldb/Host/MainLoopBase.h"
1718#include " lldb/Utility/IOObject.h"
1819#include " lldb/Utility/Status.h"
1920#include " lldb/lldb-forward.h"
21+ #include " llvm/ADT/StringExtras.h"
2022#include " llvm/ADT/StringRef.h"
2123#include " llvm/Support/Error.h"
24+ #include " llvm/Support/ErrorHandling.h"
2225#include " llvm/Support/FormatVariadic.h"
2326#include " llvm/Support/JSON.h"
27+ #include " llvm/Support/raw_ostream.h"
2428#include < string>
2529#include < system_error>
30+ #include < variant>
2631#include < vector>
2732
2833namespace lldb_private {
2934
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-
3935class TransportUnhandledContentsError
4036 : public llvm::ErrorInfo<TransportUnhandledContentsError> {
4137public:
@@ -54,112 +50,220 @@ class TransportUnhandledContentsError
5450 std::string m_unhandled_contents;
5551};
5652
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 {
5860public:
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;
6284
63- void log (llvm::raw_ostream &OS) const override ;
64- std::error_code convertToErrorCode () const override ;
85+ protected:
86+ template <typename ... Ts> inline auto Logv (const char *Fmt, Ts &&...Vals) {
87+ Log (llvm::formatv (Fmt, std::forward<Ts>(Vals)...).str ());
88+ }
89+ virtual void Log (llvm::StringRef message) = 0;
6590};
6691
67- // / A transport class that uses JSON for communication.
68- class JSONTransport {
92+ // / A JSONTransport will encode and decode messages using JSON.
93+ template <typename Req, typename Resp, typename Evt>
94+ class JSONTransport : public Transport <Req, Resp, Evt> {
6995public:
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);
96+ using Transport<Req, Resp, Evt>::Transport;
97+
98+ JSONTransport (lldb::IOObjectSP in, lldb::IOObjectSP out)
99+ : m_in(in), m_out(out) {}
100+
101+ void Event (const Evt &evt) override { Write (evt); }
102+ void Request (const Req &req) override { Write (req); }
103+ void Response (const Resp &resp) override { Write (resp); }
104+
105+ // / Run registers the transport with the given MainLoop and handles any
106+ // / incoming messages using the given MessageHandler.
107+ llvm::Error
108+ Run (MainLoop &loop,
109+ typename Transport<Req, Resp, Evt>::MessageHandler &handler) override {
110+ llvm::Error error = llvm::Error::success ();
111+ Status status;
112+ auto read_handle = loop.RegisterReadObject (
113+ m_in,
114+ std::bind (&JSONTransport::OnRead, this , &error, std::placeholders::_1,
115+ std::ref (handler)),
116+ status);
117+ if (status.Fail ()) {
118+ // This error is only set if the read object handler is invoked, mark it
119+ // as consumed if registration of the handler failed.
120+ llvm::consumeError (std::move (error));
121+ return status.takeError ();
122+ }
123+
124+ status = loop.Run ();
125+ if (status.Fail ())
126+ return status.takeError ();
127+ return error;
87128 }
88129
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- }
130+ // / Public for testing purposes, otherwise this should be an implementation
131+ // / detail.
132+ static constexpr size_t kReadBufferSize = 1024 ;
136133
137134protected:
138- template <typename ... Ts> inline auto Logv (const char *Fmt, Ts &&...Vals) {
139- Log (llvm::formatv (Fmt, std::forward<Ts>(Vals)...).str ());
135+ virtual llvm::Expected<std::vector<std::string>> Parse () = 0;
136+ virtual std::string Encode (const llvm::json::Value &message) = 0;
137+ void Write (const llvm::json::Value &message) {
138+ this ->Logv (" <-- {0}" , message);
139+ std::string output = Encode (message);
140+ size_t bytes_written = output.size ();
141+ Status status = m_out->Write (output.data (), bytes_written);
142+ if (status.Fail ()) {
143+ this ->Logv (" writing failed: s{0}" , status.AsCString ());
144+ }
140145 }
141- virtual void Log (llvm::StringRef message);
142146
143- virtual llvm::Error WriteImpl (const std::string &message) = 0;
144- virtual llvm::Expected<std::vector<std::string>> Parse () = 0;
147+ llvm::SmallString<kReadBufferSize > m_buffer;
145148
146- static constexpr size_t kReadBufferSize = 1024 ;
149+ private:
150+ void OnRead (llvm::Error *err, MainLoopBase &loop,
151+ typename Transport<Req, Resp, Evt>::MessageHandler &handler) {
152+ llvm::ErrorAsOutParameter ErrAsOutParam (err);
153+ char buf[kReadBufferSize ];
154+ size_t num_bytes = sizeof (buf);
155+ if (Status status = m_in->Read (buf, num_bytes); status.Fail ()) {
156+ *err = status.takeError ();
157+ loop.RequestTermination ();
158+ return ;
159+ }
160+
161+ if (num_bytes)
162+ m_buffer.append (llvm::StringRef (buf, num_bytes));
163+
164+ // If the buffer has contents, try parsing any pending messages.
165+ if (!m_buffer.empty ()) {
166+ llvm::Expected<std::vector<std::string>> raw_messages = Parse ();
167+ if (llvm::Error error = raw_messages.takeError ()) {
168+ *err = std::move (error);
169+ loop.RequestTermination ();
170+ return ;
171+ }
172+
173+ for (const auto &raw_message : *raw_messages) {
174+ auto message =
175+ llvm::json::parse<typename Transport<Req, Resp, Evt>::Message>(
176+ raw_message);
177+ if (!message) {
178+ *err = message.takeError ();
179+ loop.RequestTermination ();
180+ return ;
181+ }
182+
183+ if (Evt *evt = std::get_if<Evt>(&*message)) {
184+ handler.OnEvent (*evt);
185+ } else if (Req *req = std::get_if<Req>(&*message)) {
186+ handler.OnRequest (*req);
187+ } else if (Resp *resp = std::get_if<Resp>(&*message)) {
188+ handler.OnResponse (*resp);
189+ } else {
190+ llvm_unreachable (" unknown message type" );
191+ }
192+ }
193+ }
194+
195+ if (num_bytes == 0 ) {
196+ // If we're at EOF and we have unhandled contents in the buffer, return an
197+ // error for the partial message.
198+ if (m_buffer.empty ())
199+ *err = llvm::Error::success ();
200+ else
201+ *err = llvm::make_error<TransportUnhandledContentsError>(
202+ std::string (m_buffer));
203+ loop.RequestTermination ();
204+ }
205+ }
147206
148- lldb::IOObjectSP m_input;
149- lldb::IOObjectSP m_output;
150- llvm::SmallString<kReadBufferSize > m_buffer;
207+ lldb::IOObjectSP m_in;
208+ lldb::IOObjectSP m_out;
151209};
152210
153211// / A transport class for JSON with a HTTP header.
154- class HTTPDelimitedJSONTransport : public JSONTransport {
212+ template <typename Req, typename Resp, typename Evt>
213+ class HTTPDelimitedJSONTransport : public JSONTransport <Req, Resp, Evt> {
155214public:
156- HTTPDelimitedJSONTransport (lldb::IOObjectSP input, lldb::IOObjectSP output)
157- : JSONTransport(input, output) {}
158- virtual ~HTTPDelimitedJSONTransport () = default ;
215+ using JSONTransport<Req, Resp, Evt>::JSONTransport;
159216
160217protected:
161- llvm::Error WriteImpl (const std::string &message) override ;
162- llvm::Expected<std::vector<std::string>> Parse () override ;
218+ // / Encodes messages based on
219+ // / https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol
220+ std::string Encode (const llvm::json::Value &message) override {
221+ std::string output;
222+ std::string raw_message = llvm::formatv (" {0}" , message).str ();
223+ llvm::raw_string_ostream OS (output);
224+ OS << kHeaderContentLength << kHeaderFieldSeparator << ' '
225+ << std::to_string (raw_message.size ()) << kEndOfHeader << raw_message;
226+ return output;
227+ }
228+
229+ // / Parses messages based on
230+ // / https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol
231+ llvm::Expected<std::vector<std::string>> Parse () override {
232+ std::vector<std::string> messages;
233+ llvm::StringRef buffer = this ->m_buffer ;
234+ while (buffer.contains (kEndOfHeader )) {
235+ auto [headers, rest] = buffer.split (kEndOfHeader );
236+ size_t content_length = 0 ;
237+ // HTTP Headers are formatted like `<field-name> ':' [<field-value>]`.
238+ for (const auto &header : llvm::split (headers, kHeaderSeparator )) {
239+ auto [key, value] = header.split (kHeaderFieldSeparator );
240+ // 'Content-Length' is the only meaningful key at the moment. Others are
241+ // ignored.
242+ if (!key.equals_insensitive (kHeaderContentLength ))
243+ continue ;
244+
245+ value = value.trim ();
246+ if (!llvm::to_integer (value, content_length, 10 ))
247+ return llvm::createStringError (std::errc::invalid_argument,
248+ " invalid content length: %s" ,
249+ value.str ().c_str ());
250+ }
251+
252+ // Check if we have enough data.
253+ if (content_length > rest.size ())
254+ break ;
255+
256+ llvm::StringRef body = rest.take_front (content_length);
257+ buffer = rest.drop_front (content_length);
258+ messages.emplace_back (body.str ());
259+ this ->Logv (" --> {0}" , body);
260+ }
261+
262+ // Store the remainder of the buffer for the next read callback.
263+ this ->m_buffer = buffer.str ();
264+
265+ return std::move (messages);
266+ }
163267
164268 static constexpr llvm::StringLiteral kHeaderContentLength = " Content-Length" ;
165269 static constexpr llvm::StringLiteral kHeaderFieldSeparator = " :" ;
@@ -168,15 +272,31 @@ class HTTPDelimitedJSONTransport : public JSONTransport {
168272};
169273
170274// / A transport class for JSON RPC.
171- class JSONRPCTransport : public JSONTransport {
275+ template <typename Req, typename Resp, typename Evt>
276+ class JSONRPCTransport : public JSONTransport <Req, Resp, Evt> {
172277public:
173- JSONRPCTransport (lldb::IOObjectSP input, lldb::IOObjectSP output)
174- : JSONTransport(input, output) {}
175- virtual ~JSONRPCTransport () = default ;
278+ using JSONTransport<Req, Resp, Evt>::JSONTransport;
176279
177280protected:
178- llvm::Error WriteImpl (const std::string &message) override ;
179- llvm::Expected<std::vector<std::string>> Parse () override ;
281+ std::string Encode (const llvm::json::Value &message) override {
282+ return llvm::formatv (" {0}{1}" , message, kMessageSeparator ).str ();
283+ }
284+
285+ llvm::Expected<std::vector<std::string>> Parse () override {
286+ std::vector<std::string> messages;
287+ llvm::StringRef buf = this ->m_buffer ;
288+ while (buf.contains (kMessageSeparator )) {
289+ auto [raw_json, rest] = buf.split (kMessageSeparator );
290+ buf = rest;
291+ messages.emplace_back (raw_json.str ());
292+ this ->Logv (" --> {0}" , raw_json);
293+ }
294+
295+ // Store the remainder of the buffer for the next read callback.
296+ this ->m_buffer = buf.str ();
297+
298+ return messages;
299+ }
180300
181301 static constexpr llvm::StringLiteral kMessageSeparator = " \n " ;
182302};
0 commit comments