llvm-project

Форк
0
336 строк · 11.4 Кб
1
//===--- JSONTransport.cpp - sending and receiving LSP messages over JSON -===//
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
#include "Protocol.h" // For LSPError
9
#include "Transport.h"
10
#include "support/Cancellation.h"
11
#include "support/Logger.h"
12
#include "support/Shutdown.h"
13
#include "support/ThreadCrashReporter.h"
14
#include "llvm/ADT/SmallString.h"
15
#include "llvm/Support/Error.h"
16
#include <optional>
17
#include <system_error>
18

19
namespace clang {
20
namespace clangd {
21
namespace {
22

23
llvm::json::Object encodeError(llvm::Error E) {
24
  std::string Message;
25
  ErrorCode Code = ErrorCode::UnknownErrorCode;
26
  // FIXME: encode cancellation errors using RequestCancelled or ContentModified
27
  // as appropriate.
28
  if (llvm::Error Unhandled = llvm::handleErrors(
29
          std::move(E),
30
          [&](const CancelledError &C) -> llvm::Error {
31
            switch (C.Reason) {
32
            case static_cast<int>(ErrorCode::ContentModified):
33
              Code = ErrorCode::ContentModified;
34
              Message = "Request cancelled because the document was modified";
35
              break;
36
            default:
37
              Code = ErrorCode::RequestCancelled;
38
              Message = "Request cancelled";
39
              break;
40
            }
41
            return llvm::Error::success();
42
          },
43
          [&](const LSPError &L) -> llvm::Error {
44
            Message = L.Message;
45
            Code = L.Code;
46
            return llvm::Error::success();
47
          }))
48
    Message = llvm::toString(std::move(Unhandled));
49

50
  return llvm::json::Object{
51
      {"message", std::move(Message)},
52
      {"code", int64_t(Code)},
53
  };
54
}
55

56
llvm::Error decodeError(const llvm::json::Object &O) {
57
  llvm::StringRef Msg = O.getString("message").value_or("Unspecified error");
58
  if (auto Code = O.getInteger("code"))
59
    return llvm::make_error<LSPError>(Msg.str(), ErrorCode(*Code));
60
  return error(Msg.str());
61
}
62

63
class JSONTransport : public Transport {
64
public:
65
  JSONTransport(std::FILE *In, llvm::raw_ostream &Out,
66
                llvm::raw_ostream *InMirror, bool Pretty, JSONStreamStyle Style)
67
      : In(In), Out(Out), InMirror(InMirror ? *InMirror : llvm::nulls()),
68
        Pretty(Pretty), Style(Style) {}
69

70
  void notify(llvm::StringRef Method, llvm::json::Value Params) override {
71
    sendMessage(llvm::json::Object{
72
        {"jsonrpc", "2.0"},
73
        {"method", Method},
74
        {"params", std::move(Params)},
75
    });
76
  }
77
  void call(llvm::StringRef Method, llvm::json::Value Params,
78
            llvm::json::Value ID) override {
79
    sendMessage(llvm::json::Object{
80
        {"jsonrpc", "2.0"},
81
        {"id", std::move(ID)},
82
        {"method", Method},
83
        {"params", std::move(Params)},
84
    });
85
  }
86
  void reply(llvm::json::Value ID,
87
             llvm::Expected<llvm::json::Value> Result) override {
88
    if (Result) {
89
      sendMessage(llvm::json::Object{
90
          {"jsonrpc", "2.0"},
91
          {"id", std::move(ID)},
92
          {"result", std::move(*Result)},
93
      });
94
    } else {
95
      sendMessage(llvm::json::Object{
96
          {"jsonrpc", "2.0"},
97
          {"id", std::move(ID)},
98
          {"error", encodeError(Result.takeError())},
99
      });
100
    }
101
  }
102

103
  llvm::Error loop(MessageHandler &Handler) override {
104
    std::string JSON; // Messages may be large, reuse same big buffer.
105
    while (!feof(In)) {
106
      if (shutdownRequested())
107
        return error(std::make_error_code(std::errc::operation_canceled),
108
                     "Got signal, shutting down");
109
      if (ferror(In))
110
        return llvm::errorCodeToError(llvm::errnoAsErrorCode());
111
      if (readRawMessage(JSON)) {
112
        ThreadCrashReporter ScopedReporter([&JSON]() {
113
          auto &OS = llvm::errs();
114
          OS << "Signalled while processing message:\n";
115
          OS << JSON << "\n";
116
        });
117
        if (auto Doc = llvm::json::parse(JSON)) {
118
          vlog(Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc);
119
          if (!handleMessage(std::move(*Doc), Handler))
120
            return llvm::Error::success(); // we saw the "exit" notification.
121
        } else {
122
          // Parse error. Log the raw message.
123
          vlog("<<< {0}\n", JSON);
124
          elog("JSON parse error: {0}", llvm::toString(Doc.takeError()));
125
        }
126
      }
127
    }
128
    return llvm::errorCodeToError(std::make_error_code(std::errc::io_error));
129
  }
130

131
private:
132
  // Dispatches incoming message to Handler onNotify/onCall/onReply.
133
  bool handleMessage(llvm::json::Value Message, MessageHandler &Handler);
134
  // Writes outgoing message to Out stream.
135
  void sendMessage(llvm::json::Value Message) {
136
    OutputBuffer.clear();
137
    llvm::raw_svector_ostream OS(OutputBuffer);
138
    OS << llvm::formatv(Pretty ? "{0:2}" : "{0}", Message);
139
    Out << "Content-Length: " << OutputBuffer.size() << "\r\n\r\n"
140
        << OutputBuffer;
141
    Out.flush();
142
    vlog(">>> {0}\n", OutputBuffer);
143
  }
144

145
  // Read raw string messages from input stream.
146
  bool readRawMessage(std::string &JSON) {
147
    return Style == JSONStreamStyle::Delimited ? readDelimitedMessage(JSON)
148
                                               : readStandardMessage(JSON);
149
  }
150
  bool readDelimitedMessage(std::string &JSON);
151
  bool readStandardMessage(std::string &JSON);
152

153
  llvm::SmallVector<char, 0> OutputBuffer;
154
  std::FILE *In;
155
  llvm::raw_ostream &Out;
156
  llvm::raw_ostream &InMirror;
157
  bool Pretty;
158
  JSONStreamStyle Style;
159
};
160

161
bool JSONTransport::handleMessage(llvm::json::Value Message,
162
                                  MessageHandler &Handler) {
163
  // Message must be an object with "jsonrpc":"2.0".
164
  auto *Object = Message.getAsObject();
165
  if (!Object ||
166
      Object->getString("jsonrpc") != std::optional<llvm::StringRef>("2.0")) {
167
    elog("Not a JSON-RPC 2.0 message: {0:2}", Message);
168
    return false;
169
  }
170
  // ID may be any JSON value. If absent, this is a notification.
171
  std::optional<llvm::json::Value> ID;
172
  if (auto *I = Object->get("id"))
173
    ID = std::move(*I);
174
  auto Method = Object->getString("method");
175
  if (!Method) { // This is a response.
176
    if (!ID) {
177
      elog("No method and no response ID: {0:2}", Message);
178
      return false;
179
    }
180
    if (auto *Err = Object->getObject("error"))
181
      return Handler.onReply(std::move(*ID), decodeError(*Err));
182
    // Result should be given, use null if not.
183
    llvm::json::Value Result = nullptr;
184
    if (auto *R = Object->get("result"))
185
      Result = std::move(*R);
186
    return Handler.onReply(std::move(*ID), std::move(Result));
187
  }
188
  // Params should be given, use null if not.
189
  llvm::json::Value Params = nullptr;
190
  if (auto *P = Object->get("params"))
191
    Params = std::move(*P);
192

193
  if (ID)
194
    return Handler.onCall(*Method, std::move(Params), std::move(*ID));
195
  return Handler.onNotify(*Method, std::move(Params));
196
}
197

198
// Tries to read a line up to and including \n.
199
// If failing, feof(), ferror(), or shutdownRequested() will be set.
200
bool readLine(std::FILE *In, llvm::SmallVectorImpl<char> &Out) {
201
  // Big enough to hold any reasonable header line. May not fit content lines
202
  // in delimited mode, but performance doesn't matter for that mode.
203
  static constexpr int BufSize = 128;
204
  size_t Size = 0;
205
  Out.clear();
206
  for (;;) {
207
    Out.resize_for_overwrite(Size + BufSize);
208
    // Handle EINTR which is sent when a debugger attaches on some platforms.
209
    if (!retryAfterSignalUnlessShutdown(
210
            nullptr, [&] { return std::fgets(&Out[Size], BufSize, In); }))
211
      return false;
212
    clearerr(In);
213
    // If the line contained null bytes, anything after it (including \n) will
214
    // be ignored. Fortunately this is not a legal header or JSON.
215
    size_t Read = std::strlen(&Out[Size]);
216
    if (Read > 0 && Out[Size + Read - 1] == '\n') {
217
      Out.resize(Size + Read);
218
      return true;
219
    }
220
    Size += Read;
221
  }
222
}
223

224
// Returns None when:
225
//  - ferror(), feof(), or shutdownRequested() are set.
226
//  - Content-Length is missing or empty (protocol error)
227
bool JSONTransport::readStandardMessage(std::string &JSON) {
228
  // A Language Server Protocol message starts with a set of HTTP headers,
229
  // delimited  by \r\n, and terminated by an empty line (\r\n).
230
  unsigned long long ContentLength = 0;
231
  llvm::SmallString<128> Line;
232
  while (true) {
233
    if (feof(In) || ferror(In) || !readLine(In, Line))
234
      return false;
235
    InMirror << Line;
236

237
    llvm::StringRef LineRef = Line;
238

239
    // We allow comments in headers. Technically this isn't part
240

241
    // of the LSP specification, but makes writing tests easier.
242
    if (LineRef.starts_with("#"))
243
      continue;
244

245
    // Content-Length is a mandatory header, and the only one we handle.
246
    if (LineRef.consume_front("Content-Length: ")) {
247
      if (ContentLength != 0) {
248
        elog("Warning: Duplicate Content-Length header received. "
249
             "The previous value for this message ({0}) was ignored.",
250
             ContentLength);
251
      }
252
      llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength);
253
      continue;
254
    }
255

256
    // An empty line indicates the end of headers.
257
    // Go ahead and read the JSON.
258
    if (LineRef.trim().empty())
259
      break;
260

261
    // It's another header, ignore it.
262
  }
263

264
  // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999"
265
  if (ContentLength > 1 << 30) { // 1024M
266
    elog("Refusing to read message with long Content-Length: {0}. "
267
         "Expect protocol errors",
268
         ContentLength);
269
    return false;
270
  }
271
  if (ContentLength == 0) {
272
    log("Warning: Missing Content-Length header, or zero-length message.");
273
    return false;
274
  }
275

276
  JSON.resize(ContentLength);
277
  for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) {
278
    // Handle EINTR which is sent when a debugger attaches on some platforms.
279
    Read = retryAfterSignalUnlessShutdown(0, [&]{
280
      return std::fread(&JSON[Pos], 1, ContentLength - Pos, In);
281
    });
282
    if (Read == 0) {
283
      elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos,
284
           ContentLength);
285
      return false;
286
    }
287
    InMirror << llvm::StringRef(&JSON[Pos], Read);
288
    clearerr(In); // If we're done, the error was transient. If we're not done,
289
                  // either it was transient or we'll see it again on retry.
290
    Pos += Read;
291
  }
292
  return true;
293
}
294

