diff --git a/src/http/handler.zig b/src/http/handler.zig index 95e4fb3..34d6b2a 100644 --- a/src/http/handler.zig +++ b/src/http/handler.zig @@ -21,6 +21,17 @@ pub fn handleWeather( req: *httpz.Request, res: *httpz.Response, ) !void { + var client_ip_buf: [47]u8 = undefined; + // We need IP possibly for location, and possibly to determine implicit Imperial/Metric + // Let's get it once here + const client_ip = blk: { + const client_ip = getClientIpFromHeaders(req); + if (client_ip.len == 0) { + const full_location = try std.fmt.bufPrint(&client_ip_buf, "{f}", .{req.conn.address}); + break :blk full_location[0..std.mem.lastIndexOf(u8, full_location, ":").?]; + } + break :blk client_ip; + }; // Get location from path parameter or query string const location = req.param("location") orelse blk: { // Check query string for location parameter @@ -33,14 +44,14 @@ pub fn handleWeather( if (params.location) |loc| { break :blk loc; - } else { - // Fall back to IP-based detection - const client_ip = getClientIP(req); - std.log.debug("No location requested, using IP address '{s}'", .{client_ip}); - break :blk client_ip; - } + } else break :blk client_ip; // no location, just use client ip instead }; + if (location.len == 0) { + res.content_type = .TEXT; + res.body = "Sorry, we are unable to determine your location at this time. Try with / or /?location=\n"; + return; + } // Handle special endpoints if (location[0] == ':') { if (std.mem.eql(u8, location, ":help")) { @@ -54,10 +65,10 @@ pub fn handleWeather( } } - try handleWeatherInternal(opts, req, res, location); + try handleWeatherInternal(opts, req, res, location, client_ip); } -fn getClientIP(req: *httpz.Request) []const u8 { +fn getClientIpFromHeaders(req: *httpz.Request) []const u8 { // Check X-Forwarded-For header first (for proxies) if (req.header("x-forwarded-for")) |xff| { return parseXForwardedFor(xff); @@ -68,7 +79,7 @@ fn getClientIP(req: *httpz.Request) []const u8 { return real_ip; } - // Fall back to empty (no IP available) + // Fall back to client connection return ""; } @@ -86,6 +97,7 @@ fn handleWeatherInternal( req: *httpz.Request, res: *httpz.Response, location_query: []const u8, + client_ip: []const u8, ) !void { const req_alloc = req.arena; @@ -151,12 +163,8 @@ fn handleWeatherInternal( break :blk true; } } - const client_ip = getClientIP(req); - if (client_ip.len > 0) { - if (opts.geoip.isUSIP(client_ip)) { - break :blk true; - } - } + if (client_ip.len > 0 and opts.geoip.isUSIp(client_ip)) + break :blk true; break :blk false; }; diff --git a/src/location/GeoIp.zig b/src/location/GeoIp.zig index ba26c50..71838b4 100644 --- a/src/location/GeoIp.zig +++ b/src/location/GeoIp.zig @@ -89,7 +89,7 @@ fn lookupInternal(mmdb: *c.MMDB_s, ip: []const u8) !c.MMDB_lookup_result_s { return result; } -pub fn isUSIP(self: *GeoIP, ip: []const u8) bool { +pub fn isUSIp(self: *GeoIP, ip: []const u8) bool { const result = lookupInternal(&self.mmdb, ip) catch return false; if (!result.found_entry) return false; @@ -156,10 +156,10 @@ test "isUSIP detects US IPs" { defer geoip.deinit(); // Test that the function doesn't crash with various IPs - _ = geoip.isUSIP("8.8.8.8"); - _ = geoip.isUSIP("1.1.1.1"); + _ = geoip.isUSIp("8.8.8.8"); + _ = geoip.isUSIp("1.1.1.1"); // Test invalid IP returns false - const invalid = geoip.isUSIP("invalid"); + const invalid = geoip.isUSIp("invalid"); try std.testing.expect(!invalid); }