lsp4j icon indicating copy to clipboard operation
lsp4j copied to clipboard

Initialize request not handled with WebSocket connection

Open apupier opened this issue 5 years ago • 13 comments

Currently trying to connect a VS Code Client to a Language Server written on LSP4J 0.8.1.

The "connect" method is called and it throws no error (when address is wrong, I have other errors)

The client is sending an initialize request:

[Trace - 5:32:52 PM] Sending request 'initialize - (0)'.
Params: {
    "processId": 74894,
    "clientInfo": {
        "name": "vscode",
        "version": "1.42.0"
    },
    "rootPath": "/home/apupier/git/camel-k-example-basic",
    "rootUri": "file:///home/apupier/git/camel-k-example-basic",
    "capabilities": {
        "workspace": {
            "applyEdit": true,
            "workspaceEdit": {
                "documentChanges": true,
                "resourceOperations": [
                    "create",
                    "rename",
                    "delete"
                ],
                "failureHandling": "textOnlyTransactional"
            },
            "didChangeConfiguration": {
                "dynamicRegistration": true
            },
            "didChangeWatchedFiles": {
                "dynamicRegistration": true
            },
            "symbol": {
                "dynamicRegistration": true,
                "symbolKind": {
                    "valueSet": [
                        1,
                        2,
                        3,
                        4,
                        5,
                        6,
                        7,
                        8,
                        9,
                        10,
                        11,
                        12,
                        13,
                        14,
                        15,
                        16,
                        17,
                        18,
                        19,
                        20,
                        21,
                        22,
                        23,
                        24,
                        25,
                        26
                    ]
                }
            },
            "executeCommand": {
                "dynamicRegistration": true
            },
            "configuration": true,
            "workspaceFolders": true
        },
        "textDocument": {
            "publishDiagnostics": {
                "relatedInformation": true,
                "versionSupport": false,
                "tagSupport": {
                    "valueSet": [
                        1,
                        2
                    ]
                }
            },
            "synchronization": {
                "dynamicRegistration": true,
                "willSave": true,
                "willSaveWaitUntil": true,
                "didSave": true
            },
            "completion": {
                "dynamicRegistration": true,
                "contextSupport": true,
                "completionItem": {
                    "snippetSupport": true,
                    "commitCharactersSupport": true,
                    "documentationFormat": [
                        "markdown",
                        "plaintext"
                    ],
                    "deprecatedSupport": true,
                    "preselectSupport": true,
                    "tagSupport": {
                        "valueSet": [
                            1
                        ]
                    }
                },
                "completionItemKind": {
                    "valueSet": [
                        1,
                        2,
                        3,
                        4,
                        5,
                        6,
                        7,
                        8,
                        9,
                        10,
                        11,
                        12,
                        13,
                        14,
                        15,
                        16,
                        17,
                        18,
                        19,
                        20,
                        21,
                        22,
                        23,
                        24,
                        25
                    ]
                }
            },
            "hover": {
                "dynamicRegistration": true,
                "contentFormat": [
                    "markdown",
                    "plaintext"
                ]
            },
            "signatureHelp": {
                "dynamicRegistration": true,
                "signatureInformation": {
                    "documentationFormat": [
                        "markdown",
                        "plaintext"
                    ],
                    "parameterInformation": {
                        "labelOffsetSupport": true
                    }
                },
                "contextSupport": true
            },
            "definition": {
                "dynamicRegistration": true,
                "linkSupport": true
            },
            "references": {
                "dynamicRegistration": true
            },
            "documentHighlight": {
                "dynamicRegistration": true
            },
            "documentSymbol": {
                "dynamicRegistration": true,
                "symbolKind": {
                    "valueSet": [
                        1,
                        2,
                        3,
                        4,
                        5,
                        6,
                        7,
                        8,
                        9,
                        10,
                        11,
                        12,
                        13,
                        14,
                        15,
                        16,
                        17,
                        18,
                        19,
                        20,
                        21,
                        22,
                        23,
                        24,
                        25,
                        26
                    ]
                },
                "hierarchicalDocumentSymbolSupport": true
            },
            "codeAction": {
                "dynamicRegistration": true,
                "isPreferredSupport": true,
                "codeActionLiteralSupport": {
                    "codeActionKind": {
                        "valueSet": [
                            "",
                            "quickfix",
                            "refactor",
                            "refactor.extract",
                            "refactor.inline",
                            "refactor.rewrite",
                            "source",
                            "source.organizeImports"
                        ]
                    }
                }
            },
            "codeLens": {
                "dynamicRegistration": true
            },
            "formatting": {
                "dynamicRegistration": true
            },
            "rangeFormatting": {
                "dynamicRegistration": true
            },
            "onTypeFormatting": {
                "dynamicRegistration": true
            },
            "rename": {
                "dynamicRegistration": true,
                "prepareSupport": true
            },
            "documentLink": {
                "dynamicRegistration": true,
                "tooltipSupport": true
            },
            "typeDefinition": {
                "dynamicRegistration": true,
                "linkSupport": true
            },
            "implementation": {
                "dynamicRegistration": true,
                "linkSupport": true
            },
            "colorProvider": {
                "dynamicRegistration": true
            },
            "foldingRange": {
                "dynamicRegistration": true,
                "rangeLimit": 5000,
                "lineFoldingOnly": true
            },
            "declaration": {
                "dynamicRegistration": true,
                "linkSupport": true
            },
            "selectionRange": {
                "dynamicRegistration": true
            }
        },
        "window": {
            "workDoneProgress": true
        }
    },
    "trace": "verbose",
    "workspaceFolders": [
        {
            "uri": "file:///home/apupier/git/camel-k-example-basic",
            "name": "camel-k-example-basic"
        }
    ]
}

But the implementation method LanguageServer.initialize(InitializeParams) is not called. I tried to put breakpoint on org.eclipse.lsp4j.websocket.WebSocketMessageConsumer.consume(Message) and org.eclipse.lsp4j.websocket.WebSocketMessageHandler.onMessage(String) but these methods are not called either.

What is the best place to trace the request on Server side? so that i can see the path that it follows and see where the request is blocked/rejected/lost.

Please note that i was able to make it working with another client, Monaco.

apupier avatar Feb 11 '20 16:02 apupier

does vscode now have websocket support for language servers or did you come up with something custom? (in both cases do both send the same message format at monaco language client / ws jsonrpc does (e.g. regarding headers) i guess a good starting point might be the jetty code like org.eclipse.jetty.websocket.jsr356.endpoints.JsrEndpointEventDriver

here is a trace when using monaco

LanguageServerImpl.initialize(InitializeParams) line: 205	
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]	
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62	
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43	
Method.invoke(Object, Object...) line: 498	
GenericEndpoint.lambda$null$0(AnnotationUtil$MethodInfo, Object, Object) line: 65	
1918464276.apply(Object) line: not available	
GenericEndpoint.request(String, Object) line: 120	
RemoteEndpoint.handleRequest(RequestMessage) line: 261	
RemoteEndpoint.consume(Message) line: 190	
WebSocketMessageHandler.onMessage(String) line: 40	
WebSocketMessageHandler.onMessage(Object) line: 25	
TextWholeMessage.messageComplete() line: 59	
JsrEndpointEventDriver.onTextFrame(ByteBuffer, boolean) line: 218	
JsrEndpointEventDriver(AbstractEventDriver).incomingFrame(Frame) line: 150	
JsrSession(WebSocketSession).incomingFrame(Frame) line: 319	
PerMessageDeflateExtension(AbstractExtension).nextIncomingFrame(Frame) line: 147	
PerMessageDeflateExtension.nextIncomingFrame(Frame) line: 112	
PerMessageDeflateExtension(CompressExtension).forwardIncoming(Frame, ByteAccumulator) line: 142	
PerMessageDeflateExtension.incomingFrame(Frame) line: 92	
ExtensionStack.incomingFrame(Frame) line: 201	
Parser.notifyFrame(Frame) line: 226	
Parser.parse(ByteBuffer) line: 255	
WebSocketServerConnection(AbstractWebSocketConnection).readParse(ByteBuffer) line: 581	

so any of these might be a good canidate

cdietrich avatar Feb 11 '20 20:02 cdietrich

does vscode now have websocket support for language servers or did you come up with something custom?

I think they have but I'm connecting to Language Server which is not managed by VS Code. So the Language Server is launched outside of VS Code. The client part of VS Code is just connecting to this Language Server implementation. It is using Tyrus

Thanks for the trace!

apupier avatar Feb 12 '20 07:02 apupier

so how does the client in your vscode plugin look like?

cdietrich avatar Feb 12 '20 08:02 cdietrich

the important part is:

const host = 'localhost';
	const socketPort = 8077;
	const serverOptions: ServerOptions = function () {
		return new Promise((resolve) => {
			let socket = new WebSocket(`ws://${host}:${socketPort}/teiid-ddl-language-server`);
			const messageStream = WebSocket.createWebSocketStream(socket, {});
			const result: StreamInfo = {
				writer: messageStream,
				reader: messageStream
			};
			return resolve(result);
		});
	};

EDIT: here is full branch of the client https://github.com/apupier/vscode-datavirt/tree/testLSPWebsocket-githubreference

apupier avatar Feb 12 '20 08:02 apupier

i am not sure if this will send the messages the websocket on server expects or if it will sent the header as well which then will break the json parsing see e.g. https://github.com/eclipse/lsp4j/issues/189

cdietrich avatar Feb 12 '20 08:02 cdietrich

interesting link.

By following linke dissues, i came on one of your comment

you use the upcoming lsp 0.8.0-SNAPSHOT which has support for websockets directly by avoiding using StreamMessageProducer/Consumer

My Language Server is using LSP4J 0.8.1 so I would expect that it is not hitting the issue. Or I misunderstood a part? And I still need to provide a specific implementation for MessageProducer/Consumer either on VS Code side or on Server side?

apupier avatar Feb 12 '20 08:02 apupier

no. the monaco language client does not send json rpc with header but the payload json only. i assume that is you just give vscode a stream it will send the headers (maybe you can wrap the streams in the client and have a look what it actually sends on the pipe)

cdietrich avatar Feb 12 '20 08:02 cdietrich

I think that th eproblem is that VS COde is sending in Binary format although LSP4J does not provide a BinaryHandler:

here is the stack that let me think that:

Daemon Thread [Grizzly-worker(2)] (Suspended (breakpoint at line 804 in TyrusEndpointWrapper))	
	TyrusEndpointWrapper.onMessage(TyrusWebSocket, ByteBuffer) line: 804	
	TyrusWebSocket.onMessage(BinaryFrame) line: 179	
	BinaryFrame.respond(TyrusWebSocket) line: 70	
	ProtocolHandler.process(Frame, TyrusWebSocket) line: 783	
	TyrusWebSocketEngine$TyrusReadHandler.handle(ByteBuffer) line: 538	
	GrizzlyServerFilter$ProcessTask.execute() line: 355	
	TaskProcessor.processTask() line: 91	
	TaskProcessor.processTask(TaskProcessor$Task) line: 68	
	GrizzlyServerFilter.handleRead(FilterChainContext) line: 191	
	ExecutorResolver$9.execute(Filter, FilterChainContext) line: 95	
	DefaultFilterChain.executeFilter(FilterExecutor, Filter, FilterChainContext) line: 260	
	DefaultFilterChain.executeChainPart(FilterChainContext, FilterExecutor, int, int, DefaultFilterChain$FiltersState) line: 177	
	DefaultFilterChain.execute(FilterChainContext) line: 109	
	DefaultFilterChain.process(Context) line: 88	
	ProcessorExecutor.execute(Context) line: 53	
	TCPNIOTransport.fireIOEvent(IOEvent, Connection, IOEventLifeCycleListener) line: 515	
	AbstractIOStrategy.fireIOEvent(Connection, IOEvent, IOEventLifeCycleListener, Logger) line: 89	
	WorkerThreadIOStrategy.run0(Connection, IOEvent, IOEventLifeCycleListener) line: 94	
	WorkerThreadIOStrategy.access$100(Connection, IOEvent, IOEventLifeCycleListener) line: 33	
	WorkerThreadIOStrategy$WorkerThreadRunnable.run() line: 114	
	FixedThreadPool$BasicWorker(AbstractThreadPool$Worker).doWork() line: 569	
	FixedThreadPool$BasicWorker(AbstractThreadPool$Worker).run() line: 549	
	DefaultWorkerThread(Thread).run() line: 834	

