Introduce new functional web API
This commit introduces a new, functional web programming model in the org.springframework.web.reactive.function package. The key types are: - Request and Response are new Java 8-DSLs for access to the HTTP request and response - HandlerFunction represents a function to handle a request to a response - RoutingFunction maps a request to a HandlerFunction - FilterFunction filters a routing as defined by a RoutingFunction - RequestPredicate is used by Router to create RoutingFunctions - RequestPredicates offers common RequestPredicate instances
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import org.springframework.http.server.reactive.AbstractHttpHandlerIntegrationTests;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public abstract class AbstractRoutingFunctionIntegrationTests
|
||||
extends AbstractHttpHandlerIntegrationTests {
|
||||
|
||||
@Override
|
||||
protected final HttpHandler createHttpHandler() {
|
||||
RoutingFunction<?> routingFunction = routingFunction();
|
||||
return Router.toHttpHandler(routingFunction);
|
||||
}
|
||||
|
||||
protected abstract RoutingFunction<?> routingFunction();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.codec.CharSequenceEncoder;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.MockWebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class BodyResponseTests {
|
||||
|
||||
private final String body = "foo";
|
||||
|
||||
private final BodyResponse<String> publisherResponse =
|
||||
new BodyResponse<>(200, new HttpHeaders(), body);
|
||||
|
||||
@Test
|
||||
public void body() throws Exception {
|
||||
assertEquals(body, publisherResponse.body());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeTo() throws Exception {
|
||||
MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, URI.create("http://localhost"));
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
ServerWebExchange exchange = new DefaultServerWebExchange(request, response, new MockWebSessionManager());
|
||||
exchange.getAttributes().put(Router.HTTP_MESSAGE_WRITERS_ATTRIBUTE, Collections
|
||||
.singleton(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder())).stream());
|
||||
|
||||
|
||||
publisherResponse.writeTo(exchange).block();
|
||||
assertNotNull(response.getBody());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.web.reactive.function.support.RequestWrapper;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class BodyWrapperTests {
|
||||
|
||||
private Request.Body mockBody;
|
||||
|
||||
private RequestWrapper.BodyWrapper wrapper;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mockBody = mock(Request.Body.class);
|
||||
wrapper = new RequestWrapper.BodyWrapper(mockBody);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stream() throws Exception {
|
||||
DataBuffer buffer = new DefaultDataBufferFactory().allocateBuffer();
|
||||
Flux<DataBuffer> flux = Flux.just(buffer);
|
||||
when(mockBody.stream()).thenReturn(flux);
|
||||
|
||||
assertSame(flux, wrapper.stream());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertTo() throws Exception {
|
||||
Flux<String> flux = Flux.just("foo", "bar");
|
||||
when(mockBody.convertTo(String.class)).thenReturn(flux);
|
||||
|
||||
assertSame(flux, wrapper.convertTo(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertToMono() throws Exception {
|
||||
Mono<String> mono = Mono.just("foo");
|
||||
when(mockBody.convertToMono(String.class)).thenReturn(mono);
|
||||
|
||||
assertSame(mono, wrapper.convertToMono(String.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class DefaultConfigurationTests {
|
||||
|
||||
private DefaultConfiguration configuration = new DefaultConfiguration();
|
||||
|
||||
@Test
|
||||
public void messageReaders() throws Exception {
|
||||
assertEquals(4, configuration.messageReaders().get().count());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void messageWriters() throws Exception {
|
||||
assertEquals(4, configuration.messageWriters().get().count());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.codec.StringDecoder;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBuffer;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRange;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.DecoderHttpMessageReader;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class DefaultRequestTests {
|
||||
|
||||
private ServerHttpRequest mockRequest;
|
||||
|
||||
private ServerHttpResponse mockResponse;
|
||||
|
||||
private ServerWebExchange mockExchange;
|
||||
|
||||
private DefaultRequest defaultRequest;
|
||||
|
||||
@Before
|
||||
public void createMocks() {
|
||||
mockRequest = mock(ServerHttpRequest.class);
|
||||
mockResponse = mock(ServerHttpResponse.class);
|
||||
|
||||
mockExchange = mock(ServerWebExchange.class);
|
||||
when(mockExchange.getRequest()).thenReturn(mockRequest);
|
||||
when(mockExchange.getResponse()).thenReturn(mockResponse);
|
||||
|
||||
defaultRequest = new DefaultRequest(mockExchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void method() throws Exception {
|
||||
HttpMethod method = HttpMethod.HEAD;
|
||||
when(mockRequest.getMethod()).thenReturn(method);
|
||||
|
||||
assertEquals(method, defaultRequest.method());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uri() throws Exception {
|
||||
URI uri = URI.create("https://example.com");
|
||||
when(mockRequest.getURI()).thenReturn(uri);
|
||||
|
||||
assertEquals(uri, defaultRequest.uri());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attributes() throws Exception {
|
||||
Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
when(mockExchange.getAttributes()).thenReturn(attributes);
|
||||
|
||||
assertEquals(attributes, defaultRequest.attributes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryParams() throws Exception {
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||
queryParams.set("foo", "bar");
|
||||
when(mockRequest.getQueryParams()).thenReturn(queryParams);
|
||||
|
||||
assertEquals(Optional.of("bar"), defaultRequest.queryParam("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathVariables() throws Exception {
|
||||
Map<String, String> pathVariables = Collections.singletonMap("foo", "bar");
|
||||
when(mockExchange.getAttribute(Router.URI_TEMPLATE_VARIABLES_ATTRIBUTE)).thenReturn(Optional.of(pathVariables));
|
||||
|
||||
assertEquals(pathVariables, defaultRequest.pathVariables());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void header() throws Exception {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
List<MediaType> accept =
|
||||
Collections.singletonList(MediaType.APPLICATION_JSON);
|
||||
httpHeaders.setAccept(accept);
|
||||
List<Charset> acceptCharset = Collections.singletonList(StandardCharsets.UTF_8);
|
||||
httpHeaders.setAcceptCharset(acceptCharset);
|
||||
long contentLength = 42L;
|
||||
httpHeaders.setContentLength(contentLength);
|
||||
MediaType contentType = MediaType.TEXT_PLAIN;
|
||||
httpHeaders.setContentType(contentType);
|
||||
InetSocketAddress host = InetSocketAddress.createUnresolved("localhost", 80);
|
||||
httpHeaders.setHost(host);
|
||||
List<HttpRange> range = Collections.singletonList(HttpRange.createByteRange(0, 42));
|
||||
httpHeaders.setRange(range);
|
||||
|
||||
when(mockRequest.getHeaders()).thenReturn(httpHeaders);
|
||||
|
||||
Request.Headers headers = defaultRequest.headers();
|
||||
assertEquals(accept, headers.accept());
|
||||
assertEquals(acceptCharset, headers.acceptCharset());
|
||||
assertEquals(OptionalLong.of(contentLength), headers.contentLength());
|
||||
assertEquals(Optional.of(contentType), headers.contentType());
|
||||
assertEquals(httpHeaders, headers.asHttpHeaders());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void body() throws Exception {
|
||||
DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
|
||||
DefaultDataBuffer dataBuffer =
|
||||
factory.wrap(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));
|
||||
Flux<DataBuffer> body = Flux.just(dataBuffer);
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
|
||||
when(mockRequest.getHeaders()).thenReturn(httpHeaders);
|
||||
when(mockRequest.getBody()).thenReturn(body);
|
||||
|
||||
when(mockExchange.getAttribute(Router.HTTP_MESSAGE_READERS_ATTRIBUTE))
|
||||
.thenReturn(Optional.of(Collections
|
||||
.singleton(new DecoderHttpMessageReader<String>(new StringDecoder()))
|
||||
.stream()));
|
||||
|
||||
assertEquals(body, defaultRequest.body().stream());
|
||||
|
||||
Mono<String> resultMono = defaultRequest.body().convertToMono(String.class);
|
||||
assertEquals("foo", resultMono.block());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.AbstractHttpHandlerIntegrationTests;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.reactive.DispatcherHandler;
|
||||
import org.springframework.web.reactive.HandlerAdapter;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.reactive.config.WebReactiveConfiguration;
|
||||
import org.springframework.web.reactive.function.support.HandlerFunctionAdapter;
|
||||
import org.springframework.web.reactive.function.support.ResponseResultHandler;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.web.reactive.function.Router.route;
|
||||
|
||||
/**
|
||||
* Tests the use of {@link HandlerFunction} and {@link RoutingFunction} in a
|
||||
* {@link DispatcherHandler}.
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class DispatcherHandlerIntegrationTests extends AbstractHttpHandlerIntegrationTests {
|
||||
|
||||
private AnnotationConfigApplicationContext wac;
|
||||
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@Before
|
||||
public void createRestTemplate() {
|
||||
this.restTemplate = new RestTemplate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpHandler createHttpHandler() {
|
||||
this.wac = new AnnotationConfigApplicationContext();
|
||||
this.wac.register(TestConfiguration.class);
|
||||
this.wac.refresh();
|
||||
|
||||
DispatcherHandler webHandler = new DispatcherHandler();
|
||||
webHandler.setApplicationContext(this.wac);
|
||||
|
||||
return WebHttpHandlerBuilder.webHandler(webHandler).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mono() throws Exception {
|
||||
ResponseEntity<Person> result =
|
||||
restTemplate.getForEntity("http://localhost:" + port + "/mono", Person.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
assertEquals("John", result.getBody().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flux() throws Exception {
|
||||
ParameterizedTypeReference<List<Person>> reference = new ParameterizedTypeReference<List<Person>>() {};
|
||||
ResponseEntity<List<Person>> result =
|
||||
restTemplate.exchange("http://localhost:" + port + "/flux", HttpMethod.GET, null, reference);
|
||||
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
List<Person> body = result.getBody();
|
||||
assertEquals(2, body.size());
|
||||
assertEquals("John", body.get(0).getName());
|
||||
assertEquals("Jane", body.get(1).getName());
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class TestConfiguration extends WebReactiveConfiguration {
|
||||
|
||||
@Bean
|
||||
public PersonHandler personHandler() {
|
||||
return new PersonHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HandlerAdapter handlerAdapter() {
|
||||
return new HandlerFunctionAdapter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HandlerMapping handlerMapping(RoutingFunction<?> routingFunction,
|
||||
ApplicationContext applicationContext) {
|
||||
return Router.toHandlerMapping(routingFunction,
|
||||
new Router.Configuration() {
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageReader<?>>> messageReaders() {
|
||||
return () -> getMessageReaders().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<Stream<HttpMessageWriter<?>>> messageWriters() {
|
||||
return () -> getMessageWriters().stream();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RoutingFunction<?> routingFunction() {
|
||||
PersonHandler personHandler = personHandler();
|
||||
return route(RequestPredicates.GET("/mono"), personHandler::mono)
|
||||
.and(route(RequestPredicates.GET("/flux"), personHandler::flux));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResponseResultHandler responseResultHandler() {
|
||||
return new ResponseResultHandler();
|
||||
}
|
||||
}
|
||||
|
||||
private static class PersonHandler {
|
||||
|
||||
public Response<Publisher<Person>> mono(Request request) {
|
||||
Person person = new Person("John");
|
||||
return Response.ok().stream(Mono.just(person), Person.class);
|
||||
}
|
||||
|
||||
public Response<Publisher<Person>> flux(Request request) {
|
||||
Person person1 = new Person("John");
|
||||
Person person2 = new Person("Jane");
|
||||
return Response.ok().stream(Flux.just(person1, person2), Person.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class Person {
|
||||
|
||||
private String name;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Person
|
||||
person = (Person) o;
|
||||
return !(this.name != null ? !this.name.equals(person.name) : person.name != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.name != null ? this.name.hashCode() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person{" +
|
||||
"name='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.ServerSentEvent;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.http.server.reactive.bootstrap.HttpServer;
|
||||
import org.springframework.http.server.reactive.bootstrap.ReactorHttpServer;
|
||||
|
||||
import static org.springframework.web.reactive.function.RequestPredicates.GET;
|
||||
import static org.springframework.web.reactive.function.RequestPredicates.POST;
|
||||
import static org.springframework.web.reactive.function.Router.route;
|
||||
import static org.springframework.web.reactive.function.Router.toHttpHandler;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class Driver {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
PersonHandler handler = new PersonHandler();
|
||||
|
||||
RoutingFunction<Publisher<Person>> personRoute =
|
||||
route(GET("/person/{id}"), handler::person)
|
||||
.and(route(GET("/people"), handler::people))
|
||||
.filter((request, next) -> {
|
||||
System.out.println("In publisher filter");
|
||||
Response<Publisher<Person>> handlerResponse = next.handle(request);
|
||||
Publisher<Person> s = Flux.from(handlerResponse.body())
|
||||
.map(person -> new Person(person.name.toUpperCase()));
|
||||
return Response.from(handlerResponse).stream(s, Person.class);
|
||||
});
|
||||
|
||||
RoutingFunction<Publisher<String>> stringRoute = route(POST("/string"), request -> {
|
||||
Flux<String> requestBody = request.body().convertTo(String.class);
|
||||
Flux<String> responseBody = Flux.concat(Mono.just("Hello "), requestBody);
|
||||
return Response.ok().stream(responseBody, String.class);
|
||||
});
|
||||
|
||||
RoutingFunction<?> sseRoute = route(GET("/sse"), request -> {
|
||||
Flux<ServerSentEvent<Person>> eventFlux = Flux.interval(Duration.ofMillis(100)).map(l -> {
|
||||
Person person = new Person("Person " + l);
|
||||
return ServerSentEvent.<Person>builder().data(person)
|
||||
.id(Long.toString(l))
|
||||
.comment("bar")
|
||||
.build();
|
||||
}).take(20);
|
||||
|
||||
return Response.ok().sse(eventFlux);
|
||||
}).andOther(route(GET("/sse-string"), request -> {
|
||||
Flux<String> flux = Flux.interval(Duration.ofMillis(100)).map(l -> Long.toString(l)).take(20);
|
||||
return Response.ok().sse(flux, String.class);
|
||||
}));
|
||||
|
||||
createServer(toHttpHandler(
|
||||
personRoute.andOther(stringRoute).andOther(sseRoute)
|
||||
.filter((request, next) -> {
|
||||
System.out.println("Before");
|
||||
Response<?> result = next.handle(request);
|
||||
System.out.println("After");
|
||||
return result;
|
||||
})
|
||||
));
|
||||
|
||||
System.out.println("Press ENTER to exit.");
|
||||
System.in.read();
|
||||
}
|
||||
|
||||
private static HttpServer createServer(HttpHandler httpHandler) throws Exception {
|
||||
HttpServer server = new ReactorHttpServer();
|
||||
int port = 8080;
|
||||
server.setPort(port);
|
||||
server.setHandler(httpHandler);
|
||||
server.afterPropertiesSet();
|
||||
server.start();
|
||||
System.out.println("Server started at http://localhost:" + port + "/");
|
||||
return server;
|
||||
}
|
||||
|
||||
private static class PersonHandler {
|
||||
|
||||
public Response<Publisher<Person>> person(Request r) {
|
||||
System.out.println("r.pathVariable(id) = " + r.pathVariable("id"));
|
||||
return Response.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.stream(Mono.just(new Person("John")), Person.class);
|
||||
}
|
||||
|
||||
public Response<Publisher<Person>> people(Request r) {
|
||||
return Response.ok().contentType(MediaType.APPLICATION_JSON)
|
||||
.stream(Flux.just(new Person("Jane"), new Person("John")), Person.class);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static class Person {
|
||||
|
||||
private String name;
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof Person) {
|
||||
Person other = (Person) o;
|
||||
return Objects.equals(this.name, other.name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person{" +
|
||||
"name='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class EmptyResponseTests {
|
||||
|
||||
@Test
|
||||
public void statusCode() throws Exception {
|
||||
HttpStatus statusCode = HttpStatus.ACCEPTED;
|
||||
EmptyResponse emptyResponse = new EmptyResponse(statusCode.value(), new HttpHeaders());
|
||||
assertSame(statusCode, emptyResponse.statusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headers() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
EmptyResponse emptyResponse = new EmptyResponse(200, headers);
|
||||
assertEquals(headers, emptyResponse.headers());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void body() throws Exception {
|
||||
EmptyResponse emptyResponse = new EmptyResponse(200, new HttpHeaders());
|
||||
assertNull(emptyResponse.body());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeTo() throws Exception {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("MyKey", "MyValue");
|
||||
EmptyResponse emptyResponse = new EmptyResponse(201, headers);
|
||||
|
||||
ServerWebExchange exchange = mock(ServerWebExchange.class);
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
when(exchange.getResponse()).thenReturn(response);
|
||||
|
||||
|
||||
emptyResponse.writeTo(exchange).block();
|
||||
assertEquals(201, response.getStatusCode().value());
|
||||
assertEquals("MyValue", response.getHeaders().getFirst("MyKey"));
|
||||
assertNull(response.getBody());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpRange;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.support.RequestWrapper;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class HeadersWrapperTest {
|
||||
|
||||
private Request.Headers mockHeaders;
|
||||
|
||||
private RequestWrapper.HeadersWrapper wrapper;
|
||||
|
||||
|
||||
@Before
|
||||
public void createWrapper() {
|
||||
mockHeaders = mock(Request.Headers.class);
|
||||
wrapper = new RequestWrapper.HeadersWrapper(mockHeaders);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accept() throws Exception {
|
||||
List<MediaType> accept = Collections.singletonList(MediaType.APPLICATION_JSON);
|
||||
when(mockHeaders.accept()).thenReturn(accept);
|
||||
|
||||
assertSame(accept, wrapper.accept());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void acceptCharset() throws Exception {
|
||||
List<Charset> acceptCharset = Collections.singletonList(StandardCharsets.UTF_8);
|
||||
when(mockHeaders.acceptCharset()).thenReturn(acceptCharset);
|
||||
|
||||
assertSame(acceptCharset, wrapper.acceptCharset());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLength() throws Exception {
|
||||
OptionalLong contentLength = OptionalLong.of(42L);
|
||||
when(mockHeaders.contentLength()).thenReturn(contentLength);
|
||||
|
||||
assertSame(contentLength, wrapper.contentLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentType() throws Exception {
|
||||
Optional<MediaType> contentType = Optional.of(MediaType.APPLICATION_JSON);
|
||||
when(mockHeaders.contentType()).thenReturn(contentType);
|
||||
|
||||
assertSame(contentType, wrapper.contentType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void host() throws Exception {
|
||||
InetSocketAddress host = InetSocketAddress.createUnresolved("example.com", 42);
|
||||
when(mockHeaders.host()).thenReturn(host);
|
||||
|
||||
assertSame(host, wrapper.host());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void range() throws Exception {
|
||||
List<HttpRange> range = Collections.singletonList(HttpRange.createByteRange(42));
|
||||
when(mockHeaders.range()).thenReturn(range);
|
||||
|
||||
assertSame(range, wrapper.range());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void header() throws Exception {
|
||||
String name = "foo";
|
||||
List<String> value = Collections.singletonList("bar");
|
||||
when(mockHeaders.header(name)).thenReturn(value);
|
||||
|
||||
assertSame(value, wrapper.header(name));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asHttpHeaders() throws Exception {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
when(mockHeaders.asHttpHeaders()).thenReturn(httpHeaders);
|
||||
|
||||
assertSame(httpHeaders, wrapper.asHttpHeaders());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRange;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class MockRequest implements Request {
|
||||
|
||||
private final HttpMethod method;
|
||||
|
||||
private final URI uri;
|
||||
|
||||
private final MockHeaders headers;
|
||||
|
||||
private final MockBody body;
|
||||
|
||||
private final Map<String, Object> attributes;
|
||||
|
||||
private final MultiValueMap<String, String> queryParams;
|
||||
|
||||
private final Map<String, String> pathVariables;
|
||||
|
||||
private MockRequest(HttpMethod method, URI uri,
|
||||
MockHeaders headers, MockBody body, Map<String, Object> attributes,
|
||||
MultiValueMap<String, String> queryParams,
|
||||
Map<String, String> pathVariables) {
|
||||
this.method = method;
|
||||
this.uri = uri;
|
||||
this.headers = headers;
|
||||
this.body = body;
|
||||
this.attributes = attributes;
|
||||
this.queryParams = queryParams;
|
||||
this.pathVariables = pathVariables;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new BuilderImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpMethod method() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI uri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Headers headers() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Body body() {
|
||||
return this.body;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> Optional<T> attribute(String name) {
|
||||
return Optional.ofNullable((T) this.attributes.get(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> attributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> queryParams(String name) {
|
||||
return Collections.unmodifiableList(this.queryParams.get(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> pathVariables() {
|
||||
return Collections.unmodifiableMap(this.pathVariables);
|
||||
}
|
||||
|
||||
public interface Builder {
|
||||
|
||||
Builder method(HttpMethod method);
|
||||
|
||||
Builder uri(URI uri);
|
||||
|
||||
Builder header(String key, String value);
|
||||
|
||||
Builder headers(HttpHeaders headers);
|
||||
|
||||
Builder attribute(String name, Object value);
|
||||
|
||||
Builder attributes(Map<String, Object> attributes);
|
||||
|
||||
Builder queryParam(String key, String value);
|
||||
|
||||
Builder queryParams(MultiValueMap<String, String> queryParams);
|
||||
|
||||
Builder pathVariable(String key, String value);
|
||||
|
||||
Builder pathVariables(Map<String, String> pathVariables);
|
||||
|
||||
<T> MockRequest body(Flux<T> body);
|
||||
|
||||
<T> MockRequest body(Mono<T> body);
|
||||
|
||||
MockRequest build();
|
||||
|
||||
}
|
||||
|
||||
private static class BuilderImpl implements Builder {
|
||||
|
||||
private HttpMethod method = HttpMethod.GET;
|
||||
|
||||
private URI uri = URI.create("http://localhost");
|
||||
|
||||
private MockHeaders headers = new MockHeaders(new HttpHeaders());
|
||||
|
||||
private Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
|
||||
private MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>();
|
||||
|
||||
private Map<String, String> pathVariables = new LinkedHashMap<>();
|
||||
|
||||
@Override
|
||||
public Builder method(HttpMethod method) {
|
||||
Assert.notNull(method, "'method' must not be null");
|
||||
this.method = method;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder uri(URI uri) {
|
||||
Assert.notNull(uri, "'uri' must not be null");
|
||||
this.uri = uri;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder header(String key, String value) {
|
||||
Assert.notNull(key, "'key' must not be null");
|
||||
Assert.notNull(value, "'value' must not be null");
|
||||
this.headers.header(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder headers(HttpHeaders headers) {
|
||||
Assert.notNull(headers, "'headers' must not be null");
|
||||
this.headers = new MockHeaders(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder attribute(String name, Object value) {
|
||||
Assert.notNull(name, "'name' must not be null");
|
||||
Assert.notNull(value, "'value' must not be null");
|
||||
this.attributes.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder attributes(Map<String, Object> attributes) {
|
||||
Assert.notNull(attributes, "'attributes' must not be null");
|
||||
this.attributes = attributes;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder queryParam(String key, String value) {
|
||||
Assert.notNull(key, "'key' must not be null");
|
||||
Assert.notNull(value, "'value' must not be null");
|
||||
this.queryParams.add(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder queryParams(MultiValueMap<String, String> queryParams) {
|
||||
Assert.notNull(queryParams, "'queryParams' must not be null");
|
||||
this.queryParams = queryParams;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder pathVariable(String key, String value) {
|
||||
Assert.notNull(key, "'key' must not be null");
|
||||
Assert.notNull(value, "'value' must not be null");
|
||||
this.pathVariables.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder pathVariables(Map<String, String> pathVariables) {
|
||||
Assert.notNull(pathVariables, "'pathVariables' must not be null");
|
||||
this.pathVariables = pathVariables;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MockRequest body(Flux<T> flux) {
|
||||
MockBody body = new MockBody() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <S> Flux<S> convertTo(Class<? extends S> aClass) {
|
||||
return (Flux<S>) flux;
|
||||
}
|
||||
};
|
||||
return build(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> MockRequest body(Mono<T> mono) {
|
||||
MockBody body = new MockBody() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <S> Mono<S> convertToMono(Class<? extends S> aClass) {
|
||||
return (Mono<S>) mono;
|
||||
}
|
||||
};
|
||||
return build(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MockRequest build() {
|
||||
return build(new MockBody());
|
||||
}
|
||||
|
||||
private MockRequest build(MockBody body) {
|
||||
return new MockRequest(this.method, this.uri, this.headers, body, this.attributes,
|
||||
this.queryParams, this.pathVariables);
|
||||
}
|
||||
}
|
||||
|
||||
private static class MockHeaders implements Headers {
|
||||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
|
||||
public MockHeaders(HttpHeaders headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
private HttpHeaders delegate() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
public void header(String key, String value) {
|
||||
this.headers.add(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaType> accept() {
|
||||
return delegate().getAccept();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Charset> acceptCharset() {
|
||||
return delegate().getAcceptCharset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalLong contentLength() {
|
||||
return toOptionalLong(delegate().getContentLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MediaType> contentType() {
|
||||
return Optional.ofNullable(delegate().getContentType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress host() {
|
||||
return delegate().getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HttpRange> range() {
|
||||
return delegate().getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> header(String headerName) {
|
||||
List<String> headerValues = delegate().get(headerName);
|
||||
return headerValues != null ? headerValues : Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders asHttpHeaders() {
|
||||
return HttpHeaders.readOnlyHttpHeaders(delegate());
|
||||
}
|
||||
|
||||
private OptionalLong toOptionalLong(long value) {
|
||||
return value != -1 ? OptionalLong.of(value) : OptionalLong.empty();
|
||||
}
|
||||
|
||||
private Optional<ZonedDateTime> toZonedDateTime(long date) {
|
||||
if (date != -1) {
|
||||
Instant instant = Instant.ofEpochMilli(date);
|
||||
return Optional.of(ZonedDateTime.ofInstant(instant, ZoneId.of("GMT")));
|
||||
}
|
||||
else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class MockBody implements Body {
|
||||
|
||||
@Override
|
||||
public Flux<DataBuffer> stream() {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Flux<T> convertTo(Class<? extends T> aClass) {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> convertToMono(Class<? extends T> aClass) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.web.reactive.function.Router.route;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class PublisherHandlerFunctionIntegrationTests
|
||||
extends AbstractRoutingFunctionIntegrationTests {
|
||||
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@Before
|
||||
public void createRestTemplate() {
|
||||
this.restTemplate = new RestTemplate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RoutingFunction<?> routingFunction() {
|
||||
PersonHandler personHandler = new PersonHandler();
|
||||
return route(RequestPredicates.GET("/mono"), personHandler::mono)
|
||||
.and(route(RequestPredicates.GET("/flux"), personHandler::flux));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void mono() throws Exception {
|
||||
ResponseEntity<Person> result =
|
||||
restTemplate.getForEntity("http://localhost:" + port + "/mono", Person.class);
|
||||
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
assertEquals("John", result.getBody().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flux() throws Exception {
|
||||
ParameterizedTypeReference<List<Person>> reference = new ParameterizedTypeReference<List<Person>>() {};
|
||||
ResponseEntity<List<Person>> result =
|
||||
restTemplate.exchange("http://localhost:" + port + "/flux", HttpMethod.GET, null, reference);
|
||||
|
||||
assertEquals(HttpStatus.OK, result.getStatusCode());
|
||||
List<Person> body = result.getBody();
|
||||
assertEquals(2, body.size());
|
||||
assertEquals("John", body.get(0).getName());
|
||||
assertEquals("Jane", body.get(1).getName());
|
||||
}
|
||||
|
||||
|
||||
private static class PersonHandler {
|
||||
|
||||
public Response<Publisher<Person>> mono(Request request) {
|
||||
Person person = new Person("John");
|
||||
return Response.ok().stream(Mono.just(person), Person.class);
|
||||
}
|
||||
|
||||
public Response<Publisher<Person>> flux(Request request) {
|
||||
Person person1 = new Person("John");
|
||||
Person person2 = new Person("Jane");
|
||||
return Response.ok().stream(Flux.just(person1, person2), Person.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class Person {
|
||||
|
||||
private String name;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Person person = (Person) o;
|
||||
return !(this.name != null ? !this.name.equals(person.name) : person.name != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.name != null ? this.name.hashCode() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person{" +
|
||||
"name='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.core.codec.CharSequenceEncoder;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.MockWebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class PublisherResponseTests {
|
||||
|
||||
private final Publisher<String> publisher = Flux.just("foo", "bar");
|
||||
|
||||
private final PublisherResponse<String> publisherResponse =
|
||||
new PublisherResponse<>(200, new HttpHeaders(), publisher, String.class);
|
||||
|
||||
@Test
|
||||
public void body() throws Exception {
|
||||
assertEquals(publisher, publisherResponse.body());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeTo() throws Exception {
|
||||
MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, URI.create("http://localhost"));
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
ServerWebExchange exchange = new DefaultServerWebExchange(request, response, new MockWebSessionManager());
|
||||
exchange.getAttributes().put(Router.HTTP_MESSAGE_WRITERS_ATTRIBUTE, Collections
|
||||
.singleton(new EncoderHttpMessageWriter<CharSequence>(new CharSequenceEncoder())).stream());
|
||||
|
||||
|
||||
publisherResponse.writeTo(exchange).block();
|
||||
assertNotNull(response.getBody());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class RequestPredicateTests {
|
||||
|
||||
@Test
|
||||
public void and() throws Exception {
|
||||
RequestPredicate predicate1 = request -> true;
|
||||
RequestPredicate predicate2 = request -> true;
|
||||
RequestPredicate predicate3 = request -> false;
|
||||
|
||||
MockRequest request = MockRequest.builder().build();
|
||||
assertTrue(predicate1.and(predicate2).test(request));
|
||||
assertTrue(predicate2.and(predicate1).test(request));
|
||||
assertFalse(predicate1.and(predicate3).test(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void negate() throws Exception {
|
||||
RequestPredicate predicate = request -> false;
|
||||
RequestPredicate negated = predicate.negate();
|
||||
|
||||
MockRequest mockRequest = MockRequest.builder().build();
|
||||
assertTrue(negated.test(mockRequest));
|
||||
|
||||
predicate = request -> true;
|
||||
negated = predicate.negate();
|
||||
|
||||
assertFalse(negated.test(mockRequest));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void or() throws Exception {
|
||||
RequestPredicate predicate1 = request -> true;
|
||||
RequestPredicate predicate2 = request -> false;
|
||||
RequestPredicate predicate3 = request -> false;
|
||||
|
||||
MockRequest request = MockRequest.builder().build();
|
||||
assertTrue(predicate1.or(predicate2).test(request));
|
||||
assertTrue(predicate2.or(predicate1).test(request));
|
||||
assertFalse(predicate2.or(predicate3).test(request));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class RequestPredicatesTests {
|
||||
|
||||
@Test
|
||||
public void all() throws Exception {
|
||||
RequestPredicate predicate = RequestPredicates.all();
|
||||
MockRequest request = MockRequest.builder().build();
|
||||
assertTrue(predicate.test(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void method() throws Exception {
|
||||
HttpMethod httpMethod = HttpMethod.GET;
|
||||
RequestPredicate predicate = RequestPredicates.method(httpMethod);
|
||||
MockRequest request = MockRequest.builder().method(httpMethod).build();
|
||||
assertTrue(predicate.test(request));
|
||||
|
||||
request = MockRequest.builder().method(HttpMethod.POST).build();
|
||||
assertFalse(predicate.test(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methods() throws Exception {
|
||||
URI uri = URI.create("http://localhost/path");
|
||||
|
||||
RequestPredicate predicate = RequestPredicates.GET("/p*");
|
||||
MockRequest request = MockRequest.builder().method(HttpMethod.GET).uri(uri).build();
|
||||
assertTrue(predicate.test(request));
|
||||
|
||||
predicate = RequestPredicates.HEAD("/p*");
|
||||
request = MockRequest.builder().method(HttpMethod.HEAD).uri(uri).build();
|
||||
assertTrue(predicate.test(request));
|
||||
|
||||
predicate = RequestPredicates.POST("/p*");
|
||||
request = MockRequest.builder().method(HttpMethod.POST).uri(uri).build();
|
||||
assertTrue(predicate.test(request));
|
||||
|
||||
predicate = RequestPredicates.PUT("/p*");
|
||||
request = MockRequest.builder().method(HttpMethod.PUT).uri(uri).build();
|
||||
assertTrue(predicate.test(request));
|
||||
|
||||
predicate = RequestPredicates.PATCH("/p*");
|
||||
request = MockRequest.builder().method(HttpMethod.PATCH).uri(uri).build();
|
||||
assertTrue(predicate.test(request));
|
||||
|
||||
predicate = RequestPredicates.DELETE("/p*");
|
||||
request = MockRequest.builder().method(HttpMethod.DELETE).uri(uri).build();
|
||||
assertTrue(predicate.test(request));
|
||||
|
||||
predicate = RequestPredicates.OPTIONS("/p*");
|
||||
request = MockRequest.builder().method(HttpMethod.OPTIONS).uri(uri).build();
|
||||
assertTrue(predicate.test(request));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void path() throws Exception {
|
||||
URI uri = URI.create("http://localhost/path");
|
||||
RequestPredicate predicate = RequestPredicates.path("/p*");
|
||||
MockRequest request = MockRequest.builder().uri(uri).build();
|
||||
assertTrue(predicate.test(request));
|
||||
|
||||
request = MockRequest.builder().build();
|
||||
assertFalse(predicate.test(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headers() throws Exception {
|
||||
String name = "MyHeader";
|
||||
String value = "MyValue";
|
||||
RequestPredicate predicate =
|
||||
RequestPredicates.headers(headers -> {
|
||||
return headers.header(name).equals(
|
||||
Collections.singletonList(value));
|
||||
});
|
||||
MockRequest request = MockRequest.builder().header(name, value).build();
|
||||
assertTrue(predicate.test(request));
|
||||
|
||||
request = MockRequest.builder().build();
|
||||
assertFalse(predicate.test(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentType() throws Exception {
|
||||
MediaType json = MediaType.APPLICATION_JSON;
|
||||
RequestPredicate predicate = RequestPredicates.contentType(json);
|
||||
MockRequest request = MockRequest.builder().header("Content-Type", json.toString()).build();
|
||||
assertTrue(predicate.test(request));
|
||||
|
||||
request = MockRequest.builder().build();
|
||||
assertFalse(predicate.test(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accept() throws Exception {
|
||||
MediaType json = MediaType.APPLICATION_JSON;
|
||||
RequestPredicate predicate = RequestPredicates.accept(json);
|
||||
MockRequest request = MockRequest.builder().header("Accept", json.toString()).build();
|
||||
assertTrue(predicate.test(request));
|
||||
|
||||
request = MockRequest.builder().build();
|
||||
assertFalse(predicate.test(request));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.reactive.function.support.RequestWrapper;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class RequestWrapperTests {
|
||||
|
||||
private Request mockRequest;
|
||||
|
||||
private RequestWrapper wrapper;
|
||||
|
||||
@Before
|
||||
public void createWrapper() {
|
||||
mockRequest = mock(Request.class);
|
||||
wrapper = new RequestWrapper(mockRequest);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void request() throws Exception {
|
||||
assertSame(mockRequest, wrapper.request());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void method() throws Exception {
|
||||
HttpMethod method = HttpMethod.POST;
|
||||
when(mockRequest.method()).thenReturn(method);
|
||||
|
||||
assertSame(method, wrapper.method());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void uri() throws Exception {
|
||||
URI uri = URI.create("https://example.com");
|
||||
when(mockRequest.uri()).thenReturn(uri);
|
||||
|
||||
assertSame(uri, wrapper.uri());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void path() throws Exception {
|
||||
String path = "/foo/bar";
|
||||
when(mockRequest.path()).thenReturn(path);
|
||||
|
||||
assertSame(path, wrapper.path());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headers() throws Exception {
|
||||
Request.Headers headers = mock(Request.Headers.class);
|
||||
when(mockRequest.headers()).thenReturn(headers);
|
||||
|
||||
assertSame(headers, wrapper.headers());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void body() throws Exception {
|
||||
Request.Body body = mock(Request.Body.class);
|
||||
when(mockRequest.body()).thenReturn(body);
|
||||
|
||||
assertEquals(body, wrapper.body());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attribute() throws Exception {
|
||||
String name = "foo";
|
||||
String value = "bar";
|
||||
when(mockRequest.attribute(name)).thenReturn(Optional.of(value));
|
||||
|
||||
assertEquals(Optional.of(value), wrapper.attribute(name));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attributes() throws Exception {
|
||||
Map<String, Object> attributes = Collections.singletonMap("foo", "bar");
|
||||
when(mockRequest.attributes()).thenReturn(attributes);
|
||||
|
||||
assertSame(attributes, wrapper.attributes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryParam() throws Exception {
|
||||
String name = "foo";
|
||||
String value = "bar";
|
||||
when(mockRequest.queryParam(name)).thenReturn(Optional.of(value));
|
||||
|
||||
assertEquals(Optional.of(value), wrapper.queryParam(name));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryParams() throws Exception {
|
||||
String name = "foo";
|
||||
List<String> value = Collections.singletonList("bar");
|
||||
when(mockRequest.queryParams(name)).thenReturn(value);
|
||||
|
||||
assertSame(value, wrapper.queryParams(name));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathVariable() throws Exception {
|
||||
String name = "foo";
|
||||
String value = "bar";
|
||||
when(mockRequest.pathVariable(name)).thenReturn(Optional.of(value));
|
||||
|
||||
assertEquals(Optional.of(value), wrapper.pathVariable(name));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathVariables() throws Exception {
|
||||
Map<String, String> pathVariables = Collections.singletonMap("foo", "bar");
|
||||
when(mockRequest.pathVariables()).thenReturn(pathVariables);
|
||||
|
||||
assertSame(pathVariables, wrapper.pathVariables());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ResourceResponseTests {
|
||||
|
||||
private final Resource resource = new ClassPathResource("response.txt", ResourceResponseTests.class);
|
||||
|
||||
private final ResourceResponse resourceResponse =
|
||||
new ResourceResponse(200, new HttpHeaders(), resource);
|
||||
|
||||
@Test
|
||||
public void body() throws Exception {
|
||||
assertEquals(resource, resourceResponse.body());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeTo() throws Exception {
|
||||
ServerWebExchange exchange = mock(ServerWebExchange.class);
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
when(exchange.getResponse()).thenReturn(response);
|
||||
|
||||
|
||||
resourceResponse.writeTo(exchange).block();
|
||||
assertNotNull(response.getBody());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ResponseTests {
|
||||
|
||||
@Test
|
||||
public void from() throws Exception {
|
||||
Response<Void> other = Response.ok().header("foo", "bar").build();
|
||||
Response<Void> result = Response.from(other).build();
|
||||
assertEquals(HttpStatus.OK, result.statusCode());
|
||||
assertEquals("bar", result.headers().getFirst("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void status() throws Exception {
|
||||
Response<Void> result = Response.status(HttpStatus.CREATED).build();
|
||||
assertEquals(HttpStatus.CREATED, result.statusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void statusInt() throws Exception {
|
||||
Response<Void> result = Response.status(201).build();
|
||||
assertEquals(HttpStatus.CREATED, result.statusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ok() throws Exception {
|
||||
Response<Void> result = Response.ok().build();
|
||||
assertEquals(HttpStatus.OK, result.statusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void created() throws Exception {
|
||||
URI location = URI.create("http://example.com");
|
||||
Response<Void> result = Response.created(location).build();
|
||||
assertEquals(HttpStatus.CREATED, result.statusCode());
|
||||
assertEquals(location, result.headers().getLocation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accepted() throws Exception {
|
||||
Response<Void> result = Response.accepted().build();
|
||||
assertEquals(HttpStatus.ACCEPTED, result.statusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noContent() throws Exception {
|
||||
Response<Void> result = Response.noContent().build();
|
||||
assertEquals(HttpStatus.NO_CONTENT, result.statusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void badRequest() throws Exception {
|
||||
Response<Void> result = Response.badRequest().build();
|
||||
assertEquals(HttpStatus.BAD_REQUEST, result.statusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notFound() throws Exception {
|
||||
Response<Void> result = Response.notFound().build();
|
||||
assertEquals(HttpStatus.NOT_FOUND, result.statusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unprocessableEntity() throws Exception {
|
||||
Response<Void> result = Response.unprocessableEntity().build();
|
||||
assertEquals(HttpStatus.UNPROCESSABLE_ENTITY, result.statusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allow() throws Exception {
|
||||
Response<Void> result = Response.ok().allow(HttpMethod.GET).build();
|
||||
assertEquals(Collections.singleton(HttpMethod.GET), result.headers().getAllow());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentLength() throws Exception {
|
||||
Response<Void> result = Response.ok().contentLength(42).build();
|
||||
assertEquals(42, result.headers().getContentLength());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contentType() throws Exception {
|
||||
Response<Void> result = Response.ok().contentType(MediaType.APPLICATION_JSON).build();
|
||||
assertEquals(MediaType.APPLICATION_JSON, result.headers().getContentType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eTag() throws Exception {
|
||||
Response<Void> result = Response.ok().eTag("foo").build();
|
||||
assertEquals("\"foo\"", result.headers().getETag());
|
||||
}
|
||||
@Test
|
||||
public void lastModified() throws Exception {
|
||||
ZonedDateTime now = ZonedDateTime.now();
|
||||
Response<Void> result = Response.ok().lastModified(now).build();
|
||||
assertEquals(now.toInstant().toEpochMilli()/1000, result.headers().getLastModified()/1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheControlTag() throws Exception {
|
||||
Response<Void> result = Response.ok().cacheControl(CacheControl.noCache()).build();
|
||||
assertEquals("no-cache", result.headers().getCacheControl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void varyBy() throws Exception {
|
||||
Response<Void> result = Response.ok().varyBy("foo").build();
|
||||
assertEquals(Collections.singletonList("foo"), result.headers().getVary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeTo() throws Exception {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ReactiveHttpInputMessage;
|
||||
import org.springframework.http.ReactiveHttpOutputMessage;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class RouterTests {
|
||||
|
||||
@Test
|
||||
public void routeMatch() throws Exception {
|
||||
HandlerFunction<Void> handlerFunction = request -> Response.ok().build();
|
||||
|
||||
MockRequest request = MockRequest.builder().build();
|
||||
RequestPredicate requestPredicate = mock(RequestPredicate.class);
|
||||
when(requestPredicate.test(request)).thenReturn(true);
|
||||
|
||||
RoutingFunction<Void> result = Router.route(requestPredicate, handlerFunction);
|
||||
assertNotNull(result);
|
||||
|
||||
Optional<HandlerFunction<Void>> resultHandlerFunction = result.route(request);
|
||||
assertTrue(resultHandlerFunction.isPresent());
|
||||
assertEquals(handlerFunction, resultHandlerFunction.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void routeNoMatch() throws Exception {
|
||||
HandlerFunction<Void> handlerFunction = request -> Response.ok().build();
|
||||
|
||||
MockRequest request = MockRequest.builder().build();
|
||||
RequestPredicate requestPredicate = mock(RequestPredicate.class);
|
||||
when(requestPredicate.test(request)).thenReturn(false);
|
||||
|
||||
RoutingFunction<Void> result = Router.route(requestPredicate, handlerFunction);
|
||||
assertNotNull(result);
|
||||
|
||||
Optional<HandlerFunction<Void>> resultHandlerFunction = result.route(request);
|
||||
assertFalse(resultHandlerFunction.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subrouteMatch() throws Exception {
|
||||
HandlerFunction<Void> handlerFunction = request -> Response.ok().build();
|
||||
RoutingFunction<Void> routingFunction = request -> Optional.of(handlerFunction);
|
||||
|
||||
MockRequest request = MockRequest.builder().build();
|
||||
RequestPredicate requestPredicate = mock(RequestPredicate.class);
|
||||
when(requestPredicate.test(request)).thenReturn(true);
|
||||
|
||||
RoutingFunction<Void> result = Router.subroute(requestPredicate, routingFunction);
|
||||
assertNotNull(result);
|
||||
|
||||
Optional<HandlerFunction<Void>> resultHandlerFunction = result.route(request);
|
||||
assertTrue(resultHandlerFunction.isPresent());
|
||||
assertEquals(handlerFunction, resultHandlerFunction.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subrouteNoMatch() throws Exception {
|
||||
HandlerFunction<Void> handlerFunction = request -> Response.ok().build();
|
||||
RoutingFunction<Void> routingFunction = request -> Optional.of(handlerFunction);
|
||||
|
||||
MockRequest request = MockRequest.builder().build();
|
||||
RequestPredicate requestPredicate = mock(RequestPredicate.class);
|
||||
when(requestPredicate.test(request)).thenReturn(false);
|
||||
|
||||
RoutingFunction<Void> result = Router.subroute(requestPredicate, routingFunction);
|
||||
assertNotNull(result);
|
||||
|
||||
Optional<HandlerFunction<Void>> resultHandlerFunction = result.route(request);
|
||||
assertFalse(resultHandlerFunction.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toHttpHandler() throws Exception {
|
||||
Request request = mock(Request.class);
|
||||
Response response = mock(Response.class);
|
||||
when(response.writeTo(any(ServerWebExchange.class))).thenReturn(Mono.empty());
|
||||
|
||||
HandlerFunction handlerFunction = mock(HandlerFunction.class);
|
||||
when(handlerFunction.handle(any(Request.class))).thenReturn(response);
|
||||
|
||||
RoutingFunction routingFunction = mock(RoutingFunction.class);
|
||||
when(routingFunction.route(any(Request.class))).thenReturn(Optional.of(handlerFunction));
|
||||
|
||||
RequestPredicate requestPredicate = mock(RequestPredicate.class);
|
||||
when(requestPredicate.test(request)).thenReturn(false);
|
||||
|
||||
Router.Configuration configuration = mock(Router.Configuration.class);
|
||||
when(configuration.messageReaders()).thenReturn(
|
||||
() -> Collections.<HttpMessageReader<?>>emptyList().stream());
|
||||
when(configuration.messageWriters()).thenReturn(
|
||||
() -> Collections.<HttpMessageWriter<?>>emptyList().stream());
|
||||
|
||||
HttpHandler result = Router.toHttpHandler(routingFunction, configuration);
|
||||
assertNotNull(result);
|
||||
|
||||
MockServerHttpRequest serverHttpRequest = new MockServerHttpRequest(HttpMethod.GET,
|
||||
URI.create("http://localhost"));
|
||||
MockServerHttpResponse serverHttpResponse = new MockServerHttpResponse();
|
||||
result.handle(serverHttpRequest, serverHttpResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toConfiguration() throws Exception {
|
||||
StaticApplicationContext applicationContext = new StaticApplicationContext();
|
||||
applicationContext.registerSingleton("messageWriter", DummyMessageWriter.class);
|
||||
applicationContext.registerSingleton("messageReader", DummyMessageReader.class);
|
||||
applicationContext.refresh();
|
||||
|
||||
Router.Configuration configuration = Router.toConfiguration(applicationContext);
|
||||
assertTrue(configuration.messageReaders().get()
|
||||
.allMatch(r -> r instanceof DummyMessageReader));
|
||||
assertTrue(configuration.messageWriters().get()
|
||||
.allMatch(r -> r instanceof DummyMessageWriter));
|
||||
|
||||
}
|
||||
|
||||
private static class DummyMessageWriter implements HttpMessageWriter<Object> {
|
||||
|
||||
@Override
|
||||
public boolean canWrite(ResolvableType type, MediaType mediaType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaType> getWritableMediaTypes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> write(Publisher<?> inputStream, ResolvableType type,
|
||||
MediaType contentType,
|
||||
ReactiveHttpOutputMessage outputMessage) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DummyMessageReader implements HttpMessageReader<Object> {
|
||||
|
||||
@Override
|
||||
public boolean canRead(ResolvableType type, MediaType mediaType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaType> getReadableMediaTypes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Object> read(ResolvableType type, ReactiveHttpInputMessage inputMessage) {
|
||||
return Flux.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> readMono(ResolvableType type, ReactiveHttpInputMessage inputMessage) {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class RoutingFunctionTests {
|
||||
|
||||
@Test
|
||||
public void and() throws Exception {
|
||||
HandlerFunction<Void> handlerFunction = request -> Response.ok().build();
|
||||
RoutingFunction<Void> routingFunction1 = request -> Optional.empty();
|
||||
RoutingFunction<Void> routingFunction2 = request -> Optional.of(handlerFunction);
|
||||
|
||||
RoutingFunction<Void> result = routingFunction1.and(routingFunction2);
|
||||
assertNotNull(result);
|
||||
|
||||
MockRequest request = MockRequest.builder().build();
|
||||
Optional<HandlerFunction<Void>> resultHandlerFunction = result.route(request);
|
||||
assertTrue(resultHandlerFunction.isPresent());
|
||||
assertEquals(handlerFunction, resultHandlerFunction.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void andOther() throws Exception {
|
||||
HandlerFunction<String> handlerFunction = request -> Response.ok().body("42");
|
||||
RoutingFunction<Void> routingFunction1 = request -> Optional.empty();
|
||||
RoutingFunction<String> routingFunction2 = request -> Optional.of(handlerFunction);
|
||||
|
||||
RoutingFunction<?> result = routingFunction1.andOther(routingFunction2);
|
||||
assertNotNull(result);
|
||||
|
||||
MockRequest request = MockRequest.builder().build();
|
||||
Optional<? extends HandlerFunction<?>> resultHandlerFunction = result.route(request);
|
||||
assertTrue(resultHandlerFunction.isPresent());
|
||||
assertEquals(handlerFunction, resultHandlerFunction.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filter() throws Exception {
|
||||
HandlerFunction<String> handlerFunction = request -> Response.ok().body("42");
|
||||
RoutingFunction<String> routingFunction = request -> Optional.of(handlerFunction);
|
||||
|
||||
FilterFunction<String, Integer> filterFunction = (request, next) -> {
|
||||
Response<String> response = next.handle(request);
|
||||
int i = Integer.parseInt(response.body());
|
||||
return Response.ok().body(i);
|
||||
};
|
||||
RoutingFunction<Integer> result = routingFunction.filter(filterFunction);
|
||||
assertNotNull(result);
|
||||
|
||||
MockRequest request = MockRequest.builder().build();
|
||||
Optional<? extends HandlerFunction<?>> resultHandlerFunction = result.route(request);
|
||||
assertTrue(resultHandlerFunction.isPresent());
|
||||
Response<?> resultResponse = resultHandlerFunction.get().handle(request);
|
||||
assertEquals(42, resultResponse.body());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.codec.ServerSentEvent;
|
||||
import org.springframework.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.MockServerHttpResponse;
|
||||
import org.springframework.tests.TestSubscriber;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||
import org.springframework.web.server.session.MockWebSessionManager;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ServerSentEventResponseTests {
|
||||
|
||||
private final ServerSentEvent<String> sse =
|
||||
ServerSentEvent.<String>builder().data("42").build();
|
||||
|
||||
private final Publisher<ServerSentEvent<String>> body = Mono.just(sse);
|
||||
|
||||
private final ServerSentEventResponse<ServerSentEvent<String>> sseResponse =
|
||||
ServerSentEventResponse.fromSseEvents(200, new HttpHeaders(), body);
|
||||
|
||||
@Test
|
||||
public void body() throws Exception {
|
||||
assertEquals(body, sseResponse.body());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeTo() throws Exception {
|
||||
MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, URI.create("http://localhost"));
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
ServerWebExchange exchange = new DefaultServerWebExchange(request, response, new MockWebSessionManager());
|
||||
|
||||
sseResponse.writeTo(exchange);
|
||||
Publisher<Publisher<DataBuffer>> result = response.getBodyWithFlush();
|
||||
TestSubscriber.subscribe(result).
|
||||
assertNoError().
|
||||
assertValuesWith(publisher -> {
|
||||
TestSubscriber.subscribe(publisher).assertNoError();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.reactive.function;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.http.codec.ServerSentEvent;
|
||||
import org.springframework.tests.TestSubscriber;
|
||||
import org.springframework.web.client.reactive.WebClient;
|
||||
|
||||
import static org.springframework.web.client.reactive.ClientWebRequestBuilders.get;
|
||||
import static org.springframework.web.client.reactive.ResponseExtractors.bodyStream;
|
||||
import static org.springframework.web.reactive.function.Router.route;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class SseHandlerFunctionIntegrationTests
|
||||
extends AbstractRoutingFunctionIntegrationTests {
|
||||
|
||||
private WebClient webClient;
|
||||
|
||||
@Before
|
||||
public void createWebClient() {
|
||||
this.webClient = new WebClient(new ReactorClientHttpConnector());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RoutingFunction<?> routingFunction() {
|
||||
SseHandler sseHandler = new SseHandler();
|
||||
return route(RequestPredicates.GET("/string"), sseHandler::string)
|
||||
.andOther(route(RequestPredicates.GET("/person"), sseHandler::person))
|
||||
.andOther(route(RequestPredicates.GET("/event"), sseHandler::sse));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void sseAsString() throws Exception {
|
||||
Flux<String> result = this.webClient
|
||||
.perform(get("http://localhost:" + port + "/string")
|
||||
.accept(new MediaType("text", "event-stream")))
|
||||
.extract(bodyStream(String.class))
|
||||
.filter(s -> !s.equals("\n"))
|
||||
.map(s -> (s.replace("\n", "")))
|
||||
.take(2);
|
||||
|
||||
TestSubscriber
|
||||
.subscribe(result)
|
||||
.await(Duration.ofSeconds(5))
|
||||
.assertValues("data:foo 0", "data:foo 1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sseAsPerson() throws Exception {
|
||||
Mono<String> result = this.webClient
|
||||
.perform(get("http://localhost:" + port + "/person")
|
||||
.accept(new MediaType("text", "event-stream")))
|
||||
.extract(bodyStream(String.class))
|
||||
.filter(s -> !s.equals("\n"))
|
||||
.map(s -> s.replace("\n", ""))
|
||||
.takeUntil(s -> s.endsWith("foo 1\"}"))
|
||||
.reduce((s1, s2) -> s1 + s2);
|
||||
|
||||
TestSubscriber
|
||||
.subscribe(result)
|
||||
.await(Duration.ofSeconds(5))
|
||||
.assertValues("data:{\"name\":\"foo 0\"}data:{\"name\":\"foo 1\"}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sseAsEvent() throws Exception {
|
||||
Flux<String> result = this.webClient
|
||||
.perform(get("http://localhost:" + port + "/event")
|
||||
.accept(new MediaType("text", "event-stream")))
|
||||
.extract(bodyStream(String.class))
|
||||
.filter(s -> !s.equals("\n"))
|
||||
.map(s -> s.replace("\n", ""))
|
||||
.take(2);
|
||||
|
||||
TestSubscriber
|
||||
.subscribe(result)
|
||||
.await(Duration.ofSeconds(5))
|
||||
.assertValues(
|
||||
"id:0:bardata:foo",
|
||||
"id:1:bardata:foo"
|
||||
);
|
||||
}
|
||||
private static class SseHandler {
|
||||
|
||||
public Response<Publisher<String>> string(Request request) {
|
||||
Flux<String> flux = Flux.interval(Duration.ofMillis(100)).map(l -> "foo " + l).take(2);
|
||||
return Response.ok().sse(flux, String.class);
|
||||
}
|
||||
|
||||
public Response<Publisher<Person>> person(Request request) {
|
||||
Flux<Person> flux = Flux.interval(Duration.ofMillis(100))
|
||||
.map(l -> new Person("foo " + l)).take(2);
|
||||
return Response.ok().sse(flux, Person.class);
|
||||
}
|
||||
|
||||
public Response<Publisher<ServerSentEvent<String>>> sse(Request request) {
|
||||
Flux<ServerSentEvent<String>> flux = Flux.interval(Duration.ofMillis(100))
|
||||
.map(l -> ServerSentEvent.<String>builder().data("foo")
|
||||
.id(Long.toString(l))
|
||||
.comment("bar")
|
||||
.build()).take(2);
|
||||
|
||||
return Response.ok().sse(flux);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Person {
|
||||
|
||||
private String name;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Person person = (Person) o;
|
||||
return !(this.name != null ? !this.name.equals(person.name) : person.name != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.name != null ? this.name.hashCode() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person{" +
|
||||
"name='" + name + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
Hello World
|
||||
This is a sample response text file.
|
||||
Reference in New Issue
Block a user