From ab707262b1ca4d5b9891f43ca18a59b0dd4154fe Mon Sep 17 00:00:00 2001 From: Yanze Li Date: Sat, 29 Aug 2026 06:13:04 -0700 Subject: [PATCH] fix(lsp): blank line before Content-Length breaks header parsing #41524 Problem: A blank line before the Content-Length header makes header parsing fail with "Content-Length not found in header". An LF in the 'name' state falls through to the 'invalid' state, which only return to 'name' at the *next* LF. That LF terminates the next line, so the line with Content-Length is swallowed. The same issue happens when a junk line is a partial match of the header name (e.g. "Cont\nContent-Length: ..."). Solution: When seeing an LF in the 'name' state, reset the cursor and stay in 'name' rather than entering 'invalid'. --- runtime/lua/vim/lsp/rpc.lua | 4 +++- test/functional/plugin/lsp_spec.lua | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index dcb49d83df..5abd8c5f1d 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -41,7 +41,9 @@ local function get_content_length(ptr, start, len) if c >= 65 and c <= 90 then -- lower case c = c + 32 end - if (c == 32 or c == 9) and j == 1 then -- luacheck: ignore 542 + if c == 10 then -- a blank line or a line with a prefix of header + j = 1 + elseif (c == 32 or c == 9) and j == 1 then -- luacheck: ignore 542 -- skip OWS for compatibility only elseif c == name:byte(j) then j = j + 1 diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index fabe2ed53d..b9cb675a91 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -2052,6 +2052,28 @@ describe('LSP', function() end) end) + it('ignores blank lines before content-length', function() + exec_lua(function() + _G._send_msg_to_server( + 'Info: Parsed 35 declarations\n\nContent-Length: ' .. #body .. ' \r\n\r\n' .. body + ) + end) + verify_single_notification(function(method, args) ---@param args [string] + eq('body', method) + eq(body, args[1]) + end) + end) + + it('skips partial match of "content-length"', function() + exec_lua(function() + _G._send_msg_to_server('Cont\nContent-Length: ' .. #body .. ' \r\n\r\n' .. body) + end) + verify_single_notification(function(method, args) ---@param args [string] + eq('body', method) + eq(body, args[1]) + end) + end) + it('should not trim vim.NIL from the end of a list', function() local expected_handlers = { { NIL, {}, { method = 'shutdown', client_id = 1 } },