Skip to content

Commit a45bf42

Browse files
Pascal MinderPascalMinder
authored andcommitted
Improve error logging
1 parent 4e30de4 commit a45bf42

File tree

2 files changed

+170
-10
lines changed

2 files changed

+170
-10
lines changed

geoblock.go

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ func (a *GeoBlock) allowDenyCachedRequestIP(requestIPAddr *net.IP, req *http.Req
277277
if !ok {
278278
entry, err = a.createNewIPEntry(req, ipAddressString)
279279
if err != nil && !(os.IsTimeout(err) && a.ignoreAPITimeout) {
280+
a.infoLogger.Printf("%s: request denied [%s] due to error: %s", a.name, requestIPAddr, err)
280281
return false, ""
281282
} else if os.IsTimeout(err) && a.ignoreAPITimeout {
282283
a.infoLogger.Printf("%s: request allowed [%s] due to API timeout", a.name, requestIPAddr)
@@ -295,16 +296,38 @@ func (a *GeoBlock) allowDenyCachedRequestIP(requestIPAddr *net.IP, req *http.Req
295296
if time.Since(entry.Timestamp).Hours() >= numberOfHoursInMonth && a.forceMonthlyUpdate {
296297
entry, err = a.createNewIPEntry(req, ipAddressString)
297298
if err != nil {
299+
a.infoLogger.Printf("%s: request denied [%s] due to error: %s", a.name, requestIPAddr, err)
298300
return false, ""
299301
}
300302
}
301303

302304
// check if we are in black/white-list mode and allow/deny based on country code
303-
isAllowed := (stringInSlice(entry.Country, a.countries) != a.blackListMode) ||
304-
(entry.Country == unknownCountryCode && a.allowUnknownCountries)
305+
isUnknownCountry := entry.Country == unknownCountryCode
306+
isCountryAllowed := stringInSlice(entry.Country, a.countries) != a.blackListMode
307+
isAllowed := isCountryAllowed || (isUnknownCountry && a.allowUnknownCountries)
305308

306309
if !isAllowed {
307-
a.infoLogger.Printf("%s: request denied [%s] for country [%s]", a.name, requestIPAddr, entry.Country)
310+
switch {
311+
case isUnknownCountry && !a.allowUnknownCountries:
312+
a.infoLogger.Printf(
313+
"%s: request denied [%s] for country [%s] due to: unknown country",
314+
a.name,
315+
requestIPAddr,
316+
entry.Country)
317+
case !isCountryAllowed:
318+
a.infoLogger.Printf(
319+
"%s: request denied [%s] for country [%s] due to: country is not allowed",
320+
a.name,
321+
requestIPAddr,
322+
entry.Country)
323+
default:
324+
a.infoLogger.Printf(
325+
"%s: request denied [%s] for country [%s]",
326+
a.name,
327+
requestIPAddr,
328+
entry.Country)
329+
}
330+
308331
return false, entry.Country
309332
}
310333

@@ -406,13 +429,11 @@ func (a *GeoBlock) getCountryCode(req *http.Request, ipAddressString string) (st
406429
return country, nil
407430
}
408431

409-
if a.logAPIRequests {
410-
a.infoLogger.Printf(
411-
"%s: Failed to read country from HTTP header field [%s], continuing with API lookup.",
412-
a.name,
413-
a.iPGeolocationHTTPHeaderField,
414-
)
415-
}
432+
a.infoLogger.Printf(
433+
"%s: Failed to read country from HTTP header field [%s], continuing with API lookup.",
434+
a.name,
435+
a.iPGeolocationHTTPHeaderField,
436+
)
416437
}
417438

418439
country, err := a.callGeoJS(ipAddressString)

geoblock_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,145 @@ func TestCustomLogFile(t *testing.T) {
11771177
assertStatusCode(t, recorder.Result(), http.StatusOK)
11781178
}
11791179

1180+
func TestLogDeniedDueToHeaderError_FirstCall(t *testing.T) {
1181+
apiHandler := &CountryCodeHandler{ResponseCountryCode: "CA"}
1182+
1183+
// set up our fake api server
1184+
var apiStub = httptest.NewServer(apiHandler)
1185+
1186+
tempDir, err := os.MkdirTemp("", "logtest")
1187+
if err != nil {
1188+
t.Fatalf("Failed to create temporary directory: %v", err)
1189+
}
1190+
defer os.RemoveAll(tempDir)
1191+
1192+
cfg := createTesterConfig()
1193+
cfg.API = apiStub.URL + "/{ip}"
1194+
cfg.Countries = append(cfg.Countries, "CH")
1195+
cfg.IPGeolocationHTTPHeaderField = ipGeolocationHTTPHeaderField
1196+
cfg.LogFilePath = tempDir + "/info.log"
1197+
cfg.LogAllowedRequests = true
1198+
1199+
ctx := context.Background()
1200+
next := http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})
1201+
1202+
handler, err := geoblock.New(ctx, next, cfg, "GeoBlock")
1203+
if err != nil {
1204+
t.Fatal(err)
1205+
}
1206+
1207+
recorder := httptest.NewRecorder()
1208+
1209+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://localhost", nil)
1210+
if err != nil {
1211+
t.Fatal(err)
1212+
}
1213+
1214+
req.Header.Add(xForwardedFor, chExampleIP)
1215+
req.Header.Set(cfg.IPGeolocationHTTPHeaderField, "C")
1216+
1217+
handler.ServeHTTP(recorder, req)
1218+
1219+
assertStatusCode(t, recorder.Result(), http.StatusForbidden)
1220+
1221+
content, err := os.ReadFile(cfg.LogFilePath)
1222+
if err != nil {
1223+
t.Fatalf("Failed to read log file: %v", err)
1224+
}
1225+
1226+
wrongCountryCode := "Failed to read country from HTTP header field [cf-ipcountry], continuing with API lookup"
1227+
countryNotAllowed := "request denied [82.220.110.18] for country [CA] due to: country is not allowed"
1228+
1229+
if len(content) == 0 ||
1230+
!strings.Contains(string(content), wrongCountryCode) ||
1231+
!strings.Contains(string(content), countryNotAllowed) {
1232+
t.Fatalf("Empty custom log file or missing expected log lines.")
1233+
}
1234+
}
1235+
1236+
func TestTimeoutOnApiResponse_DenyWhenIgnoreTimeoutFalse(t *testing.T) {
1237+
tempDir, err := os.MkdirTemp("", "logtest")
1238+
if err != nil {
1239+
t.Fatalf("Failed to create temporary directory: %v", err)
1240+
}
1241+
defer os.RemoveAll(tempDir)
1242+
1243+
// Stub server that responds too slowly for our client timeout.
1244+
apiStub := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
1245+
time.Sleep(50 * time.Millisecond) // > APITimeoutMs below
1246+
w.WriteHeader(http.StatusOK)
1247+
_, _ = w.Write([]byte("CH"))
1248+
}))
1249+
defer apiStub.Close()
1250+
1251+
cfg := createTesterConfig()
1252+
cfg.API = apiStub.URL + "/{ip}"
1253+
cfg.Countries = append(cfg.Countries, "CH")
1254+
cfg.APITimeoutMs = 5 // 5ms client timeout
1255+
cfg.IgnoreAPITimeout = false // timeouts should DENY
1256+
cfg.LogFilePath = tempDir + "/info.log"
1257+
cfg.LogAllowedRequests = true
1258+
1259+
ctx := context.Background()
1260+
next := http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})
1261+
1262+
handler, err := geoblock.New(ctx, next, cfg, "GeoBlock")
1263+
if err != nil {
1264+
t.Fatal(err)
1265+
}
1266+
1267+
rec := httptest.NewRecorder()
1268+
req := httptest.NewRequest(http.MethodGet, "http://localhost", nil)
1269+
req.Header.Add(xForwardedFor, chExampleIP)
1270+
1271+
handler.ServeHTTP(rec, req)
1272+
1273+
assertStatusCode(t, rec.Result(), http.StatusForbidden)
1274+
1275+
content, err := os.ReadFile(cfg.LogFilePath)
1276+
if err != nil {
1277+
t.Fatalf("Failed to read log file: %v", err)
1278+
}
1279+
1280+
timeoutError := "context deadline exceeded"
1281+
1282+
if len(content) == 0 || !strings.Contains(string(content), timeoutError) {
1283+
t.Fatalf("Empty custom log file or missing expected log lines.")
1284+
}
1285+
}
1286+
1287+
func TestTimeoutOnApiResponse_AllowWhenIgnoreTimeoutTrue(t *testing.T) {
1288+
// Stub server that responds too slowly for our client timeout.
1289+
apiStub := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
1290+
time.Sleep(50 * time.Millisecond) // > APITimeoutMs below
1291+
w.WriteHeader(http.StatusOK)
1292+
_, _ = w.Write([]byte("CH"))
1293+
}))
1294+
defer apiStub.Close()
1295+
1296+
cfg := createTesterConfig()
1297+
cfg.API = apiStub.URL + "/{ip}"
1298+
cfg.Countries = append(cfg.Countries, "CH")
1299+
cfg.APITimeoutMs = 5 // 5ms client timeout
1300+
cfg.IgnoreAPITimeout = true // timeouts should ALLOW
1301+
1302+
ctx := context.Background()
1303+
next := http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})
1304+
1305+
handler, err := geoblock.New(ctx, next, cfg, "GeoBlock")
1306+
if err != nil {
1307+
t.Fatal(err)
1308+
}
1309+
1310+
rec := httptest.NewRecorder()
1311+
req := httptest.NewRequest(http.MethodGet, "http://localhost", nil)
1312+
req.Header.Add(xForwardedFor, chExampleIP)
1313+
1314+
handler.ServeHTTP(rec, req)
1315+
1316+
assertStatusCode(t, rec.Result(), http.StatusOK)
1317+
}
1318+
11801319
func assertStatusCode(t *testing.T, req *http.Response, expected int) {
11811320
t.Helper()
11821321

0 commit comments

Comments
 (0)