Skip to content

Commit 5b80f51

Browse files
committed
display timings
1 parent c4c63ab commit 5b80f51

File tree

12 files changed

+675
-19
lines changed

12 files changed

+675
-19
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.3.0] 2025-04-03
9+
### Added
10+
* Request Profiling Capabilities
11+
* Detailed timing metrics for HTTP requests
12+
* DNS resolution, connection, TLS handshake, and transfer time tracking
13+
* Configurable display in response window
14+
* Toggle profiling with `:HttpProfiling` command
15+
* New keybinding: `<leader>hp` to toggle profiling
16+
* Configuration options:
17+
* `profiling.enabled` - Enable/disable profiling (default: true)
18+
* `profiling.show_in_response` - Show metrics in response (default: true)
19+
* `profiling.detailed_metrics` - Show detailed breakdown (default: true)
20+
821
## [1.2.2] 2025-26-01
922
### Fixed
1023
* Improved JSON response formatting

README.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ The core goal is to ensure compatibility with .http files from IntelliJ or VSCod
1717
- [Commands](#commands)
1818
- [Keybindings](#keybindings)
1919
- [Response handler](#response-handler)
20+
- [Request Profiling](#request-profiling)
2021
- [Telescope Integration](#telescope-integration)
2122
- [Documentation](#documentation)
2223
- [Examples](#examples)
@@ -40,6 +41,7 @@ The core goal is to ensure compatibility with .http files from IntelliJ or VSCod
4041
- Syntax highlighting for .http files and response buffers
4142
- Verbose mode for debugging
4243
- Dry run capability for request inspection
44+
- Request profiling with detailed timing metrics
4345
- Telescope integration for environment selection
4446
- Compatible with [JetBrains HTTP Client](https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html) and [VSCode Restclient](https://github.com/Huachao/vscode-restclient)
4547

@@ -76,13 +78,19 @@ local http_client = require("http_client")
7678
local config = {
7779
default_env_file = '.env.json',
7880
request_timeout = 30000, -- 30 seconds
81+
profiling = {
82+
enabled = true,
83+
show_in_response = true,
84+
detailed_metrics = true,
85+
},
7986
keybindings = {
8087
select_env_file = "<leader>hf",
8188
set_env = "<leader>he",
8289
run_request = "<leader>hr",
8390
stop_request = "<leader>hs",
8491
dry_run = "<leader>hd",
85-
toggle_verbose = "<leader>hv"
92+
toggle_verbose = "<leader>hv",
93+
toggle_profiling = "<leader>hp"
8694
},
8795
}
8896

@@ -115,6 +123,7 @@ You can adjust these settings to your preferences.
115123
- `:HttpRunAll`: Run all HTTP requests in the current file.
116124
- `:HttpStop`: Stop the currently running HTTP request.
117125
- `:HttpVerbose`: Toggle verbose mode for debugging.
126+
- `:HttpProfiling`: Toggle request profiling.
118127
- `:HttpDryRun`: Perform a dry run of the request under the cursor.
119128
- `:HttpCopyCurl`: Copy the curl command for the HTTP request under the cursor.
120129

@@ -136,6 +145,7 @@ The plugin comes with the following default keybindings:
136145
- `<leader>hr`: Run HTTP request under cursor
137146
- `<leader>hx`: Stop running HTTP request
138147
- `<leader>hv`: Toggle verbose mode
148+
- `<leader>hp`: Toggle request profiling
139149
- `<leader>hd`: Perform dry run
140150
- `<leader>hc`: Copy curl command for HTTP request under cursor
141151

@@ -156,6 +166,7 @@ To customize these keybindings, you can add the following to your Neovim configu
156166
{ "<leader>hr", "<cmd>HttpRun<cr>", desc = "Run HTTP request" },
157167
{ "<leader>hx", "<cmd>HttpStop<cr>", desc = "Stop HTTP request" },
158168
{ "<leader>hv", "<cmd>HttpVerbose<cr>", desc = "Toggle verbose mode" },
169+
{ "<leader>hp", "<cmd>HttpProfiling<cr>", desc = "Toggle request profiling" },
159170
{ "<leader>hd", "<cmd>HttpDryRun<cr>", desc = "Perform dry run" },
160171
},
161172
cmd = {
@@ -164,6 +175,7 @@ To customize these keybindings, you can add the following to your Neovim configu
164175
"HttpRun",
165176
"HttpStop",
166177
"HttpVerbose",
178+
"HttpProfiling",
167179
"HttpDryRun"
168180
},
169181
}
@@ -191,6 +203,38 @@ GET {{base_url}}/protected
191203
Authorization: Bearer {{auth_token}}
192204
```
193205

206+
### Request Profiling
207+
208+
The plugin includes request profiling capabilities that show you detailed timing metrics for your HTTP requests.
209+
210+
#### Configuration
211+
212+
You can configure profiling in your setup:
213+
214+
```lua
215+
profiling = {
216+
enabled = true, -- Enable or disable profiling
217+
show_in_response = true, -- Show timing metrics in response output
218+
detailed_metrics = true, -- Show detailed breakdown of timings
219+
},
220+
```
221+
222+
#### Viewing Timing Metrics
223+
224+
When profiling is enabled, the response window will include a "Timing" section that shows:
225+
226+
- Total request time
227+
- DNS resolution time
228+
- TCP connection time
229+
- TLS handshake time (for HTTPS requests)
230+
- Request sending time
231+
- Content transfer time
232+
233+
#### Commands and Keybindings
234+
235+
- `:HttpProfiling` - Toggle profiling on/off
236+
- `<leader>hp` - Default keybinding to toggle profiling
237+
194238
### Running Without Environment
195239
You can now run requests without selecting an environment file. If environment variables are needed but not set, the plugin will display a message suggesting to select an environment file or set properties via a response handler.
196240

lua/http_client/config.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ M.defaults = {
55
request_timeout = 30000, -- 30 seconds
66
split_direction = "right",
77
create_keybindings = true,
8+
profiling = {
9+
enabled = true,
10+
show_in_response = true,
11+
detailed_metrics = true,
12+
},
813
keybindings = {
914
select_env_file = "<leader>hf",
1015
set_env = "<leader>he",
@@ -14,6 +19,7 @@ M.defaults = {
1419
toggle_verbose = "<leader>hv",
1520
copy_curl = "<leader>hc",
1621
save_response = "<header>hs",
22+
toggle_profiling = "<leader>hp",
1723
},
1824
}
1925

lua/http_client/core/http_client.lua

Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ local M = {}
22
local curl = require('plenary.curl')
33
local vvv = require('http_client.utils.verbose')
44
local state = require('http_client.state')
5+
local profiling = require('http_client.utils.profiling')
56

67
local current_request = nil
78

@@ -119,7 +120,9 @@ local function prepare_response(request, response)
119120
url = request.url,
120121
http_version = request.http_version or "N/A",
121122
test_name = request.test_name or "N/A",
122-
}
123+
},
124+
request_id = request.request_id,
125+
timing_metrics = response.timing_metrics or {}
123126
}
124127

125128
state.store_response(pr)
@@ -129,6 +132,17 @@ end
129132

130133
local function display_response(pr)
131134
local ui = require('http_client.ui.display')
135+
local config = require('http_client.config')
136+
137+
-- Format timing metrics if available
138+
local timing_str = ""
139+
local profiling_config = config.get('profiling')
140+
141+
if profiling_config and profiling_config.enabled and profiling_config.show_in_response then
142+
if pr.timing_metrics and next(pr.timing_metrics) then
143+
timing_str = "\n# Timing:\n" .. profiling.format_metrics(pr.timing_metrics)
144+
end
145+
end
132146

133147
local content = string.format([[
134148
Response Information (%s):
@@ -137,7 +151,7 @@ Response Information (%s):
137151
# Status: %s
138152
139153
# Headers:
140-
%s
154+
%s%s
141155
142156
# Body (%s):
143157
%s
@@ -148,6 +162,7 @@ Response Information (%s):
148162
pr.request.http_version,
149163
pr.status,
150164
format_headers(pr.headers),
165+
timing_str,
151166
pr.content_type,
152167
pr.formatted_body
153168
)
@@ -188,6 +203,17 @@ M.send_request = function(request)
188203
return
189204
end
190205

206+
local config = require('http_client.config')
207+
local profiling_config = config.get('profiling')
208+
local profiling_enabled = profiling_config and profiling_config.enabled
209+
210+
-- Generate request ID and add it to the request object
211+
request.request_id = profiling.generate_request_id()
212+
213+
if profiling_enabled then
214+
profiling.start_metric(request.request_id, "total")
215+
end
216+
191217
vvv.debug_print("Headers:")
192218
for k, v in pairs(request.headers) do
193219
vvv.debug_print(string.format(" %s: %s", k, v))
@@ -210,6 +236,10 @@ M.send_request = function(request)
210236
body = request.body,
211237
headers = request.headers,
212238
callback = function(response)
239+
if profiling_enabled then
240+
profiling.end_metric(request.request_id, "total")
241+
end
242+
213243
vvv.debug_print("Response received")
214244
vvv.debug_print(string.format("Status: %s", response.status))
215245
vvv.debug_print("Response headers:")
@@ -220,15 +250,54 @@ M.send_request = function(request)
220250
vvv.debug_print(response.body)
221251

222252
current_request = nil
253+
254+
if profiling_enabled then
255+
response.timing_metrics = profiling.get_metrics(request.request_id)
256+
response.timing_metrics.url = request.url
257+
profiling.clear_metrics(request.request_id)
258+
end
223259

224260
vvv.debug_print("Calling ui.display_response")
225261
local pr = prepare_response(request, response)
226262
display_response(pr)
227263
handle_response(pr)
228264
end
229-
230265
}
231266

267+
-- Add event handlers only if profiling is enabled
268+
if profiling_enabled then
269+
curl_options.on_start = function()
270+
profiling.end_metric(request.request_id, "dns_resolution")
271+
profiling.start_metric(request.request_id, "connection")
272+
end
273+
274+
curl_options.on_connect = function()
275+
profiling.end_metric(request.request_id, "connection")
276+
profiling.start_metric(request.request_id, "send_request")
277+
end
278+
279+
curl_options.on_first_byte = function()
280+
profiling.end_metric(request.request_id, "send_request")
281+
282+
-- Calculate server processing time
283+
local metrics = profiling.get_metrics(request.request_id)
284+
local start_time = 0
285+
286+
if metrics.send_request and metrics.send_request.end_time then
287+
start_time = metrics.send_request.end_time
288+
289+
local server_metrics = {}
290+
server_metrics.start_time = start_time
291+
server_metrics.end_time = vim.loop.hrtime()
292+
server_metrics.duration = (server_metrics.end_time - start_time) / 1000000
293+
294+
metrics.server_processing = server_metrics
295+
end
296+
297+
profiling.start_metric(request.request_id, "content_transfer")
298+
end
299+
end
300+
232301
-- Handle different HTTP versions
233302
if request.http_version then
234303
if request.http_version == "HTTP/2" then
@@ -246,11 +315,18 @@ M.send_request = function(request)
246315
if ssl_config.verifyHostCertificate == false then
247316
curl_options.insecure = true
248317
end
318+
319+
if profiling_enabled then
320+
profiling.start_metric(request.request_id, "dns_resolution")
321+
end
249322

250323
current_request = curl.request(curl_options)
251324

252325
if not current_request then
253326
vvv.debug_print("Failed to initiate request")
327+
if profiling_enabled then
328+
profiling.clear_metrics(request.request_id)
329+
end
254330
return
255331
end
256332

@@ -276,6 +352,16 @@ M.get_current_request = function()
276352
end
277353

278354
M.send_request_sync = function(request)
355+
local config = require('http_client.config')
356+
local profiling_config = config.get('profiling')
357+
local profiling_enabled = profiling_config and profiling_config.enabled
358+
359+
request.request_id = profiling.generate_request_id()
360+
361+
if profiling_enabled then
362+
profiling.start_metric(request.request_id, "total")
363+
end
364+
279365
local response = {}
280366
local curl_options = {
281367
url = request.url,
@@ -300,8 +386,19 @@ M.send_request_sync = function(request)
300386
end
301387

302388
response = curl.get(curl_options)
389+
390+
if profiling_enabled then
391+
profiling.end_metric(request.request_id, "total")
392+
response.timing_metrics = profiling.get_metrics(request.request_id)
393+
response.timing_metrics.url = request.url
394+
end
395+
303396
local pr = prepare_response(request, response)
304397
handle_response(pr)
398+
399+
if profiling_enabled then
400+
profiling.clear_metrics(request.request_id)
401+
end
305402

306403
return response
307404
end

lua/http_client/health.lua

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ M.check = function()
3535
else
3636
health.error("curl is not available", "Install curl")
3737
end
38+
39+
-- Check if profiling is enabled
40+
local profiling_config = M.config.get('profiling')
41+
if profiling_config and profiling_config.enabled then
42+
health.ok('Profiling: enabled')
43+
else
44+
health.info('Profiling: disabled (use :HttpProfiling to enable)')
45+
end
46+
47+
-- Check other parts of the plugin
3848
end
3949

4050
return M

lua/http_client/init.lua

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ local function set_keybindings()
3535
vim.keymap.set("n", keybindings.toggle_verbose, ":HttpVerbose<CR>", opts)
3636
vim.keymap.set("n", keybindings.copy_curl, ":HttpCopyCurl<CR>", opts)
3737
vim.keymap.set("n", keybindings.save_response, ":HttpSaveResponse<CR>", opts)
38+
vim.keymap.set("n", keybindings.toggle_profiling, ":HttpProfiling<CR>", opts)
3839
end,
3940
})
4041
end
@@ -92,6 +93,14 @@ M.setup = function(opts)
9293
desc = "Toggle verbose mode for HTTP request.",
9394
})
9495

96+
vim.api.nvim_create_user_command("HttpProfiling", function()
97+
local profiling_enabled = M.config.get("profiling").enabled
98+
M.config.options.profiling.enabled = not profiling_enabled
99+
print(string.format("HTTP Client profiling %s", not profiling_enabled and "enabled" or "disabled"))
100+
end, {
101+
desc = "Toggle profiling for HTTP requests.",
102+
})
103+
95104
vim.api.nvim_create_user_command("HttpDryRun", function()
96105
M.dry_run.display_dry_run(M)
97106
end, {

0 commit comments

Comments
 (0)