it is throwing the IllegalStateException in this piece of code:

            if (session.isWholeBinaryHandlerPresent()) {
                session.notifyMessageHandlers(messageBytes, findApplicableDecoders(session, messageBytes, false));
            } else if (session.isPartialBinaryHandlerPresent()) {
                session.notifyMessageHandlers(messageBytes, true);
            } else {
                throw new IllegalStateException(LocalizationMessages.BINARY_MESSAGE_HANDLER_NOT_FOUND(session));
            }

apupier avatar Feb 21 '20 09:02 apupier

so, this can be workarounded on client side by configuring it to use text format, for TypeScript, when using ws library, this can be done using decodeStrings:false options when creating WebSocketStream, so something like:

			let socket = new WebSocket(`ws://${host}:${socketPort}/teiid-ddl-language-server`);
			const messageStream = WebSocket.createWebSocketStream(socket, {decodeStrings:false});
			const result: StreamInfo = {
				writer: messageStream,
				reader: messageStream
			};

unfortunately it is still not working.

The lsp4j MessageJSOnHandler is unable to read the incoming content:

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $

apupier avatar Feb 21 '20 10:02 apupier

and what is in the message?

cdietrich avatar Feb 21 '20 10:02 cdietrich

the content of the message is:

Content-Length: 3111\r\n\r\n

strange

apupier avatar Feb 21 '20 10:02 apupier

as i have said and is mentioned in that other issue: VSCode sends the content header length but the websocket impl here cannot deal with it. (as https://github.com/TypeFox/monaco-languageclient does not send it) (for the responses there will be the same problem)

am not sure how https://github.com/TypeFox/vscode-ws-jsonrpc will behave

cdietrich avatar Feb 21 '20 10:02 cdietrich

asked on StaskOverflow how to avoid "CONTENT-LENGTH" with a Websocket connection: https://stackoverflow.com/questions/60372566/how-to-configure-websocket-to-not-send-content-length-in-javascript-typescript

apupier avatar Feb 24 '20 10:02 apupier

Just to chime in, we've been having the same issue once in a while as well. In our case, it happens in plain java JUnit tests so VS Code is not even in the loop. What we've seen is that the jsonRPC message is sometimes jumbled up for unknown reasons. This message can then of course not be interpreted by LSP4J. An example is shown below. As you can see somehow the Content-Length is sent separately as well as at the end of the initialize message ( }Content-Length: 53) instead of being a header of the initialize message.

Do note that this is for LSP4J <= 0.12.0

Started language server listening on port 34679
Oct 19, 2022 9:52:39 AM org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer fireError
SEVERE: Missing header Content-Length in input "Content-Length: 5530

"
java.lang.IllegalStateException: Missing header Content-Length in input "Content-Length: 5530

"
	at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:91)
	at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:113)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)

