Fix URI parsing in Reactor Netty request

Issue: SPR-15560
This commit is contained in:
Rossen Stoyanchev 2017-05-19 21:46:06 -04:00
parent c59e192b0f
commit 11075f12bc
5 changed files with 71 additions and 48 deletions

View File

@ -16,6 +16,7 @@
package org.springframework.http.server.reactive; package org.springframework.http.server.reactive;
import java.net.URISyntaxException;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
@ -53,10 +54,19 @@ public class ReactorHttpHandlerAdapter
public Mono<Void> apply(HttpServerRequest request, HttpServerResponse response) { public Mono<Void> apply(HttpServerRequest request, HttpServerResponse response) {
NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(response.alloc()); NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(response.alloc());
ReactorServerHttpRequest req = new ReactorServerHttpRequest(request, bufferFactory); ReactorServerHttpRequest adaptedRequest;
ReactorServerHttpResponse resp = new ReactorServerHttpResponse(response, bufferFactory); ReactorServerHttpResponse adaptedResponse;
try {
adaptedRequest = new ReactorServerHttpRequest(request, bufferFactory);
adaptedResponse = new ReactorServerHttpResponse(response, bufferFactory);
}
catch (URISyntaxException ex) {
logger.error("Invalid URL " + ex.getMessage(), ex);
response.status(HttpResponseStatus.BAD_REQUEST);
return Mono.empty();
}
return this.httpHandler.handle(req, resp) return this.httpHandler.handle(adaptedRequest, adaptedResponse)
.onErrorResume(ex -> { .onErrorResume(ex -> {
logger.error("Could not complete request", ex); logger.error("Could not complete request", ex);
response.status(HttpResponseStatus.INTERNAL_SERVER_ERROR); response.status(HttpResponseStatus.INTERNAL_SERVER_ERROR);

View File

@ -48,28 +48,25 @@ public class ReactorServerHttpRequest extends AbstractServerHttpRequest {
private final NettyDataBufferFactory bufferFactory; private final NettyDataBufferFactory bufferFactory;
public ReactorServerHttpRequest(HttpServerRequest request, NettyDataBufferFactory bufferFactory) { public ReactorServerHttpRequest(HttpServerRequest request, NettyDataBufferFactory bufferFactory)
throws URISyntaxException {
super(initUri(request), initHeaders(request)); super(initUri(request), initHeaders(request));
Assert.notNull(bufferFactory, "'bufferFactory' must not be null"); Assert.notNull(bufferFactory, "'bufferFactory' must not be null");
this.request = request; this.request = request;
this.bufferFactory = bufferFactory; this.bufferFactory = bufferFactory;
} }
private static URI initUri(HttpServerRequest channel) { private static URI initUri(HttpServerRequest channel) throws URISyntaxException {
Assert.notNull(channel, "'channel' must not be null"); Assert.notNull(channel, "'channel' must not be null");
InetSocketAddress address = channel.remoteAddress(); InetSocketAddress address = channel.remoteAddress();
String requestUri = channel.uri(); String requestUri = channel.uri();
return (address != null ? getBaseUrl(address).resolve(requestUri) : URI.create(requestUri)); return (address != null ? createUrl(address, requestUri) : new URI(requestUri));
} }
private static URI getBaseUrl(InetSocketAddress address) { private static URI createUrl(InetSocketAddress address, String requestUri) throws URISyntaxException {
try { URI baseUrl = new URI(null, null, address.getHostString(), address.getPort(), null, null, null);
return new URI(null, null, address.getHostString(), address.getPort(), null, null, null); return new URI(baseUrl.toString() + requestUri);
}
catch (URISyntaxException ex) {
// Should not happen...
throw new IllegalStateException(ex);
}
} }
private static HttpHeaders initHeaders(HttpServerRequest channel) { private static HttpHeaders initHeaders(HttpServerRequest channel) {

View File

@ -16,7 +16,6 @@
package org.springframework.http.server.reactive; package org.springframework.http.server.reactive;
import java.io.IOException;
import java.net.URI; import java.net.URI;
import org.junit.Test; import org.junit.Test;
@ -45,45 +44,55 @@ public class ErrorHandlerIntegrationTests extends AbstractHttpHandlerIntegration
} }
@Test @Test
public void response() throws Exception { public void responseBodyError() throws Exception {
// TODO: fix Reactor // TODO: fix Reactor
assumeFalse(server instanceof ReactorHttpServer); assumeFalse(server instanceof ReactorHttpServer);
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(NO_OP_ERROR_HANDLER); restTemplate.setErrorHandler(NO_OP_ERROR_HANDLER);
ResponseEntity<String> response = restTemplate URI url = new URI("http://localhost:" + port + "/response-body-error");
.getForEntity(new URI("http://localhost:" + port + "/response"), ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
String.class);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
} }
@Test @Test
public void returnValue() throws Exception { public void handlingError() throws Exception {
// TODO: fix Reactor // TODO: fix Reactor
assumeFalse(server instanceof ReactorHttpServer); assumeFalse(server instanceof ReactorHttpServer);
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(NO_OP_ERROR_HANDLER); restTemplate.setErrorHandler(NO_OP_ERROR_HANDLER);
ResponseEntity<String> response = restTemplate URI url = new URI("http://localhost:" + port + "/handling-error");
.getForEntity(new URI("http://localhost:" + port + "/returnValue"), ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
String.class);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
} }
@Test // SPR-15560
public void emptyPathSegments() throws Exception {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(NO_OP_ERROR_HANDLER);
URI url = new URI("http://localhost:" + port + "//");
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
private static class ErrorHandler implements HttpHandler { private static class ErrorHandler implements HttpHandler {
@Override @Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) { public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
Exception error = new UnsupportedOperationException(); Exception error = new UnsupportedOperationException();
String path = request.getURI().getPath(); String path = request.getURI().getPath();
if (path.endsWith("response")) { if (path.endsWith("response-body-error")) {
return response.writeWith(Mono.error(error)); return response.writeWith(Mono.error(error));
} }
else if (path.endsWith("returnValue")) { else if (path.endsWith("handling-error")) {
return Mono.error(error); return Mono.error(error);
} }
else { else {
@ -92,17 +101,16 @@ public class ErrorHandlerIntegrationTests extends AbstractHttpHandlerIntegration
} }
} }
private static final ResponseErrorHandler NO_OP_ERROR_HANDLER = private static final ResponseErrorHandler NO_OP_ERROR_HANDLER = new ResponseErrorHandler() {
new ResponseErrorHandler() {
@Override @Override
public boolean hasError(ClientHttpResponse response) throws IOException { public boolean hasError(ClientHttpResponse response) {
return false; return false;
} }
@Override @Override
public void handleError(ClientHttpResponse response) throws IOException { public void handleError(ClientHttpResponse response) {
} }
}; };
} }

View File

@ -17,6 +17,7 @@
package org.springframework.http.server.reactive; package org.springframework.http.server.reactive;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.URISyntaxException;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -63,8 +64,17 @@ public class RxNettyHttpHandlerAdapter implements RequestHandler<ByteBuf, ByteBu
NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(channel.alloc()); NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(channel.alloc());
InetSocketAddress remoteAddress = (InetSocketAddress) channel.remoteAddress(); InetSocketAddress remoteAddress = (InetSocketAddress) channel.remoteAddress();
RxNettyServerHttpRequest request = new RxNettyServerHttpRequest(nativeRequest, bufferFactory, remoteAddress); RxNettyServerHttpRequest request;
RxNettyServerHttpResponse response = new RxNettyServerHttpResponse(nativeResponse, bufferFactory); RxNettyServerHttpResponse response;
try {
request = new RxNettyServerHttpRequest(nativeRequest, bufferFactory, remoteAddress);
response = new RxNettyServerHttpResponse(nativeResponse, bufferFactory);
}
catch (URISyntaxException ex) {
logger.error("Could not complete request", ex);
nativeResponse.setStatus(HttpResponseStatus.BAD_REQUEST);
return Observable.empty();
}
Publisher<Void> result = this.httpHandler.handle(request, response) Publisher<Void> result = this.httpHandler.handle(request, response)
.onErrorResume(ex -> { .onErrorResume(ex -> {

View File

@ -55,7 +55,8 @@ public class RxNettyServerHttpRequest extends AbstractServerHttpRequest {
public RxNettyServerHttpRequest(HttpServerRequest<ByteBuf> request, public RxNettyServerHttpRequest(HttpServerRequest<ByteBuf> request,
NettyDataBufferFactory dataBufferFactory, InetSocketAddress remoteAddress) { NettyDataBufferFactory dataBufferFactory, InetSocketAddress remoteAddress)
throws URISyntaxException {
super(initUri(request, remoteAddress), initHeaders(request)); super(initUri(request, remoteAddress), initHeaders(request));
@ -65,20 +66,17 @@ public class RxNettyServerHttpRequest extends AbstractServerHttpRequest {
this.remoteAddress = remoteAddress; this.remoteAddress = remoteAddress;
} }
private static URI initUri(HttpServerRequest<ByteBuf> request, InetSocketAddress remoteAddress) { private static URI initUri(HttpServerRequest<ByteBuf> request, InetSocketAddress remoteAddress)
throws URISyntaxException {
Assert.notNull(request, "'request' must not be null"); Assert.notNull(request, "'request' must not be null");
String requestUri = request.getUri(); String requestUri = request.getUri();
return remoteAddress != null ? getBaseUrl(remoteAddress).resolve(requestUri) : URI.create(requestUri); return remoteAddress != null ? createUrl(remoteAddress, requestUri) : URI.create(requestUri);
} }
private static URI getBaseUrl(InetSocketAddress address) { private static URI createUrl(InetSocketAddress address, String requestUri) throws URISyntaxException {
try { URI baseUrl = new URI(null, null, address.getHostString(), address.getPort(), null, null, null);
return new URI(null, null, address.getHostString(), address.getPort(), null, null, null); return new URI(baseUrl.toString() + requestUri);
}
catch (URISyntaxException ex) {
// Should not happen...
throw new IllegalStateException(ex);
}
} }
private static HttpHeaders initHeaders(HttpServerRequest<ByteBuf> request) { private static HttpHeaders initHeaders(HttpServerRequest<ByteBuf> request) {