You can solve it in following manner.
Create a filter, that intercepts body with BufferingDecorator that reads body when it's being sent and calculates required header. Be aware that this code is called just before sending the request, so if you use some header loggers may not see the calculated header if they are executed before.
For the response it's easier, you just need to use BodyExtractor.
Feel free to ask, if you have further questions.
public class HmacFilter implements ExchangeFilterFunction {
@Override
public @NonNull Mono<ClientResponse> filter(@NonNull ClientRequest request, ExchangeFunction next) {
ClientRequest modifiedRequest = ClientRequest.from(request)
.body((outputMessage, context) ->
request.body().insert(
new BufferingDecorator(outputMessage),
context
))
.build();
return next.exchange(modifiedRequest)
.flatMap(this::appendResponseHeader);
}
private static final class BufferingDecorator extends ClientHttpRequestDecorator {
private BufferingDecorator(ClientHttpRequest delegate) {
super(delegate);
}
@Override
public @NonNull Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
return DataBufferUtils.join(body).flatMap(buffer -> {
HttpHeaders headers = getHeaders();
headers.set("Hmac", calculateHmac(getDelegate(), readBytes(buffer)));
return super.writeWith(Mono.just(buffer));
});
}
/*
writeWith is not called for GET with empty body, in such cases header needs to be added in setComplete()
*/
@Override
public @NonNull Mono<Void> setComplete() {
HttpHeaders headers = getHeaders();
headers.putIfAbsent("Hmac", Collections.singletonList(calculateHmac(getDelegate(), new byte[0])));
return super.setComplete();
}
private String calculateHmac(ClientHttpRequest request, byte[] payload) {
// calculate HMAC using request body
}
private String calculateHmac(byte[] responseBody) {
// calculate HMAC using response body
}
public byte[] readBytes(DataBuffer dataBuffer) {
ByteBuffer byteBuffer = dataBuffer.asByteBuffer();
byte[] bytes = new byte[byteBuffer.remaining()];
byteBuffer.get(bytes);
return bytes;
}
private Mono<ClientResponse> appendResponseHeader(ClientResponse response) {
HttpHeaders headers = response.headers().asHttpHeaders();
Flux<DataBuffer> body = response.body(BodyExtractors.toDataBuffers())
.map(dataBuffer -> {
ByteBuffer byteBuffer = dataBuffer.asByteBuffer();
byte[] bytes = new byte[byteBuffer.remaining()];
byteBuffer.get(bytes);
headers.putIfAbsent("Hmac-Response", Collections.singletonList(calculateHmac(bytes)));
return dataBuffer;
});
response.mutate().body(body);
return Mono.just(response);
}
}
BodyExtractor
as inresponse.body(BodyExtractor.toMono(Foobar.class))
and no i dont have a code sample. docs.spring.io/spring-framework/docs/current/javadoc-api/org/… – Drying