295
// For lit tests we support a simplified syntax:
296
// - messages are delimited by '---' on a line by itself
297
// - lines starting with # are ignored.
298
// This is a testing path, so favor simplicity over performance here.
299
// When returning false: feof(), ferror(), or shutdownRequested() will be set.
300
bool JSONTransport::readDelimitedMessage(std::string &JSON) {
301
  JSON.clear();
302
  llvm::SmallString<128> Line;
303
  while (readLine(In, Line)) {
304
    InMirror << Line;
305
    auto LineRef = Line.str().trim();
306
    if (LineRef.starts_with("#")) // comment
307
      continue;
308

309
    // found a delimiter
310
    if (LineRef.rtrim() == "---")
311
      break;
312

313
    JSON += Line;
314
  }
315

316
  if (shutdownRequested())
317
    return false;
318
  if (ferror(In)) {
319
    elog("Input error while reading message!");
320
    return false;
321
  }
322
  return true; // Including at EOF
323
}
324

325
} // namespace
326

327
std::unique_ptr<Transport> newJSONTransport(std::FILE *In,
328
                                            llvm::raw_ostream &Out,
329
                                            llvm::raw_ostream *InMirror,
330
                                            bool Pretty,
331
                                            JSONStreamStyle Style) {
332
  return std::make_unique<JSONTransport>(In, Out, InMirror, Pretty, Style);
333
}
334

335
} // namespace clangd
336
} // namespace clang
337

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.