Details
-
Bug
-
Status: Resolved
-
Minor
-
Resolution: Fixed
-
5.2.1, 5.3-alpha1
-
JAVA 18
kotlin 1.7.22
Ubuntu 22.04 x64
Description
BasicHttpClientConnectionManager doesn't handle request timeout correctly and return the response of a previous request.
The unit test below should output:
http://localhost:41751/1 -> 1
http://localhost:41751/2 -> java.net.SocketTimeoutException: Read timed out
http://localhost:41751/2 -> java.net.SocketTimeoutException: Read timed out
http://localhost:41751/3 -> 3
http://localhost:41751/3 -> 3
http://localhost:41751/3 -> 3
But instead it returns:
http://localhost:33875/1 -> 1
http://localhost:33875/2 -> java.net.SocketTimeoutException: Read timed out
http://localhost:33875/2 -> java.net.SocketTimeoutException: Read timed out
http://localhost:33875/3 -> java.net.SocketTimeoutException: Read timed out
http://localhost:33875/3 -> 2
http://localhost:33875/3 -> 3
As you can see it returns 2 when requesting the uri /3 which returned 3.
Also it timeout on the first request to /3 while it shouldn't.
Replace with PoolingHttpClientConnectionManager() and it works as expected.
Kotlin unit test to reproduce :
import com.github.tomakehurst.wiremock.client.WireMock.*
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo
import com.github.tomakehurst.wiremock.junit5.WireMockTest
import org.apache.hc.client5.http.classic.methods.HttpGet
import org.apache.hc.client5.http.config.RequestConfig
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient
import org.apache.hc.client5.http.impl.classic.HttpClients
import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager
import org.apache.hc.core5.http.io.entity.EntityUtils
import org.apache.hc.core5.util.Timeout
import org.junit.jupiter.api.Test@WireMockTest
internal class HttpClientRaceConditionDebug {@Test
fun `debugging z`(wm: WireMockRuntimeInfo) {
stubFor(get(urlEqualTo("/1")).willReturn(aResponse().withBody("1")))
stubFor(get(urlEqualTo("/2")).willReturn(aResponse().withFixedDelay(2000).withBody("2")))
stubFor(get(urlEqualTo("/3")).willReturn(aResponse().withBody("3")))val client = HttpClients.custom()
.setConnectionManager(BasicHttpClientConnectionManager())
// .setConnectionManager(PoolingHttpClientConnectionManager())
.setDefaultRequestConfig(
RequestConfig.custom()
.setResponseTimeout(Timeout.ofSeconds(1))
.build()
)
.build()client.executeAndLog("${wm.httpBaseUrl}/1")
client.executeAndLog("${wm.httpBaseUrl}/2")
client.executeAndLog("${wm.httpBaseUrl}/2")
client.executeAndLog("${wm.httpBaseUrl}/3")
client.executeAndLog("${wm.httpBaseUrl}/3")
client.executeAndLog("${wm.httpBaseUrl}/3")
}private fun CloseableHttpClient.executeAndLog(uri: String) {
try {
execute(HttpGet(uri)) { println("$uri -> ${EntityUtils.toString(it.entity)}") }
} catch (ex: Exception)Unknown macro: { println("$uri -> $ex") }}
}