Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get curl command line for a request #80

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
21360f1
http.request: Add :to_curl method that returns the equivalent request…
daurnimator Dec 8, 2015
bf2e5ca
http.request: :to_curl: fix command line escaping
daurnimator Dec 8, 2015
e66c5f3
http.request: :to_curl better comments
daurnimator Dec 8, 2015
588e655
http/request: Fix NYI. curl gained a flag for setting the expect timeout
daurnimator Dec 18, 2015
5a7ae9d
Merge branch 'master' into http.request.tocurl
daurnimator Jan 20, 2016
60e039d
http.request: Reorder checks in to_curl
daurnimator Jan 20, 2016
40d81ca
http.request: :to_curl: add support for post301 and post302 options
daurnimator Jan 20, 2016
f264b44
http.request: :to_curl: Don't return --post303
daurnimator Jan 20, 2016
6b147be
http.request: :to_curl: add support for (maps to --location-trusted)
daurnimator Jan 20, 2016
54c0d9d
http/request: `:to_curl`: if URL contains a curl-globbable character,…
daurnimator Jan 20, 2016
84bb632
http/request: `:to_curl`: Validate path is okay for curl
daurnimator Jan 20, 2016
2926c01
http/request: `:to_curl`: handle `.version` field
daurnimator Jan 20, 2016
c4df6a4
http/request: `:to_curl`: Add `-e ;auto` to command lines reflect tha…
daurnimator Jan 20, 2016
c0da486
http/request: `:to_curl`: Don't bother setting redirect related optio…
daurnimator Jan 20, 2016
7166c1c
Merge branch 'master' into http.request.tocurl
daurnimator Oct 31, 2016
d09cd72
spec/request_spec: Add tests for :to_curl()
daurnimator Oct 31, 2016
9deed93
http/request: Fix incorrect assert for validating referer
daurnimator Oct 31, 2016
7844940
spec/request_spec: Add more tests for :to_curl()
daurnimator Oct 31, 2016
953c1cc
http/request: Have :to_curl() pay attention to proxies
daurnimator Oct 31, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions http/request.lua
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,146 @@ function request_methods:to_uri(with_userinfo)
return scheme .. "://" .. authority .. path
end

function request_methods:to_curl()
local cmd = {
"curl";
}
local n = 1

if self.version then
if self.version == 1 then
cmd[n+1] = "-0"
elseif self.version == 1.1 then
cmd[n+1] = "--http1.1"
elseif self.version == 2 then
cmd[n+1] = "--http2"
else
error("invalid version")
end
n = n + 1
end

if self.proxy then
if type(self.proxy) ~= "string" then
error("NYI")
end
cmd[n+1] = "--proxy"
cmd[n+2] = self.proxy
n = n + 2
elseif not self.proxies then
cmd[n+1] = "--noproxy"
cmd[n+2] = "*"
n = n + 2
elseif self.proxies ~= default_proxies then
assert(getmetatable(self.proxies) == http_proxies.mt, "proxies property should be a http.proxies object")
error("NYI")
end

if self.expect_100_timeout ~= 1 then
cmd[n+1] = "--expect100-timeout"
cmd[n+2] = string.format("%d", self.expect_100_timeout)
n = n + 2
end

if self.follow_redirects then
cmd[n+1] = "--location-trusted"
cmd[n+2] = "-e"
cmd[n+3] = ";auto"
n = n + 3

if self.max_redirects ~= 50 then -- curl default is 50
cmd[n+1] = "--max-redirs"
cmd[n+2] = string.format("%d", self.max_redirects or -1)
n = n + 2
end

if self.post301 then
cmd[n+1] = "--post301"
n = n + 1
end

if self.post302 then
cmd[n+1] = "--post302"
n = n + 1
end
end

if self.tls and self.tls ~= true then
error("NYI")
end

local scheme = self.headers:get(":scheme")
-- Unlike the ':to_uri' method, curl needs the authority in the URI to be the actual host/port
local authority = http_util.to_authority(self.host, self.port, scheme)
local path = self.headers:get(":path")
assert(path == "" or path:sub(1,1) == "/" or path:sub(1,1) == "?", "invalid path for cURL")
local url = scheme .. "://" .. authority .. path
if url:match("[%[%]%{%}]") then
-- Turn off curl URL globbing
cmd[n+1] = "-g"
n = n + 1
end
cmd[n+1] = url
n = n + 1

for name, value in self.headers:each() do
if name:sub(1,1) == ":" then
if name == ":authority" then
if value ~= authority then
cmd[n+1] = "-H"
cmd[n+2] = "host: " .. value
n = n + 2
end
elseif name == ":method" then
if value == "HEAD" then
cmd[n+1] = "-I"
n = n + 1
elseif (value ~= "GET" or self.body ~= nil) and (value ~= "POST" or self.body == nil) then
cmd[n+1] = "-X"
cmd[n+2] = value
n = n + 2
end
end
elseif name == "user-agent" then
cmd[n+1] = "-A"
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If user agent not set then need to tell curl not to send one. -H "User-Agent;"

cmd[n+2] = value
n = n + 2
elseif name == "referer" then
cmd[n+1] = "-e"
assert(not value:match(";"), "cannot render referer")
if self.follow_redirects then
cmd[n+2] = value .. ";auto"
else
cmd[n+2] = value
end
n = n + 2
else
cmd[n+1] = "-H"
cmd[n+2] = name .. ": " .. value
n = n + 2
end
end

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to tell curl to not send Accept if we don't set that header ourselves.

if self.body then
if type(self.body) == "string" then
cmd[n+1] = "--data-raw"
cmd[n+2] = self.body
n = n + 2
else
error("NYI")
end
end

-- escape ready for a command line
for i=1, n do
local arg = cmd[i]
if arg:match("[^%w%_%:%/%@%^%.%-]") then
cmd[i] = "'" .. arg:gsub("'", "'\\''") .. "'"
end
end
return table.concat(cmd, " ", 1, n)
end

function request_methods:handle_redirect(orig_headers)
local max_redirects = self.max_redirects
if max_redirects <= 0 then
Expand Down
125 changes: 125 additions & 0 deletions spec/request_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,131 @@ describe("http.request module", function()
test("https://foo:[email protected]:1234")
assert.has.errors(function() test("https://example.com/path") end)
end)
describe(":to_curl() #curl", function()
it("lua-http defaults", function()
local req = request.new_from_uri("http://example.com/")
req.headers:delete "user-agent" -- take variability out of tests
assert.same("curl --location-trusted -e ';auto' --max-redirs 5 http://example.com/", req:to_curl())
end)
local req_template = request.new_from_uri("http://example.com/")
req_template.headers:delete "user-agent" -- take variability out of tests
req_template.follow_redirects = false -- closer to curl defaults
req_template.max_redirects = 50
it("curl defaults", function()
local req = req_template:clone()
assert.same("curl http://example.com/", req:to_curl())
end)
it("http version 1.0", function()
local req = req_template:clone()
req.version = 1
assert.same("curl -0 http://example.com/", req:to_curl())
end)
it("http version 1.1", function()
local req = req_template:clone()
req.version = 1.1
assert.same("curl --http1.1 http://example.com/", req:to_curl())
end)
it("http version 2", function()
local req = req_template:clone()
req.version = 2
assert.same("curl --http2 http://example.com/", req:to_curl())
end)
it("expect_100_timeout flag", function()
local req = req_template:clone()
req.expect_100_timeout = 2
assert.same("curl --expect100-timeout 2 http://example.com/", req:to_curl())
end)
it("post301 flag", function()
local req = req_template:clone()
req.follow_redirects = nil
req.post301 = true
assert.same("curl --location-trusted -e ';auto' --post301 http://example.com/", req:to_curl())
end)
it("post302 flag", function()
local req = req_template:clone()
req.follow_redirects = nil
req.post302 = true
assert.same("curl --location-trusted -e ';auto' --post302 http://example.com/", req:to_curl())
end)
it("path component", function()
local req = req_template:clone()
req.headers:upsert(":path", "/[complex]&path{component}")
assert.same("curl -g 'http://example.com/[complex]&path{component}'", req:to_curl())
end)
it("query component", function()
local req = req_template:clone()
req.headers:upsert(":path", "/path?query")
assert.same("curl 'http://example.com/path?query'", req:to_curl())
end)
it("scheme change", function()
local req = req_template:clone()
req.headers:upsert(":scheme", "https")
assert.same("curl https://example.com:80/ -H 'host: example.com'", req:to_curl())
end)
it("scheme and port change", function()
local req = req_template:clone()
req.headers:upsert(":scheme", "https")
req.port = 443
assert.same("curl https://example.com/", req:to_curl())
end)
it("port change", function()
local req = req_template:clone()
req.port = 443
assert.same("curl http://example.com:443/ -H 'host: example.com'", req:to_curl())
end)
it("host change", function()
local req = req_template:clone()
req.headers:upsert(":authority", "foo.com")
assert.same("curl http://example.com/ -H 'host: foo.com'", req:to_curl())
end)
it("proxy", function()
local req = req_template:clone()
req.proxy = "http://foo.com"
assert.same("curl --proxy http://foo.com http://example.com/", req:to_curl())
end)
it("proxies", function()
local req = req_template:clone()
req.proxies = false
assert.same("curl --noproxy '*' http://example.com/", req:to_curl())
end)
it("head method", function()
local req = req_template:clone()
req.headers:upsert(":method", "HEAD")
assert.same("curl http://example.com/ -I", req:to_curl())
end)
it("post method with body", function()
local req = req_template:clone()
req.headers:upsert(":method", "POST")
req.body = "foo"
assert.same("curl http://example.com/ --data-raw foo", req:to_curl())
end)
it("post method without body", function()
local req = req_template:clone()
req.headers:upsert(":method", "POST")
assert.same("curl http://example.com/ -X POST", req:to_curl())
end)
it("referer header", function()
local req = req_template:clone()
req.headers:upsert("user-agent", "myuseragent")
assert.same("curl http://example.com/ -A myuseragent", req:to_curl())
end)
it("referer header", function()
local req = req_template:clone()
req.headers:upsert("referer", "foo.com")
assert.same("curl http://example.com/ -e foo.com", req:to_curl())
end)
it("referer header when follow_redirects is on", function()
local req = req_template:clone()
req.headers:upsert("referer", "foo.com")
req.follow_redirects = nil
assert.same("curl --location-trusted -e ';auto' http://example.com/ -e 'foo.com;auto'", req:to_curl())
end)
it("custom headers", function()
local req = req_template:clone()
req.headers:append("foo", "bar")
assert.same("curl http://example.com/ -H 'foo: bar'", req:to_curl())
end)
end)
it(":set_body sets content-length for string arguments", function()
local req = request.new_from_uri("http://example.com")
assert.falsy(req.headers:has("content-length"))
Expand Down