forked from sudo-tee/opencode.nvim
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcurl.lua
More file actions
174 lines (147 loc) · 3.96 KB
/
curl.lua
File metadata and controls
174 lines (147 loc) · 3.96 KB
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
local M = {}
--- Build curl command arguments from options
--- @param opts table Options for the curl request
--- @return table args Array of curl command arguments
local function build_curl_args(opts)
local args = { 'curl', '-s', '--no-buffer' }
if opts.method and opts.method ~= 'GET' then
table.insert(args, '-X')
table.insert(args, opts.method)
end
if opts.headers then
for key, value in pairs(opts.headers) do
table.insert(args, '-H')
table.insert(args, key .. ': ' .. value)
end
end
if opts.body then
table.insert(args, '-d')
table.insert(args, '@-') -- Read from stdin
end
if opts.proxy and opts.proxy ~= '' then
table.insert(args, '--proxy')
table.insert(args, opts.proxy)
end
table.insert(args, opts.url)
return args
end
--- Parse HTTP response headers and body
--- @param output string Raw curl output with headers
--- @return table response Response object with status, headers, and body
local function parse_response(output)
local lines = vim.split(output, '\n')
local status = 200
local headers = {}
local body_start = 1
-- Find status line and headers
for i, line in ipairs(lines) do
if line:match('^HTTP/') then
status = math.floor(tonumber(line:match('HTTP/[%d%.]+%s+(%d+)')) or 200)
elseif line:match('^[%w%-]+:') then
local key, value = line:match('^([%w%-]+):%s*(.*)$')
if key and value then
headers[key:lower()] = value
end
elseif line == '' then
body_start = i + 1
break
end
end
local body_lines = {}
for i = body_start, #lines do
table.insert(body_lines, lines[i])
end
local body = table.concat(body_lines, '\n')
return {
status = status,
headers = headers,
body = body,
}
end
--- Make an HTTP request
--- @param opts table Request options
--- @return table|nil job Job object for streaming requests, nil for regular requests
function M.request(opts)
local args = build_curl_args(opts)
if opts.stream then
local buffer = ''
-- job.pid is not cleared on process exit
local is_running = true
local job_opts = {
stdout = function(err, chunk)
if err then
if opts.on_error then
opts.on_error({ message = err })
end
return
end
if chunk then
buffer = buffer .. chunk
-- Extract complete lines
while buffer:find('\n') do
local line, rest = buffer:match('([^\n]*\n)(.*)')
if line then
opts.stream(nil, line)
buffer = rest
else
break
end
end
end
end,
stderr = function(err, data)
if err and opts.on_error then
opts.on_error({ message = err })
end
end,
}
if opts.body then
job_opts.stdin = opts.body
end
local job = vim.system(args, job_opts, function(result)
is_running = false
if buffer and buffer ~= '' then
opts.stream(nil, buffer)
end
if opts.on_exit then
opts.on_exit(result.code, result.signal)
end
end)
return {
_job = job,
is_running = function()
return is_running
end,
shutdown = function()
-- Flip state before kill so callers immediately observe shutdown.
is_running = false
if job and job.pid then
pcall(function()
job:kill(15) -- SIGTERM
end)
end
end,
}
else
table.insert(args, 2, '-i')
local job_opts = {
text = true,
}
if opts.body then
job_opts.stdin = opts.body
end
vim.system(args, job_opts, function(result)
if result.code ~= 0 then
if opts.on_error then
opts.on_error({ message = result.stderr or 'curl failed' })
end
return
end
local response = parse_response(result.stdout or '')
if opts.callback then
opts.callback(response)
end
end)
end
end
return M