Add cookies to ClientHttpRequest/Response
This commit is contained in:
parent
e19abf9c90
commit
72dbe9012e
|
|
@ -150,6 +150,12 @@ public final class ResponseCookie extends HttpCookie {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseCookieBuilder maxAge(long maxAgeSeconds) {
|
||||
this.maxAge = maxAgeSeconds >= 0 ? Duration.ofSeconds(maxAgeSeconds) : Duration.ofSeconds(-1);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseCookieBuilder domain(String domain) {
|
||||
this.domain = domain;
|
||||
|
|
@ -163,14 +169,14 @@ public final class ResponseCookie extends HttpCookie {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ResponseCookieBuilder secure() {
|
||||
this.secure = true;
|
||||
public ResponseCookieBuilder secure(boolean secure) {
|
||||
this.secure = secure;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseCookieBuilder httpOnly() {
|
||||
this.httpOnly = true;
|
||||
public ResponseCookieBuilder httpOnly(boolean httpOnly) {
|
||||
this.httpOnly = httpOnly;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -197,6 +203,11 @@ public final class ResponseCookie extends HttpCookie {
|
|||
*/
|
||||
ResponseCookieBuilder maxAge(Duration maxAge);
|
||||
|
||||
/**
|
||||
* Set the cookie "Max-Age" attribute in seconds.
|
||||
*/
|
||||
ResponseCookieBuilder maxAge(long maxAgeSeconds);
|
||||
|
||||
/**
|
||||
* Set the cookie "Path" attribute.
|
||||
*/
|
||||
|
|
@ -210,13 +221,13 @@ public final class ResponseCookie extends HttpCookie {
|
|||
/**
|
||||
* Add the "Secure" attribute to the cookie.
|
||||
*/
|
||||
ResponseCookieBuilder secure();
|
||||
ResponseCookieBuilder secure(boolean secure);
|
||||
|
||||
/**
|
||||
* Add the "HttpOnly" attribute to the cookie.
|
||||
* @see <a href="http://www.owasp.org/index.php/HTTPOnly">http://www.owasp.org/index.php/HTTPOnly</a>
|
||||
*/
|
||||
ResponseCookieBuilder httpOnly();
|
||||
ResponseCookieBuilder httpOnly(boolean httpOnly);
|
||||
|
||||
/**
|
||||
* Create the HttpCookie.
|
||||
|
|
|
|||
|
|
@ -23,8 +23,12 @@ import java.util.function.Supplier;
|
|||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Base class for {@link ClientHttpRequest} implementations.
|
||||
|
|
@ -36,6 +40,8 @@ public abstract class AbstractClientHttpRequest implements ClientHttpRequest {
|
|||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
private final MultiValueMap<String, HttpCookie> cookies;
|
||||
|
||||
private AtomicReference<State> state = new AtomicReference<>(State.NEW);
|
||||
|
||||
private final List<Supplier<? extends Mono<Void>>> beforeCommitActions = new ArrayList<>(4);
|
||||
|
|
@ -47,6 +53,7 @@ public abstract class AbstractClientHttpRequest implements ClientHttpRequest {
|
|||
else {
|
||||
this.headers = httpHeaders;
|
||||
}
|
||||
this.cookies = new LinkedMultiValueMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -57,6 +64,14 @@ public abstract class AbstractClientHttpRequest implements ClientHttpRequest {
|
|||
return this.headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiValueMap<String, HttpCookie> getCookies() {
|
||||
if (State.COMITTED.equals(this.state.get())) {
|
||||
return CollectionUtils.unmodifiableMultiValueMap(this.cookies);
|
||||
}
|
||||
return this.cookies;
|
||||
}
|
||||
|
||||
protected Mono<Void> applyBeforeCommit() {
|
||||
Mono<Void> mono = Mono.empty();
|
||||
if (this.state.compareAndSet(State.NEW, State.COMMITTING)) {
|
||||
|
|
|
|||
|
|
@ -20,8 +20,10 @@ import java.net.URI;
|
|||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ReactiveHttpOutputMessage;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Represents a reactive client-side HTTP request.
|
||||
|
|
@ -41,6 +43,11 @@ public interface ClientHttpRequest extends ReactiveHttpOutputMessage {
|
|||
*/
|
||||
URI getURI();
|
||||
|
||||
/**
|
||||
* Return a mutable map of request cookies to send to the server.
|
||||
*/
|
||||
MultiValueMap<String, HttpCookie> getCookies();
|
||||
|
||||
/**
|
||||
* Execute this request, resulting in a reactive stream of a single
|
||||
* {@link org.springframework.http.client.ClientHttpResponse}.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ package org.springframework.http.client.reactive;
|
|||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ReactiveHttpInputMessage;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Represents a reactive client-side HTTP response.
|
||||
|
|
@ -31,4 +33,9 @@ public interface ClientHttpResponse extends ReactiveHttpInputMessage {
|
|||
*/
|
||||
HttpStatus getStatusCode();
|
||||
|
||||
/**
|
||||
* Return a read-only map of response cookies received from the server.
|
||||
*/
|
||||
MultiValueMap<String, ResponseCookie> getCookies();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,16 +17,19 @@
|
|||
package org.springframework.http.client.reactive;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.io.buffer.Buffer;
|
||||
import reactor.io.netty.http.HttpClient;
|
||||
import reactor.io.netty.http.model.Cookie;
|
||||
import reactor.io.netty.http.model.Method;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferAllocator;
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
|
|
@ -96,13 +99,13 @@ public class ReactorClientHttpRequest extends AbstractClientHttpRequest {
|
|||
channel.headers().removeTransferEncodingChunked();
|
||||
}
|
||||
return applyBeforeCommit()
|
||||
.after(() ->
|
||||
{
|
||||
getHeaders().entrySet().stream()
|
||||
.forEach(e -> channel.headers().set(e.getKey(), e.getValue()));
|
||||
return Mono.empty();
|
||||
}
|
||||
)
|
||||
.after(() -> {
|
||||
getHeaders().entrySet().stream().forEach(e ->
|
||||
channel.headers().set(e.getKey(), e.getValue()));
|
||||
getCookies().values().stream().flatMap(Collection::stream).forEach(cookie ->
|
||||
channel.addCookie(cookie.getName(), new ReactorCookie(cookie)));
|
||||
return Mono.empty();
|
||||
})
|
||||
.after(() -> {
|
||||
if (body != null) {
|
||||
return channel.writeBufferWith(body);
|
||||
|
|
@ -115,5 +118,29 @@ public class ReactorClientHttpRequest extends AbstractClientHttpRequest {
|
|||
.map(httpChannel -> new ReactorClientHttpResponse(httpChannel, allocator));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* At present Reactor does not provide a {@link Cookie} implementation.
|
||||
*/
|
||||
private final static class ReactorCookie extends Cookie {
|
||||
|
||||
private final HttpCookie httpCookie;
|
||||
|
||||
|
||||
public ReactorCookie(HttpCookie httpCookie) {
|
||||
this.httpCookie = httpCookie;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return this.httpCookie.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String value() {
|
||||
return this.httpCookie.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.http.client.reactive;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collection;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.io.buffer.Buffer;
|
||||
|
|
@ -26,6 +27,10 @@ import org.springframework.core.io.buffer.DataBuffer;
|
|||
import org.springframework.core.io.buffer.DataBufferAllocator;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpResponse} implementation for the Reactor Net HTTP client
|
||||
|
|
@ -62,10 +67,27 @@ public class ReactorClientHttpResponse implements ClientHttpResponse {
|
|||
return HttpStatus.valueOf(this.channel.responseStatus().getCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiValueMap<String, ResponseCookie> getCookies() {
|
||||
MultiValueMap<String, ResponseCookie> result = new LinkedMultiValueMap<>();
|
||||
this.channel.cookies().values().stream().flatMap(Collection::stream)
|
||||
.forEach(cookie -> {
|
||||
ResponseCookie responseCookie = ResponseCookie.from(cookie.name(), cookie.value())
|
||||
.domain(cookie.domain())
|
||||
.path(cookie.path())
|
||||
.maxAge(cookie.maxAge())
|
||||
.secure(cookie.secure())
|
||||
.httpOnly(cookie.httpOnly())
|
||||
.build();
|
||||
result.add(cookie.name(), responseCookie);
|
||||
});
|
||||
return CollectionUtils.unmodifiableMultiValueMap(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ReactorClientHttpResponse{" +
|
||||
"request=" + this.channel.method() + " " + this.channel.uri().toString() + "," +
|
||||
"request=" + this.channel.method() + " " + this.channel.uri() + "," +
|
||||
"status=" + getStatusCode() +
|
||||
'}';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,10 +17,12 @@
|
|||
package org.springframework.http.client.reactive;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||
import io.reactivex.netty.protocol.http.client.HttpClient;
|
||||
import io.reactivex.netty.protocol.http.client.HttpClientRequest;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
|
@ -31,6 +33,7 @@ import rx.Observable;
|
|||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.NettyDataBufferAllocator;
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
|
|
@ -102,6 +105,11 @@ public class RxNettyClientHttpRequest extends AbstractClientHttpRequest {
|
|||
req = req.addHeader(entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, List<HttpCookie>> entry : getCookies().entrySet()) {
|
||||
for (HttpCookie cookie : entry.getValue()) {
|
||||
req.addCookie(new DefaultCookie(cookie.getName(), cookie.getValue()));
|
||||
}
|
||||
}
|
||||
return req;
|
||||
})
|
||||
.map(req -> {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.http.client.reactive;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.reactivex.netty.protocol.http.client.HttpClientResponse;
|
||||
import reactor.core.converter.RxJava1ObservableConverter;
|
||||
|
|
@ -25,7 +27,11 @@ import org.springframework.core.io.buffer.DataBuffer;
|
|||
import org.springframework.core.io.buffer.NettyDataBufferAllocator;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link ClientHttpResponse} implementation for the RxNetty HTTP client
|
||||
|
|
@ -38,8 +44,11 @@ public class RxNettyClientHttpResponse implements ClientHttpResponse {
|
|||
|
||||
private final HttpHeaders headers;
|
||||
|
||||
private final MultiValueMap<String, ResponseCookie> cookies;
|
||||
|
||||
private final NettyDataBufferAllocator allocator;
|
||||
|
||||
|
||||
public RxNettyClientHttpResponse(HttpClientResponse<ByteBuf> response,
|
||||
NettyDataBufferAllocator allocator) {
|
||||
Assert.notNull("'request', request must not be null");
|
||||
|
|
@ -48,8 +57,26 @@ public class RxNettyClientHttpResponse implements ClientHttpResponse {
|
|||
this.response = response;
|
||||
this.headers = new HttpHeaders();
|
||||
this.response.headerIterator().forEachRemaining(e -> this.headers.set(e.getKey(), e.getValue()));
|
||||
this.cookies = initCookies(response);
|
||||
}
|
||||
|
||||
private static MultiValueMap<String, ResponseCookie> initCookies(HttpClientResponse<ByteBuf> response) {
|
||||
MultiValueMap<String, ResponseCookie> result = new LinkedMultiValueMap<>();
|
||||
response.getCookies().values().stream().flatMap(Collection::stream)
|
||||
.forEach(cookie -> {
|
||||
ResponseCookie responseCookie = ResponseCookie.from(cookie.name(), cookie.value())
|
||||
.domain(cookie.domain())
|
||||
.path(cookie.path())
|
||||
.maxAge(cookie.maxAge())
|
||||
.secure(cookie.isSecure())
|
||||
.httpOnly(cookie.isHttpOnly())
|
||||
.build();
|
||||
result.add(cookie.name(), responseCookie);
|
||||
});
|
||||
return CollectionUtils.unmodifiableMultiValueMap(result);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public HttpStatus getStatusCode() {
|
||||
return HttpStatus.valueOf(this.response.getStatus().code());
|
||||
|
|
@ -64,4 +91,10 @@ public class RxNettyClientHttpResponse implements ClientHttpResponse {
|
|||
public HttpHeaders getHeaders() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiValueMap<String, ResponseCookie> getCookies() {
|
||||
return this.cookies;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public interface ServerHttpResponse extends ReactiveHttpOutputMessage {
|
|||
void setStatusCode(HttpStatus status);
|
||||
|
||||
/**
|
||||
* Return a mutable map with cookies to be sent to the client.
|
||||
* Return a mutable map with the cookies to send to the server.
|
||||
*/
|
||||
MultiValueMap<String, ResponseCookie> getCookies();
|
||||
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ public class CookieIntegrationTests extends AbstractHttpHandlerIntegrationTests
|
|||
this.requestCookies.size(); // Cause lazy loading
|
||||
|
||||
response.getCookies().add("SID", ResponseCookie.from("SID", "31d4d96e407aad42")
|
||||
.path("/").secure().httpOnly().build());
|
||||
.path("/").secure(true).httpOnly(true).build());
|
||||
response.getCookies().add("lang", ResponseCookie.from("lang", "en-US")
|
||||
.domain("example.com").path("/").build());
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue