diff --git a/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java b/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java index 40cba344c7..b0bb9d4909 100644 --- a/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/http/server/reactive/MockServerHttpRequest.java @@ -15,12 +15,14 @@ */ package org.springframework.mock.http.server.reactive; +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.Arrays; import java.util.List; +import java.util.Optional; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; @@ -57,17 +59,21 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { private final MultiValueMap cookies; + private final InetSocketAddress remoteAddress; + private final Flux body; private MockServerHttpRequest(HttpMethod httpMethod, URI uri, String contextPath, HttpHeaders headers, MultiValueMap cookies, + InetSocketAddress remoteAddress, Publisher body) { super(uri, headers); this.httpMethod = httpMethod; this.contextPath = (contextPath != null ? contextPath : ""); this.cookies = cookies; + this.remoteAddress = remoteAddress; this.body = Flux.from(body); } @@ -82,6 +88,11 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { return this.contextPath; } + @Override + public Optional getRemoteAddress() { + return Optional.ofNullable(this.remoteAddress); + } + @Override public Flux getBody() { return this.body; @@ -198,6 +209,11 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { */ B contextPath(String contextPath); + /** + * Set the remote address to return. + */ + B remoteAddress(InetSocketAddress remoteAddress); + /** * Add one or more cookies. */ @@ -318,6 +334,8 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { private final MultiValueMap cookies = new LinkedMultiValueMap<>(); + private InetSocketAddress remoteAddress; + public DefaultBodyBuilder(HttpMethod method, URI url) { this.method = method; @@ -330,6 +348,12 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { return this; } + @Override + public BodyBuilder remoteAddress(InetSocketAddress remoteAddress) { + this.remoteAddress = remoteAddress; + return this; + } + @Override public BodyBuilder cookie(String path, HttpCookie... cookies) { this.cookies.put(path, Arrays.asList(cookies)); @@ -395,7 +419,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { @Override public MockServerHttpRequest body(Publisher body) { return new MockServerHttpRequest(this.method, this.url, this.contextPath, - this.headers, this.cookies, body); + this.headers, this.cookies, this.remoteAddress, body); } @Override diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpRequest.java index e1346e9cf2..174c0112ca 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpRequest.java @@ -19,6 +19,7 @@ package org.springframework.http.server.reactive; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; +import java.util.Optional; import io.netty.handler.codec.http.cookie.Cookie; import reactor.core.publisher.Flux; @@ -57,7 +58,8 @@ public class ReactorServerHttpRequest extends AbstractServerHttpRequest { private static URI initUri(HttpServerRequest channel) { Assert.notNull(channel, "'channel' must not be null"); InetSocketAddress address = channel.remoteAddress(); - return (address == null ? URI.create(channel.uri()) : getBaseUrl(address).resolve(channel.uri())); + String requestUri = channel.uri(); + return (address != null ? getBaseUrl(address).resolve(requestUri) : URI.create(requestUri)); } private static URI getBaseUrl(InetSocketAddress address) { @@ -100,6 +102,11 @@ public class ReactorServerHttpRequest extends AbstractServerHttpRequest { return cookies; } + @Override + public Optional getRemoteAddress() { + return Optional.ofNullable(this.request.remoteAddress()); + } + @Override public Flux getBody() { return this.request.receive().retain().map(this.bufferFactory::wrap); diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/RxNettyHttpHandlerAdapter.java b/spring-web/src/main/java/org/springframework/http/server/reactive/RxNettyHttpHandlerAdapter.java index a0c5425a57..9bc170859d 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/RxNettyHttpHandlerAdapter.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/RxNettyHttpHandlerAdapter.java @@ -16,10 +16,13 @@ package org.springframework.http.server.reactive; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.Map; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; +import io.netty.channel.Channel; import io.netty.handler.codec.http.HttpResponseStatus; import io.reactivex.netty.protocol.http.server.HttpServerRequest; import io.reactivex.netty.protocol.http.server.HttpServerResponse; @@ -54,10 +57,11 @@ public class RxNettyHttpHandlerAdapter extends HttpHandlerAdapterSupport public Observable handle(HttpServerRequest nativeRequest, HttpServerResponse nativeResponse) { - ByteBufAllocator allocator = nativeResponse.unsafeNettyChannel().alloc(); - NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(allocator); + Channel channel = nativeResponse.unsafeNettyChannel(); + NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(channel.alloc()); + InetSocketAddress remoteAddress = (InetSocketAddress) channel.remoteAddress(); - RxNettyServerHttpRequest request = new RxNettyServerHttpRequest(nativeRequest, bufferFactory); + RxNettyServerHttpRequest request = new RxNettyServerHttpRequest(nativeRequest, bufferFactory, remoteAddress); RxNettyServerHttpResponse response = new RxNettyServerHttpResponse(nativeResponse, bufferFactory); Publisher result = getHttpHandler().handle(request, response) diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java index 26c9d9f0af..28effa2f95 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java @@ -19,6 +19,7 @@ package org.springframework.http.server.reactive; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; +import java.util.Optional; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http.cookie.Cookie; @@ -35,7 +36,6 @@ import org.springframework.http.HttpMethod; import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; /** * Adapt {@link ServerHttpRequest} to the RxNetty {@link HttpServerRequest}. @@ -50,27 +50,27 @@ public class RxNettyServerHttpRequest extends AbstractServerHttpRequest { private final NettyDataBufferFactory dataBufferFactory; + private final InetSocketAddress remoteAddress; + public RxNettyServerHttpRequest(HttpServerRequest request, - NettyDataBufferFactory dataBufferFactory) { + NettyDataBufferFactory dataBufferFactory, InetSocketAddress remoteAddress) { - super(initUri(request), initHeaders(request)); + super(initUri(request, remoteAddress), initHeaders(request)); Assert.notNull(dataBufferFactory, "'dataBufferFactory' must not be null"); this.request = request; this.dataBufferFactory = dataBufferFactory; + this.remoteAddress = remoteAddress; } - private static URI initUri(HttpServerRequest request) { + private static URI initUri(HttpServerRequest request, InetSocketAddress remoteAddress) { Assert.notNull(request, "'request' must not be null"); - return StringUtils.isEmpty(request.getHostHeader()) ? - URI.create(request.getUri()) : getBaseUrl(request).resolve(request.getUri()); + String requestUri = request.getUri(); + return remoteAddress != null ? getBaseUrl(remoteAddress).resolve(requestUri) : URI.create(requestUri); } - private static URI getBaseUrl(HttpServerRequest request) { - HttpHeaders headers = new HttpHeaders(); - headers.add("Host", request.getHostHeader()); - InetSocketAddress address = headers.getHost(); + private static URI getBaseUrl(InetSocketAddress address) { try { return new URI(null, null, address.getHostString(), address.getPort(), null, null, null); } @@ -110,6 +110,11 @@ public class RxNettyServerHttpRequest extends AbstractServerHttpRequest { return cookies; } + @Override + public Optional getRemoteAddress() { + return Optional.ofNullable(this.remoteAddress); + } + @Override public Flux getBody() { Observable content = this.request.getContent().map(dataBufferFactory::wrap); diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequest.java index be0a9f0882..1710f60d50 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequest.java @@ -16,6 +16,9 @@ package org.springframework.http.server.reactive; +import java.net.InetSocketAddress; +import java.util.Optional; + import org.springframework.http.HttpCookie; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRequest; @@ -53,6 +56,12 @@ public interface ServerHttpRequest extends HttpRequest, ReactiveHttpInputMessage */ MultiValueMap getCookies(); + /** + * Returns the remote address where this request is connected to. + * @return remote address if available + */ + Optional getRemoteAddress(); + /** * Return a builder to mutate properties of this request by wrapping it diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequestDecorator.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequestDecorator.java index 60dcb5d0d8..38a42cba98 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequestDecorator.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ServerHttpRequestDecorator.java @@ -15,7 +15,9 @@ */ package org.springframework.http.server.reactive; +import java.net.InetSocketAddress; import java.net.URI; +import java.util.Optional; import reactor.core.publisher.Flux; @@ -76,6 +78,11 @@ public class ServerHttpRequestDecorator implements ServerHttpRequest { return getDelegate().getCookies(); } + @Override + public Optional getRemoteAddress() { + return getDelegate().getRemoteAddress(); + } + @Override public Flux getBody() { return getDelegate().getBody(); diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java index d0bc01743b..95eed9cef3 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java @@ -17,11 +17,13 @@ package org.springframework.http.server.reactive; import java.io.IOException; +import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.Enumeration; import java.util.Map; +import java.util.Optional; import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; @@ -175,6 +177,12 @@ public class ServletServerHttpRequest extends AbstractServerHttpRequest { return httpCookies; } + @Override + public Optional getRemoteAddress() { + return Optional.of(new InetSocketAddress( + this.request.getRemoteHost(), this.request.getRemotePort())); + } + @Override public Flux getBody() { return Flux.from(this.bodyPublisher); diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java index bae47f5f3e..68bb3aa8f7 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java @@ -17,9 +17,11 @@ package org.springframework.http.server.reactive; import java.io.IOException; +import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; +import java.util.Optional; import io.undertow.connector.ByteBufferPool; import io.undertow.connector.PooledByteBuffer; @@ -111,6 +113,11 @@ public class UndertowServerHttpRequest extends AbstractServerHttpRequest { return cookies; } + @Override + public Optional getRemoteAddress() { + return Optional.ofNullable(this.exchange.getSourceAddress()); + } + @Override public Flux getBody() { return Flux.from(this.body); diff --git a/spring-web/src/test/java/org/springframework/http/server/reactive/ServerHttpRequestIntegrationTests.java b/spring-web/src/test/java/org/springframework/http/server/reactive/ServerHttpRequestIntegrationTests.java index eed78bd62c..6e400d8fe3 100644 --- a/spring-web/src/test/java/org/springframework/http/server/reactive/ServerHttpRequestIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/http/server/reactive/ServerHttpRequestIntegrationTests.java @@ -48,10 +48,11 @@ public class ServerHttpRequestIntegrationTests extends AbstractHttpHandlerIntegr @Override public Mono handle(ServerHttpRequest request, ServerHttpResponse response) { URI uri = request.getURI(); - assertNotNull("Request URI host must not be null", uri.getHost()); - assertNotEquals("Request URI port must not be undefined", -1, uri.getPort()); - assertEquals("Request URI path is not valid", "/foo", uri.getPath()); - assertEquals("Request URI query is not valid", "param=bar", uri.getQuery()); + assertNotNull(uri.getHost()); + assertNotEquals(-1, uri.getPort()); + assertNotNull(request.getRemoteAddress()); + assertEquals("/foo", uri.getPath()); + assertEquals("param=bar", uri.getQuery()); return Mono.empty(); } } diff --git a/spring-web/src/test/java/org/springframework/mock/http/server/reactive/test/MockServerHttpRequest.java b/spring-web/src/test/java/org/springframework/mock/http/server/reactive/test/MockServerHttpRequest.java index 24db87396e..709e6d3039 100644 --- a/spring-web/src/test/java/org/springframework/mock/http/server/reactive/test/MockServerHttpRequest.java +++ b/spring-web/src/test/java/org/springframework/mock/http/server/reactive/test/MockServerHttpRequest.java @@ -15,12 +15,14 @@ */ package org.springframework.mock.http.server.reactive.test; +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.Arrays; import java.util.List; +import java.util.Optional; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; @@ -57,17 +59,21 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { private final MultiValueMap cookies; + private final InetSocketAddress remoteAddress; + private final Flux body; private MockServerHttpRequest(HttpMethod httpMethod, URI uri, String contextPath, HttpHeaders headers, MultiValueMap cookies, + InetSocketAddress remoteAddress, Publisher body) { super(uri, headers); this.httpMethod = httpMethod; this.contextPath = (contextPath != null ? contextPath : ""); this.cookies = cookies; + this.remoteAddress = remoteAddress; this.body = Flux.from(body); } @@ -82,6 +88,11 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { return this.contextPath; } + @Override + public Optional getRemoteAddress() { + return Optional.ofNullable(this.remoteAddress); + } + @Override public Flux getBody() { return this.body; @@ -198,6 +209,11 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { */ B contextPath(String contextPath); + /** + * Set the remote address to return. + */ + B remoteAddress(InetSocketAddress remoteAddress); + /** * Add one or more cookies. */ @@ -318,6 +334,8 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { private final MultiValueMap cookies = new LinkedMultiValueMap<>(); + private InetSocketAddress remoteAddress; + public DefaultBodyBuilder(HttpMethod method, URI url) { this.method = method; @@ -330,6 +348,12 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { return this; } + @Override + public BodyBuilder remoteAddress(InetSocketAddress remoteAddress) { + this.remoteAddress = remoteAddress; + return this; + } + @Override public BodyBuilder cookie(String path, HttpCookie... cookies) { this.cookies.put(path, Arrays.asList(cookies)); @@ -395,7 +419,7 @@ public class MockServerHttpRequest extends AbstractServerHttpRequest { @Override public MockServerHttpRequest body(Publisher body) { return new MockServerHttpRequest(this.method, this.url, this.contextPath, - this.headers, this.cookies, body); + this.headers, this.cookies, this.remoteAddress, body); } @Override