Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialize request not handled with WebSocket connection #406

Closed
apupier opened this issue Feb 11, 2020 · 57 comments
Closed

Initialize request not handled with WebSocket connection #406

apupier opened this issue Feb 11, 2020 · 57 comments

Comments

@apupier
Copy link
Contributor

apupier commented Feb 11, 2020

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.

@cdietrich
Copy link
Contributor

cdietrich commented Feb 11, 2020

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

@apupier
Copy link
Contributor Author

apupier commented Feb 12, 2020

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!

@cdietrich
Copy link
Contributor

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

@apupier
Copy link
Contributor Author

apupier commented Feb 12, 2020

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

@cdietrich
Copy link
Contributor

cdietrich commented Feb 12, 2020

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. #189

@apupier
Copy link
Contributor Author

apupier commented Feb 12, 2020

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?

@cdietrich
Copy link
Contributor

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)

@apupier
Copy link
Contributor Author

apupier commented Feb 21, 2020

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
Copy link
Contributor Author

apupier commented Feb 21, 2020

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 $

@cdietrich
Copy link
Contributor

and what is in the message?

@apupier
Copy link
Contributor Author

apupier commented Feb 21, 2020

the content of the message is:

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

strange

@cdietrich
Copy link
Contributor

cdietrich commented Feb 21, 2020

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

@apupier
Copy link
Contributor Author

apupier commented Feb 24, 2020

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

@tivervac
Copy link
Contributor

tivervac commented Mar 23, 2023

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)

@javaduke
Copy link

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.

@cdietrich
Copy link
Contributor

is the header sent or not by vscode?

@tivervac
Copy link
Contributor

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

@javaduke
Copy link

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
Copy link

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
Copy link

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});

@cdietrich
Copy link
Contributor

cdietrich commented Jul 19, 2023

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

@javaduke
Copy link

javaduke commented Jul 19, 2023

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.

@cdietrich
Copy link
Contributor

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

@javaduke
Copy link

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?

@cdietrich
Copy link
Contributor

cdietrich commented Jul 19, 2023

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

@javaduke
Copy link

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

@cdietrich
Copy link
Contributor

cdietrich commented Jul 19, 2023

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?

@javaduke
Copy link

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});
}

@cdietrich
Copy link
Contributor

cdietrich commented Jul 19, 2023

and which of the two websocket flavours do you use? (also make sure you use the same in debugger)

@javaduke
Copy link

Sorry, don't understand your question, what do you mean by "two flavors"? Binary vs Text? That's what I suspect is an issue, the VSCode sends BINARY frames, whereas Monaco sends TEXT, and I'm wondering what should I do to make LSP4J accept and decode BINARY frames.

@cdietrich
Copy link
Contributor

there is two websocket packages in lsp4j

@javaduke
Copy link

I only see one, org.eclipse.lsp4j.websocket, this is the dependency:

<dependency>
            <groupId>org.eclipse.lsp4j</groupId>
            <artifactId>org.eclipse.lsp4j.websocket</artifactId>
            <version>0.21.0</version>
        </dependency>

What is the other one?

@cdietrich
Copy link
Contributor

org.eclipse.lsp4j.websocket.jakarta

@cdietrich
Copy link
Contributor

cdietrich commented Jul 19, 2023

which WebSocket class are you using in client?

@javaduke
Copy link

On the server side I was using javax but I just tried switching to Jakarta and the problem is the same.

In client I use the WebSocket class from the ws package: https://www.npmjs.com/package/ws

@cdietrich
Copy link
Contributor

i dont know any interna of that package but cannot get this client conncting to my server

@javaduke
Copy link

Yes, and I believe the problem is still with the Content-Length header sent by the client. I don't see any way to disable this in client, so I'm wondering if it is possible somehow for LSP4j to understand or ignore this header.

@cdietrich
Copy link
Contributor

this is why i said to debug onMessage
but i dont end up there

@javaduke
Copy link

Yes, it fails before calling onMessage, but I cannot figure out what the issue is...

@javaduke
Copy link

OK, after further debugging here's what's going on. I set the breakpoint at the MessageJsonHandler.parseMessage. The very first input received from the client looks like this: Content-Length: 5938\r\n\r\n. It tries to parse it as JSON and obviously fails.
So the fix I guess would be to verify that the message contains Content-Length and remove it from there. Now is there any way for me to implement and set a custom MessageJsonHandler?

@cdietrich
Copy link
Contributor

cdietrich commented Jul 19, 2023

i assume you have to copy & paste subclass the class that calls the constructor.
my test unfortunately arrives in

org.eclipse.lsp4j.websocket.WebSocketMessageHandler.WebSocketMessageHandler(MessageConsumer, MessageJsonHandler, MessageIssueHandler)

but no more.
but that might be me using jetty.
(am getting binary frame, what did you change)
what do you server side to serve the websocket

@javaduke
Copy link

See my sample code above, it's Jetty.
One issue I noticed is that the VSCode sends message in chunks rather than the entire message, so this may be an issue too.

@cdietrich
Copy link
Contributor

i am still see
org.eclipse.jetty.websocket.jsr356.endpoints.JsrEndpointEventDriver.onBinaryFrame(ByteBuffer, boolean)
called which has no wrapper
and thus message is ignored.
wonder why this works for you

@javaduke
Copy link

Well, I tried to set decodeStrings to true in the client: return WebSocket.createWebSocketStream(ws, {decodeStrings: true});
Without it, it doesn't reach the Json handler.

@cdietrich
Copy link
Contributor

which jsonHandler?

@javaduke
Copy link

MessageJsonHandler class.

@cdietrich
Copy link
Contributor

i dont see this called at all. wonder how you setup jetty

@javaduke
Copy link

Nothing special about my jetty setup, I posted the code above: #406 (comment)
The jetty version is 11.0.15

@javaduke
Copy link

Well, I tried everything I could and now I'm kinda stuck. I was able to successfully connect Monaco client to my LSP4J implementation, but not VSCode extension.

@cdietrich
Copy link
Contributor

Is there a reason / usecase you need to connect via web socket / process io or normal socket not an option

@javaduke
Copy link

Actually, no, normal socket would also be an option, but I'm not sure I know how to implement this in VSCode.
Ideally I would like to keep websocket option because it would allow me to use web-based clients as well as other clients too.

@javaduke
Copy link

Yep, thanks, with plain socket it works now. I will still need to solve the websocket problem though, but I can save this for later.

@cdietrich
Copy link
Contributor

cdietrich commented Jul 20, 2023

Maybe also talk to vscode language client Team to introduce an option to configure if array is sent or plain text

@xyangwu
Copy link

xyangwu commented Oct 11, 2023

i assume you have to copy & paste subclass the class that calls the constructor.

This subclass solution fixed the JSON-RPC header issue for me, but the backend LS I was using is implemented with pygls.
(I don't know wether the issue should be solved on vscode client or on server side. Just post it here for someone has encountered similar problem)

@eclipse-lsp4j eclipse-lsp4j locked and limited conversation to collaborators Feb 12, 2024
@jonahgraham jonahgraham converted this issue into discussion #797 Feb 12, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants