@Override public <T> T body(BodyExtractor<T, ? super ClientHttpResponse> extractor) { return this.delegate.body(extractor); }
@Override public <T> T body(BodyExtractor<T, ? super ClientHttpResponse> extractor) { return delegate.body(extractor); }
/** * Consume up to the specified number of bytes from the response body and * cancel if any more data arrives. * <p>Internally delegates to {@link DataBufferUtils#takeUntilByteCount}. * @param maxByteCount the limit as number of bytes * @return the filter to limit the response size with * @since 5.1 */ public static ExchangeFilterFunction limitResponseSize(long maxByteCount) { return (request, next) -> next.exchange(request).map(response -> { Flux<DataBuffer> body = response.body(BodyExtractors.toDataBuffers()); body = DataBufferUtils.takeUntilByteCount(body, maxByteCount); return ClientResponse.from(response).body(body).build(); }); }
@Test public void bodyExtractor() { Mono<String> result = Mono.just("foo"); BodyExtractor<Mono<String>, ReactiveHttpInputMessage> extractor = BodyExtractors.toMono(String.class); when(mockResponse.body(extractor)).thenReturn(result); assertSame(result, wrapper.body(extractor)); }
private <T> Publisher<? extends T> readResponseBody(String logId, Request request, ClientResponse response, Class<T> responseType) { if (RawActionResponse.class.equals(responseType)) { ClientLogger.logRawResponse(logId, response.statusCode()); return Mono.just(responseType.cast(RawActionResponse.create(response))); } if (response.statusCode().is5xxServerError()) { ClientLogger.logRawResponse(logId, response.statusCode()); return handleServerError(request, response); } return response.body(BodyExtractors.toMono(byte[].class)) // .map(it -> new String(it, StandardCharsets.UTF_8)) // .doOnNext(it -> ClientLogger.logResponse(logId, response.statusCode(), it)) // .flatMap(content -> doDecode(response, responseType, content)); }
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // NOTICE: nothing in "pre" filter stage as CLIENT_RESPONSE_ATTR is not added // until the WebHandler is run return chain.filter(exchange).then(Mono.defer(() -> { ClientResponse clientResponse = exchange.getAttribute(CLIENT_RESPONSE_ATTR); if (clientResponse == null) { return Mono.empty(); } log.trace("WebClientWriteResponseFilter start"); ServerHttpResponse response = exchange.getResponse(); return response.writeWith(clientResponse.body(BodyExtractors.toDataBuffers())).log("webClient response"); })); }
private static Mono<WebClientResponseException> createResponseException( ClientResponse response, HttpRequest request) { return DataBufferUtils.join(response.body(BodyExtractors.toDataBuffers())) .map(dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()];
@RequestMapping(path = REQUEST_MAPPING_PATH, method = {RequestMethod.GET, RequestMethod.HEAD, RequestMethod.POST, RequestMethod.PUT, RequestMethod.PATCH, RequestMethod.DELETE, RequestMethod.OPTIONS}) public Mono<Void> endpointProxy(@PathVariable("instanceId") String instanceId, ServerHttpRequest request, ServerHttpResponse response) { String endpointLocalPath = getEndpointLocalPath(request.getPath().pathWithinApplication().value()); URI uri = UriComponentsBuilder.fromPath(endpointLocalPath) .query(request.getURI().getRawQuery()) .build(true) .toUri(); return super.forward(instanceId, uri, request.getMethod(), request.getHeaders(), () -> BodyInserters.fromDataBuffers(request.getBody())).flatMap(clientResponse -> { response.setStatusCode(clientResponse.statusCode()); response.getHeaders().addAll(filterHeaders(clientResponse.headers().asHttpHeaders())); return response.writeAndFlushWith(clientResponse.body(BodyExtractors.toDataBuffers()).window(1)); }); } }
response.flush(); return clientResponse.body(BodyExtractors.toDataBuffers()) .window(1) .concatMap(body -> writeAndFlush(body, responseBody))
@Test public void webCallShouldTriggerWebSessionSaveAction() { when(mockWebSession.getAttributes()).thenReturn(new HashMap<>()); when(mockWebSession.save()).thenReturn(Mono.empty()); Mono<Map> result = webClient.get() .uri("/get") .exchange() .flatMap(response -> response.body(toMono(Map.class))); StepVerifier.create(result) .consumeNextWith(response -> {/* Don't care about data, just need to catch signal */}) .expectComplete() .verify(Duration.ofMinutes(10)); verify(mockWebSession).save(); }
.refreshToken("refresh-1") .build(); when(this.exchange.getResponse().body(any())).thenReturn(Mono.just(response)); Instant issuedAt = Instant.now().minus(Duration.ofDays(1)); Instant accessTokenExpiresAt = issuedAt.plus(Duration.ofHours(1));
.refreshToken("refresh-1") .build(); when(this.exchange.getResponse().body(any())).thenReturn(Mono.just(response)); Instant issuedAt = Instant.now().minus(Duration.ofDays(1)); Instant accessTokenExpiresAt = issuedAt.plus(Duration.ofHours(1));
private Mono<OAuth2AuthorizedClient> authorizeWithRefreshToken(ClientRequest request, ExchangeFunction next, OAuth2AuthorizedClient authorizedClient) { ClientRegistration clientRegistration = authorizedClient .getClientRegistration(); String tokenUri = clientRegistration .getProviderDetails().getTokenUri(); ClientRequest refreshRequest = ClientRequest.create(HttpMethod.POST, URI.create(tokenUri)) .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .headers(headers -> headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret())) .body(refreshTokenBody(authorizedClient.getRefreshToken().getTokenValue())) .build(); return next.exchange(refreshRequest) .flatMap(response -> response.body(oauth2AccessTokenResponse())) .map(accessTokenResponse -> new OAuth2AuthorizedClient(authorizedClient.getClientRegistration(), authorizedClient.getPrincipalName(), accessTokenResponse.getAccessToken(), accessTokenResponse.getRefreshToken())) .map(result -> { Authentication principal = (Authentication) request.attribute( AUTHENTICATION_ATTR_NAME).orElse(new PrincipalNameAuthentication(authorizedClient.getPrincipalName())); HttpServletRequest httpRequest = (HttpServletRequest) request.attributes().get( HTTP_SERVLET_REQUEST_ATTR_NAME); HttpServletResponse httpResponse = (HttpServletResponse) request.attributes().get( HTTP_SERVLET_RESPONSE_ATTR_NAME); this.authorizedClientRepository.saveAuthorizedClient(result, principal, httpRequest, httpResponse); return result; }) .publishOn(Schedulers.elastic()); }
.refreshToken("refresh-1") .build(); when(this.exchange.getResponse().body(any())).thenReturn(Mono.just(response)); Instant issuedAt = Instant.now().minus(Duration.ofDays(1)); Instant accessTokenExpiresAt = issuedAt.plus(Duration.ofHours(1));
private Mono<OAuth2AuthorizedClient> authorizeWithRefreshToken(ExchangeFunction next, OAuth2AuthorizedClient authorizedClient, OAuth2AuthorizedClientResolver.Request r) { ServerWebExchange exchange = r.getExchange(); Authentication authentication = r.getAuthentication(); ClientRegistration clientRegistration = authorizedClient .getClientRegistration(); String tokenUri = clientRegistration .getProviderDetails().getTokenUri(); ClientRequest refreshRequest = ClientRequest.create(HttpMethod.POST, URI.create(tokenUri)) .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .headers(headers -> headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret())) .body(refreshTokenBody(authorizedClient.getRefreshToken().getTokenValue())) .build(); return next.exchange(refreshRequest) .flatMap(refreshResponse -> refreshResponse.body(oauth2AccessTokenResponse())) .map(accessTokenResponse -> new OAuth2AuthorizedClient(authorizedClient.getClientRegistration(), authorizedClient.getPrincipalName(), accessTokenResponse.getAccessToken(), accessTokenResponse.getRefreshToken())) .flatMap(result -> this.authorizedClientRepository.saveAuthorizedClient(result, authentication, exchange) .thenReturn(result)); }
@Test public void multipartFormDataWorks() { ClassPathResource img = new ClassPathResource("1x1.png"); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.IMAGE_PNG); HttpEntity<ClassPathResource> entity = new HttpEntity<>(img, headers); MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>(); parts.add("imgpart", entity); Mono<Map> result = webClient.post() .uri("/post") .contentType(MediaType.MULTIPART_FORM_DATA) .body(BodyInserters.fromMultipartData(parts)) .exchange() .flatMap(response -> response.body(toMono(Map.class))); StepVerifier.create(result) .consumeNextWith(map -> { Map<String, Object> files = getMap(map, "files"); assertThat(files).containsKey("imgpart"); String file = (String) files.get("imgpart"); assertThat(file).startsWith("data:").contains(";base64,"); }) .expectComplete() .verify(DURATION); }
@Override public Mono<OAuth2AccessTokenResponse> getTokenResponse(OAuth2AuthorizationCodeGrantRequest authorizationGrantRequest) { return Mono.defer(() -> { ClientRegistration clientRegistration = authorizationGrantRequest.getClientRegistration(); OAuth2AuthorizationExchange authorizationExchange = authorizationGrantRequest.getAuthorizationExchange(); String tokenUri = clientRegistration.getProviderDetails().getTokenUri(); BodyInserters.FormInserter<String> body = body(authorizationExchange); return this.webClient.post() .uri(tokenUri) .accept(MediaType.APPLICATION_JSON) .headers(headers -> headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret())) .body(body) .exchange() .flatMap(response -> response.body(oauth2AccessTokenResponse())) .map(response -> { if (response.getAccessToken().getScopes().isEmpty()) { response = OAuth2AccessTokenResponse.withResponse(response) .scopes(authorizationExchange.getAuthorizationRequest().getScopes()) .build(); } return response; }); }); }
); return response.body(oauth2AccessTokenResponse()); }) .map(response -> { if (response.getAccessToken().getScopes().isEmpty()) {
@Test public void postWorks() { Mono<Map> result = webClient.post() .uri("/post") .header("Host", "www.example.org") .syncBody("testdata") .exchange() .flatMap(response -> response.body(toMono(Map.class))); StepVerifier.create(result) .consumeNextWith(map -> assertThat(map).containsEntry("data", "testdata")) .expectComplete() .verify(DURATION); }
@Test public void limitResponseSize() { DefaultDataBufferFactory bufferFactory = new DefaultDataBufferFactory(); DataBuffer b1 = dataBuffer("foo", bufferFactory); DataBuffer b2 = dataBuffer("bar", bufferFactory); DataBuffer b3 = dataBuffer("baz", bufferFactory); ClientRequest request = ClientRequest.create(HttpMethod.GET, DEFAULT_URL).build(); ClientResponse response = ClientResponse.create(HttpStatus.OK).body(Flux.just(b1, b2, b3)).build(); Mono<ClientResponse> result = ExchangeFilterFunctions.limitResponseSize(5) .filter(request, req -> Mono.just(response)); StepVerifier.create(result.flatMapMany(res -> res.body(BodyExtractors.toDataBuffers()))) .consumeNextWith(buffer -> assertEquals("foo", string(buffer))) .consumeNextWith(buffer -> assertEquals("ba", string(buffer))) .expectComplete() .verify(); }