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:
Arjen Poutsma
2016-07-27 10:19:26 +02:00
parent 18e491ac0a
commit f1319f58ec
46 changed files with 5242 additions and 0 deletions

View File

@@ -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();
}

View File

@@ -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());
}
}

View File

@@ -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));
}
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -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 + '\'' +
'}';
}
}
}

View File

@@ -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 + '\'' +
'}';
}
}
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}
}

View File

@@ -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 + '\'' +
'}';
}
}
}

View File

@@ -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());
}
}

View File

@@ -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));
}
}

View File

@@ -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));
}
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -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 {
}
}

View File

@@ -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();
}
}
}

View File

@@ -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());
}
}

View File

@@ -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();
});
}
}

View File

@@ -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 + '\'' +
'}';
}
}
}

View File

@@ -0,0 +1,2 @@
Hello World
This is a sample response text file.