Oct 19, 2022 9:52:39 AM org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer fireError
SEVERE: Missing header Content-Length in input "{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "initialize",
  "params": {
    "processId": null,
    "rootUri": null,
    "initializationOptions": {
    },
    "capabilities": {
      "workspace": {
        "applyEdit": false,
        "workspaceEdit": {
          "documentChanges": false,
          "resourceOperations": [
            "create",
            "delete",
            "rename"
          ],
          "failureHandling": "abort"
        },
        "didChangeConfiguration": {
          "dynamicRegistration": false
        },
        "didChangeWatchedFiles": {
          "dynamicRegistration": false
        },
        "symbol": {
          "symbolKind": {
            "valueSet": []
          },
          "dynamicRegistration": false
        },
        "executeCommand": {
          "dynamicRegistration": false
        },
        "workspaceFolders": true,
        "configuration": false
      },
      "textDocument": {
        "synchronization": {
          "willSave": false,
          "willSaveWaitUntil": false,
          "didSave": false,
          "dynamicRegistration": false
        },
        "completion": {
          "completionItem": {
            "snippetSupport": true,
            "commitCharactersSupport": true,
            "documentationFormat": [
              "markdown",
              "plaintext"
            ],
            "deprecatedSupport": true,
            "preselectSupport": true
          },
          "completionItemKind": {
            "valueSet": [
              1,
              2,
              3,
              4,
              5,
              6,
              7,
              8,
              9,
              10,
              11,
              12,
              13,
              14,
              15,
              16,
              17,
              18,
              19,
              20,
              21,
              22,
              23,
              24,
              25
            ]
          },
          "contextSupport": true,
          "dynamicRegistration": false
        },
        "hover": {
          "contentFormat": [
            "markdown",
            "plaintext"
          ],
          "dynamicRegistration": false
        },
        "signatureHelp": {
          "signatureInformation": {
            "documentationFormat": [
              "markdown",
              "plaintext"
            ]
          },
          "dynamicRegistration": false
        },
        "references": {
          "dynamicRegistration": false
        },
        "documentHighlight": {
          "dynamicRegistration": false
        },
        "documentSymbol": {
          "symbolKind": {
            "valueSet": [
              1,
              2,
              3,
              4,
              5,
              6,
              7,
              8,
              9,
              10,
              11,
              12,
              13,
              14,
              15,
              16,
              17,
              18,
              19,
              20,
              21,
              22,
              23,
              24,
              25,
              26
            ]
          },
          "hierarchicalDocumentSymbolSupport": true,
          "dynamicRegistration": false
        },
        "formatting": {
          "dynamicRegistration": false
        },
        "rangeFormatting": {
          "dynamicRegistration": false
        },
        "onTypeFormatting": {
          "dynamicRegistration": false
        },
        "definition": {
          "dynamicRegistration": false
        },
        "typeDefinition": {
          "dynamicRegistration": false
        },
        "implementation": {
          "dynamicRegistration": false
        },
        "codeAction": {
          "codeActionLiteralSupport": {}
        },
        "codeLens": {
          "dynamicRegistration": false
        },
        "documentLink": {
          "dynamicRegistration": false
        },
        "colorProvider": {
          "dynamicRegistration": false
        },
        "rename": {
          "prepareSupport": false,
          "dynamicRegistration": false
        },
        "publishDiagnostics": {
          "relatedInformation": true
        },
        "foldingRange": {
          "rangeLimit": 1000,
          "lineFoldingOnly": false,
          "dynamicRegistration": false
        },
        "semanticTokens": {
          "requests": {
            "full": true
          },
          "tokenTypes": [
            "namespace",
            "type",
            "class",
            "enum",
            "interface",
            "struct",
            "typeparameter",
            "parameter",
            "variable",
            "property",
            "enummember",
            "event",
            "function",
            "method",
            "macro",
            "keyword",
            "modifier",
            "comment",
            "string",
            "number",
            "regexp",
            "operator"
          ],
          "tokenModifiers": [
            "declaration",
            "definition",
            "readonly",
            "static",
            "deprecated",
            "abstract",
            "async",
            "modification",
            "documentation",
            "defaultlibrary"
          ],
          "formats": [
            "relative"
          ]
        }
      }
    },
    "trace": "false",
    "workspaceFolders": [
      {
        "uri": "file:///tmp/workspace_folder4001760077488830224/"
      }
    ]
  }
}Content-Length: 53

