Skip to content

Commit

Permalink
fix(curl/cli): parse redirected response as list
Browse files Browse the repository at this point in the history
Don't merge multiple responses from redirected requests into one.
Parse them individually and return last response instead.
`parser.parse_verbose()` method should return all request&response
history in future
  • Loading branch information
boltlessengineer committed Feb 9, 2025
1 parent 4449bca commit d5228fe
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 9 deletions.
23 changes: 14 additions & 9 deletions lua/rest-nvim/client/curl/cli.lua
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,19 @@ end
---@param lines string[]
---@return rest.Response
function parser.parse_verbose(lines)
local response = {
headers = {},
statistics = {},
}
---@type rest.Response[]
local list = {}
---@type rest.Response
local response
vim.iter(lines):map(parser.parse_verbose_line):each(function(ln)
if ln.prefix == VERBOSE_PREFIX_RES_HEADER then
if not response.status then
-- response status
response.status = parser.parse_verbose_status(ln.str)
if vim.startswith(ln.str, "HTTP/") then
response = {
status = parser.parse_verbose_status(ln.str),
headers = {},
statistics = {},
}
table.insert(list, response)
else
-- response header
local key, value = parser.parse_header_pair(ln.str)
Expand All @@ -137,14 +141,15 @@ function parser.parse_verbose(lines)
table.insert(response.headers[key], value)
end
end
elseif ln.prefix == VERBOSE_PREFIX_STAT then
elseif response and ln.prefix == VERBOSE_PREFIX_STAT then
local key, value = parser.parse_stat_pair(ln.str)
if key then
response.statistics[key] = value
end
end
end)
return response
-- TODO: return all response history
return list[#list]
end

--- Builder ---
Expand Down
131 changes: 131 additions & 0 deletions spec/client/curl/cli_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,137 @@ describe("curl cli response parser", function()
},
}, response)
end)
it("Redirected response", function()
local stdin = {
"* Trying 34.78.67.165:80...",
"* Connected to ijhttp-examples.jetbrains.com (34.78.67.165) port 80 (#0)",
-- request
"> POST /post HTTP/1.1",
"> Host: ijhttp-examples.jetbrains.com",
"> User-Agent: curl/7.81.0",
"> Accept: */*",
">",
"* Mark bundle as not supporting multiuse",
-- resopnse (301)
"< HTTP/1.1 301 Moved Permanently",
"< Date: Sun, 09 Feb 2025 15:25:31 GMT",
"< Content-Type: text/html",
"< Content-Length: 162",
"< Connection: keep-alive",
"< Location: http://examples.http-client.intellij.net/post",
"<",
"* Ignoring the response-body",
"* Connection #0 to host ijhttp-examples.jetbrains.com left intact",
"* Issue another request to this URL: 'http://examples.http-client.intellij.net/post'",
"* Trying 34.78.67.165:80...",
"* Connected to examples.http-client.intellij.net (34.78.67.165) port 80 (#1)",
-- request
"> POST /post HTTP/1.1",
"> Host: examples.http-client.intellij.net",
"> User-Agent: curl/7.81.0",
"> Accept: */*",
">",
"* Mark bundle as not supporting multiuse",
-- response (308)
"< HTTP/1.1 308 Permanent Redirect",
"< Date: Sun, 09 Feb 2025 15:25:32 GMT",
"< Content-Type: text/html",
"< Content-Length: 164",
"< Connection: keep-alive",
"< Location: https://examples.http-client.intellij.net/post",
"<",
"* Ignoring the response-body",
"* Connection #1 to host examples.http-client.intellij.net left intact",
"* Clear auth, redirects to port from 80 to 443",
"* Issue another request to this URL: 'https://examples.http-client.intellij.net/post'",
"* Trying 34.78.67.165:443...",
"* Connected to examples.http-client.intellij.net (34.78.67.165) port 443 (#2)",
"* ALPN, offering h2",
"* ALPN, offering http/1.1",
"* CAfile: /etc/ssl/certs/ca-certificates.crt",
"* CApath: /etc/ssl/certs",
"* TLSv1.0 (OUT), TLS header, Certificate Status (22):",
"* TLSv1.3 (OUT), TLS handshake, Client hello (1):",
"* TLSv1.2 (IN), TLS header, Certificate Status (22):",
"* TLSv1.3 (IN), TLS handshake, Server hello (2):",
"* TLSv1.2 (IN), TLS header, Finished (20):",
"* TLSv1.2 (IN), TLS header, Supplemental data (23):",
"* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):",
"* TLSv1.2 (IN), TLS header, Supplemental data (23):",
"* TLSv1.3 (IN), TLS handshake, Certificate (11):",
"* TLSv1.2 (IN), TLS header, Supplemental data (23):",
"* TLSv1.3 (IN), TLS handshake, CERT verify (15):",
"* TLSv1.2 (IN), TLS header, Supplemental data (23):",
"* TLSv1.3 (IN), TLS handshake, Finished (20):",
"* TLSv1.2 (OUT), TLS header, Finished (20):",
"* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):",
"* TLSv1.2 (OUT), TLS header, Supplemental data (23):",
"* TLSv1.3 (OUT), TLS handshake, Finished (20):",
"* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384",
"* ALPN, server accepted to use h2",
"* Server certificate:",
"* subject: CN=examples.http-client.intellij.net",
"* start date: Feb 9 08:45:35 2025 GMT",
"* expire date: May 10 08:45:34 2025 GMT",
[[* subjectAltName: host "examples.http-client.intellij.net" matched cert's "examples.http-client.intellij.net"]],
"* issuer: C=US; O=Let's Encrypt; CN=R11",
"* SSL certificate verify ok.",
"* Using HTTP2, server supports multiplexing",
"* Connection state changed (HTTP/2 confirmed)",
"* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0",
"* TLSv1.2 (OUT), TLS header, Supplemental data (23):",
"* TLSv1.2 (OUT), TLS header, Supplemental data (23):",
"* TLSv1.2 (OUT), TLS header, Supplemental data (23):",
"* Using Stream ID: 1 (easy handle 0xaf9f59cebcc0)",
"* TLSv1.2 (OUT), TLS header, Supplemental data (23):",
-- request
"> POST /post HTTP/2",
"> Host: examples.http-client.intellij.net",
"> user-agent: curl/7.81.0",
"> accept: */*",
">",
"* TLSv1.2 (IN), TLS header, Supplemental data (23):",
"* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):",
"* TLSv1.2 (IN), TLS header, Supplemental data (23):",
"* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):",
"* old SSL session ID is stale, removing",
"* TLSv1.2 (IN), TLS header, Supplemental data (23):",
"* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!",
"* TLSv1.2 (OUT), TLS header, Supplemental data (23):",
"* TLSv1.2 (IN), TLS header, Supplemental data (23):",
"* TLSv1.2 (IN), TLS header, Supplemental data (23):",
-- response (200)
"< HTTP/2 200",
"< date: Sun, 09 Feb 2025 15:25:32 GMT",
"< content-type: application/json",
"< content-length: 419",
"< vary: Accept-Encoding",
"< access-control-allow-origin: https://examples.http-client.intellij.net",
"< access-control-allow-credentials: true",
"< strict-transport-security: max-age=31536000; includeSubDomains",
"<",
"* TLSv1.2 (IN), TLS header, Supplemental data (23):",
"* Connection #2 to host examples.http-client.intellij.net left intact",
}
local response = parser.parse_verbose(stdin)
assert.same({
status = {
version = "HTTP/2",
code = 200,
text = "",
},
statistics = {},
headers = {
date = { "Sun, 09 Feb 2025 15:25:32 GMT" },
["content-type"] = { "application/json" },
["content-length"] = { "419" },
["vary"] = { "Accept-Encoding" },
["access-control-allow-origin"] = { "https://examples.http-client.intellij.net" },
["access-control-allow-credentials"] = { "true" },
["strict-transport-security"] = { "max-age=31536000; includeSubDomains" },
},
}, response)
end)
end)

-- -- don't run real request on test by default
Expand Down

0 comments on commit d5228fe

Please sign in to comment.