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::Expected<MainLoop::ReadHandleUP>
170 RegisterMessageHandler(MainLoop &loop, MessageHandler &handler) = 0;
171
172protected:
173 template <typename... Ts> inline auto Logv(const char *Fmt, Ts &&...Vals) {
174 Log(llvm::formatv(Fmt, std::forward<Ts>(Vals)...).str());
175 }
176 virtual void Log(llvm::StringRef message) = 0;
177};
178
179/// An IOTransport sends and receives messages using an IOObject.
180template <typename Proto> class IOTransport : public JSONTransport<Proto> {
181public:
184
187
188 llvm::Error Send(const typename Proto::Evt &evt) override {
189 return Write(evt);
190 }
191 llvm::Error Send(const typename Proto::Req &req) override {
192 return Write(req);
193 }
194 llvm::Error Send(const typename Proto::Resp &resp) override {
195 return Write(resp);
196 }
197
198 llvm::Expected<MainLoop::ReadHandleUP>
200 Status status;
202 m_in,
203 std::bind(&IOTransport::OnRead, this, std::placeholders::_1,
204 std::ref(handler)),
205 status);
206 if (status.Fail()) {
207 return status.takeError();
208 }
209 return read_handle;
210 }
211
212 /// Public for testing purposes, otherwise this should be an implementation
213 /// detail.
214 static constexpr size_t kReadBufferSize = 1024;
215
216protected:
217 llvm::Error Write(const llvm::json::Value &message) {
218 this->Logv("<-- {0}", message);
219 std::string output = Encode(message);
220 size_t bytes_written = output.size();
221 return m_out->Write(output.data(), bytes_written).takeError();
222 }
223
224 virtual llvm::Expected<std::vector<std::string>> Parse() = 0;
225 virtual std::string Encode(const llvm::json::Value &message) = 0;
226
227 llvm::SmallString<kReadBufferSize> m_buffer;
228
229private:
230 void OnRead(MainLoopBase &loop, MessageHandler &handler) {
231 char buf[kReadBufferSize];
232 size_t num_bytes = sizeof(buf);
233 if (Status status = m_in->Read(buf, num_bytes); status.Fail()) {
234 handler.OnError(status.takeError());
235 return;
236 }
237
238 if (num_bytes)
239 m_buffer.append(llvm::StringRef(buf, num_bytes));
240
241 // If the buffer has contents, try parsing any pending messages.
242 if (!m_buffer.empty()) {
243 llvm::Expected<std::vector<std::string>> raw_messages = Parse();
244 if (llvm::Error error = raw_messages.takeError()) {
245 handler.OnError(std::move(error));
246 return;
247 }
248
249 for (const std::string &raw_message : *raw_messages) {
250 llvm::Expected<Message> message =
251 llvm::json::parse<Message>(raw_message);
252 if (!message) {
253 handler.OnError(message.takeError());
254 return;
255 }
256
257 std::visit([&handler](auto &&msg) { handler.Received(msg); }, *message);
258 }
259 }
260
261 // Check if we reached EOF.
262 if (num_bytes == 0) {
263 // EOF reached, but there may still be unhandled contents in the buffer.
264 if (!m_buffer.empty())
265 handler.OnError(llvm::make_error<TransportUnhandledContentsError>(
266 std::string(m_buffer.str())));
267 handler.OnClosed();
268 }
269 }
270
273};
274
275/// A transport class for JSON with a HTTP header.
276#if __cplusplus >= 202002L
277template <ProtocolDescriptor Proto>
278#else
279template <typename Proto>
280#endif
282public:
283 using IOTransport<Proto>::IOTransport;
284
285protected:
286 /// Encodes messages based on
287 /// https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol
288 std::string Encode(const llvm::json::Value &message) override {
289 std::string output;
290 std::string raw_message = llvm::formatv("{0}", message).str();
291 llvm::raw_string_ostream OS(output);
293 << std::to_string(raw_message.size()) << kEndOfHeader << raw_message;
294 return output;
295 }
296
297 /// Parses messages based on
298 /// https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol
299 llvm::Expected<std::vector<std::string>> Parse() override {
300 std::vector<std::string> messages;
301 llvm::StringRef buffer = this->m_buffer;
302 while (buffer.contains(kEndOfHeader)) {
303 auto [headers, rest] = buffer.split(kEndOfHeader);
304 size_t content_length = 0;
305 // HTTP Headers are formatted like `<field-name> ':' [<field-value>]`.
306 for (const llvm::StringRef &header :
307 llvm::split(headers, kHeaderSeparator)) {
308 auto [key, value] = header.split(kHeaderFieldSeparator);
309 // 'Content-Length' is the only meaningful key at the moment. Others
310 // are ignored.
311 if (!key.equals_insensitive(kHeaderContentLength))
312 continue;
313
314 value = value.trim();
315 if (!llvm::to_integer(value, content_length, 10)) {
316 // Clear the buffer to avoid re-parsing this malformed message.
317 this->m_buffer.clear();
318 return llvm::createStringError(std::errc::invalid_argument,
319 "invalid content length: %s",
320 value.str().c_str());
321 }
322 }
323
324 // Check if we have enough data.
325 if (content_length > rest.size())
326 break;
327
328 llvm::StringRef body = rest.take_front(content_length);
329 buffer = rest.drop_front(content_length);
330 messages.emplace_back(body.str());
331 this->Logv("--> {0}", body);
332 }
333
334 // Store the remainder of the buffer for the next read callback.
335 this->m_buffer = buffer.str();
336
337 return std::move(messages);
338 }
339
340 static constexpr llvm::StringLiteral kHeaderContentLength = "Content-Length";
341 static constexpr llvm::StringLiteral kHeaderFieldSeparator = ":";
342 static constexpr llvm::StringLiteral kHeaderSeparator = "\r\n";
343 static constexpr llvm::StringLiteral kEndOfHeader = "\r\n\r\n";
344};
345
346/// A transport class for JSON RPC.
347#if __cplusplus >= 202002L
348template <ProtocolDescriptor Proto>
349#else
350template <typename Proto>
351#endif
352class JSONRPCTransport : public IOTransport<Proto> {
353public:
354 using IOTransport<Proto>::IOTransport;
355
356protected:
357 std::string Encode(const llvm::json::Value &message) override {
358 return llvm::formatv("{0}{1}", message, kMessageSeparator).str();
359 }
360
361 llvm::Expected<std::vector<std::string>> Parse() override {
362 std::vector<std::string> messages;
363 llvm::StringRef buf = this->m_buffer;
364 while (buf.contains(kMessageSeparator)) {
365 auto [raw_json, rest] = buf.split(kMessageSeparator);
366 buf = rest;
367 messages.emplace_back(raw_json.str());
368 this->Logv("--> {0}", raw_json);
369 }
370
371 // Store the remainder of the buffer for the next read callback.
372 this->m_buffer = buf.str();
373
374 return messages;
375 }
376
377 static constexpr llvm::StringLiteral kMessageSeparator = "\n";
378};
379
380/// A handler for the response to an outgoing request.
381template <typename T>
382using Reply =
383 std::conditional_t<std::is_void_v<T>,
384 llvm::unique_function<void(llvm::Error)>,
385 llvm::unique_function<void(llvm::Expected<T>)>>;
386
387namespace detail {
388template <typename R, typename P> struct request_t final {
389 using type = llvm::unique_function<void(const P &, Reply<R>)>;
390};
391template <typename R> struct request_t<R, void> final {
392 using type = llvm::unique_function<void(Reply<R>)>;
393};
394template <typename P> struct event_t final {
395 using type = llvm::unique_function<void(const P &)>;
396};
397template <> struct event_t<void> final {
398 using type = llvm::unique_function<void()>;
399};
400} // namespace detail
401
402template <typename R, typename P>
404
405/// A function to send an outgoing event.
406template <typename P> using OutgoingEvent = typename detail::event_t<P>::type;
407
408#if __cplusplus >= 202002L
409/// This represents a protocol description that includes additional helpers
410/// for constructing requests, responses and events to work with `Binder`.
411template <typename T>
412concept BindingBuilder =
413 ProtocolDescriptor<T> &&
414 requires(T::Id id, T::Req req, T::Resp resp, T::Evt evt,
415 llvm::StringRef method, std::optional<llvm::json::Value> params,
416 std::optional<llvm::json::Value> result, llvm::Error err) {
417 /// For initializing the unique sequence identifier;
418 { T::InitialId() } -> std::same_as<typename T::Id>;
419 /// Incrementing the sequence identifier.
420 { id++ } -> std::same_as<typename T::Id>;
421
422 /// Constructing protocol types
423 /// @{
424 /// Construct a new request.
425 { T::Make(id, method, params) } -> std::same_as<typename T::Req>;
426 /// Construct a new error response.
427 { T::Make(req, std::move(err)) } -> std::same_as<typename T::Resp>;
428 /// Construct a new success response.
429 { T::Make(req, result) } -> std::same_as<typename T::Resp>;
430 /// Construct a new event.
431 { T::Make(method, params) } -> std::same_as<typename T::Evt>;
432 /// @}
433
434 /// Keys for associated types.
435 /// @{
436 /// Looking up in flight responses.
437 { T::KeyFor(resp) } -> std::same_as<typename T::Id>;
438 /// Extract method from request.
439 { T::KeyFor(req) } -> std::same_as<std::string>;
440 /// Extract method from event.
441 { T::KeyFor(evt) } -> std::same_as<std::string>;
442 /// @}
443
444 /// Extracting information from associated types.
445 /// @{
446 /// Extract parameters from a request.
447 { T::Extract(req) } -> std::same_as<std::optional<llvm::json::Value>>;
448 /// Extract result from a response.
449 { T::Extract(resp) } -> std::same_as<llvm::Expected<llvm::json::Value>>;
450 /// Extract parameters from an event.
451 { T::Extract(evt) } -> std::same_as<std::optional<llvm::json::Value>>;
452 /// @}
453 };
454#endif
455
456/// Binder collects a table of functions that handle calls.
457///
458/// The wrapper takes care of parsing/serializing responses.
459///
460/// This allows a JSONTransport to handle incoming and outgoing requests and
461/// events.
462///
463/// A bind of an incoming request to a lambda.
464/// \code{cpp}
465/// Binder binder{transport};
466/// binder.bind<int, vector<int>>("adder", [](const vector<int> &params) {
467/// int sum = 0;
468/// for (int v : params)
469/// sum += v;
470/// return sum;
471/// });
472/// \endcode
473///
474/// A bind of an outgoing request.
475/// \code{cpp}
476/// OutgoingRequest<int, vector<int>> call_add =
477/// binder.bind<int, vector<int>>("add");
478/// call_add({1,2,3}, [](Expected<int> result) {
479/// cout << *result << "\n";
480/// });
481/// \endcode
482#if __cplusplus >= 202002L
483template <BindingBuilder Proto>
484#else
485template <typename Proto>
486#endif
488 using Req = typename Proto::Req;
489 using Resp = typename Proto::Resp;
490 using Evt = typename Proto::Evt;
491 using Id = typename Proto::Id;
494
495public:
497
498 Binder(const Binder &) = delete;
499 Binder &operator=(const Binder &) = delete;
500
501 /// Bind a handler on transport disconnect.
502 template <typename Fn, typename... Args>
503 void OnDisconnect(Fn &&fn, Args &&...args);
504
505 /// Bind a handler on error when communicating with the transport.
506 template <typename Fn, typename... Args>
507 void OnError(Fn &&fn, Args &&...args);
508
509 /// Bind a handler for an incoming request.
510 /// e.g. `bind("peek", &ThisModule::peek, this);`.
511 /// Handler should be e.g. `Expected<PeekResult> peek(const PeekParams&);`
512 /// PeekParams must be JSON parsable and PeekResult must be serializable.
513 template <typename Result, typename Params, typename Fn, typename... Args>
514 void Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args);
515
516 /// Bind a handler for an incoming event.
517 /// e.g. `bind("peek", &ThisModule::peek, this);`
518 /// Handler should be e.g. `void peek(const PeekParams&);`
519 /// PeekParams must be JSON parsable.
520 template <typename Params, typename Fn, typename... Args>
521 void Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args);
522
523 /// Bind a function object to be used for outgoing requests.
524 /// e.g. `OutgoingRequest<Params, Result> Edit = bind("edit");`
525 /// Params must be JSON-serializable, Result must be parsable.
526 template <typename Result, typename Params>
527 OutgoingRequest<Result, Params> Bind(llvm::StringLiteral method);
528
529 /// Bind a function object to be used for outgoing events.
530 /// e.g. `OutgoingEvent<LogParams> Log = bind("log");`
531 /// LogParams must be JSON-serializable.
532 template <typename Params>
533 OutgoingEvent<Params> Bind(llvm::StringLiteral method);
534
535 void Received(const Evt &evt) override {
536 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
537 auto it = m_event_handlers.find(Proto::KeyFor(evt));
538 if (it == m_event_handlers.end()) {
539 OnError(llvm::createStringError(
540 llvm::formatv("no handled for event {0}", toJSON(evt))));
541 return;
542 }
543 it->second(evt);
544 }
545
546 void Received(const Req &req) override {
547 ReplyOnce reply(req, &m_transport, this);
548
549 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
550 auto it = m_request_handlers.find(Proto::KeyFor(req));
551 if (it == m_request_handlers.end()) {
552 reply(Proto::Make(req, llvm::createStringError("method not found")));
553 return;
554 }
555
556 it->second(req, std::move(reply));
557 }
558
559 void Received(const Resp &resp) override {
560 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
561
562 Id id = Proto::KeyFor(resp);
563 auto it = m_pending_responses.find(id);
564 if (it == m_pending_responses.end()) {
565 OnError(llvm::createStringError(
566 llvm::formatv("no pending request for {0}", toJSON(resp))));
567 return;
568 }
569
570 it->second(resp);
571 m_pending_responses.erase(it);
572 }
573
574 void OnError(llvm::Error err) override {
575 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
576 if (m_error_handler)
577 m_error_handler(std::move(err));
578 }
579
580 void OnClosed() override {
581 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
584 }
585
586private:
587 template <typename T>
588 llvm::Expected<T> static Parse(const llvm::json::Value &raw,
589 llvm::StringRef method);
590
591 template <typename T> using Callback = llvm::unique_function<T>;
592
593 std::recursive_mutex m_mutex;
596 std::map<Id, Callback<void(const Resp &)>> m_pending_responses;
597 llvm::StringMap<Callback<void(const Req &, Callback<void(const Resp &)>)>>
599 llvm::StringMap<Callback<void(const Evt &)>> m_event_handlers;
601 Callback<void(llvm::Error)> m_error_handler;
602
603 /// Function object to reply to a call.
604 /// Each instance must be called exactly once, otherwise:
605 /// - the bug is logged, and (in debug mode) an assert will fire
606 /// - if there was no reply, an error reply is sent
607 /// - if there were multiple replies, only the first is sent
608 class ReplyOnce {
609 std::atomic<bool> replied = {false};
610 const Req req;
611 Transport *transport; // Null when moved-from.
612 MessageHandler *handler; // Null when moved-from.
613
614 public:
620 : replied(other.replied.load()), req(other.req),
621 transport(other.transport), handler(other.handler) {
622 other.transport = nullptr;
623 other.handler = nullptr;
624 }
626 ReplyOnce(const ReplyOnce &) = delete;
627 ReplyOnce &operator=(const ReplyOnce &) = delete;
628
630 if (transport && handler && !replied) {
631 assert(false && "must reply to all calls!");
632 (*this)(Proto::Make(req, llvm::createStringError("failed to reply")));
633 }
634 }
635
636 void operator()(const Resp &resp) {
637 assert(transport && handler && "moved-from!");
638 if (replied.exchange(true)) {
639 assert(false && "must reply to each call only once!");
640 return;
641 }
642
643 if (llvm::Error error = transport->Send(resp))
644 handler->OnError(std::move(error));
645 }
646 };
647};
648
649#if __cplusplus >= 202002L
650template <BindingBuilder Proto>
651#else
652template <typename Proto>
653#endif
654template <typename Fn, typename... Args>
655void Binder<Proto>::OnDisconnect(Fn &&fn, Args &&...args) {
656 m_disconnect_handler = [fn, args...]() mutable {
657 std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
658 };
659}
660
661#if __cplusplus >= 202002L
662template <BindingBuilder Proto>
663#else
664template <typename Proto>
665#endif
666template <typename Fn, typename... Args>
667void Binder<Proto>::OnError(Fn &&fn, Args &&...args) {
668 m_error_handler = [fn, args...](llvm::Error error) mutable {
669 std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...,
670 std::move(error));
671 };
672}
673
674#if __cplusplus >= 202002L
675template <BindingBuilder Proto>
676#else
677template <typename Proto>
678#endif
679template <typename Result, typename Params, typename Fn, typename... Args>
680void Binder<Proto>::Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args) {
681 assert(m_request_handlers.find(method) == m_request_handlers.end() &&
682 "request already bound");
683 if constexpr (std::is_void_v<Result> && std::is_void_v<Params>) {
684 m_request_handlers[method] =
685 [fn, args...](const Req &req,
686 llvm::unique_function<void(const Resp &)> reply) mutable {
687 llvm::Error result =
688 std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
689 reply(Proto::Make(req, std::move(result)));
690 };
691 } else if constexpr (std::is_void_v<Params>) {
692 m_request_handlers[method] =
693 [fn, args...](const Req &req,
694 llvm::unique_function<void(const Resp &)> reply) mutable {
695 llvm::Expected<Result> result =
696 std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
697 if (!result)
698 return reply(Proto::Make(req, result.takeError()));
699 reply(Proto::Make(req, toJSON(*result)));
700 };
701 } else if constexpr (std::is_void_v<Result>) {
702 m_request_handlers[method] =
703 [method, fn,
704 args...](const Req &req,
705 llvm::unique_function<void(const Resp &)> reply) mutable {
706 llvm::Expected<Params> params =
707 Parse<Params>(Proto::Extract(req), method);
708 if (!params)
709 return reply(Proto::Make(req, params.takeError()));
710
711 llvm::Error result = std::invoke(
712 std::forward<Fn>(fn), std::forward<Args>(args)..., *params);
713 reply(Proto::Make(req, std::move(result)));
714 };
715 } else {
716 m_request_handlers[method] =
717 [method, fn,
718 args...](const Req &req,
719 llvm::unique_function<void(const Resp &)> reply) mutable {
720 llvm::Expected<Params> params =
721 Parse<Params>(Proto::Extract(req), method);
722 if (!params)
723 return reply(Proto::Make(req, params.takeError()));
724
725 llvm::Expected<Result> result = std::invoke(
726 std::forward<Fn>(fn), std::forward<Args>(args)..., *params);
727 if (!result)
728 return reply(Proto::Make(req, result.takeError()));
729
730 reply(Proto::Make(req, toJSON(*result)));
731 };
732 }
733}
734
735#if __cplusplus >= 202002L
736template <BindingBuilder Proto>
737#else
738template <typename Proto>
739#endif
740template <typename Params, typename Fn, typename... Args>
741void Binder<Proto>::Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args) {
742 assert(m_event_handlers.find(method) == m_event_handlers.end() &&
743 "event already bound");
744 if constexpr (std::is_void_v<Params>) {
745 m_event_handlers[method] = [fn, args...](const Evt &) mutable {
746 std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
747 };
748 } else {
749 m_event_handlers[method] = [this, method, fn,
750 args...](const Evt &evt) mutable {
751 llvm::Expected<Params> params =
752 Parse<Params>(Proto::Extract(evt), method);
753 if (!params)
754 return OnError(params.takeError());
755 std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)..., *params);
756 };
757 }
758}
759
760#if __cplusplus >= 202002L
761template <BindingBuilder Proto>
762#else
763template <typename Proto>
764#endif
765template <typename Result, typename Params>
767Binder<Proto>::Bind(llvm::StringLiteral method) {
768 if constexpr (std::is_void_v<Result> && std::is_void_v<Params>) {
769 return [this, method](Reply<Result> fn) {
770 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
771 Id id = ++m_seq;
772 Req req = Proto::Make(id, method, std::nullopt);
773 m_pending_responses[id] = [fn = std::move(fn)](const Resp &resp) mutable {
774 llvm::Expected<llvm::json::Value> result = Proto::Extract(resp);
775 if (!result)
776 return fn(result.takeError());
777 fn(llvm::Error::success());
778 };
779 if (llvm::Error error = m_transport.Send(req))
780 OnError(std::move(error));
781 };
782 } else if constexpr (std::is_void_v<Params>) {
783 return [this, method](Reply<Result> fn) {
784 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
785 Id id = ++m_seq;
786 Req req = Proto::Make(id, method, std::nullopt);
787 m_pending_responses[id] = [fn = std::move(fn),
788 method](const Resp &resp) mutable {
789 llvm::Expected<llvm::json::Value> result = Proto::Extract(resp);
790 if (!result)
791 return fn(result.takeError());
792 fn(Parse<Result>(*result, method));
793 };
794 if (llvm::Error error = m_transport.Send(req))
795 OnError(std::move(error));
796 };
797 } else if constexpr (std::is_void_v<Result>) {
798 return [this, method](const Params &params, Reply<Result> fn) {
799 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
800 Id id = ++m_seq;
801 Req req = Proto::Make(id, method, llvm::json::Value(params));
802 m_pending_responses[id] = [fn = std::move(fn)](const Resp &resp) mutable {
803 llvm::Expected<llvm::json::Value> result = Proto::Extract(resp);
804 if (!result)
805 return fn(result.takeError());
806 fn(llvm::Error::success());
807 };
808 if (llvm::Error error = m_transport.Send(req))
809 OnError(std::move(error));
810 };
811 } else {
812 return [this, method](const Params &params, Reply<Result> fn) {
813 std::scoped_lock<std::recursive_mutex> guard(m_mutex);
814 Id id = ++m_seq;
815 Req req = Proto::Make(id, method, llvm::json::Value(params));
816 m_pending_responses[id] = [fn = std::move(fn),
817 method](const Resp &resp) mutable {
818 llvm::Expected<llvm::json::Value> result = Proto::Extract(resp);
819 if (llvm::Error err = result.takeError())
820 return fn(std::move(err));
821 fn(Parse<Result>(*result, method));
822 };
823 if (llvm::Error error = m_transport.Send(req))
824 OnError(std::move(error));
825 };
826 }
827}
828
829#if __cplusplus >= 202002L
830template <BindingBuilder Proto>
831#else
832template <typename Proto>
833#endif
834template <typename Params>
835OutgoingEvent<Params> Binder<Proto>::Bind(llvm::StringLiteral method) {
836 if constexpr (std::is_void_v<Params>) {
837 return [this, method]() {
838 if (llvm::Error error =
839 m_transport.Send(Proto::Make(method, std::nullopt)))
840 OnError(std::move(error));
841 };
842 } else {
843 return [this, method](const Params &params) {
844 if (llvm::Error error =
845 m_transport.Send(Proto::Make(method, toJSON(params))))
846 OnError(std::move(error));
847 };
848 }
849}
850
851#if __cplusplus >= 202002L
852template <BindingBuilder Proto>
853#else
854template <typename Proto>
855#endif
856template <typename T>
857llvm::Expected<T> Binder<Proto>::Parse(const llvm::json::Value &raw,
858 llvm::StringRef method) {
859 T result;
860 llvm::json::Path::Root root;
861 if (!fromJSON(raw, result, root)) {
862 // Dump the relevant parts of the broken message.
863 std::string context;
864 llvm::raw_string_ostream OS(context);
865 root.printErrorContext(raw, OS);
866 return llvm::make_error<InvalidParams>(method.str(), context);
867 }
868 return std::move(result);
869}
870
871} // namespace lldb_private::transport
872
873#endif
static llvm::raw_ostream & error(Stream &strm)
A command line argument class.
Definition Args.h:33
std::unique_ptr< ReadHandle > ReadHandleUP
ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp, const Callback &callback, Status &error) override
An error handling class.
Definition Status.h:118
llvm::Error takeError()
Definition Status.h:170
bool Fail() const
Test for error condition.
Definition Status.cpp:294
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 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 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::unique_function< T > Callback
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)
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
IOTransport(lldb::IOObjectSP in, lldb::IOObjectSP out)
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
llvm::Error Write(const llvm::json::Value &message)
llvm::Error Send(const typename Proto::Req &req) override
llvm::Expected< MainLoop::ReadHandleUP > RegisterMessageHandler(MainLoop &loop, MessageHandler &handler) override
RegisterMessageHandler registers the Transport with the given MainLoop and handles any incoming messa...
llvm::SmallString< kReadBufferSize > m_buffer
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::Expected< MainLoop::ReadHandleUP > RegisterMessageHandler(MainLoop &loop, MessageHandler &handler)=0
RegisterMessageHandler registers the Transport with the given MainLoop and handles any incoming messa...
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.
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
bool fromJSON(const llvm::json::Value &value, TraceSupportedResponse &info, llvm::json::Path path)
MainLoopPosix MainLoop
Definition MainLoop.h:20
llvm::json::Value toJSON(const TraceSupportedResponse &packet)
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