"
java.lang.IllegalStateException: Missing header Content-Length in input "{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "initialize",
  "params": {
    "processId": null,
    "rootUri": null,
    "initializationOptions": {
      "useUPD": true
    },
    "capabilities": {
      "workspace": {
        "applyEdit": false,
        "workspaceEdit": {
          "documentChanges": false,
          "resourceOperations": [
            "create",
            "delete",
            "rename"
          ],
          "failureHandling": "abort"
        },
        "didChangeConfiguration": {
          "dynamicRegistration": false
        },
        "didChangeWatchedFiles": {
          "dynamicRegistration": false
        },
        "symbol": {
          "symbolKind": {
            "valueSet": []
          },
          "dynamicRegistration": false
        },
        "executeCommand": {
          "dynamicRegistration": false
        },
        "workspaceFolders": true,
        "configuration": false
      },
      "textDocument": {
        "synchronization": {
          "willSave": false,
          "willSaveWaitUntil": false,
          "didSave": false,
          "dynamicRegistration": false
        },
        "completion": {
          "completionItem": {
            "snippetSupport": true,
            "commitCharactersSupport": true,
            "documentationFormat": [
              "markdown",
              "plaintext"
            ],
            "deprecatedSupport": true,
            "preselectSupport": true
          },
          "completionItemKind": {
            "valueSet": [
              1,
              2,
              3,
              4,
              5,
              6,
              7,
              8,
              9,
              10,
              11,
              12,
              13,
              14,
              15,
              16,
              17,
              18,
              19,
              20,
              21,
              22,
              23,
              24,
              25
            ]
          },
          "contextSupport": true,
          "dynamicRegistration": false
        },
        "hover": {
          "contentFormat": [
            "markdown",
            "plaintext"
          ],
          "dynamicRegistration": false
        },
        "signatureHelp": {
          "signatureInformation": {
            "documentationFormat": [
              "markdown",
              "plaintext"
            ]
          },
          "dynamicRegistration": false
        },
        "references": {
          "dynamicRegistration": false
        },
        "documentHighlight": {
          "dynamicRegistration": false
        },
        "documentSymbol": {
          "symbolKind": {
            "valueSet": [
              1,
              2,
              3,
              4,
              5,
              6,
              7,
              8,
              9,
              10,
              11,
              12,
              13,
              14,
              15,
              16,
              17,
              18,
              19,
              20,
              21,
              22,
              23,
              24,
              25,
              26
            ]
          },
          "hierarchicalDocumentSymbolSupport": true,
          "dynamicRegistration": false
        },
        "formatting": {
          "dynamicRegistration": false
        },
        "rangeFormatting": {
          "dynamicRegistration": false
        },
        "onTypeFormatting": {
          "dynamicRegistration": false
        },
        "definition": {
          "dynamicRegistration": false
        },
        "typeDefinition": {
          "dynamicRegistration": false
        },
        "implementation": {
          "dynamicRegistration": false
        },
        "codeAction": {
          "codeActionLiteralSupport": {}
        },
        "codeLens": {
          "dynamicRegistration": false
        },
        "documentLink": {
          "dynamicRegistration": false
        },
        "colorProvider": {
          "dynamicRegistration": false
        },
        "rename": {
          "prepareSupport": false,
          "dynamicRegistration": false
        },
        "publishDiagnostics": {
          "relatedInformation": true
        },
        "foldingRange": {
          "rangeLimit": 1000,
          "lineFoldingOnly": false,
          "dynamicRegistration": false
        },
        "semanticTokens": {
          "requests": {
            "full": true
          },
          "tokenTypes": [
            "namespace",
            "type",
            "class",
            "enum",
            "interface",
            "struct",
            "typeparameter",
            "parameter",
            "variable",
            "property",
            "enummember",
            "event",
            "function",
            "method",
            "macro",
            "keyword",
            "modifier",
            "comment",
            "string",
            "number",
            "regexp",
            "operator"
          ],
          "tokenModifiers": [
            "declaration",
            "definition",
            "readonly",
            "static",
            "deprecated",
            "abstract",
            "async",
            "modification",
            "documentation",
            "defaultlibrary"
          ],
          "formats": [
            "relative"
          ]
        }
      }
    },
    "trace": "false",
    "workspaceFolders": [
      {
        "uri": "file:///tmp/workspace_folder4001760077488830224/"
      }
    ]
  }
}Content-Length: 53

"
	at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:91)
	at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:113)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)

tivervac avatar Mar 23 '23 10:03 tivervac

Sorry to resurrect this old issue but I have exact same problem. My LSP4J version is 0.21.0 and I cannot get it to work with VSCode LSP client. Wondering if anyone can give me any pointers on how to troubleshoot.

Many thanks!

E.

javaduke avatar Jul 19 '23 12:07 javaduke

is the header sent or not by vscode?

cdietrich avatar Jul 19 '23 12:07 cdietrich

@javaduke Are you getting the same stacktrace consistently? In our case this is a very rare occurence

tivervac avatar Jul 19 '23 12:07 tivervac

Well, I'm not getting an actual error, I just know that my initialize method is not called. I do see the trace on the client side but nothing happens on the server side. And then after a few seconds I see idle timeout exception on the server side.

javaduke avatar Jul 19 '23 12:07 javaduke

Here's what I see on the server side:

