diff --git a/.github/workflows/http-platforms.yml b/.github/workflows/http-platforms.yml new file mode 100644 index 00000000..8513cf93 --- /dev/null +++ b/.github/workflows/http-platforms.yml @@ -0,0 +1,55 @@ +name: HTTP resilience cross-OS + +on: + workflow_dispatch: + push: + branches: [main] + paths: + - .github/workflows/http-platforms.yml + - pom.xml + - teaql-core/** + - teaql-runtime/** + - teaql-context-runtime-tools/** + - teaql-tool-http/** + - examples/http-resilience/** + pull_request: + branches: [main] + paths: + - .github/workflows/http-platforms.yml + - pom.xml + - teaql-core/** + - teaql-runtime/** + - teaql-context-runtime-tools/** + - teaql-tool-http/** + - examples/http-resilience/** + +permissions: + contents: read + +jobs: + http-resilience: + name: HTTP contract and example (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + os: [macos-26, macos-26-intel, windows-2025] + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: temurin + cache: maven + - name: Run focused HTTP contract tests + run: >- + mvn -B -pl teaql-tool-http -am + -Dtest=HttpResilienceTest + -Dsurefire.failIfNoSpecifiedTests=false test + - name: Run Context-owned HTTP example twice + run: bash examples/http-resilience/verify.sh diff --git a/teaql-tool-http/src/test/java/io/teaql/tools/http/HttpResilienceTest.java b/teaql-tool-http/src/test/java/io/teaql/tools/http/HttpResilienceTest.java index 6c66e1ac..1b0a9d91 100644 --- a/teaql-tool-http/src/test/java/io/teaql/tools/http/HttpResilienceTest.java +++ b/teaql-tool-http/src/test/java/io/teaql/tools/http/HttpResilienceTest.java @@ -668,7 +668,9 @@ private void handle(Socket socket) { if (line.toLowerCase(java.util.Locale.ROOT).startsWith("content-length:")) contentLength = Integer.parseInt(line.substring(line.indexOf(':') + 1).trim()); } - if (target.contains("/retry-post")) { + // Drain the request before replying. Closing a socket with unread POST bytes + // can reset an otherwise valid response on Windows TCP stacks. + if (target.contains("/retry-post") || target.contains("/private-response")) { char[] body = new char[contentLength]; int offset = 0; while (offset < body.length) { @@ -676,7 +678,7 @@ private void handle(Socket socket) { if (count < 0) return; offset += count; } - retryBodies.add(new String(body)); + if (target.contains("/retry-post")) retryBodies.add(new String(body)); } if (target.contains("/close")) return; if (target.endsWith("/slow")) Thread.sleep(300);