Affects: SpringBoot 2.1.6.RELEASE
Usecase Want to proxy a Multipart content in reactive manner from server to another server (eg. curl/postman call -> serverApp1 -> serverApp2).
Problem The the whole chain hangs and timeouts when reaching server2. All Reactive Filters on the way on both servers are called properly, proper endpoint on server2 is determined, but the actual controller method of server2 never gets called. After the set timeout, the connection is cancelled.
Suspected cause Setting the timeouts on the WebClient (see commented section of code below).
Notes * The the example of broken code is below, to fix it, comment out the 'doOnConnected' section -> the whole flow finishes successfully * The problem seems to be related only when multipart content is involved, 'basic' JSON-API calls seem to work fine * The approach how to configure is similar as recommened in #390
Code (prototype of the error extracted from the actual app, stripped of filters, security, etc): ``` @RestController class Multipart {
@PostMapping("/multipartA")
fun endpointA(
@RequestPart("file") file: FilePart
): Mono<Unit> = client.post()
.uri("localhost:8080/multipartB")
.syncBody(
MultipartBodyBuilder().also { builder ->
builder.asyncPart("file", file.content(), DataBuffer::class.java).headers {
it.addAll(file.headers())
}
builder.asyncPart("some", SomJsonDTO("xx").toMono(), SomJsonDTO::class.java)
}.build()
).exchange()
.map {
if (!it.statusCode().is2xxSuccessful)
throw IllegalStateException("Invalid response: ${it.statusCode()}")
}
@PostMapping("/multipartB")
fun endpointB(
@RequestPart("file") file: FilePart,
@RequestPart("some") some: SomJsonDTO,
serverWebExchange: ServerWebExchange
): Mono<Unit> {
println("Received the request: $some")
return file.transferTo(File("/home/some-user/stuff/result.png")).map {
Unit
}
}
companion object {
val client = HttpClient.create().tcpConfiguration { client ->
with(client) {
option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 300000)
// *** commenting out the 'doOnConnected' fixes the issue ***
doOnConnected { conn ->
conn.addHandlerLast(ReadTimeoutHandler(30))
conn.addHandlerLast(WriteTimeoutHandler(30))
}
// ***
}
}.compress(true).let {
WebClient.builder()
.clientConnector(ReactorClientHttpConnector(it))
.build()
}
}
}
data class SomJsonDTO(val x: String) `
Comment From: snicoll
@zacharo sorry for the delay. Can you please move this code in text into an actual sample that we can run, ideally using Java only. You can share that with us by attaching a zip to this issue or by pushing the code to a separate GitHub repository.
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: spring-projects-issues
Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.