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