diff --git a/lib/ruby_lsp/base_server.rb b/lib/ruby_lsp/base_server.rb index ad5de201c..2a5a1c971 100644 --- a/lib/ruby_lsp/base_server.rb +++ b/lib/ruby_lsp/base_server.rb @@ -74,6 +74,8 @@ def start ) end end + rescue URI::Error + # A client can send a document URI with a scheme Ruby's URI parser rejects, so we don't want to fail rescue Store::NonExistingDocumentError # If we receive a request for a file that no longer exists, we don't want to fail end diff --git a/test/server_test.rb b/test/server_test.rb index 783e7af28..1301c8b59 100644 --- a/test/server_test.rb +++ b/test/server_test.rb @@ -1749,6 +1749,36 @@ def test_unrecognized_request_returns_method_not_found assert_equal("Method not found: #{non_existent_method}", error.message) end + def test_does_not_crash_when_a_document_uri_cannot_be_parsed + # A document URI with a scheme Ruby's URI parser rejects must not crash the server + body = { + id: 1, + method: "textDocument/hover", + params: { + textDocument: { uri: "_diff_view:/path/to/file.rb" }, + position: { line: 0, character: 0 }, + }, + }.to_json + server = RubyLsp::Server.new( + test_mode: true, + reader: StringIO.new("Content-Length: #{body.bytesize}\r\n\r\n#{body}"), + writer: StringIO.new, + ) + + error = nil #: StandardError? + capture_subprocess_io do + server.start + rescue StandardError => e + error = e + end + + assert_nil(error, "server crashed on an unparseable document URI: #{error&.message}") + # The request still gets an error response instead of hanging + assert_instance_of(RubyLsp::Error, server.pop_response) + ensure + server&.run_shutdown + end + private def run_initialize_server_with_setup_error(error)