Last active
August 21, 2023 06:42
-
-
Save sandipchitale/8fae3e3cc263e9b65593f61ab923b273 to your computer and use it in GitHub Desktop.
Webclient passthru #webflux #webclient #springboot
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package sandipchitale.webclient; | |
import org.springframework.boot.SpringApplication; | |
import org.springframework.boot.autoconfigure.SpringBootApplication; | |
import org.springframework.http.HttpHeaders; | |
import org.springframework.http.HttpStatusCode; | |
import org.springframework.http.ResponseEntity; | |
import org.springframework.http.client.reactive.ClientHttpRequest; | |
import org.springframework.http.server.reactive.ServerHttpRequest; | |
import org.springframework.web.bind.annotation.*; | |
import org.springframework.web.reactive.function.BodyInserters; | |
import org.springframework.web.reactive.function.client.ClientRequest; | |
import org.springframework.web.reactive.function.client.ClientResponse; | |
import org.springframework.web.reactive.function.client.ExchangeFilterFunction; | |
import org.springframework.web.reactive.function.client.WebClient; | |
import reactor.core.publisher.Mono; | |
import reactor.netty.http.client.HttpClientRequest; | |
import java.time.Duration; | |
@SpringBootApplication | |
public class WebclientPassthruApplication { | |
public static void main(String[] args) { | |
SpringApplication.run(WebclientApplication.class, args); | |
} | |
@RestController | |
public static class WebclientController { | |
private final WebClient.Builder webClientBuilder; | |
public WebclientController() { | |
this.webClientBuilder = WebClient.builder() | |
.baseUrl("https://postman-echo.com") | |
.filter(getXTimeoutMillisFilter()) | |
.filter(getXFrameOptionsFilter()); | |
} | |
/** | |
* If X-Timeout-Millis request header is present in the request then set the response timeout to that value. | |
* Remove cookie header. | |
* | |
* @return | |
*/ | |
private static ExchangeFilterFunction getXTimeoutMillisFilter() { | |
return ExchangeFilterFunction.ofRequestProcessor((ClientRequest clientRequest) -> { | |
String timeoutMillis = clientRequest.headers().getFirst("X-Timeout-Millis"); | |
if (timeoutMillis != null) { | |
return Mono.just(ClientRequest.from(clientRequest) | |
.headers(httpHeaders -> { | |
// Cleanup the header | |
// httpHeaders.remove("X-Timeout-Millis"); | |
// sensitive headers | |
httpHeaders.remove(HttpHeaders.COOKIE); | |
}) | |
.httpRequest((ClientHttpRequest clientHttpRequest) -> { | |
HttpClientRequest reactorRequest = clientHttpRequest.getNativeRequest(); | |
reactorRequest.responseTimeout(Duration.ofMillis(Long.parseLong(timeoutMillis))); | |
}) | |
.build()); | |
} | |
return Mono.just(clientRequest); | |
}); | |
} | |
/** | |
* Set X-Frame-Options response header to SAMEORIGIN. | |
* | |
* @return | |
*/ | |
private static ExchangeFilterFunction getXFrameOptionsFilter() { | |
return ExchangeFilterFunction.ofResponseProcessor((ClientResponse clientResponse) -> { | |
clientResponse = clientResponse | |
.mutate() | |
.headers(httpHeaders -> { | |
// Remove some headers | |
// httpHeaders.remove(HttpHeaders.SET_COOKIE); | |
}) | |
.header("X-Frame-Options", "SAMEORIGIN") | |
.build(); | |
return Mono.just(clientResponse); | |
}); | |
} | |
@RequestMapping("/method") | |
public Mono<ResponseEntity<byte[]>> method(ServerHttpRequest serverHttpRequest) { | |
return webClientBuilder | |
.build() | |
.method(serverHttpRequest.getMethod()) | |
.uri("/" + serverHttpRequest.getMethod().name().toLowerCase()) | |
.headers(httpHeaders -> { | |
httpHeaders.addAll(serverHttpRequest.getHeaders()); | |
}) | |
// Pass body through as is | |
.body(BodyInserters.fromDataBuffers(serverHttpRequest.getBody())) | |
.retrieve() | |
// Pass error response through as is | |
.onStatus((HttpStatusCode status) -> { | |
return status.isError(); | |
}, | |
(ClientResponse clientResponse) -> { | |
return Mono.empty(); | |
}) | |
// Pass response through as is | |
.toEntity(byte[].class); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment