Browse Source
This change adds a WebServerExchange and updates all contracts at the the same level (i.e. org.springframework.web.server) as well as the org.springframework.web.reactive level to use it so that all framework-related code will have access to server-side processing features such as request attributes (and others to come).pull/1111/head
47 changed files with 831 additions and 614 deletions
@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
|
||||
/* |
||||
* Copyright 2002-2015 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.server; |
||||
|
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest; |
||||
import org.springframework.http.server.reactive.ServerHttpResponse; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* Default implementation of {@link WebServerExchange}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class DefaultWebServerExchange implements WebServerExchange { |
||||
|
||||
private final ServerHttpRequest request; |
||||
|
||||
private final ServerHttpResponse response; |
||||
|
||||
private final Map<String, Object> attributes = new ConcurrentHashMap<>(); |
||||
|
||||
|
||||
public DefaultWebServerExchange(ServerHttpRequest request, ServerHttpResponse response) { |
||||
Assert.notNull(request, "'request' is required."); |
||||
Assert.notNull(response, "'response' is required."); |
||||
this.request = request; |
||||
this.response = response; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public ServerHttpRequest getRequest() { |
||||
return this.request; |
||||
} |
||||
|
||||
@Override |
||||
public ServerHttpResponse getResponse() { |
||||
return this.response; |
||||
} |
||||
|
||||
@Override |
||||
public Map<String, Object> getAttributes() { |
||||
return this.attributes; |
||||
} |
||||
|
||||
} |
||||
@ -1,69 +0,0 @@
@@ -1,69 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2015 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.server; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
import reactor.Mono; |
||||
|
||||
import org.springframework.http.server.reactive.HttpHandler; |
||||
import org.springframework.http.server.reactive.ServerHttpRequest; |
||||
import org.springframework.http.server.reactive.ServerHttpResponse; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* {@link HttpHandler} that delegates to a target {@link HttpHandler} and handles |
||||
* any errors from it by invoking one or more {@link HttpExceptionHandler}s |
||||
* sequentially until one of them completes successfully. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @author Stephane Maldini |
||||
*/ |
||||
public class ErrorHandlingHttpHandler extends HttpHandlerDecorator { |
||||
|
||||
private final List<HttpExceptionHandler> exceptionHandlers; |
||||
|
||||
|
||||
public ErrorHandlingHttpHandler(HttpHandler targetHandler, HttpExceptionHandler... exceptionHandlers) { |
||||
super(targetHandler); |
||||
Assert.notEmpty(exceptionHandlers, "At least one exception handler is required"); |
||||
this.exceptionHandlers = Arrays.asList(exceptionHandlers); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) { |
||||
Mono<Void> mono; |
||||
try { |
||||
mono = getDelegate().handle(request, response); |
||||
} |
||||
catch (Throwable ex) { |
||||
mono = Mono.error(ex); |
||||
} |
||||
for (HttpExceptionHandler handler : this.exceptionHandlers) { |
||||
mono = applyExceptionHandler(mono, handler, request, response); |
||||
} |
||||
return mono; |
||||
} |
||||
|
||||
private static Mono<Void> applyExceptionHandler(Mono<Void> mono, HttpExceptionHandler handler, |
||||
ServerHttpRequest request, ServerHttpResponse response) { |
||||
|
||||
return mono.otherwise(ex -> handler.handle(request, response, ex)).after(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
/* |
||||
* Copyright 2002-2015 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.server; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import reactor.Mono; |
||||
|
||||
import org.springframework.http.HttpStatus; |
||||
|
||||
/** |
||||
* {@code WebHandler} that decorates another with exception handling using one |
||||
* or more instances of {@link WebExceptionHandler}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class ExceptionHandlingWebHandler extends WebHandlerDecorator { |
||||
|
||||
private final List<WebExceptionHandler> exceptionHandlers; |
||||
|
||||
|
||||
public ExceptionHandlingWebHandler(WebHandler delegate, WebExceptionHandler... exceptionHandlers) { |
||||
super(delegate); |
||||
this.exceptionHandlers = initList(exceptionHandlers); |
||||
} |
||||
|
||||
private static List<WebExceptionHandler> initList(WebExceptionHandler[] list) { |
||||
return (list != null ? Collections.unmodifiableList(Arrays.asList(list)): Collections.emptyList()); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* @return a read-only list of the configured exception handlers. |
||||
*/ |
||||
public List<WebExceptionHandler> getExceptionHandlers() { |
||||
return this.exceptionHandlers; |
||||
} |
||||
|
||||
@Override |
||||
public Mono<Void> handle(WebServerExchange exchange) { |
||||
Mono<Void> mono; |
||||
try { |
||||
mono = getDelegate().handle(exchange); |
||||
} |
||||
catch (Throwable ex) { |
||||
mono = Mono.error(ex); |
||||
} |
||||
for (WebExceptionHandler exceptionHandler : this.exceptionHandlers) { |
||||
mono = mono.otherwise(ex -> exceptionHandler.handle(exchange, ex)); |
||||
} |
||||
return mono.otherwise(ex -> handleUnresolvedException(exchange)); |
||||
} |
||||
|
||||
private Mono<? extends Void> handleUnresolvedException(WebServerExchange exchange) { |
||||
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); |
||||
return Mono.empty(); |
||||
} |
||||
|
||||
} |
||||
@ -1,67 +0,0 @@
@@ -1,67 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2015 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.server; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import reactor.Mono; |
||||
|
||||
import org.springframework.http.server.reactive.HttpHandler; |
||||
import org.springframework.http.server.reactive.ServerHttpRequest; |
||||
import org.springframework.http.server.reactive.ServerHttpResponse; |
||||
|
||||
/** |
||||
* {@link HttpHandler} that delegates to a chain of {@link HttpFilter}s followed |
||||
* by a target {@link HttpHandler}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class FilterChainHttpHandler extends HttpHandlerDecorator { |
||||
|
||||
private final List<HttpFilter> filters; |
||||
|
||||
|
||||
public FilterChainHttpHandler(HttpHandler targetHandler, HttpFilter... filters) { |
||||
super(targetHandler); |
||||
this.filters = (filters != null ? Arrays.asList(filters) : Collections.emptyList()); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) { |
||||
return new DefaultHttpFilterChain().filter(request, response); |
||||
} |
||||
|
||||
|
||||
private class DefaultHttpFilterChain implements HttpFilterChain { |
||||
|
||||
private int index; |
||||
|
||||
@Override |
||||
public Mono<Void> filter(ServerHttpRequest request, ServerHttpResponse response) { |
||||
if (this.index < filters.size()) { |
||||
HttpFilter filter = filters.get(this.index++); |
||||
return filter.filter(request, response, this); |
||||
} |
||||
else { |
||||
return getDelegate().handle(request, response); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
/* |
||||
* Copyright 2002-2015 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.server; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import reactor.Mono; |
||||
|
||||
/** |
||||
* {@code WebHandler} that decorates another with a chain of {@link WebFilter}s. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class FilteringWebHandler extends WebHandlerDecorator { |
||||
|
||||
private final List<WebFilter> filters; |
||||
|
||||
|
||||
public FilteringWebHandler(WebHandler targetHandler, WebFilter... filters) { |
||||
super(targetHandler); |
||||
this.filters = initList(filters); |
||||
} |
||||
|
||||
private static List<WebFilter> initList(WebFilter[] list) { |
||||
return (list != null ? Collections.unmodifiableList(Arrays.asList(list)): Collections.emptyList()); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* @return a read-only list of the configured filters. |
||||
*/ |
||||
public List<WebFilter> getFilters() { |
||||
return this.filters; |
||||
} |
||||
|
||||
@Override |
||||
public Mono<Void> handle(WebServerExchange exchange) { |
||||
return new DefaultWebFilterChain().filter(exchange); |
||||
} |
||||
|
||||
|
||||
private class DefaultWebFilterChain implements WebFilterChain { |
||||
|
||||
private int index; |
||||
|
||||
|
||||
@Override |
||||
public Mono<Void> filter(WebServerExchange exchange) { |
||||
if (this.index < filters.size()) { |
||||
WebFilter filter = filters.get(this.index++); |
||||
return filter.filter(exchange, this); |
||||
} |
||||
else { |
||||
return getDelegate().handle(exchange); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,50 +0,0 @@
@@ -1,50 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2015 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.server; |
||||
|
||||
import reactor.Mono; |
||||
|
||||
import org.springframework.http.server.reactive.HttpHandler; |
||||
import org.springframework.http.server.reactive.ServerHttpRequest; |
||||
import org.springframework.http.server.reactive.ServerHttpResponse; |
||||
|
||||
/** |
||||
* Contract for interception-style, chained processing of HTTP requests. |
||||
* |
||||
* <p>Filters may be used to implement cross-cutting, application-agnostic |
||||
* requirements such as security, timeouts, and others. |
||||
* |
||||
* <p>{@link FilterChainHttpHandler} provides a way of constructing a chain of |
||||
* {@link HttpFilter}s followed by a target {@link HttpHandler}. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @see FilterChainHttpHandler |
||||
*/ |
||||
public interface HttpFilter { |
||||
|
||||
/** |
||||
* Process the given request and optionally delegate to the next HttpFilter. |
||||
* |
||||
* @param request current HTTP request. |
||||
* @param response current HTTP response. |
||||
* @param chain provides a way to delegate to the next HttpFilter. |
||||
* @return {@code Mono<Void>} to indicate when request processing is complete. |
||||
*/ |
||||
Mono<Void> filter(ServerHttpRequest request, ServerHttpResponse response, |
||||
HttpFilterChain chain); |
||||
|
||||
} |
||||
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
/* |
||||
* Copyright 2002-2015 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.server; |
||||
|
||||
import reactor.Mono; |
||||
|
||||
/** |
||||
* Contract for handling exceptions during web server exchange processing. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public interface WebExceptionHandler { |
||||
|
||||
/** |
||||
* Handle the given exception. A completion signal through the return value |
||||
* indicates error handling is complete while an error signal indicates the |
||||
* exception is still not handled. |
||||
* |
||||
* @param exchange the current exchange |
||||
* @param ex the exception to handle |
||||
* @return {@code Mono<Void>} to indicate when exception handling is complete |
||||
*/ |
||||
Mono<Void> handle(WebServerExchange exchange, Throwable ex); |
||||
|
||||
} |
||||
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
/* |
||||
* Copyright 2002-2015 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.server; |
||||
|
||||
import reactor.Mono; |
||||
|
||||
/** |
||||
* Contract for interception-style, chained processing of Web requests that may |
||||
* be used to implement cross-cutting, application-agnostic requirements such |
||||
* as security, timeouts, and others. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public interface WebFilter { |
||||
|
||||
/** |
||||
* Process the Web request and (optionally) delegate to the next |
||||
* {@code WebFilter} through the given {@link WebFilterChain}. |
||||
* |
||||
* @param exchange the current server exchange |
||||
* @param chain provides a way to delegate to the next filter |
||||
* @return {@code Mono<Void>} to indicate when request processing is complete |
||||
*/ |
||||
Mono<Void> filter(WebServerExchange exchange, WebFilterChain chain); |
||||
|
||||
} |
||||
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
/* |
||||
* Copyright 2002-2015 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.server; |
||||
|
||||
import reactor.Mono; |
||||
|
||||
/** |
||||
* Contract to handle a web server exchange. |
||||
* |
||||
* <p>Use {@link WebToHttpHandlerAdapter} to adapt a {@code WebHandler} to an |
||||
* {@link org.springframework.http.server.reactive.HttpHandler HttpHandler}. |
||||
* The {@link WebToHttpHandlerBuilder} provides a convenient way to do that while |
||||
* also optionally configuring one or more filters and/or exception handlers. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @see WebToHttpHandlerBuilder |
||||
*/ |
||||
public interface WebHandler { |
||||
|
||||
/** |
||||
* Handle the web server exchange. |
||||
* |
||||
* @param exchange the current server exchange |
||||
* @return {@code Mono<Void>} to indicate when request handling is complete |
||||
*/ |
||||
Mono<Void> handle(WebServerExchange exchange); |
||||
|
||||
} |
||||
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
/* |
||||
* Copyright 2002-2015 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.server; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ObjectUtils; |
||||
|
||||
/** |
||||
* Assist with building an |
||||
* {@link org.springframework.http.server.reactive.HttpHandler HttpHandler} to |
||||
* invoke a target {@link WebHandler} with an optional chain of |
||||
* {@link WebFilter}s and one or more {@link WebExceptionHandler}s. |
||||
* |
||||
* <p>Effective this sets up the following {@code WebHandler} delegation:<br> |
||||
* {@link WebToHttpHandlerAdapter} {@code -->} |
||||
* {@link ExceptionHandlingWebHandler} {@code -->} |
||||
* {@link FilteringWebHandler} |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class WebToHttpHandlerBuilder { |
||||
|
||||
private final WebHandler targetHandler; |
||||
|
||||
private final List<WebFilter> filters = new ArrayList<>(); |
||||
|
||||
private final List<WebExceptionHandler> exceptionHandlers = new ArrayList<>(); |
||||
|
||||
|
||||
private WebToHttpHandlerBuilder(WebHandler targetHandler) { |
||||
Assert.notNull(targetHandler, "'targetHandler' must not be null"); |
||||
this.targetHandler = targetHandler; |
||||
} |
||||
|
||||
|
||||
public static WebToHttpHandlerBuilder webHandler(WebHandler webHandler) { |
||||
return new WebToHttpHandlerBuilder(webHandler); |
||||
} |
||||
|
||||
public WebToHttpHandlerBuilder filters(WebFilter... filters) { |
||||
if (!ObjectUtils.isEmpty(filters)) { |
||||
this.filters.addAll(Arrays.asList(filters)); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
public WebToHttpHandlerBuilder exceptionHandlers(WebExceptionHandler... exceptionHandlers) { |
||||
if (!ObjectUtils.isEmpty(exceptionHandlers)) { |
||||
this.exceptionHandlers.addAll(Arrays.asList(exceptionHandlers)); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
public WebToHttpHandlerAdapter build() { |
||||
WebHandler webHandler = this.targetHandler; |
||||
if (!this.exceptionHandlers.isEmpty()) { |
||||
WebExceptionHandler[] array = new WebExceptionHandler[this.exceptionHandlers.size()]; |
||||
webHandler = new ExceptionHandlingWebHandler(webHandler, this.exceptionHandlers.toArray(array)); |
||||
} |
||||
if (!this.filters.isEmpty()) { |
||||
WebFilter[] array = new WebFilter[this.filters.size()]; |
||||
webHandler = new FilteringWebHandler(webHandler, this.filters.toArray(array)); |
||||
} |
||||
return new WebToHttpHandlerAdapter(webHandler); |
||||
} |
||||
|
||||
} |
||||
@ -1,154 +0,0 @@
@@ -1,154 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2015 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.server; |
||||
|
||||
|
||||
import java.net.URI; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.reactivestreams.Publisher; |
||||
import reactor.Mono; |
||||
import reactor.rx.Streams; |
||||
import reactor.rx.stream.Signal; |
||||
|
||||
import org.springframework.http.HttpMethod; |
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.http.server.reactive.HttpHandler; |
||||
import org.springframework.http.server.reactive.MockServerHttpRequest; |
||||
import org.springframework.http.server.reactive.MockServerHttpResponse; |
||||
import org.springframework.http.server.reactive.ServerHttpRequest; |
||||
import org.springframework.http.server.reactive.ServerHttpResponse; |
||||
import org.springframework.web.server.ErrorHandlingHttpHandler; |
||||
import org.springframework.web.server.HttpExceptionHandler; |
||||
import org.springframework.web.server.InternalServerErrorExceptionHandler; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertNull; |
||||
|
||||
/** |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
@SuppressWarnings("ThrowableResultOfMethodCallIgnored") |
||||
public class ErrorHandlingHttpHandlerTests { |
||||
|
||||
private MockServerHttpRequest request; |
||||
|
||||
private MockServerHttpResponse response; |
||||
|
||||
|
||||
@Before |
||||
public void setUp() throws Exception { |
||||
this.request = new MockServerHttpRequest(HttpMethod.GET, new URI("http://localhost:8080")); |
||||
this.response = new MockServerHttpResponse(); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void handleErrorSignal() throws Exception { |
||||
HttpExceptionHandler exceptionHandler = new InternalServerErrorExceptionHandler(); |
||||
HttpHandler targetHandler = new TestHttpHandler(new IllegalStateException("boo")); |
||||
HttpHandler handler = new ErrorHandlingHttpHandler(targetHandler, exceptionHandler); |
||||
|
||||
handler.handle(this.request, this.response).get(); |
||||
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, this.response.getStatus()); |
||||
} |
||||
|
||||
@Test |
||||
public void handleErrorSignalWithMultipleHttpErrorHandlers() throws Exception { |
||||
HttpExceptionHandler[] exceptionHandlers = new HttpExceptionHandler[] { |
||||
new UnresolvedExceptionHandler(), |
||||
new UnresolvedExceptionHandler(), |
||||
new InternalServerErrorExceptionHandler(), |
||||
new UnresolvedExceptionHandler() |
||||
}; |
||||
HttpHandler targetHandler = new TestHttpHandler(new IllegalStateException("boo")); |
||||
HttpHandler httpHandler = new ErrorHandlingHttpHandler(targetHandler, exceptionHandlers); |
||||
|
||||
httpHandler.handle(this.request, this.response).get(); |
||||
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, this.response.getStatus()); |
||||
} |
||||
|
||||
@Test |
||||
public void unresolvedException() throws Exception { |
||||
HttpExceptionHandler exceptionHandler = new UnresolvedExceptionHandler(); |
||||
HttpHandler targetHandler = new TestHttpHandler(new IllegalStateException("boo")); |
||||
HttpHandler httpHandler = new ErrorHandlingHttpHandler(targetHandler, exceptionHandler); |
||||
|
||||
Publisher<Void> publisher = httpHandler.handle(this.request, this.response); |
||||
Throwable ex = awaitErrorSignal(publisher); |
||||
|
||||
assertEquals("boo", ex.getMessage()); |
||||
assertNull(this.response.getStatus()); |
||||
} |
||||
|
||||
@Test |
||||
public void thrownExceptionBecomesErrorSignal() throws Exception { |
||||
HttpExceptionHandler exceptionHandler = new InternalServerErrorExceptionHandler(); |
||||
HttpHandler targetHandler = new TestHttpHandler(new IllegalStateException("boo"), true); |
||||
HttpHandler handler = new ErrorHandlingHttpHandler(targetHandler, exceptionHandler); |
||||
|
||||
handler.handle(this.request, this.response).get(); |
||||
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, this.response.getStatus()); |
||||
} |
||||
|
||||
|
||||
private Throwable awaitErrorSignal(Publisher<?> publisher) throws Exception { |
||||
Signal<?> signal = Streams.from(publisher).materialize().toList().get().get(0); |
||||
assertEquals("Unexpected signal: " + signal, Signal.Type.ERROR, signal.getType()); |
||||
return signal.getThrowable(); |
||||
} |
||||
|
||||
|
||||
private static class TestHttpHandler implements HttpHandler { |
||||
|
||||
private final RuntimeException exception; |
||||
|
||||
private final boolean raise; |
||||
|
||||
|
||||
public TestHttpHandler(RuntimeException exception) { |
||||
this(exception, false); |
||||
} |
||||
|
||||
public TestHttpHandler(RuntimeException exception, boolean raise) { |
||||
this.exception = exception; |
||||
this.raise = raise; |
||||
} |
||||
|
||||
@Override |
||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) { |
||||
if (this.raise) { |
||||
throw this.exception; |
||||
} |
||||
return Mono.error(this.exception); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** Leave the exception unresolved. */ |
||||
private static class UnresolvedExceptionHandler implements HttpExceptionHandler { |
||||
|
||||
@Override |
||||
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response, Throwable ex) { |
||||
return Mono.error(ex); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,140 @@
@@ -0,0 +1,140 @@
|
||||
/* |
||||
* Copyright 2002-2015 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.server; |
||||
|
||||
|
||||
import java.net.URI; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import reactor.Mono; |
||||
|
||||
import org.springframework.http.HttpMethod; |
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.http.server.reactive.MockServerHttpRequest; |
||||
import org.springframework.http.server.reactive.MockServerHttpResponse; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
|
||||
/** |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
@SuppressWarnings("ThrowableResultOfMethodCallIgnored") |
||||
public class ExceptionHandlingHttpHandlerTests { |
||||
|
||||
private MockServerHttpResponse response; |
||||
|
||||
private WebServerExchange exchange; |
||||
|
||||
private WebHandler targetHandler; |
||||
|
||||
|
||||
@Before |
||||
public void setUp() throws Exception { |
||||
URI uri = new URI("http://localhost:8080"); |
||||
MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, uri); |
||||
this.response = new MockServerHttpResponse(); |
||||
this.exchange = new DefaultWebServerExchange(request, this.response); |
||||
this.targetHandler = new StubWebHandler(new IllegalStateException("boo")); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void handleErrorSignal() throws Exception { |
||||
WebExceptionHandler exceptionHandler = new BadRequestExceptionHandler(); |
||||
createWebHandler(exceptionHandler).handle(this.exchange).get(); |
||||
|
||||
assertEquals(HttpStatus.BAD_REQUEST, this.response.getStatus()); |
||||
} |
||||
|
||||
@Test |
||||
public void handleErrorSignalWithMultipleHttpErrorHandlers() throws Exception { |
||||
WebExceptionHandler[] exceptionHandlers = new WebExceptionHandler[] { |
||||
new UnresolvedExceptionHandler(), |
||||
new UnresolvedExceptionHandler(), |
||||
new BadRequestExceptionHandler(), |
||||
new UnresolvedExceptionHandler() |
||||
}; |
||||
createWebHandler(exceptionHandlers).handle(this.exchange).get(); |
||||
|
||||
assertEquals(HttpStatus.BAD_REQUEST, this.response.getStatus()); |
||||
} |
||||
|
||||
@Test |
||||
public void unresolvedException() throws Exception { |
||||
WebExceptionHandler exceptionHandler = new UnresolvedExceptionHandler(); |
||||
createWebHandler(exceptionHandler).handle(this.exchange).get(); |
||||
|
||||
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, this.response.getStatus()); |
||||
} |
||||
|
||||
@Test |
||||
public void thrownExceptionBecomesErrorSignal() throws Exception { |
||||
WebExceptionHandler exceptionHandler = new BadRequestExceptionHandler(); |
||||
createWebHandler(exceptionHandler).handle(this.exchange).get(); |
||||
|
||||
assertEquals(HttpStatus.BAD_REQUEST, this.response.getStatus()); |
||||
} |
||||
|
||||
private WebHandler createWebHandler(WebExceptionHandler... handlers) { |
||||
return new ExceptionHandlingWebHandler(this.targetHandler, handlers); |
||||
} |
||||
|
||||
|
||||
private static class StubWebHandler implements WebHandler { |
||||
|
||||
private final RuntimeException exception; |
||||
|
||||
private final boolean raise; |
||||
|
||||
|
||||
public StubWebHandler(RuntimeException exception) { |
||||
this(exception, false); |
||||
} |
||||
|
||||
public StubWebHandler(RuntimeException exception, boolean raise) { |
||||
this.exception = exception; |
||||
this.raise = raise; |
||||
} |
||||
|
||||
@Override |
||||
public Mono<Void> handle(WebServerExchange exchange) { |
||||
if (this.raise) { |
||||
throw this.exception; |
||||
} |
||||
return Mono.error(this.exception); |
||||
} |
||||
} |
||||
|
||||
private static class BadRequestExceptionHandler implements WebExceptionHandler { |
||||
|
||||
@Override |
||||
public Mono<Void> handle(WebServerExchange exchange, Throwable ex) { |
||||
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST); |
||||
return Mono.empty(); |
||||
} |
||||
} |
||||
|
||||
/** Leave the exception unresolved. */ |
||||
private static class UnresolvedExceptionHandler implements WebExceptionHandler { |
||||
|
||||
@Override |
||||
public Mono<Void> handle(WebServerExchange exchange, Throwable ex) { |
||||
return Mono.error(ex); |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue