better handling of internet issues on initial requests

also improve json output
This commit is contained in:
mikkelam 2025-06-19 19:37:21 +02:00
parent 39d2cbd9d7
commit 08643d487c
2 changed files with 64 additions and 23 deletions

View file

@ -13,16 +13,6 @@ const progress = @import("../lib/progress.zig");
const HttpLatencyTester = @import("../lib/http_latency_tester.zig").HttpLatencyTester;
const log = std.log.scoped(.cli);
/// Update spinner text with current speed measurement
fn updateSpinnerText(spinner: anytype, measurement: SpeedMeasurement) void {
spinner.updateText("⬇️ {d:.1} {s}", .{ measurement.value, measurement.unit.toString() }) catch {};
}
/// Update spinner text with current upload speed measurement
fn updateUploadSpinnerText(spinner: anytype, measurement: SpeedMeasurement) void {
spinner.updateText("⬆️ {d:.1} {s}", .{ measurement.value, measurement.unit.toString() }) catch {};
}
const https_flag = zli.Flag{
.name = "https",
.description = "Use https when connecting to fast.com",
@ -86,7 +76,11 @@ fn run(ctx: zli.CommandContext) !void {
if (!json_output) {
try ctx.spinner.fail("Failed to get URLs: {}", .{err});
} else {
std.debug.print("{{\"error\": \"{}\"}}\n", .{err});
const error_msg = switch (err) {
error.ConnectionTimeout => "Failed to contact fast.com servers",
else => "Failed to get URLs",
};
try outputJson(null, null, null, error_msg);
}
return;
};
@ -133,7 +127,7 @@ fn run(ctx: zli.CommandContext) !void {
// JSON mode: clean output only
break :blk speed_tester.measure_download_speed_stability(urls, criteria) catch |err| {
log.err("Download test failed: {}", .{err});
std.debug.print("{{\"error\": \"{}\"}}\n", .{err});
try outputJson(null, null, null, "Download test failed");
return;
};
} else blk: {
@ -155,7 +149,7 @@ fn run(ctx: zli.CommandContext) !void {
// JSON mode: clean output only
break :blk speed_tester.measure_upload_speed_stability(urls, criteria) catch |err| {
log.err("Upload test failed: {}", .{err});
std.debug.print("{{\"error\": \"{}\"}}\n", .{err});
try outputJson(download_result.speed.value, latency_ms, null, "Upload test failed");
return;
};
} else blk: {
@ -184,13 +178,33 @@ fn run(ctx: zli.CommandContext) !void {
}
}
} else {
std.debug.print("{{\"download_mbps\": {d:.1}", .{download_result.speed.value});
if (latency_ms) |ping| {
std.debug.print(", \"ping_ms\": {d:.1}", .{ping});
}
if (upload_result) |up| {
std.debug.print(", \"upload_mbps\": {d:.1}", .{up.speed.value});
}
std.debug.print("}}\n", .{});
const upload_speed = if (upload_result) |up| up.speed.value else null;
try outputJson(download_result.speed.value, latency_ms, upload_speed, null);
}
}
/// Update spinner text with current speed measurement
fn updateSpinnerText(spinner: anytype, measurement: SpeedMeasurement) void {
spinner.updateText("⬇️ {d:.1} {s}", .{ measurement.value, measurement.unit.toString() }) catch {};
}
/// Update spinner text with current upload speed measurement
fn updateUploadSpinnerText(spinner: anytype, measurement: SpeedMeasurement) void {
spinner.updateText("⬆️ {d:.1} {s}", .{ measurement.value, measurement.unit.toString() }) catch {};
}
fn outputJson(download_mbps: ?f64, ping_ms: ?f64, upload_mbps: ?f64, error_message: ?[]const u8) !void {
const stdout = std.io.getStdOut().writer();
var download_buf: [32]u8 = undefined;
var ping_buf: [32]u8 = undefined;
var upload_buf: [32]u8 = undefined;
var error_buf: [256]u8 = undefined;
const download_str = if (download_mbps) |d| try std.fmt.bufPrint(&download_buf, "{d:.1}", .{d}) else "null";
const ping_str = if (ping_ms) |p| try std.fmt.bufPrint(&ping_buf, "{d:.1}", .{p}) else "null";
const upload_str = if (upload_mbps) |u| try std.fmt.bufPrint(&upload_buf, "{d:.1}", .{u}) else "null";
const error_str = if (error_message) |e| try std.fmt.bufPrint(&error_buf, "\"{s}\"", .{e}) else "null";
try stdout.print("{{\"download_mbps\": {s}, \"ping_ms\": {s}, \"upload_mbps\": {s}, \"error\": {s}}}\n", .{ download_str, ping_str, upload_str, error_str });
}

View file

@ -6,11 +6,13 @@ const testing = std.testing;
const log = std.log.scoped(.fast_api);
const mvzr = @import("mvzr");
const FastError = error{
HttpRequestFailed,
ScriptNotFound,
TokenNotFound,
JsonParseError,
ConnectionTimeout,
};
const Location = struct {
@ -159,11 +161,36 @@ pub const Fast = struct {
_ = allocator;
var response_body = std.ArrayList(u8).init(self.arena.allocator());
const response: http.Client.FetchResult = try self.client.fetch(.{
const response: http.Client.FetchResult = self.client.fetch(.{
.method = .GET,
.location = .{ .url = url },
.response_storage = .{ .dynamic = &response_body },
});
}) catch |err| switch (err) {
error.NetworkUnreachable, error.ConnectionRefused => {
log.err("Failed to reach fast.com servers (network/connection error) for URL: {s}", .{url});
return error.ConnectionTimeout;
},
error.UnknownHostName, error.NameServerFailure, error.TemporaryNameServerFailure, error.HostLacksNetworkAddresses => {
log.err("Failed to resolve fast.com hostname (DNS/internet connection issue) for URL: {s}", .{url});
return error.ConnectionTimeout;
},
error.ConnectionTimedOut, error.ConnectionResetByPeer => {
log.err("Connection to fast.com servers timed out or was reset for URL: {s}", .{url});
return error.ConnectionTimeout;
},
error.TlsInitializationFailed => {
log.err("Failed to establish secure connection to fast.com servers for URL: {s}", .{url});
return error.ConnectionTimeout;
},
error.UnexpectedConnectFailure => {
log.err("Unexpected connection failure to fast.com servers for URL: {s}", .{url});
return error.ConnectionTimeout;
},
else => {
log.err("Network error: {} for URL: {s}", .{ err, url });
return error.ConnectionTimeout;
},
};
log.debug("HTTP response status: {} for URL: {s}", .{ response.status, url });