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