diff --git a/src/main/java/cz/trask/adfs/context/ProcessHandler.java b/src/main/java/cz/trask/adfs/context/ProcessHandler.java index 9b3af74..0edca91 100644 --- a/src/main/java/cz/trask/adfs/context/ProcessHandler.java +++ b/src/main/java/cz/trask/adfs/context/ProcessHandler.java @@ -33,12 +33,6 @@ public class ProcessHandler extends BaseContextHandler { return; } - int index = clientIdIndex.get() % clientIds.size(); - String currentClientId = clientIds.get(index); - - TokenPayloadIdp token = tokenService.getToken(currentClientId); - String accessToken = token.getAccessToken(); - String backendBaseUrl = appConfig.getBackendUrl(); if (backendBaseUrl == null || backendBaseUrl.isBlank()) { logger.error("backend_url is not configured"); @@ -84,34 +78,50 @@ public class ProcessHandler extends BaseContextHandler { body = exchange.getRequestBody().readAllBytes(); } - logger.info("Forwarding {} request to backend: {} (Client ID: {})", method, targetUrl, currentClientId); - BackendResponse backendResponse = callBackend(targetUrl, method, exchange.getRequestHeaders(), body, accessToken); + BackendResponse backendResponse = null; + int maxAttempts = clientIds.size(); + for (int attempt = 1; attempt <= maxAttempts; attempt++) { + int index = clientIdIndex.get() % clientIds.size(); + if (index < 0) index += clientIds.size(); + String currentClientId = clientIds.get(index); - int retryCount = 0; - int maxRetries = clientIds.size() - 1; - while (backendResponse.statusCode == 429 && retryCount < maxRetries) { - retryCount++; - int nextIndex = clientIdIndex.incrementAndGet() % clientIds.size(); - String retryClientId = clientIds.get(nextIndex); - - logger.warn("Backend returned 429. Retrying ({}/{}) with next client ID: {}...", - retryCount, maxRetries, retryClientId); - - token = tokenService.getToken(retryClientId); - accessToken = token.getAccessToken(); + if (attempt > 1) { + logger.info("Retrying request to backend with next Client ID: {} (attempt {}/{})", currentClientId, attempt, maxAttempts); + } - logger.info("Retrying request to backend with new token (Client ID: {})", retryClientId); - backendResponse = callBackend(targetUrl, method, exchange.getRequestHeaders(), body, accessToken); + try { + TokenPayloadIdp token = tokenService.getToken(currentClientId); + String accessToken = token.getAccessToken(); + + backendResponse = callBackend(targetUrl, method, exchange.getRequestHeaders(), body, accessToken); + + if (backendResponse.statusCode != 429) { + break; // Success or fatal backend error, exit loop + } + + logger.warn("Backend returned 429 Too Many Requests for Client ID: {}. Switching to next identity.", currentClientId); + } catch (Exception e) { + logger.warn("Failed to process request with Client ID: {}: {}", currentClientId, e.getMessage()); + if (attempt == maxAttempts) { + throw e; // Last identity failed, propagate exception + } + // Else, treat as retryable failure and move to next identity + backendResponse = new BackendResponse(429, null, null); + } + + clientIdIndex.incrementAndGet(); } - if (backendResponse.statusCode == 429) { + if (backendResponse != null && backendResponse.statusCode == 429) { logger.error("Throttling limit reached. Exhausted all {} identities and backend still returns 429.", clientIds.size()); sendError(exchange, 429, ERR_TOO_MANY_REQUESTS, - "All configured client identities are currently throttled by the backend after " + retryCount + " retries."); + "All configured client identities are currently throttled by the backend after " + maxAttempts + " attempts."); return; } - sendResponse(exchange, backendResponse.statusCode, backendResponse.contentType, backendResponse.body); + if (backendResponse != null) { + sendResponse(exchange, backendResponse.statusCode, backendResponse.contentType, backendResponse.body); + } } catch (IllegalArgumentException e) { sendError(exchange, 400, ERR_INVALID_CONFIG, e.getMessage()); diff --git a/src/main/resources/config.yaml b/src/main/resources/config.yaml index b042ad9..08325d9 100644 --- a/src/main/resources/config.yaml +++ b/src/main/resources/config.yaml @@ -14,6 +14,7 @@ adfs: audience: "https://fs.komercpoj.loc/adfs/oauth2/token" resource: "urn:kamma:api" clientIds: + - "db2edf05-7af2-4523-97f9-39f930afc634a" - "db2edf05-7af2-4523-97f9-39f930afc634" certificate: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURFRENDQWZpZ0F3SUJBZ0lRRjhOQTNUQytVWUpJL1VRNTNTSFVGakFOQmdrcWhraUc5dzBCQVFVRkFEQWIKTVJrd0Z3WURWUVFEREJCUFFYVjBhQ0JEYkdsbGJuUWdTbGRVTUI0WERUSTJNRFl6TURFME1UazBOVm9YRFRJMwpNRFl6TURFME16azBOVm93R3pFWk1CY0dBMVVFQXd3UVQwRjFkR2dnUTJ4cFpXNTBJRXBYVkRDQ0FTSXdEUVlKCktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQU1UVzFpL0tNUFY1cTl2TXgzTGV4V1lsNFdFTmJ6UDAKQVV5ZjhlNm1GcHdEcE5PWUVXV1RQYnAwUm5HRmY4SGpCaUFwTWFDSFZCbmJqMC81Q3pSWWk0N3lWWEMzaGRiQgpYdDdZRGNCeEduYnN5Zy9LdTB5SFRQRW5YblB5VCtpYXUzS2JqQXgyOGU1OXhzRHlFMzZxaktveGRTbURZT0M4CmpzM0hsMHNGTWNjd3JzZ0JxOFEyYllIVUt1TERORmRveE1DaEhOTHlxOE1sNFdnNmVHdWtMN2p4R0tGNXlzczEKM096emJoZkc5dEpWY290WUt0WDlHUThUckdpREF2UGFOREs2WXMxUTZWbDNKVjY5dmdET1ZSVlJCUzhpWmdLaQpQbTdob1hHb2s3VVEzV2R0c3dpU09ReE1iMWZhVDFLOWp0T3lmTm1vNjgyUkluU1VJSi9sTm9rQ0F3RUFBYU5RCk1FNHdEZ1lEVlIwUEFRSC9CQVFEQWdXZ01CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUNCZ2dyQmdFRkJRY0QKQVRBZEJnTlZIUTRFRmdRVTlRNUJSVnpRVDVNbG5mY3N6VzFwUHZiZmRud3dEUVlKS29aSWh2Y05BUUVGQlFBRApnZ0VCQUpUMVlIYk03UCtrYi9wSmJ5bENLUUd6OEZ6eGFpeDVyay9MdndJNDVmcVJTQ0NUaERmemUrdTNiK05ZCjcxV2V0Z2Y2cUhubkVrdkRnVXJyWXU1QUhrZE9WWXlDUkJnN0dWKzIrQUtpWExEYUsrV0FnM2U4Zkk2cFNvOUMKeXdvc0lNNFcyaWJWSnhzVWNIZGhTUFBVVlkrVzNSY1B4VlZmWmV4eTZXUXBsVlV0Z3JYQmpYSEdEWE5YQUw3awo4ZUFwSEpDQVByKytabktQc3VzSWFyeVNqRXRrZXR0UHViMGp0MnJlajdqaHJuU1EyRlg4czhqcU43L0lCeWRlCjd2N2YwMlowMjVPV2hZRHozRVVQaWZLOXpmM2Nac2hZRDJ3OE1jdGVUZkZKaUxPMzM2bGhCRjBNVW5laDFEaDQKOWhSVEp2NVUwNXd3bnRRbWc3bloxOFpJRGNFPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==" privateKey: "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRREUxdFl2eWpEMWVhdmIKek1keTNzVm1KZUZoRFc4ejlBRk1uL0h1cGhhY0E2VFRtQkZsa3oyNmRFWnhoWC9CNHdZZ0tUR2doMVFaMjQ5UAorUXMwV0l1TzhsVnd0NFhXd1Y3ZTJBM0FjUnAyN01vUHlydE1oMHp4SjE1ejhrL29tcnR5bTR3TWR2SHVmY2JBCjhoTitxb3lxTVhVcGcyRGd2STdOeDVkTEJUSEhNSzdJQWF2RU5tMkIxQ3Jpd3pSWGFNVEFvUnpTOHF2REplRm8KT25ocnBDKzQ4UmloZWNyTE5kenM4MjRYeHZiU1ZYS0xXQ3JWL1JrUEU2eG9nd0x6MmpReXVtTE5VT2xaZHlWZQp2YjRBemxVVlVRVXZJbVlDb2o1dTRhRnhxSk8xRU4xbmJiTUlramtNVEc5WDJrOVN2WTdUc256WnFPdk5rU0owCmxDQ2Y1VGFKQWdNQkFBRUNnZ0VBTlk3ZktKR3pVSmVTazNQY3NQeThhVmJWUkRzTGp1OU5pelBaK0QxbzJYcUUKVnE2QnpVbUsramk1RWhwbzhMQjg4ak5ETlpLYzU0dytLbHh4R0FVZnMvMXNFZ2RkZTFtU0hzQmF2WW9JMFdNTQpkOCtKdHdENWhvUlh4dVNmcVlLd2pqYVVuSytnbEd2VXNKc3Rnc0dWRkpud2J5TTRNTzRkVFJDSnNmZnYyMnFCClNVRVh2MVFzRmhlbTBySnFuendPRlpycXVObXlvSnhTUzREYUJ4bmtqY3VHVG90Mk9qMlpVRjBTZW5HaU16NVkKRjZkR2dtWHNDTXBtc1k2czBrRUtDcExGMXIwS2VaUG8vZmVheU1GUDdONUtxVmxUSWExVGJjS2dQaWJFMGRqNwpXckhwR0l0Rm40ZHhJc2FCQlJ2dTlkZ2JJTVJuc0NhOFE1bURwbVg1SVFLQmdRRFVJQndFZjd1S0JqQ0M3OFFHClZzTmRLY1RuWTRrVDVxOFZiRlpVdkI2M052N0NDYmVCc1d0ZDRSMDFNUnkwVnhVUG4zekpWYk1DNUN5ckFHZ1EKTVRrVzlZQmVxb1A3aEk1ZnM5a1B5cG42SHlJN1NZS0ZldHF5RW5Ca1B4RklPSzUzRXJRWGxkZlRWbUdGdWpNUwpXYVpkOWpEOURmcGZKSzR6S2xuWHlXVlZkd0tCZ1FEdGpWVW9kNldRVEp5eHl1Vjk2Z29xbEl4L2t2UFNoQ2pqCkVqM2ZXY21HTGVBaUU2Z0lBNUh3YnFGM0tCSUVaSURpVVlJTjNkUUxLUlpVdXhDRkxpak9Ea3lBUkV1WFFUeFkKaUl0dWR3a3BmTS90OXdaQ09YSUczSWNXMDUvZHJrSzl5eXloMkRGTEJuZ3JERitoMzhDbWo0bExoc2RadDBQcgpXZjUxWnB6VC93S0JnR3d5aEpmMjN4MmowcEsyNFhHcVI3UDVYaW40Snk4emR4S2lVOWFjcmI0ZUd3dTJFUmZoCit6WERZVGFFZW5PeUIxZ1VyWDIwYkw2SXpBL2RBVGRoSkJHRjM1aHB1VEJOaUtGZ0J0TjdMOWJZa29sVEVYUXMKR2VqQ1p2bDdBY0dveDdTTW9iZDJBc1FWUjJFQ2ZKSmJqL1JWWXQ5d2hjaUoyU0RYOVVPUHdsUTVBb0dBWWp5VwpRTnZwemRqQTNBMktCaDRwQWg3WVUxR1VIelNrSy9NNVB3cEVlb2F5TDZWdFVaTVlZUk4vRm1XdHZiOUtSVTFyCnVReEpTaXc5bmVDV0hsMU9acGduTHN3UGJvZDl5eWI4Y2p3cnY4cHJ1bjd6U2FPejhmNTBwdzN4Q0oydDRBc0wKZEFxUnAvTU84czUxSmQ0QUwyRWdaK2xldTAwOGV6R0dOMHF1QkVVQ2dZRUF1VDZRS0FRUndxeWV4WlZPbmJPZwowZWVtelJSWDJYQStVV3J6YTRKbzV2MnZhYTR0cXhuQ2prQ1BaTldTc0diWUlTZVJNUUV0UXEwdUl0ZW9zMTFWCjJWM25BNXhwZ1ErbmZWNnpyQTFYK3VGekltWHpTbWgxSkpkK0RGUVhNT2xBWWNGY2J2aFdYWUVoaS9SR01DT3kKdU1MNWxBa2swV2JXRHFicFNGM1RMMlk9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0="