2023-07-19 08:37:02.524:DEBUG:oejwci.PerMessageDeflateExtension:qtp662441761-49: nextIncomingFrame(TEXT@76158de8[len=24,fin=true,rsv=000,m=null]HeapByteBuffer@2c6037a0[p=0,l=24,c=8192,r=24]={<<<Content-Length: 5938\r\n\r\n>>>\x00\x00\x00\x00\x00\x00\x00\x00\x00...\x00\x00\x00\x00\x00\x00\x00})
2023-07-19 08:37:02.524:DEBUG:oejwci.WebSocketCoreSession:qtp662441761-49: receiveFrame(TEXT@76158de8[len=24,fin=true,rsv=000,m=null]HeapByteBuffer@2c6037a0[p=0,l=24,c=8192,r=24]={<<<Content-Length: 5938\r\n\r\n>>>\x00\x00\x00\x00\x00\x00\x00\x00\x00...\x00\x00\x00\x00\x00\x00\x00}, org.eclipse.jetty.util.Callback$3@515b35d7) - connectionState=WebSocketSessionState@7205a427{OPEN,i=NO-OP,o=NO-OP,c=null}, handler=JavaxWebSocketFrameHandler@6cabe758[endpoint=com.datasonnet.lsp.launcher.websocket.DataSonnetWSEndpoint]
2023-07-19 08:37:02.524:DEBUG:oejwjcm.AbstractDecodedMessageSink:qtp662441761-49: accepting frame TEXT@76158de8[len=24,fin=true,rsv=000,m=null]HeapByteBuffer@2c6037a0[p=0,l=24,c=8192,r=24]={<<<Content-Length: 5938\r\n\r\n>>>\x00\x00\x00\x00\x00\x00\x00\x00\x00...\x00\x00\x00\x00\x00\x00\x00} for org.eclipse.jetty.websocket.core.internal.messages.StringMessageSink@18de47ee
2023-07-19 08:37:02.526:DEBUG:oejwci.PerMessageDeflateExtension:qtp662441761-49: Decompress finished: true TEXT@76158de8[len=0,fin=true,rsv=000,m=null]HeapByteBuffer@2c6037a0[p=0,l=0,c=8192,r=0]={<<<>>>Content-L...\x00\x00\x00\x00\x00\x00\x00}
2023-07-19 08:37:02.527:DEBUG:oejwci.WebSocketConnection:qtp662441761-49: moreDemand? d=0 fp=true RetainableByteBuffer@6d95f874{DirectByteBuffer@63520bb6[p=32,l=1865,c=4096,r=1833]={\xC1\x9a->\xC7\xC4r\xCe\xCf...\xB6\xE0\xE5\xE2\xE5\x02\x00<<<\xC2\xFe\x07!\x7f\xAe3\xA1\xB3\xF6\xEe\xF2\xA4\x96#^(\x94M\xF762%{\x8d.../\xC8\x13\xD5w\xD4\x8c6g\xDe\x03\x0ch\xEe\xD2\xD1tQ\xFb\xE0\xC1\xAe\x7f>>>\x00\x00\x00\x00\x00\x00\x00\x00\x00...\x00\x00\x00\x00\x00\x00\x00},r=2} WebSocketConnection@55ac1264::SocketChannelEndPoint@750abc03[{l=/127.0.0.1:3000,r=/127.0.0.1:58239,OPEN,fill=-,flush=-,to=9/30000}{io=0/0,kio=0,kro=1}]->[WebSocketConnection@55ac1264[SERVER,p=Parser@2f713fc[s=START,c=0,o=0x0,m=-,l=-1],f=Flusher@14b1ed92[IDLE][queueSize=0,aggregate=null],g=org.eclipse.jetty.websocket.core.internal.Generator@7c703704]]
2023-07-19 08:37:32.521:DEBUG:oejwci.WebSocketConnection:Connector-Scheduler-6b71769e-1: onIdleExpired()
2023-07-19 08:37:32.521:DEBUG:oejwci.WebSocketCoreSession:Connector-Scheduler-6b71769e-1: processHandlerError WSCoreSession@6547ef64{SERVER,WebSocketSessionState@7205a427{OPEN,i=NO-OP,o=NO-OP,c=null},[ws://0.0.0.0:3000/datasonnet-language-server,null,false.[permessage-deflate]],af=true,i/o=4096/4096,fs=65536}->JavaxWebSocketFrameHandler@6cabe758[endpoint=com.datasonnet.lsp.launcher.websocket.DataSonnetWSEndpoint]

javaduke avatar Jul 19 '23 12:07 javaduke

Now, previously VSCode was sending frames in BINARY mode, I thought this could be an issue so I set the decodeStrings to false, e.g. return WebSocket.createWebSocketStream(ws, {decodeStrings: false});

javaduke avatar Jul 19 '23 12:07 javaduke

client and server just need to make sure they are on same if they send and expect content length in the past monaco-language client did have the problem it could not deal with it the question is what the current server side does.

WebSocketMessageHandler / WebSocketMessageConsumer

i assume they dont expect the content length attribute

cdietrich avatar Jul 19 '23 12:07 cdietrich

It looks like the client does send the Content-Length, but the server still does not call initialize. Looks like it doesn't even reach the message handler, I set breakpoint at the JavaxWebSocketFrameHandler, it invokes onFrame but that's about it.

javaduke avatar Jul 19 '23 12:07 javaduke

no i mean other way round. in websocket mode it does not expect it. => what happens if you strip it out at WebSocketMessageHandler
and readd it at WebSocketMessageConsumer

cdietrich avatar Jul 19 '23 12:07 cdietrich

Hmm, I don't have any WebSocketMessageHandler implementation, just to clarify, here's the relevant portion of my code:

int _port = getPort(args);

        Server server = new Server(_port);
        ServerConnector connector = new ServerConnector(server);
        server.addConnector(connector);

        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        server.setHandler(context);

        JavaxWebSocketServletContainerInitializer.configure(context, (servletContext, wsContainer) ->
        {
            // Configure default max size
            wsContainer.setDefaultMaxTextMessageBufferSize(65535);
            wsContainer.setDefaultMaxBinaryMessageBufferSize(65535);
            // Add websockets
            wsContainer.addEndpoint(MyWSEndpoint.class);
        });

        server.start();
        server.join();
@ServerEndpoint(value="/test-language-server")
public class MyWSEndpoint extends WebSocketEndpoint<LanguageClient> {

    private final MyLanguageServer server = new MyLanguageServer();

    @Override
    protected void connect(Collection<Object> localServices, LanguageClient remoteProxy) {
        super.connect(localServices, remoteProxy);
        server.connect(remoteProxy);
    }

    @Override
    protected void configure(Launcher.Builder builder) {
        builder
                .traceMessages(new PrintWriter(System.out))
                .setLocalService(server)
                .setRemoteInterface(LanguageClient.class)
                .setExecutorService(Executors.newCachedThreadPool());
    }


}

It works with the Monaco client, so what am I missing here?

javaduke avatar Jul 19 '23 12:07 javaduke

cause monaco-language client does NOT wanna have /send the content length? it at least was when i checked back in 2020 or so

the classes are hooked up at WebSocketLauncherBuilder

cdietrich avatar Jul 19 '23 12:07 cdietrich

Sorry, I'm confused, looks like I'm doing it wrong, but is there any good example of how to do it right?

javaduke avatar Jul 19 '23 12:07 javaduke

i dont know the websocket feature is vscode so i cannot tell. the websocket feature here was developed with monaco language client in mind. i propose you debug what comes up at the two classes and patch the messages in the debugger that should be quick to try

can you provide a sample client extension.ts how this is setup in vscode?

cdietrich avatar Jul 19 '23 12:07 cdietrich

That's my point exactly, it does not call WebSocketMessageHandler at all, but I was wondering if I create a websocket language server in LSP4J the correct way. I suspect I don't so I was asking if there's any sample implementation.

javaduke avatar Jul 19 '23 12:07 javaduke

how does your client look like?

cdietrich avatar Jul 19 '23 13:07 cdietrich

Like this:

function startClient(context: ExtensionContext) {
	console.log('Starting Test LSP Client...');

	const clientOptions: LanguageClientOptions = {
		documentSelector: [{ scheme: 'file', language: 'testlang' }],
	};

	connection = connectToServer("0.0.0.0", "3000", "test-language-server");	
	client = new LanguageClient(
		"TestLSP",
		"Test Language Server",
		() => Promise.resolve<StreamInfo>({
			reader: connection,
			writer: connection,
		}),
		clientOptions);
		
	client.start();
	context.subscriptions.push(client);

	console.log('Test LSP Client started!');
}
function connectToServer(host: string, port: string, path: string): Duplex {
    const ws = new WebSocket(`ws://${host}:${port}/${path}`);
    return WebSocket.createWebSocketStream(ws, {decodeStrings: true});
}

javaduke avatar Jul 19 '23 13:07 javaduke