From 77a52a0629bfe722f582e0666d060b01ac064f1b Mon Sep 17 00:00:00 2001 From: Alex Zayats Date: Thu, 7 Aug 2025 13:59:44 +0300 Subject: [PATCH 1/3] fix: Patch RetryHandler.tryParseTimeHeader to support comma separated values returned by the server --- .../kiota/http/middleware/RetryHandler.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RetryHandler.java b/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RetryHandler.java index 86a551e6b..35c26fe3c 100644 --- a/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RetryHandler.java +++ b/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RetryHandler.java @@ -141,16 +141,26 @@ long getRetryAfter(Response response, long delay, int executionCount) { return (long) Math.min(retryDelay, RetryHandlerOption.MAX_DELAY * DELAY_MILLISECONDS); } + /** + * Return first positive value from the Retry-After header. + * @param retryAfterHeader The value of the Retry-After header. Sometimes it can contain multiple values separated by commas. + * For example: "31,120". + * @return Retry interval in milliseconds + */ double tryParseTimeHeader(String retryAfterHeader) { - double retryDelay = -1; - try { - retryDelay = Integer.parseInt(retryAfterHeader) * DELAY_MILLISECONDS; - } catch (NumberFormatException e) { - return retryDelay; + String[] values = retryAfterHeader.split(","); + for (String value : values) { + try { + double parsedValue = Double.parseDouble(value.trim()); + if (parsedValue > 0) { + return parsedValue * DELAY_MILLISECONDS; + } + } catch (NumberFormatException e) { + // Continue to the next value + } } - return retryDelay; + return -1; } - double tryParseDateHeader(String retryAfterHeader) { double retryDelay = -1; try { From 98a1ec2bfeba2a64c55afde220ed53e97571995a Mon Sep 17 00:00:00 2001 From: Alex Zayats Date: Thu, 7 Aug 2025 16:44:02 +0300 Subject: [PATCH 2/3] Add unit tests for RetryHandler.tryParseTimeHeader --- .../http/middleware/RetryHandlerTest.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 components/http/okHttp/src/test/java/com/microsoft/kiota/http/middleware/RetryHandlerTest.java diff --git a/components/http/okHttp/src/test/java/com/microsoft/kiota/http/middleware/RetryHandlerTest.java b/components/http/okHttp/src/test/java/com/microsoft/kiota/http/middleware/RetryHandlerTest.java new file mode 100644 index 000000000..87282ebed --- /dev/null +++ b/components/http/okHttp/src/test/java/com/microsoft/kiota/http/middleware/RetryHandlerTest.java @@ -0,0 +1,61 @@ +package com.microsoft.kiota.http.middleware; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +class RetryHandlerTest { + + private static final double DELAY_MILLISECONDS = 1000.0; + private static final double DEFAULT_DELAY_MILLISECONDS = -1.0; + private static final double DELTA = 0.01; + + private RetryHandler retryHandler; + + @BeforeEach + void setUp() { + retryHandler = new RetryHandler(); + } + + private static Stream retryAfterHeaderValues() { + return Stream.of( + // Single values + Arguments.of("10", 10.0 * DELAY_MILLISECONDS), + Arguments.of("0", DEFAULT_DELAY_MILLISECONDS), // Zero should return default + Arguments.of("-5", DEFAULT_DELAY_MILLISECONDS), // Negative should return default + + // Comma-separated values + Arguments.of("31,120", 31.0 * DELAY_MILLISECONDS), // First positive value + Arguments.of("0,45", 45.0 * DELAY_MILLISECONDS), // Skip zero, take next + Arguments.of("-10,25", 25.0 * DELAY_MILLISECONDS), // Skip negative, take next + Arguments.of("0,-5,60", 60.0 * DELAY_MILLISECONDS), // Skip multiple invalid + + // Edge cases + Arguments.of("", DEFAULT_DELAY_MILLISECONDS), // Empty string + Arguments.of(" ", DEFAULT_DELAY_MILLISECONDS), // Whitespace only + Arguments.of("abc", DEFAULT_DELAY_MILLISECONDS), // Invalid number + Arguments.of("10,abc,20", 10.0 * DELAY_MILLISECONDS), // Mixed valid/invalid + Arguments.of("abc,20", 20.0 * DELAY_MILLISECONDS), // Invalid first, valid second + Arguments.of("0,0,0", DEFAULT_DELAY_MILLISECONDS), // All zeros + Arguments.of("-1,-2,-3", DEFAULT_DELAY_MILLISECONDS), // All negative + + // Whitespace handling + Arguments.of(" 15 ", 15.0 * DELAY_MILLISECONDS), // Whitespace around value + Arguments.of("10, 20", 10.0 * DELAY_MILLISECONDS), // Whitespace after comma + Arguments.of(" 0 , 30 ", 30.0 * DELAY_MILLISECONDS) // Multiple whitespaces + ); + } + + @ParameterizedTest + @MethodSource("retryAfterHeaderValues") + void testTryParseTimeHeader(String headerValue, double expectedDelay) { + double result = retryHandler.tryParseTimeHeader(headerValue); + assertEquals(expectedDelay, result, DELTA, + "Failed for header value: '" + headerValue + "'"); + } +} From 96dfb7ac97d65b7be80f9fc2fd12bc5ffb3ba46b Mon Sep 17 00:00:00 2001 From: Alex Zayats Date: Thu, 7 Aug 2025 17:21:58 +0300 Subject: [PATCH 3/3] Fix code formatting --- .../com/microsoft/kiota/http/middleware/RetryHandler.java | 1 + .../microsoft/kiota/http/middleware/RetryHandlerTest.java | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RetryHandler.java b/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RetryHandler.java index 35c26fe3c..81d34562b 100644 --- a/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RetryHandler.java +++ b/components/http/okHttp/src/main/java/com/microsoft/kiota/http/middleware/RetryHandler.java @@ -161,6 +161,7 @@ long getRetryAfter(Response response, long delay, int executionCount) { } return -1; } + double tryParseDateHeader(String retryAfterHeader) { double retryDelay = -1; try { diff --git a/components/http/okHttp/src/test/java/com/microsoft/kiota/http/middleware/RetryHandlerTest.java b/components/http/okHttp/src/test/java/com/microsoft/kiota/http/middleware/RetryHandlerTest.java index 87282ebed..41ad313d8 100644 --- a/components/http/okHttp/src/test/java/com/microsoft/kiota/http/middleware/RetryHandlerTest.java +++ b/components/http/okHttp/src/test/java/com/microsoft/kiota/http/middleware/RetryHandlerTest.java @@ -48,14 +48,14 @@ private static Stream retryAfterHeaderValues() { Arguments.of(" 15 ", 15.0 * DELAY_MILLISECONDS), // Whitespace around value Arguments.of("10, 20", 10.0 * DELAY_MILLISECONDS), // Whitespace after comma Arguments.of(" 0 , 30 ", 30.0 * DELAY_MILLISECONDS) // Multiple whitespaces - ); + ); } @ParameterizedTest @MethodSource("retryAfterHeaderValues") void testTryParseTimeHeader(String headerValue, double expectedDelay) { double result = retryHandler.tryParseTimeHeader(headerValue); - assertEquals(expectedDelay, result, DELTA, - "Failed for header value: '" + headerValue + "'"); + assertEquals( + expectedDelay, result, DELTA, "Failed for header value: '" + headerValue + "'"); } }