lsp4j
lsp4j copied to clipboard
Initialize request not handled with WebSocket connection
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.
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
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!
so how does the client in your vscode plugin look like?
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
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
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?
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)
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));
}
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 $
and what is in the message?
the content of the message is:
Content-Length: 3111\r\n\r\n
strange
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
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
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)
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.
is the header sent or not by vscode?
@javaduke Are you getting the same stacktrace consistently? In our case this is a very rare occurence
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.
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]
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});
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
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.
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
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?
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
Sorry, I'm confused, looks like I'm doing it wrong, but is there any good example of how to do it right?
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?
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.
how does your client look like?
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});
}