@@ -56,7 +56,7 @@ public final class JSONRPCConnection: Connection {
56
56
/// - `init`: Reference to `JSONRPCConnection` trivially can't have escaped to other isolation domains yet.
57
57
/// - `start`: Is required to be called in the same serial region as the initializer, so
58
58
/// `JSONRPCConnection` can't have escaped to other isolation domains yet.
59
- /// - `_close `: Synchronized on `queue`.
59
+ /// - `closeAssumingOnQueue `: Synchronized on `queue`.
60
60
/// - `readyToSend`: Synchronized on `queue`.
61
61
/// - `deinit`: Can also only trivially be called once.
62
62
private nonisolated ( unsafe) var state: State
@@ -230,6 +230,133 @@ public final class JSONRPCConnection: Connection {
230
230
}
231
231
}
232
232
233
+ /// Send a notification to the client that informs the user about a message decoding error and tells them to file an
234
+ /// issue.
235
+ ///
236
+ /// `message` describes what has gone wrong to the user.
237
+ ///
238
+ /// - Important: Must be called on `queue`
239
+ private func sendMessageDecodingErrorNotificationToClient( message: String ) {
240
+ dispatchPrecondition ( condition: . onQueue( queue) )
241
+ let showMessage = ShowMessageNotification (
242
+ type: . error,
243
+ message: """
244
+ \( message) . Please run 'sourcekit-lsp diagnose' to file an issue.
245
+ """
246
+ )
247
+ self . send ( . notification( showMessage) )
248
+ }
249
+
250
+ /// Decode a single JSONRPC message from the given `messageBytes`.
251
+ ///
252
+ /// `messageBytes` should be valid JSON, ie. this is the message sent from the client without the `Content-Length`
253
+ /// header.
254
+ ///
255
+ /// If an error occurs during message parsing, this tries to recover as gracefully as possible and returns `nil`.
256
+ /// Callers should consider the message handled and ignore it when this function returns `nil`.
257
+ ///
258
+ /// - Important: Must be called on `queue`
259
+ private func decodeJSONRPCMessage( messageBytes: Slice < UnsafeBufferPointer < UInt8 > > ) -> JSONRPCMessage ? {
260
+ dispatchPrecondition ( condition: . onQueue( queue) )
261
+ let decoder = JSONDecoder ( )
262
+
263
+ // Set message registry to use for model decoding.
264
+ decoder. userInfo [ . messageRegistryKey] = messageRegistry
265
+
266
+ // Setup callback for response type.
267
+ decoder. userInfo [ . responseTypeCallbackKey] = { ( id: RequestID ) -> ResponseType . Type ? in
268
+ guard let outstanding = self . outstandingRequests [ id] else {
269
+ logger. error ( " Unknown request for \( id, privacy: . public) " )
270
+ return nil
271
+ }
272
+ return outstanding. responseType
273
+ }
274
+
275
+ do {
276
+ let pointer = UnsafeMutableRawPointer ( mutating: UnsafeBufferPointer ( rebasing: messageBytes) . baseAddress!)
277
+ return try decoder. decode (
278
+ JSONRPCMessage . self,
279
+ from: Data ( bytesNoCopy: pointer, count: messageBytes. count, deallocator: . none)
280
+ )
281
+ } catch let error as MessageDecodingError {
282
+ logger. fault ( " Failed to decode message: \( error. forLogging) " )
283
+
284
+ // We failed to decode the message. Under those circumstances try to behave as LSP-conform as possible.
285
+ // Always log at the fault level so that we know something is going wrong from the logs.
286
+ //
287
+ // The pattern below is to handle the message in the best possible way and then `return nil` to acknowledge the
288
+ // handling. That way the compiler enforces that we handle all code paths.
289
+ switch error. messageKind {
290
+ case . request:
291
+ if let id = error. id {
292
+ // If we know it was a request and we have the request ID, simply reply to the request and tell the client
293
+ // that we couldn't parse it. That complies with LSP that all requests should eventually get a response.
294
+ logger. fault (
295
+ " Replying to request \( id, privacy: . public) with error response because we failed to decode the request "
296
+ )
297
+ self . send ( . errorResponse( ResponseError ( error) , id: id) )
298
+ return nil
299
+ } else {
300
+ // If we don't know the ID of the request, ignore it and show a notification to the user.
301
+ // That way the user at least knows that something is going wrong even if the client never gets a response
302
+ // for the request.
303
+ logger. fault ( " Ignoring request because we failed to decode the request and don't have a request ID " )
304
+ sendMessageDecodingErrorNotificationToClient ( message: " sourcekit-lsp failed to decode a request " )
305
+ return nil
306
+ }
307
+ case . response:
308
+ if let id = error. id {
309
+ if let outstanding = self . outstandingRequests. removeValue ( forKey: id) {
310
+ // If we received a response to a request we sent to the client, assume that the client responded with an
311
+ // error. That complies with LSP that all requests should eventually get a response.
312
+ logger. fault (
313
+ " Assuming an error response to request \( id, privacy: . public) because response from client could not be decoded "
314
+ )
315
+ outstanding. replyHandler ( . failure( ResponseError ( error) ) )
316
+ return nil
317
+ } else {
318
+ // If there's an error in the response but we don't even know about the request, we can ignore it.
319
+ logger. fault (
320
+ " Ignoring response to request \( id, privacy: . public) because message could not be decoded but we don't know about the request ID "
321
+ )
322
+ return nil
323
+ }
324
+ } else {
325
+ // And if we can't even recover the ID the response is for, we drop it. This means that whichever code in
326
+ // sourcekit-lsp sent the request will probably never get a reply but there's nothing we can do about that.
327
+ // Ideally requests sent from sourcekit-lsp to the client would have some kind of timeout anyway.
328
+ logger. fault ( " Ignoring response because the ID of the request the response is to could not be recovered " )
329
+ return nil
330
+ }
331
+ case . notification:
332
+ if error. code == . methodNotFound {
333
+ // If we receive a notification we don't know about, this might be a client sending a new LSP notification
334
+ // that we don't know about. It can't be very critical so we ignore it without bothering the user with an
335
+ // error notification.
336
+ logger. fault ( " silently ignoring notification because we don't know about it's method " )
337
+ return nil
338
+ }
339
+ // Ignoring any other notification might result in corrupted behavior. For example, ignoring a
340
+ // `textDocument/didChange` will result in an out-of-sync state between the editor and sourcekit-lsp.
341
+ // Warn the user about the error.
342
+ logger. fault ( " ignoring notification " )
343
+ sendMessageDecodingErrorNotificationToClient ( message: " sourcekit-lsp failed to decode a notification " )
344
+ return nil
345
+ case . unknown:
346
+ // We don't know what has gone wrong. This could be any level of badness. Inform the user about it.
347
+ logger. fault ( " ignoring unknown message " )
348
+ sendMessageDecodingErrorNotificationToClient ( message: " sourcekit-lsp failed to decode a message " )
349
+ return nil
350
+ }
351
+ } catch {
352
+ // We don't know what has gone wrong. This could be any level of badness. Inform the user about it and ignore the
353
+ // message.
354
+ logger. fault ( " ignoring unknown message " )
355
+ sendMessageDecodingErrorNotificationToClient ( message: " sourcekit-lsp failed to decode an unknown message " )
356
+ return nil
357
+ }
358
+ }
359
+
233
360
/// Whether we can send messages in the current state.
234
361
///
235
362
/// - parameter shouldLog: Whether to log an info message if not ready.
@@ -250,69 +377,30 @@ public final class JSONRPCConnection: Connection {
250
377
/// - Important: Must be called on `queue`
251
378
func parseAndHandleMessages( from bytes: UnsafeBufferPointer < UInt8 > ) -> UnsafeBufferPointer < UInt8 > . SubSequence {
252
379
dispatchPrecondition ( condition: . onQueue( queue) )
253
- let decoder = JSONDecoder ( )
254
-
255
- // Set message registry to use for model decoding.
256
- decoder. userInfo [ . messageRegistryKey] = messageRegistry
257
-
258
- // Setup callback for response type.
259
- decoder. userInfo [ . responseTypeCallbackKey] =
260
- { id in
261
- guard let outstanding = self . outstandingRequests [ id] else {
262
- logger. error ( " Unknown request for \( id, privacy: . public) " )
263
- return nil
264
- }
265
- return outstanding. responseType
266
- } as JSONRPCMessage . ResponseTypeCallback
267
380
268
381
var bytes = bytes [ ... ]
269
382
270
383
MESSAGE_LOOP: while true {
384
+ // Split the messages based on the Content-Length header.
385
+ let messageBytes : Slice < UnsafeBufferPointer < UInt8 > >
271
386
do {
272
- guard let ( ( messageBytes , _ ) , rest) = try bytes. jsonrpcSplitMessage ( ) else {
387
+ guard let ( header : _ , message : message , rest : rest) = try bytes. jsonrpcSplitMessage ( ) else {
273
388
return bytes
274
389
}
390
+ messageBytes = message
275
391
bytes = rest
276
-
277
- let pointer = UnsafeMutableRawPointer ( mutating: UnsafeBufferPointer ( rebasing: messageBytes) . baseAddress!)
278
- let message = try decoder. decode (
279
- JSONRPCMessage . self,
280
- from: Data ( bytesNoCopy: pointer, count: messageBytes. count, deallocator: . none)
281
- )
282
-
283
- handle ( message)
284
- } catch let error as MessageDecodingError {
285
- switch error. messageKind {
286
- case . request:
287
- if let id = error. id {
288
- queue. async {
289
- self . send ( . errorResponse( ResponseError ( error) , id: id) )
290
- }
291
- continue MESSAGE_LOOP
292
- }
293
- case . response:
294
- if let id = error. id {
295
- if let outstanding = self . outstandingRequests. removeValue ( forKey: id) {
296
- outstanding. replyHandler ( . failure( ResponseError ( error) ) )
297
- } else {
298
- logger. error ( " error in response to unknown request \( id, privacy: . public) \( error. forLogging) " )
299
- }
300
- continue MESSAGE_LOOP
301
- }
302
- case . notification:
303
- if error. code == . methodNotFound {
304
- logger. error ( " ignoring unknown notification \( error. forLogging) " )
305
- continue MESSAGE_LOOP
306
- }
307
- case . unknown:
308
- break
309
- }
310
- // FIXME: graceful shutdown?
311
- fatalError ( " fatal error encountered decoding message \( error) " )
312
392
} catch {
313
- // FIXME: graceful shutdown?
314
- fatalError ( " fatal error encountered decoding message \( error) " )
393
+ // We failed to parse the message header. There isn't really much we can do to recover because we lost our
394
+ // anchor in the stream where new messages start. Crashing and letting ourselves be restarted by the client is
395
+ // probably the best option.
396
+ sendMessageDecodingErrorNotificationToClient ( message: " Failed to find next message in connection to editor. " )
397
+ fatalError ( " fatal error encountered while splitting JSON RPC messages \( error) " )
398
+ }
399
+
400
+ guard let message = decodeJSONRPCMessage ( messageBytes: messageBytes) else {
401
+ continue
315
402
}
403
+ handle ( message)
316
404
}
317
405
}
318
406
0 commit comments