13
13
#ifndef LLDB_HOST_JSONTRANSPORT_H
14
14
#define LLDB_HOST_JSONTRANSPORT_H
15
15
16
+ #include " lldb/Host/MainLoopBase.h"
17
+ #include " lldb/Utility/IOObject.h"
18
+ #include " lldb/Utility/Status.h"
16
19
#include " lldb/lldb-forward.h"
17
20
#include " llvm/ADT/StringRef.h"
18
21
#include " llvm/Support/Error.h"
19
22
#include " llvm/Support/FormatVariadic.h"
20
23
#include " llvm/Support/JSON.h"
21
- #include < chrono >
24
+ #include < string >
22
25
#include < system_error>
26
+ #include < vector>
23
27
24
28
namespace lldb_private {
25
29
@@ -28,27 +32,26 @@ class TransportEOFError : public llvm::ErrorInfo<TransportEOFError> {
28
32
static char ID;
29
33
30
34
TransportEOFError () = default ;
31
-
32
- void log (llvm::raw_ostream &OS) const override {
33
- OS << " transport end of file reached" ;
34
- }
35
- std::error_code convertToErrorCode () const override {
36
- return llvm::inconvertibleErrorCode ();
37
- }
35
+ void log (llvm::raw_ostream &OS) const override ;
36
+ std::error_code convertToErrorCode () const override ;
38
37
};
39
38
40
- class TransportTimeoutError : public llvm ::ErrorInfo<TransportTimeoutError> {
39
+ class TransportUnhandledContentsError
40
+ : public llvm::ErrorInfo<TransportUnhandledContentsError> {
41
41
public:
42
42
static char ID;
43
43
44
- TransportTimeoutError () = default ;
44
+ explicit TransportUnhandledContentsError (std::string unhandled_contents) ;
45
45
46
- void log (llvm::raw_ostream &OS) const override {
47
- OS << " transport operation timed out " ;
48
- }
49
- std::error_code convertToErrorCode () const override {
50
- return std::make_error_code (std::errc::timed_out) ;
46
+ void log (llvm::raw_ostream &OS) const override ;
47
+ std::error_code convertToErrorCode () const override ;
48
+
49
+ const std::string & getUnhandledContents () const {
50
+ return m_unhandled_contents ;
51
51
}
52
+
53
+ private:
54
+ std::string m_unhandled_contents;
52
55
};
53
56
54
57
class TransportInvalidError : public llvm ::ErrorInfo<TransportInvalidError> {
@@ -57,17 +60,17 @@ class TransportInvalidError : public llvm::ErrorInfo<TransportInvalidError> {
57
60
58
61
TransportInvalidError () = default ;
59
62
60
- void log (llvm::raw_ostream &OS) const override {
61
- OS << " transport IO object invalid" ;
62
- }
63
- std::error_code convertToErrorCode () const override {
64
- return std::make_error_code (std::errc::not_connected);
65
- }
63
+ void log (llvm::raw_ostream &OS) const override ;
64
+ std::error_code convertToErrorCode () const override ;
66
65
};
67
66
68
67
// / A transport class that uses JSON for communication.
69
68
class JSONTransport {
70
69
public:
70
+ using ReadHandleUP = MainLoopBase::ReadHandleUP;
71
+ template <typename T>
72
+ using Callback = std::function<void (MainLoopBase &, const llvm::Expected<T>)>;
73
+
71
74
JSONTransport (lldb::IOObjectSP input, lldb::IOObjectSP output);
72
75
virtual ~JSONTransport () = default ;
73
76
@@ -83,24 +86,68 @@ class JSONTransport {
83
86
return WriteImpl (message);
84
87
}
85
88
86
- // / Reads the next message from the input stream .
89
+ // / Registers the transport with the MainLoop .
87
90
template <typename T>
88
- llvm::Expected<T> Read (const std::chrono::microseconds &timeout) {
89
- llvm::Expected<std::string> message = ReadImpl (timeout);
90
- if (!message)
91
- return message.takeError ();
92
- return llvm::json::parse<T>(/* JSON=*/ *message);
91
+ llvm::Expected<ReadHandleUP> RegisterReadObject (MainLoopBase &loop,
92
+ Callback<T> read_cb) {
93
+ Status error;
94
+ ReadHandleUP handle = loop.RegisterReadObject (
95
+ m_input,
96
+ [read_cb, this ](MainLoopBase &loop) {
97
+ char buf[kReadBufferSize ];
98
+ size_t num_bytes = sizeof (buf);
99
+ if (llvm::Error error = m_input->Read (buf, num_bytes).takeError ()) {
100
+ read_cb (loop, std::move (error));
101
+ return ;
102
+ }
103
+ if (num_bytes)
104
+ m_buffer.append (std::string (buf, num_bytes));
105
+
106
+ // If the buffer has contents, try parsing any pending messages.
107
+ if (!m_buffer.empty ()) {
108
+ llvm::Expected<std::vector<std::string>> messages = Parse ();
109
+ if (llvm::Error error = messages.takeError ()) {
110
+ read_cb (loop, std::move (error));
111
+ return ;
112
+ }
113
+
114
+ for (const auto &message : *messages)
115
+ if constexpr (std::is_same<T, std::string>::value)
116
+ read_cb (loop, message);
117
+ else
118
+ read_cb (loop, llvm::json::parse<T>(message));
119
+ }
120
+
121
+ // On EOF, notify the callback after the remaining messages were
122
+ // handled.
123
+ if (num_bytes == 0 ) {
124
+ if (m_buffer.empty ())
125
+ read_cb (loop, llvm::make_error<TransportEOFError>());
126
+ else
127
+ read_cb (loop, llvm::make_error<TransportUnhandledContentsError>(
128
+ std::string (m_buffer)));
129
+ }
130
+ },
131
+ error);
132
+ if (error.Fail ())
133
+ return error.takeError ();
134
+ return handle;
93
135
}
94
136
95
137
protected:
138
+ template <typename ... Ts> inline auto Logv (const char *Fmt, Ts &&...Vals) {
139
+ Log (llvm::formatv (Fmt, std::forward<Ts>(Vals)...).str ());
140
+ }
96
141
virtual void Log (llvm::StringRef message);
97
142
98
143
virtual llvm::Error WriteImpl (const std::string &message) = 0;
99
- virtual llvm::Expected<std::string>
100
- ReadImpl (const std::chrono::microseconds &timeout) = 0 ;
144
+ virtual llvm::Expected<std::vector<std::string>> Parse () = 0;
145
+
146
+ static constexpr size_t kReadBufferSize = 1024 ;
101
147
102
148
lldb::IOObjectSP m_input;
103
149
lldb::IOObjectSP m_output;
150
+ llvm::SmallString<kReadBufferSize > m_buffer;
104
151
};
105
152
106
153
// / A transport class for JSON with a HTTP header.
@@ -111,14 +158,13 @@ class HTTPDelimitedJSONTransport : public JSONTransport {
111
158
virtual ~HTTPDelimitedJSONTransport () = default ;
112
159
113
160
protected:
114
- virtual llvm::Error WriteImpl (const std::string &message) override ;
115
- virtual llvm::Expected<std::string>
116
- ReadImpl (const std::chrono::microseconds &timeout) override ;
117
-
118
- // FIXME: Support any header.
119
- static constexpr llvm::StringLiteral kHeaderContentLength =
120
- " Content-Length: " ;
121
- static constexpr llvm::StringLiteral kHeaderSeparator = " \r\n\r\n " ;
161
+ llvm::Error WriteImpl (const std::string &message) override ;
162
+ llvm::Expected<std::vector<std::string>> Parse () override ;
163
+
164
+ static constexpr llvm::StringLiteral kHeaderContentLength = " Content-Length" ;
165
+ static constexpr llvm::StringLiteral kHeaderFieldSeparator = " :" ;
166
+ static constexpr llvm::StringLiteral kHeaderSeparator = " \r\n " ;
167
+ static constexpr llvm::StringLiteral kEndOfHeader = " \r\n\r\n " ;
122
168
};
123
169
124
170
// / A transport class for JSON RPC.
@@ -129,9 +175,8 @@ class JSONRPCTransport : public JSONTransport {
129
175
virtual ~JSONRPCTransport () = default ;
130
176
131
177
protected:
132
- virtual llvm::Error WriteImpl (const std::string &message) override ;
133
- virtual llvm::Expected<std::string>
134
- ReadImpl (const std::chrono::microseconds &timeout) override ;
178
+ llvm::Error WriteImpl (const std::string &message) override ;
179
+ llvm::Expected<std::vector<std::string>> Parse () override ;
135
180
136
181
static constexpr llvm::StringLiteral kMessageSeparator = " \n " ;
137
182
};
0 commit comments