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,214 @@ 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+ // / 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 ;
62102
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;
65108};
66109
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> {
69113public:
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;
87136 }
88137
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 ;
136141
137142protected:
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 ();
140150 }
141- virtual void Log (llvm::StringRef message);
142151
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;
145153
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+ }
147196
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;
151199};
152200
153201// / 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> {
155204public:
156- HTTPDelimitedJSONTransport (lldb::IOObjectSP input, lldb::IOObjectSP output)
157- : JSONTransport(input, output) {}
158- virtual ~HTTPDelimitedJSONTransport () = default ;
205+ using JSONTransport<Req, Resp, Evt>::JSONTransport;
159206
160207protected:
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+ }
163261
164262 static constexpr llvm::StringLiteral kHeaderContentLength = " Content-Length" ;
165263 static constexpr llvm::StringLiteral kHeaderFieldSeparator = " :" ;
@@ -168,15 +266,31 @@ class HTTPDelimitedJSONTransport : public JSONTransport {
168266};
169267
170268// / 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> {
172271public:
173- JSONRPCTransport (lldb::IOObjectSP input, lldb::IOObjectSP output)
174- : JSONTransport(input, output) {}
175- virtual ~JSONRPCTransport () = default ;
272+ using JSONTransport<Req, Resp, Evt>::JSONTransport;
176273
177274protected:
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+ }
180294
181295 static constexpr llvm::StringLiteral kMessageSeparator = " \n " ;
182296};
0 commit comments