LLDB mainline
JSONTransport.h
Go to the documentation of this file.
1//===-- JSONTransport.h ---------------------------------------------------===//
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// Transport layer for encoding and decoding JSON protocol messages.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLDB_HOST_JSONTRANSPORT_H
14#define LLDB_HOST_JSONTRANSPORT_H
15
16#include "lldb/Host/MainLoop.h"
19#include "lldb/Utility/Status.h"
20#include "lldb/lldb-forward.h"
21#include "llvm/ADT/FunctionExtras.h"
22#include "llvm/ADT/StringExtras.h"
23#include "llvm/ADT/StringRef.h"
24#include "llvm/Support/Error.h"
25#include "llvm/Support/ErrorHandling.h"
26#include "llvm/Support/FormatVariadic.h"
27#include "llvm/Support/JSON.h"
28#include "llvm/Support/raw_ostream.h"
29#include <atomic>
30#include <functional>
31#include <mutex>
32#include <optional>
33#include <string>
34#include <system_error>
35#include <type_traits>
36#include <utility>
37#include <variant>
38#include <vector>
39#if __cplusplus >= 202002L
40#include <concepts>
41#endif
42
44
45/// An error to indicate that the transport reached EOF but there were still
46/// unhandled contents in the read buffer.
48 : public llvm::ErrorInfo<TransportUnhandledContentsError> {
49public:
50 static char ID;
51
52 explicit TransportUnhandledContentsError(std::string unhandled_contents);
53
54 void log(llvm::raw_ostream &OS) const override;
55 std::error_code convertToErrorCode() const override;
56
57 const std::string &getUnhandledContents() const {
59 }
60
61private:
63};
64
65/// An error to indicate that the parameters of a Req, Resp or Evt could not be
66/// deserialized.
67class InvalidParams : public llvm::ErrorInfo<InvalidParams> {
68public:
69 static char ID;
70
71 static constexpr int kErrorCode = -32602;
72
73 explicit InvalidParams(std::string method, std::string context)
74 : m_method(std::move(method)), m_context(std::move(context)) {}
75
76 void log(llvm::raw_ostream &OS) const override;
77 std::error_code convertToErrorCode() const override;
78
79private:
80 /// The JSONRPC remote method call.
81 std::string m_method;
82
83 /// Additional context from the parsing failure, e.g. "missing value at
84 /// (root)[1].str".
85 std::string m_context;
86};
87
88/// An error to indicate that an incoming message could not be parsed as a
89/// valid protocol message.
90class InvalidMessage : public llvm::ErrorInfo<InvalidMessage> {
91public:
92 static char ID;
93
94 static constexpr int kErrorCode = -32700;
95
96 explicit InvalidMessage(std::string raw_message, std::string reason)
97 : m_raw_message(std::move(raw_message)), m_reason(std::move(reason)) {}
98
99 void log(llvm::raw_ostream &OS) const override;
100 std::error_code convertToErrorCode() const override;
101
102private:
103 std::string m_raw_message;
104 std::string m_reason;
105};
106
107/// An error to indicate that no handler was registered for a given method.
108class MethodNotFound : public llvm::ErrorInfo<MethodNotFound> {
109public:
110 static char ID;
111
112 static constexpr int kErrorCode = -32601;
113
114 explicit MethodNotFound(std::string method) : m_method(std::move(method)) {}
115
116 void log(llvm::raw_ostream &OS) const override;
117 std::error_code convertToErrorCode() const override;
118
119private:
120 std::string m_method;
121};
122
123#if __cplusplus >= 202002L
124/// A ProtocolDescriptor details the types used in a JSONTransport for handling
125/// transport communication.
126template <typename T>
127concept ProtocolDescriptor = requires {
128 typename T::Id;
129 typename T::Req;
130 typename T::Resp;
131 typename T::Evt;
132};
133#endif
134
135/// A transport is responsible for maintaining the connection to a client
136/// application, and reading/writing structured messages to it.
137///
138/// JSONTransport have limited thread safety requirements:
139/// - Messages will not be sent concurrently.
140/// - Messages MAY be sent while Run() is reading, or its callback is active.
141///
142#if __cplusplus >= 202002L
143template <ProtocolDescriptor Proto>
144#else
145template <typename Proto>
146#endif
148public:
149 using Req = typename Proto::Req;
150 using Resp = typename Proto::Resp;
151 using Evt = typename Proto::Evt;
152 using Message = std::variant<Req, Resp, Evt>;
153
154 virtual ~JSONTransport() = default;
155
156 /// Sends an event, a message that does not require a response.
157 virtual llvm::Error Send(const Evt &) = 0;
158 /// Sends a request, a message that expects a response.
159 virtual llvm::Error Send(const Req &) = 0;
160 /// Sends a response to a specific request.
161 virtual llvm::Error Send(const Resp &) = 0;
162
163 /// Sends an error response for a message that failed to parse, described by
164 /// \p reason. Sends nothing if no request id can be recovered from it, since
165 /// there is then no request to respond to.
166 virtual llvm::Error ReplyWithParseError(llvm::StringRef raw_message,
167 llvm::StringRef reason) {
168 return llvm::Error::success();
169 }
170
171 /// Implemented to handle incoming messages. (See `RegisterMessageHandler()`
172 /// below).
174 public:
175 virtual ~MessageHandler() = default;
176 /// Called when an event is received.
177 virtual void Received(const Evt &) = 0;
178 /// Called when a request is received.
179 virtual void Received(const Req &) = 0;
180 /// Called when a response is received.
181 virtual void Received(const Resp &) = 0;
182
183 /// Called when an error occurs while reading from the transport.
184 ///
185 /// NOTE: This does *NOT* indicate that a specific request failed, but that
186 /// there was an error in the underlying transport.
187 virtual void OnError(llvm::Error) = 0;
188
189 /// Called on EOF or client disconnect.
190 virtual void OnClosed() = 0;
191 };
192
193 /// RegisterMessageHandler registers the Transport with the given MainLoop and
194 /// handles any incoming messages using the given MessageHandler.
195 ///
196 /// If an unexpected error occurs, the MainLoop will be terminated and a log
197 /// message will include additional information about the termination reason.
198 virtual llvm::Error RegisterMessageHandler(MessageHandler &handler) = 0;
199
200protected:
201 template <typename... Ts> inline auto Logv(const char *Fmt, Ts &&...Vals) {
202 Log(llvm::formatv(Fmt, std::forward<Ts>(Vals)...).str());
203 }
204 virtual void Log(llvm::StringRef message) = 0;
205};
206
207/// An IOTransport sends and receives messages using an IOObject.
208template <typename Proto> class IOTransport : public JSONTransport<Proto> {
209public:
212
214 : m_loop(loop), m_in(in), m_out(out) {}
215
216 llvm::Error Send(const typename Proto::Evt &evt) override {
217 return Write(evt);
218 }
219
220 llvm::Error Send(const typename Proto::Req &req) override {
221 return Write(req);
222 }
223
224 llvm::Error Send(const typename Proto::Resp &resp) override {
225 return Write(resp);
226 }
227
228 llvm::Error RegisterMessageHandler(MessageHandler &handler) override {
229 Status status;
230 m_read_handle = m_loop.RegisterReadObject(
231 m_in, [this, &handler](MainLoopBase &base) { OnRead(base, handler); },
232 status);
233 return status.takeError();
234 }
235
236 /// Public for testing purposes, otherwise this should be an implementation
237 /// detail.
238 static constexpr size_t kReadBufferSize = 1024;
239
240protected:
241 llvm::Error Write(const llvm::json::Value &message) {
242 std::string output = Encode(message);
243 size_t bytes_written = output.size();
244 llvm::Error err = m_out->Write(output.data(), bytes_written).takeError();
245 if (err)
246 this->Logv("write failed [{0}]: <-- {1}",
247 llvm::toStringWithoutConsuming(err), message);
248 else
249 this->Logv("<-- {0}", message);
250 return err;
251 }
252
253 virtual llvm::Expected<std::vector<std::string>> Parse() = 0;
254 virtual std::string Encode(const llvm::json::Value &message) = 0;
255
256 llvm::SmallString<kReadBufferSize> m_buffer;
257
258private:
259 void OnRead(MainLoopBase &loop, MessageHandler &handler) {
260 char buf[kReadBufferSize];
261 size_t num_bytes = sizeof(buf);
262 if (Status status = m_in->Read(buf, num_bytes); status.Fail()) {
263 handler.OnError(status.takeError());
264 return;
265 }
266
267 if (num_bytes)
268 m_buffer.append(llvm::StringRef(buf, num_bytes));
269
270 // If the buffer has contents, try parsing any pending messages.
271 if (!m_buffer.empty()) {
272 llvm::Expected<std::vector<std::string>> raw_messages = Parse();
273 if (llvm::Error error = raw_messages.takeError()) {
274 handler.OnError(std::move(error));
275 return;
276 }
277
278 for (const std::string &raw_message : *raw_messages) {
279 llvm::Expected<Message> message =
280 llvm::json::parse<Message>(raw_message);
281 if (!message) {
282 // Messages are independent, so one that fails to parse must not
283 // discard those already buffered behind it.
284 std::string reason = llvm::toString(message.takeError());
285 if (llvm::Error error =
286 this->ReplyWithParseError(raw_message, reason))
287 handler.OnError(std::move(error));
288 handler.OnError(
289 llvm::make_error<InvalidMessage>(raw_message, std::move(reason)));
290 continue;
291 }
292
293 std::visit([&handler](auto &&msg) { handler.Received(msg); }, *message);
294 }
295 }
296
297 // Check if we reached EOF.
298 if (num_bytes == 0) {
299 // EOF reached, but there may still be unhandled contents in the buffer.
300 if (!m_buffer.empty())
301 handler.OnError(llvm::make_error<TransportUnhandledContentsError>(
302 std::string(m_buffer.str())));
303 // Move the read handle to a local before notifying the handler. The
304 // handler may destroy this transport (e.g. by erasing it from a
305 // connection map), so accessing members after OnClosed() is unsafe.
306 auto read_handle = std::move(m_read_handle);
307 handler.OnClosed();
308 }
309 }
310
315};
316
317/// A transport class for JSON with a HTTP header.
318#if __cplusplus >= 202002L
319template <ProtocolDescriptor Proto>
320#else
321template <typename Proto>
322#endif
324public:
325 using IOTransport<Proto>::IOTransport;
326
327protected:
328 /// Encodes messages based on
329 /// https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol
330 std::string Encode(const llvm::json::Value &message) override {
331 std::string output;
332 std::string raw_message = llvm::formatv("{0}", message).str();
333 llvm::raw_string_ostream OS(output);
335 << std::to_string(raw_message.size()) << kEndOfHeader << raw_message;
336 return output;
337 }
338
339 /// Parses messages based on
340 /// https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol
341 llvm::Expected<std::vector<std::string>> Parse() override {
342 std::vector<std::string> messages;
343 llvm::StringRef buffer = this->m_buffer;
344 while (buffer.contains(kEndOfHeader)) {
345 auto [headers, rest] = buffer.split(kEndOfHeader);
346 size_t content_length = 0;
347 // HTTP Headers are formatted like `<field-name> ':' [<field-value>]`.
348 for (const llvm::StringRef &header :
349 llvm::split(headers, kHeaderSeparator)) {
350 auto [key, value] = header.split(kHeaderFieldSeparator);
351 // 'Content-Length' is the only meaningful key at the moment. Others
352 // are ignored.
353 if (!key.equals_insensitive(kHeaderContentLength))
354 continue;
355
356 value = value.trim();
357 if (!llvm::to_integer(value, content_length, 10)) {
358 // Clear the buffer to avoid re-parsing this malformed message.
359 this->m_buffer.clear();
360 return llvm::createStringError(std::errc::invalid_argument,
361 "invalid content length: %s",
362 value.str().c_str());
363 }
364 }
365
366 // Check if we have enough data.
367 if (content_length > rest.size())
368 break;
369
370 llvm::StringRef body = rest.take_front(content_length);
371 buffer = rest.drop_front(content_length);
372 messages.emplace_back(body.str());
373 this->Logv("--> {0}", body);
374 }
375
376 // Store the remainder of the buffer for the next read callback.
377 this->m_buffer = buffer.str();
378
379 return std::move(messages);
380 }
381
382 static constexpr llvm::StringLiteral kHeaderContentLength = "Content-Length";
383 static constexpr llvm::StringLiteral kHeaderFieldSeparator = ":";
384 static constexpr llvm::StringLiteral kHeaderSeparator = "\r\n";
385 static constexpr llvm::StringLiteral kEndOfHeader = "\r\n\r\n";
386};
387
388/// A transport class for JSON RPC.
389#if __cplusplus >= 202002L
390template <ProtocolDescriptor Proto>
391#else
392template <typename Proto>
393#endif
394class JSONRPCTransport : public IOTransport<Proto> {
395public:
396 using IOTransport<Proto>::IOTransport;
397
398protected:
399 std::string Encode(const llvm::json::Value &message) override {
400 return llvm::formatv("{0}{1}", message, kMessageSeparator).str();
401 }
402
403 llvm::Expected<std::vector<std::string>> Parse() override {
404 std::vector<std::string> messages;
405 llvm::StringRef buf = this->m_buffer;
406 while (buf.contains(kMessageSeparator)) {
407 auto [raw_json, rest] = buf.split(kMessageSeparator);
408 buf = rest;
409 messages.emplace_back(raw_json.str());
410 this->Logv("--> {0}", raw_json);
411 }
412
413 // Store the remainder of the buffer for the next read callback.
414 this->m_buffer = buf.str();
415
416 return messages;
417 }
418
419 static constexpr llvm::StringLiteral kMessageSeparator = "\n";
420};
421
422/// A handler for the response to an outgoing request.
423template <typename T>
424using Reply =
425 std::conditional_t<std::is_void_v<T>,
426 llvm::unique_function<void(llvm::Error)>,
427 llvm::unique_function<void(llvm::Expected<T>)>>;
428
429namespace detail {
430template <typename R, typename P> struct request_t final {
431 using type = llvm::unique_function<void(const P &, Reply<R>)>;
432};
433template <typename R> struct request_t<R, void> final {
434 using type = llvm::unique_function<void(Reply<R>)>;
435};
436template <typename P> struct event_t final {
437 using type = llvm::unique_function<void(const P &)>;
438};
439template <> struct event_t<void> final {
440 using type = llvm::unique_function<void()>;
441};
442} // namespace detail
443
444template <typename R, typename P>
446
447/// A function to send an outgoing event.
448template <typename P> using OutgoingEvent = typename detail::event_t<P>::type;
449
450#if __cplusplus >= 202002L
451/// This represents a protocol description that includes additional helpers
452/// for constructing requests, responses and events to work with `Binder`.
453template <typename T>
454concept BindingBuilder =
455 ProtocolDescriptor<T> &&
456 requires(T::Id id, T::Req req, T::Resp resp, T::Evt evt,
457 llvm::StringRef method, std::optional<llvm::json::Value> params,
458 std::optional<llvm::json::Value> result, llvm::Error err) {
459 /// For initializing the unique sequence identifier;
460 { T::InitialId() } -> std::same_as<typename T::Id>;
461 /// Incrementing the sequence identifier.
462 { id++ } -> std::same_as<typename T::Id>;
463
464 /// Constructing protocol types
465 /// @{
466 /// Construct a new request.
467 { T::Make(id, method, params) } -> std::same_as<typename T::Req>;
468 /// Construct a new error response.
469 { T::Make(req, std::move(err)) } -> std::same_as<typename T::Resp>;
470 /// Construct a new success response.
471 { T::Make(req, result) } -> std::same_as<typename T::Resp>;
472 /// Construct a new event.
473 { T::Make(method, params) } -> std::same_as<typename T::Evt>;
474 /// @}
475
476 /// Keys for associated types.
477 /// @{
478 /// Looking up in flight responses.
479 { T::KeyFor(resp) } -> std::same_as<typename T::Id>;
480 /// Extract method from request.
481 { T::KeyFor(req) } -> std::same_as<std::string>;
482 /// Extract method from event.
483 { T::KeyFor(evt) } -> std::same_as<std::string>;
484 /// @}
485
486 /// Extracting information from associated types.
487 /// @{
488 /// Extract parameters from a request.
489 { T::Extract(req) } -> std::same_as<std::optional<llvm::json::Value>>;
490 /// Extract result from a response.
491 { T::Extract(resp) } -> std::same_as<llvm::Expected<llvm::json::Value>>;
492 /// Extract parameters from an event.
493 { T::Extract(evt) } -> std::same_as<std::optional<llvm::json::Value>>;
494 /// @}
495 };
496#endif
497
498/// Binder collects a table of functions that handle calls.
499///
500/// The wrapper takes care of parsing/serializing responses.
501///
502/// This allows a JSONTransport to handle incoming and outgoing requests and
503/// events.
504///
505/// A bind of an incoming request to a lambda.
506/// \code{cpp}
507/// Binder binder{transport};
508/// binder.bind<int, vector<int>>("adder", [](const vector<int> &params) {
509/// int sum = 0;
510/// for (int v : params)
511/// sum += v;
512/// return sum;
513/// });
514/// \endcode
515///
516/// A bind of an outgoing request.
517/// \code{cpp}
518/// OutgoingRequest<int, vector<int>> call_add =
519/// binder.bind<int, vector<int>>("add");
520/// call_add({1,2,3}, [](Expected<int> result) {
521/// cout << *result << "\n";
522/// });
523/// \endcode
524#if __cplusplus >= 202002L
525template <BindingBuilder Proto>
526#else
527template <typename Proto>
528#endif
530 using Req = typename Proto::Req;
531 using Resp = typename Proto::Resp;
532 using Evt = typename Proto::Evt;
533 using Id = typename Proto::Id;
536
537public:
539
540 Binder(const Binder &) = delete;
541 Binder &operator=(const Binder &) = delete;
542
543 /// Bind a handler on transport disconnect.
544 template <typename Fn, typename... Args>
545 void OnDisconnect(Fn &&fn, Args &&...args);
546
547 /// Bind a handler on error when communicating with the transport.
548 template <typename Fn, typename... Args>
549 void OnError(Fn &&fn, Args &&...args);
550
551 /// Bind a handler for an incoming request.
552 /// e.g. `bind("peek", &ThisModule::peek, this);`.
553 /// Handler should be e.g. `Expected<PeekResult> peek(const PeekParams&);`
554 /// PeekParams must be JSON parsable and PeekResult must be serializable.
555 template <typename Result, typename Params, typename Fn, typename... Args>
556 void Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args);
557
558 /// Bind an asynchronous handler for an incoming request. The handler receives
559 /// a Reply to invoke later instead of returning a result. This lets it defer
560 /// the response, e.g. until a request it forwarded elsewhere is answered.
561 /// Handler should be e.g. `void peek(const PeekParams&, Reply<PeekResult>);`
562 /// PeekParams must be JSON parsable and PeekResult must be serializable.
563 template <typename Result, typename Params, typename Fn, typename... Args>
564 void BindAsync(llvm::StringLiteral method, Fn &&fn, Args &&...args);
565
566 /// Bind a handler for an incoming event.
567 /// e.g. `bind("peek", &ThisModule::peek, this);`
568 /// Handler should be e.g. `void peek(const PeekParams&);`
569 /// PeekParams must be JSON parsable.
570 template <typename Params, typename Fn, typename... Args>
571 void Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args);
572
573 /// Bind a function object to be used for outgoing requests.
574 /// e.g. `OutgoingRequest<Params, Result> Edit = bind("edit");`
575 /// Params must be JSON-serializable, Result must be parsable.
576 template <typename Result, typename Params>
577 OutgoingRequest<Result, Params> Bind(llvm::StringLiteral method);
578
579 /// Bind a function object to be used for outgoing events.
580 /// e.g. `OutgoingEvent<LogParams> Log = bind("log");`
581 /// LogParams must be JSON-serializable.
582 template <typename Params>
583 OutgoingEvent<Params> Bind(llvm::StringLiteral method);
584
585 void Received(const Evt &evt) override {
586 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
587 auto it = m_event_handlers.find(Proto::KeyFor(evt));
588 if (it == m_event_handlers.end()) {
589 OnError(llvm::createStringError(
590 llvm::formatv("no handler for event {0}", toJSON(evt))));
591 return;
592 }
593 it->second(evt);
594 }
595
596 void Received(const Req &req) override {
597 ReplyOnce reply(req, &m_transport, this);
598
599 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
600 auto it = m_request_handlers.find(Proto::KeyFor(req));
601 if (it == m_request_handlers.end()) {
602 reply(Proto::Make(req,
603 llvm::make_error<MethodNotFound>(Proto::KeyFor(req))));
604 return;
605 }
606
607 it->second(req, std::move(reply));
608 }
609
610 void Received(const Resp &resp) override {
611 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
612
613 Id id = Proto::KeyFor(resp);
614 auto it = m_pending_responses.find(id);
615 if (it == m_pending_responses.end()) {
616 OnError(llvm::createStringError(
617 llvm::formatv("no pending request for {0}", toJSON(resp))));
618 return;
619 }
620
621 it->second(resp);
622 m_pending_responses.erase(it);
623 }
624
625 void OnError(llvm::Error err) override {
626 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
627 if (m_error_handler)
628 m_error_handler(std::move(err));
629 }
630
631 void OnClosed() override {
632 // The disconnect handler may destroy this Binder -- e.g. the server
633 // removes the disconnected client, which owns the transport and, with it,
634 // this handler. Move the handler out and release the lock before invoking
635 // it, so we neither run the teardown while holding m_mutex nor destroy a
636 // still-locked mutex.
637 Callback<void()> disconnect_handler;
638 {
639 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
640 disconnect_handler = std::move(m_disconnect_handler);
641 }
642 if (disconnect_handler)
643 disconnect_handler();
644 }
645
646 /// Fails every in-flight outgoing request, invoking its reply with an error.
647 /// Call when the connection is going away, so pending replies are satisfied
648 /// rather than destroyed unanswered.
649 void FailPendingRequests(llvm::StringRef reason) {
650 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
651 std::map<Id, Callback<void(const Resp &)>> pending;
652 std::swap(pending, m_pending_responses);
653 for (auto &entry : pending) {
654 Req req = Proto::Make(entry.first, /*method=*/"", std::nullopt);
655 entry.second(Proto::Make(req, llvm::createStringError(reason)));
656 }
657 }
658
659private:
660 template <typename T>
661 llvm::Expected<T> static Parse(const llvm::json::Value &raw,
662 llvm::StringRef method);
663
664 template <typename T> using Callback = llvm::unique_function<T>;
665
666 std::recursive_mutex m_mutex;
669 std::map<Id, Callback<void(const Resp &)>> m_pending_responses;
670 llvm::StringMap<Callback<void(const Req &, Callback<void(const Resp &)>)>>
672 llvm::StringMap<Callback<void(const Evt &)>> m_event_handlers;
674 Callback<void(llvm::Error)> m_error_handler;
675
676 /// Function object to reply to a call.
677 /// Each instance must be called exactly once, otherwise:
678 /// - the bug is logged, and (in debug mode) an assert will fire
679 /// - if there was no reply, an error reply is sent
680 /// - if there were multiple replies, only the first is sent
681 class ReplyOnce {
682 std::atomic<bool> replied = {false};
683 const Req req;
684 Transport *transport; // Null when moved-from.
685 MessageHandler *handler; // Null when moved-from.
686
687 public:
693 : replied(other.replied.load()), req(other.req),
694 transport(other.transport), handler(other.handler) {
695 other.transport = nullptr;
696 other.handler = nullptr;
697 }
699 ReplyOnce(const ReplyOnce &) = delete;
700 ReplyOnce &operator=(const ReplyOnce &) = delete;
701
703 if (transport && handler && !replied) {
704 assert(false && "must reply to all calls!");
705 (*this)(Proto::Make(req, llvm::createStringError("failed to reply")));
706 }
707 }
708
709 void operator()(const Resp &resp) {
710 assert(transport && handler && "moved-from!");
711 if (replied.exchange(true)) {
712 assert(false && "must reply to each call only once!");
713 return;
714 }
715
716 if (llvm::Error error = transport->Send(resp))
717 handler->OnError(std::move(error));
718 }
719 };
720};
721
722#if __cplusplus >= 202002L
723template <BindingBuilder Proto>
724#else
725template <typename Proto>
726#endif
727template <typename Fn, typename... Args>
728void Binder<Proto>::OnDisconnect(Fn &&fn, Args &&...args) {
729 m_disconnect_handler = [fn, args...]() mutable {
730 std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
731 };
732}
733
734#if __cplusplus >= 202002L
735template <BindingBuilder Proto>
736#else
737template <typename Proto>
738#endif
739template <typename Fn, typename... Args>
740void Binder<Proto>::OnError(Fn &&fn, Args &&...args) {
741 m_error_handler = [fn, args...](llvm::Error error) mutable {
742 std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...,
743 std::move(error));
744 };
745}
746
747#if __cplusplus >= 202002L
748template <BindingBuilder Proto>
749#else
750template <typename Proto>
751#endif
752template <typename Result, typename Params, typename Fn, typename... Args>
753void Binder<Proto>::Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args) {
754 assert(m_request_handlers.find(method) == m_request_handlers.end() &&
755 "request already bound");
756 if constexpr (std::is_void_v<Result> && std::is_void_v<Params>) {
757 m_request_handlers[method] =
758 [fn, args...](const Req &req,
759 llvm::unique_function<void(const Resp &)> reply) mutable {
760 llvm::Error result =
761 std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
762 reply(Proto::Make(req, std::move(result)));
763 };
764 } else if constexpr (std::is_void_v<Params>) {
765 m_request_handlers[method] =
766 [fn, args...](const Req &req,
767 llvm::unique_function<void(const Resp &)> reply) mutable {
768 llvm::Expected<Result> result =
769 std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
770 if (!result)
771 return reply(Proto::Make(req, result.takeError()));
772 reply(Proto::Make(req, toJSON(*result)));
773 };
774 } else if constexpr (std::is_void_v<Result>) {
775 m_request_handlers[method] =
776 [method, fn,
777 args...](const Req &req,
778 llvm::unique_function<void(const Resp &)> reply) mutable {
779 llvm::Expected<Params> params =
780 Parse<Params>(Proto::Extract(req), method);
781 if (!params)
782 return reply(Proto::Make(req, params.takeError()));
783
784 llvm::Error result = std::invoke(
785 std::forward<Fn>(fn), std::forward<Args>(args)..., *params);
786 reply(Proto::Make(req, std::move(result)));
787 };
788 } else {
789 m_request_handlers[method] =
790 [method, fn,
791 args...](const Req &req,
792 llvm::unique_function<void(const Resp &)> reply) mutable {
793 llvm::Expected<Params> params =
794 Parse<Params>(Proto::Extract(req), method);
795 if (!params)
796 return reply(Proto::Make(req, params.takeError()));
797
798 llvm::Expected<Result> result = std::invoke(
799 std::forward<Fn>(fn), std::forward<Args>(args)..., *params);
800 if (!result)
801 return reply(Proto::Make(req, result.takeError()));
802
803 reply(Proto::Make(req, toJSON(*result)));
804 };
805 }
806}
807
808#if __cplusplus >= 202002L
809template <BindingBuilder Proto>
810#else
811template <typename Proto>
812#endif
813template <typename Params, typename Fn, typename... Args>
814void Binder<Proto>::Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args) {
815 assert(m_event_handlers.find(method) == m_event_handlers.end() &&
816 "event already bound");
817 if constexpr (std::is_void_v<Params>) {
818 m_event_handlers[method] = [fn, args...](const Evt &) mutable {
819 std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
820 };
821 } else {
822 m_event_handlers[method] = [this, method, fn,
823 args...](const Evt &evt) mutable {
824 llvm::Expected<Params> params =
825 Parse<Params>(Proto::Extract(evt), method);
826 if (!params)
827 return OnError(params.takeError());
828 std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)..., *params);
829 };
830 }
831}
832
833#if __cplusplus >= 202002L
834template <BindingBuilder Proto>
835#else
836template <typename Proto>
837#endif
838template <typename Result, typename Params>
840Binder<Proto>::Bind(llvm::StringLiteral method) {
841 if constexpr (std::is_void_v<Result> && std::is_void_v<Params>) {
842 return [this, method](Reply<Result> fn) {
843 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
844 Id id = ++m_seq;
845 Req req = Proto::Make(id, method, std::nullopt);
846 m_pending_responses[id] = [fn = std::move(fn)](const Resp &resp) mutable {
847 llvm::Expected<llvm::json::Value> result = Proto::Extract(resp);
848 if (!result)
849 return fn(result.takeError());
850 fn(llvm::Error::success());
851 };
852 if (llvm::Error error = m_transport.Send(req))
853 OnError(std::move(error));
854 };
855 } else if constexpr (std::is_void_v<Params>) {
856 return [this, method](Reply<Result> fn) {
857 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
858 Id id = ++m_seq;
859 Req req = Proto::Make(id, method, std::nullopt);
860 m_pending_responses[id] = [fn = std::move(fn),
861 method](const Resp &resp) mutable {
862 llvm::Expected<llvm::json::Value> result = Proto::Extract(resp);
863 if (!result)
864 return fn(result.takeError());
865 fn(Parse<Result>(*result, method));
866 };
867 if (llvm::Error error = m_transport.Send(req))
868 OnError(std::move(error));
869 };
870 } else if constexpr (std::is_void_v<Result>) {
871 return [this, method](const Params &params, Reply<Result> fn) {
872 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
873 Id id = ++m_seq;
874 Req req = Proto::Make(id, method, llvm::json::Value(params));
875 m_pending_responses[id] = [fn = std::move(fn)](const Resp &resp) mutable {
876 llvm::Expected<llvm::json::Value> result = Proto::Extract(resp);
877 if (!result)
878 return fn(result.takeError());
879 fn(llvm::Error::success());
880 };
881 if (llvm::Error error = m_transport.Send(req))
882 OnError(std::move(error));
883 };
884 } else {
885 return [this, method](const Params &params, Reply<Result> fn) {
886 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
887 Id id = ++m_seq;
888 Req req = Proto::Make(id, method, llvm::json::Value(params));
889 m_pending_responses[id] = [fn = std::move(fn),
890 method](const Resp &resp) mutable {
891 llvm::Expected<llvm::json::Value> result = Proto::Extract(resp);
892 if (llvm::Error err = result.takeError())
893 return fn(std::move(err));
894 fn(Parse<Result>(*result, method));
895 };
896 if (llvm::Error error = m_transport.Send(req))
897 OnError(std::move(error));
898 };
899 }
900}
901
902#if __cplusplus >= 202002L
903template <BindingBuilder Proto>
904#else
905template <typename Proto>
906#endif
907template <typename Params>
908OutgoingEvent<Params> Binder<Proto>::Bind(llvm::StringLiteral method) {
909 if constexpr (std::is_void_v<Params>) {
910 return [this, method]() {
911 if (llvm::Error error =
912 m_transport.Send(Proto::Make(method, std::nullopt)))
913 OnError(std::move(error));
914 };
915 } else {
916 return [this, method](const Params &params) {
917 if (llvm::Error error =
918 m_transport.Send(Proto::Make(method, toJSON(params))))
919 OnError(std::move(error));
920 };
921 }
922}
923
924#if __cplusplus >= 202002L
925template <BindingBuilder Proto>
926#else
927template <typename Proto>
928#endif
929template <typename T>
930llvm::Expected<T> Binder<Proto>::Parse(const llvm::json::Value &raw,
931 llvm::StringRef method) {
932 T result;
933 llvm::json::Path::Root root;
934 if (!fromJSON(raw, result, root)) {
935 // Dump the relevant parts of the broken message.
936 std::string context;
937 llvm::raw_string_ostream OS(context);
938 root.printErrorContext(raw, OS);
939 return llvm::make_error<InvalidParams>(method.str(), context);
940 }
941 return std::move(result);
942}
943
944#if __cplusplus >= 202002L
945template <BindingBuilder Proto>
946#else
947template <typename Proto>
948#endif
949template <typename Result, typename Params, typename Fn, typename... Args>
950void Binder<Proto>::BindAsync(llvm::StringLiteral method, Fn &&fn,
951 Args &&...args) {
952 assert(m_request_handlers.find(method) == m_request_handlers.end() &&
953 "request already bound");
954 // The handler is captured by value and may be invoked once per incoming
955 // request, so it is invoked as an lvalue (never forwarded) to avoid moving
956 // from it between calls.
957 if constexpr (std::is_void_v<Params>) {
958 m_request_handlers[method] =
959 [fn, args...](const Req &req,
960 Callback<void(const Resp &)> reply) mutable {
961 Reply<Result> typed_reply =
962 [req, reply = std::move(reply)](
963 llvm::Expected<Result> result) mutable {
964 if (!result)
965 return reply(Proto::Make(req, result.takeError()));
966 reply(Proto::Make(req, toJSON(*result)));
967 };
968 std::invoke(fn, args..., std::move(typed_reply));
969 };
970 } else {
971 m_request_handlers[method] =
972 [method, fn, args...](const Req &req,
973 Callback<void(const Resp &)> reply) mutable {
974 Reply<Result> typed_reply =
975 [req, reply = std::move(reply)](
976 llvm::Expected<Result> result) mutable {
977 if (!result)
978 return reply(Proto::Make(req, result.takeError()));
979 reply(Proto::Make(req, toJSON(*result)));
980 };
981 llvm::Expected<Params> params =
982 Parse<Params>(Proto::Extract(req), method);
983 if (!params)
984 return typed_reply(params.takeError());
985 std::invoke(fn, args..., *params, std::move(typed_reply));
986 };
987 }
988}
989
990} // namespace lldb_private::transport
991
992#endif
static llvm::raw_ostream & error(Stream &strm)
A command line argument class.
Definition Args.h:33
std::unique_ptr< ReadHandle > ReadHandleUP
An error handling class.
Definition Status.h:118
llvm::Error takeError()
Definition Status.h:170
ReplyOnce & operator=(const ReplyOnce &)=delete
ReplyOnce(const Req req, Transport *transport, MessageHandler *handler)
ReplyOnce & operator=(ReplyOnce &&)=delete
void Received(const Resp &resp) override
Called when a response is received.
JSONTransport< Proto > Transport
typename Transport::MessageHandler MessageHandler
void OnDisconnect(Fn &&fn, Args &&...args)
Bind a handler on transport disconnect.
void Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args)
Bind a handler for an incoming request.
void BindAsync(llvm::StringLiteral method, Fn &&fn, Args &&...args)
Bind an asynchronous handler for an incoming request.
void Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args)
Bind a handler for an incoming event.
llvm::StringMap< Callback< void(const Req &, Callback< void(const Resp &)>)> > m_request_handlers
void FailPendingRequests(llvm::StringRef reason)
Fails every in-flight outgoing request, invoking its reply with an error.
void OnClosed() override
Called on EOF or client disconnect.
void OnError(Fn &&fn, Args &&...args)
Bind a handler on error when communicating with the transport.
void Received(const Evt &evt) override
Called when an event is received.
OutgoingRequest< Result, Params > Bind(llvm::StringLiteral method)
Bind a function object to be used for outgoing requests.
std::map< Id, Callback< void(const Resp &)> > m_pending_responses
Binder & operator=(const Binder &)=delete
Binder(Transport &transport)
void Received(const Req &req) override
Called when a request is received.
static llvm::Expected< T > Parse(const llvm::json::Value &raw, llvm::StringRef method)
OutgoingEvent< Params > Bind(llvm::StringLiteral method)
Bind a function object to be used for outgoing events.
Binder(const Binder &)=delete
void OnError(llvm::Error err) override
Called when an error occurs while reading from the transport.
llvm::StringMap< Callback< void(const Evt &)> > m_event_handlers
A transport class for JSON with a HTTP header.
static constexpr llvm::StringLiteral kHeaderFieldSeparator
static constexpr llvm::StringLiteral kEndOfHeader
static constexpr llvm::StringLiteral kHeaderSeparator
std::string Encode(const llvm::json::Value &message) override
Encodes messages based on https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol.
llvm::Expected< std::vector< std::string > > Parse() override
Parses messages based on https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol.
static constexpr llvm::StringLiteral kHeaderContentLength
void OnRead(MainLoopBase &loop, MessageHandler &handler)
IOTransport(MainLoop &loop, lldb::IOObjectSP in, lldb::IOObjectSP out)
static constexpr size_t kReadBufferSize
Public for testing purposes, otherwise this should be an implementation detail.
typename JSONTransport< Proto >::MessageHandler MessageHandler
llvm::Error Send(const typename Proto::Resp &resp) override
typename JSONTransport< Proto >::Message Message
llvm::Error Send(const typename Proto::Evt &evt) override
virtual std::string Encode(const llvm::json::Value &message)=0
virtual llvm::Expected< std::vector< std::string > > Parse()=0
MainLoop::ReadHandleUP m_read_handle
llvm::Error Write(const llvm::json::Value &message)
llvm::Error Send(const typename Proto::Req &req) override
llvm::SmallString< kReadBufferSize > m_buffer
llvm::Error RegisterMessageHandler(MessageHandler &handler) override
RegisterMessageHandler registers the Transport with the given MainLoop and handles any incoming messa...
void log(llvm::raw_ostream &OS) const override
InvalidMessage(std::string raw_message, std::string reason)
std::error_code convertToErrorCode() const override
std::error_code convertToErrorCode() const override
std::string m_context
Additional context from the parsing failure, e.g.
InvalidParams(std::string method, std::string context)
std::string m_method
The JSONRPC remote method call.
void log(llvm::raw_ostream &OS) const override
A transport class for JSON RPC.
llvm::Expected< std::vector< std::string > > Parse() override
static constexpr llvm::StringLiteral kMessageSeparator
std::string Encode(const llvm::json::Value &message) override
Implemented to handle incoming messages.
virtual void OnError(llvm::Error)=0
Called when an error occurs while reading from the transport.
virtual void OnClosed()=0
Called on EOF or client disconnect.
virtual void Received(const Req &)=0
Called when a request is received.
virtual void Received(const Evt &)=0
Called when an event is received.
virtual void Received(const Resp &)=0
Called when a response is received.
A transport is responsible for maintaining the connection to a client application,...
virtual llvm::Error ReplyWithParseError(llvm::StringRef raw_message, llvm::StringRef reason)
Sends an error response for a message that failed to parse, described by reason.
virtual llvm::Error Send(const Resp &)=0
Sends a response to a specific request.
std::variant< Req, Resp, Evt > Message
virtual llvm::Error Send(const Evt &)=0
Sends an event, a message that does not require a response.
virtual llvm::Error RegisterMessageHandler(MessageHandler &handler)=0
RegisterMessageHandler registers the Transport with the given MainLoop and handles any incoming messa...
auto Logv(const char *Fmt, Ts &&...Vals)
virtual llvm::Error Send(const Req &)=0
Sends a request, a message that expects a response.
virtual void Log(llvm::StringRef message)=0
std::error_code convertToErrorCode() const override
void log(llvm::raw_ostream &OS) const override
void log(llvm::raw_ostream &OS) const override
TransportUnhandledContentsError(std::string unhandled_contents)
std::conditional_t< std::is_void_v< T >, llvm::unique_function< void(llvm::Error)>, llvm::unique_function< void(llvm::Expected< T >)> > Reply
A handler for the response to an outgoing request.
typename detail::event_t< P >::type OutgoingEvent
A function to send an outgoing event.
typename detail::request_t< R, P >::type OutgoingRequest
llvm::json::Value toJSON(const Diagnostics::Report &report)
Render a diagnostics report as JSON, for diagnostics dump's terminal output.
bool fromJSON(const llvm::json::Value &value, SymbolValue &data, llvm::json::Path path)
MainLoopPosix MainLoop
Definition MainLoop.h:20
std::shared_ptr< lldb_private::IOObject > IOObjectSP
llvm::unique_function< void(const P &)> type
llvm::unique_function< void(Reply< R >)> type
llvm::unique_function< void(const P &, Reply< R >)> type