ebkalderon / tower-lsp

Language Server Protocol implementation written in Rust
Apache License 2.0
962 stars 54 forks source link

Connect to client TCP port in example #336

Closed jmcphers closed 2 years ago

jmcphers commented 2 years ago

Connects to the TCP port on which the client is listening, rather than trying to accept a connection from the client.

Fixes https://github.com/ebkalderon/tower-lsp/issues/334.

ebkalderon commented 2 years ago

Hmm, I actually get an error when attempting to use this with coc.nvim (doc). The server process must be started before the client does, which means that when the tcp server is started up with no client already running, it crashes immediately with:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 111, kind: ConnectionRefused, message: "Connection refused" }', examples/tcp.rs:127:61
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

TcpListener binds to the port and blocks tower-lsp initialization until the connection between the server and client is established, while TcpStream fails immediately if the port is not already open and ready to accept connections.

Interesting that VSCode and coc.nvim behave differently in this regard. Are you able to confirm that the original TcpListener code does not work as expected in VSCode and that the use of TcpStream is necessary?

ebkalderon commented 2 years ago

Since this is just an example program, though, perhaps it doesn't matter. This PR could potentially be accepted as-is but with a --listen flag which uses a TcpListener instead of a TcpStream, for clients that require it. What do you think about this?

jmcphers commented 2 years ago

Are you able to confirm that the original TcpListener code does not work as expected in VSCode and that the use of TcpStream is necessary?

Yes. Technically it isn't VS Code proper but the vscode-languageclient package that has this behavior. When I use TcpListener in Rust, I get the following error on the VS Code side if I start the language server first:

Error: listen EADDRINUSE: address already in use 127.0.0.1:9277
    at Server.setupListenHandle [as _listen2] (net.js:1318:16)
    at listenInCluster (net.js:1366:12)
    at doListen (net.js:1503:7)
    at processTicksAndRejections (internal/process/task_queues.js:81:21)

and, of course, an error from the Rust side if I start the language client first:

thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 48, kind: AddrInUse, message: "Address already in use" }'

It's interesting that coc.nvim has a different pattern with TCP, but it sounds like most implementations use stdin/stdout so it's possible it's just not exercised much!

I'll update this PR with a switch to control the behavior.

ebkalderon commented 2 years ago

That would be fantastic! Thanks so much, @jmcphers. :heart:

jmcphers commented 2 years ago

Done!