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