Fix URI parsing in Reactor Netty request
Issue: SPR-15560
This commit is contained in:
		
							parent
							
								
									c59e192b0f
								
							
						
					
					
						commit
						11075f12bc
					
				|  | @ -16,6 +16,7 @@ | |||
| 
 | ||||
| package org.springframework.http.server.reactive; | ||||
| 
 | ||||
| import java.net.URISyntaxException; | ||||
| import java.util.function.BiFunction; | ||||
| 
 | ||||
| import io.netty.handler.codec.http.HttpResponseStatus; | ||||
|  | @ -53,10 +54,19 @@ public class ReactorHttpHandlerAdapter | |||
| 	public Mono<Void> apply(HttpServerRequest request, HttpServerResponse response) { | ||||
| 
 | ||||
| 		NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(response.alloc()); | ||||
| 		ReactorServerHttpRequest req = new ReactorServerHttpRequest(request, bufferFactory); | ||||
| 		ReactorServerHttpResponse resp = new ReactorServerHttpResponse(response, bufferFactory); | ||||
| 		ReactorServerHttpRequest adaptedRequest; | ||||
| 		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 -> { | ||||
| 					logger.error("Could not complete request", ex); | ||||
| 					response.status(HttpResponseStatus.INTERNAL_SERVER_ERROR); | ||||
|  |  | |||
|  | @ -48,28 +48,25 @@ public class ReactorServerHttpRequest extends AbstractServerHttpRequest { | |||
| 	private final NettyDataBufferFactory bufferFactory; | ||||
| 
 | ||||
| 
 | ||||
| 	public ReactorServerHttpRequest(HttpServerRequest request, NettyDataBufferFactory bufferFactory) { | ||||
| 	public ReactorServerHttpRequest(HttpServerRequest request, NettyDataBufferFactory bufferFactory) | ||||
| 			throws URISyntaxException { | ||||
| 
 | ||||
| 		super(initUri(request), initHeaders(request)); | ||||
| 		Assert.notNull(bufferFactory, "'bufferFactory' must not be null"); | ||||
| 		this.request = request; | ||||
| 		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"); | ||||
| 		InetSocketAddress address = channel.remoteAddress(); | ||||
| 		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) { | ||||
| 		try { | ||||
| 			return new URI(null, null, address.getHostString(), address.getPort(), null, null, null); | ||||
| 		} | ||||
| 		catch (URISyntaxException ex) { | ||||
| 			// Should not happen... | ||||
| 			throw new IllegalStateException(ex); | ||||
| 		} | ||||
| 	private static URI createUrl(InetSocketAddress address, String requestUri) throws URISyntaxException { | ||||
| 		URI baseUrl = new URI(null, null, address.getHostString(), address.getPort(), null, null, null); | ||||
| 		return new URI(baseUrl.toString() + requestUri); | ||||
| 	} | ||||
| 
 | ||||
| 	private static HttpHeaders initHeaders(HttpServerRequest channel) { | ||||
|  |  | |||
|  | @ -16,7 +16,6 @@ | |||
| 
 | ||||
| package org.springframework.http.server.reactive; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.net.URI; | ||||
| 
 | ||||
| import org.junit.Test; | ||||
|  | @ -45,45 +44,55 @@ public class ErrorHandlerIntegrationTests extends AbstractHttpHandlerIntegration | |||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void response() throws Exception { | ||||
| 	public void responseBodyError() throws Exception { | ||||
| 		// TODO: fix Reactor | ||||
| 		assumeFalse(server instanceof ReactorHttpServer); | ||||
| 
 | ||||
| 		RestTemplate restTemplate = new RestTemplate(); | ||||
| 		restTemplate.setErrorHandler(NO_OP_ERROR_HANDLER); | ||||
| 
 | ||||
| 		ResponseEntity<String> response = restTemplate | ||||
| 				.getForEntity(new URI("http://localhost:" + port + "/response"), | ||||
| 						String.class); | ||||
| 		URI url = new URI("http://localhost:" + port + "/response-body-error"); | ||||
| 		ResponseEntity<String> response = restTemplate.getForEntity(url, String.class); | ||||
| 
 | ||||
| 		assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); | ||||
| 	} | ||||
| 
 | ||||
| 	@Test | ||||
| 	public void returnValue() throws Exception { | ||||
| 	public void handlingError() throws Exception { | ||||
| 		// TODO: fix Reactor | ||||
| 		assumeFalse(server instanceof ReactorHttpServer); | ||||
| 
 | ||||
| 		RestTemplate restTemplate = new RestTemplate(); | ||||
| 		restTemplate.setErrorHandler(NO_OP_ERROR_HANDLER); | ||||
| 
 | ||||
| 		ResponseEntity<String> response = restTemplate | ||||
| 				.getForEntity(new URI("http://localhost:" + port + "/returnValue"), | ||||
| 						String.class); | ||||
| 		URI url = new URI("http://localhost:" + port + "/handling-error"); | ||||
| 		ResponseEntity<String> response = restTemplate.getForEntity(url, String.class); | ||||
| 
 | ||||
| 		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 { | ||||
| 
 | ||||
| 		@Override | ||||
| 		public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) { | ||||
| 			Exception error = new UnsupportedOperationException(); | ||||
| 			String path = request.getURI().getPath(); | ||||
| 			if (path.endsWith("response")) { | ||||
| 			if (path.endsWith("response-body-error")) { | ||||
| 				return response.writeWith(Mono.error(error)); | ||||
| 			} | ||||
| 			else if (path.endsWith("returnValue")) { | ||||
| 			else if (path.endsWith("handling-error")) { | ||||
| 				return Mono.error(error); | ||||
| 			} | ||||
| 			else { | ||||
|  | @ -92,17 +101,16 @@ public class ErrorHandlerIntegrationTests extends AbstractHttpHandlerIntegration | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private static final ResponseErrorHandler NO_OP_ERROR_HANDLER = | ||||
| 			new ResponseErrorHandler() { | ||||
| 	private static final ResponseErrorHandler NO_OP_ERROR_HANDLER = new ResponseErrorHandler() { | ||||
| 
 | ||||
| 				@Override | ||||
| 				public boolean hasError(ClientHttpResponse response) throws IOException { | ||||
| 					return false; | ||||
| 				} | ||||
| 		@Override | ||||
| 		public boolean hasError(ClientHttpResponse response) { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 				@Override | ||||
| 				public void handleError(ClientHttpResponse response) throws IOException { | ||||
| 				} | ||||
| 			}; | ||||
| 		@Override | ||||
| 		public void handleError(ClientHttpResponse response) { | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ | |||
| package org.springframework.http.server.reactive; | ||||
| 
 | ||||
| import java.net.InetSocketAddress; | ||||
| import java.net.URISyntaxException; | ||||
| 
 | ||||
| import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
|  | @ -63,8 +64,17 @@ public class RxNettyHttpHandlerAdapter implements RequestHandler<ByteBuf, ByteBu | |||
| 		NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(channel.alloc()); | ||||
| 		InetSocketAddress remoteAddress = (InetSocketAddress) channel.remoteAddress(); | ||||
| 
 | ||||
| 		RxNettyServerHttpRequest request = new RxNettyServerHttpRequest(nativeRequest, bufferFactory, remoteAddress); | ||||
| 		RxNettyServerHttpResponse response = new RxNettyServerHttpResponse(nativeResponse, bufferFactory); | ||||
| 		RxNettyServerHttpRequest request; | ||||
| 		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) | ||||
| 				.onErrorResume(ex -> { | ||||
|  |  | |||
|  | @ -55,7 +55,8 @@ public class RxNettyServerHttpRequest extends AbstractServerHttpRequest { | |||
| 
 | ||||
| 
 | ||||
| 	public RxNettyServerHttpRequest(HttpServerRequest<ByteBuf> request, | ||||
| 			NettyDataBufferFactory dataBufferFactory, InetSocketAddress remoteAddress) { | ||||
| 			NettyDataBufferFactory dataBufferFactory, InetSocketAddress remoteAddress) | ||||
| 			throws URISyntaxException { | ||||
| 
 | ||||
| 		super(initUri(request, remoteAddress), initHeaders(request)); | ||||
| 
 | ||||
|  | @ -65,20 +66,17 @@ public class RxNettyServerHttpRequest extends AbstractServerHttpRequest { | |||
| 		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"); | ||||
| 		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) { | ||||
| 		try { | ||||
| 			return new URI(null, null, address.getHostString(), address.getPort(), null, null, null); | ||||
| 		} | ||||
| 		catch (URISyntaxException ex) { | ||||
| 			// Should not happen... | ||||
| 			throw new IllegalStateException(ex); | ||||
| 		} | ||||
| 	private static URI createUrl(InetSocketAddress address, String requestUri) throws URISyntaxException { | ||||
| 		URI baseUrl = new URI(null, null, address.getHostString(), address.getPort(), null, null, null); | ||||
| 		return new URI(baseUrl.toString() + requestUri); | ||||
| 	} | ||||
| 
 | ||||
| 	private static HttpHeaders initHeaders(HttpServerRequest<ByteBuf> request) { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue