1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
local Result = require "mason-core.result"
local _ = require "mason-core.functional"
local a = require "mason-core.async"
local async_uv = require "mason-core.async.uv"
local log = require "mason-core.log"
local platform = require "mason-core.platform"
local powershell = require "mason-core.installer.managers.powershell"
local spawn = require "mason-core.spawn"
local version = require "mason.version"
local USER_AGENT = ("mason.nvim %s (+https://github.com/mason-org/mason.nvim)"):format(version.VERSION)
local TIMEOUT_SECONDS = 30
---@alias FetchMethod
---| '"GET"'
---| '"POST"'
---| '"PUT"'
---| '"PATCH"'
---| '"DELETE"'
---@alias FetchOpts {out_file: string?, method: FetchMethod?, headers: table<string, string>?, data: string?}
---@async
---@param url string The url to fetch.
---@param opts FetchOpts?
---@return Result # Result<string>
local function fetch(url, opts)
opts = opts or {}
if not opts.headers then
opts.headers = {}
end
if not opts.method then
opts.method = "GET"
end
opts.headers["User-Agent"] = USER_AGENT
log.fmt_debug("Fetching URL %s", url)
local platform_specific = Result.failure
if platform.is.win then
local header_entries = _.join(
"; ",
_.map(function(pair)
return ("%q=%q"):format(pair[1], pair[2])
end, _.to_pairs(opts.headers))
)
local headers = ("-Headers @{%s}"):format(header_entries)
if opts.out_file then
platform_specific = function()
return powershell.command(
([[iwr %s -TimeoutSec %s -UseBasicParsing -Method %q -Uri %q %s -OutFile %q;]]):format(
headers,
TIMEOUT_SECONDS,
opts.method,
url,
opts.data and ("-Body %s"):format(opts.data) or "",
opts.out_file
)
)
end
else
platform_specific = function()
return powershell.command(
([[Write-Output (iwr %s -TimeoutSec %s -Method %q -UseBasicParsing %s -Uri %q).Content;]]):format(
headers,
TIMEOUT_SECONDS,
opts.method,
opts.data and ("-Body %s"):format(opts.data) or "",
url
)
)
end
end
end
local function wget()
local headers = _.sort_by(
_.nth(2),
_.map(
_.compose(function(header)
return { "--header", header }
end, _.join ": "),
_.to_pairs(opts.headers)
)
)
if opts.data and opts.method ~= "POST" then
return Result.failure(("fetch: data provided but method is not POST (was %s)"):format(opts.method or "-"))
end
if not _.any(_.equals(opts.method), { "GET", "POST" }) then
-- Note: --spider can be used for HEAD support, if ever needed
return Result.failure(("fetch: wget doesn't support HTTP method %s"):format(opts.method))
end
return spawn.wget {
headers,
"-o",
"/dev/null",
"-O",
opts.out_file or "-",
"-T",
TIMEOUT_SECONDS,
opts.data and opts.method == "POST" and {
"--post-data",
opts.data,
} or vim.NIL,
url,
}
end
local function curl()
local headers = _.sort_by(
_.nth(2),
_.map(
_.compose(function(header)
return { "-H", header }
end, _.join ": "),
_.to_pairs(opts.headers)
)
)
return spawn.curl {
headers,
"-fsSL",
{
"-X",
opts.method,
},
opts.data and { "-d", "@-" } or vim.NIL,
opts.out_file and { "-o", opts.out_file } or vim.NIL,
{ "--connect-timeout", TIMEOUT_SECONDS },
url,
on_spawn = a.scope(function(_, stdio)
local stdin = stdio[1]
if opts.data then
log.trace("Writing stdin to curl", opts.data)
async_uv.write(stdin, opts.data)
end
async_uv.shutdown(stdin)
async_uv.close(stdin)
end),
}
end
return curl():or_else(wget):or_else(platform_specific):map(function(result)
if opts.out_file then
return result
else
return result.stdout
end
end)
end
return fetch
|