Browse Source

Add cookies to ClientHttpRequest/Response

pull/1111/head
Rossen Stoyanchev 10 years ago
parent
commit
72dbe9012e
  1. 23
      spring-web-reactive/src/main/java/org/springframework/http/ResponseCookie.java
  2. 15
      spring-web-reactive/src/main/java/org/springframework/http/client/reactive/AbstractClientHttpRequest.java
  3. 7
      spring-web-reactive/src/main/java/org/springframework/http/client/reactive/ClientHttpRequest.java
  4. 7
      spring-web-reactive/src/main/java/org/springframework/http/client/reactive/ClientHttpResponse.java
  5. 41
      spring-web-reactive/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpRequest.java
  6. 24
      spring-web-reactive/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpResponse.java
  7. 8
      spring-web-reactive/src/main/java/org/springframework/http/client/reactive/RxNettyClientHttpRequest.java
  8. 33
      spring-web-reactive/src/main/java/org/springframework/http/client/reactive/RxNettyClientHttpResponse.java
  9. 2
      spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServerHttpResponse.java
  10. 2
      spring-web-reactive/src/test/java/org/springframework/http/server/reactive/CookieIntegrationTests.java

23
spring-web-reactive/src/main/java/org/springframework/http/ResponseCookie.java

@ -150,6 +150,12 @@ public final class ResponseCookie extends HttpCookie { @@ -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 { @@ -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 { @@ -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 { @@ -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.

15
spring-web-reactive/src/main/java/org/springframework/http/client/reactive/AbstractClientHttpRequest.java

@ -23,8 +23,12 @@ import java.util.function.Supplier; @@ -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 { @@ -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 { @@ -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 { @@ -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)) {

7
spring-web-reactive/src/main/java/org/springframework/http/client/reactive/ClientHttpRequest.java

@ -20,8 +20,10 @@ import java.net.URI; @@ -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 { @@ -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}.

7
spring-web-reactive/src/main/java/org/springframework/http/client/reactive/ClientHttpResponse.java

@ -18,6 +18,8 @@ package org.springframework.http.client.reactive; @@ -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 { @@ -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();
}

41
spring-web-reactive/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpRequest.java

@ -17,16 +17,19 @@ @@ -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 { @@ -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 { @@ -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();
}
}
}

24
spring-web-reactive/src/main/java/org/springframework/http/client/reactive/ReactorClientHttpResponse.java

@ -17,6 +17,7 @@ @@ -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; @@ -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 { @@ -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() +
'}';
}

8
spring-web-reactive/src/main/java/org/springframework/http/client/reactive/RxNettyClientHttpRequest.java

@ -17,10 +17,12 @@ @@ -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; @@ -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 { @@ -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 -> {

33
spring-web-reactive/src/main/java/org/springframework/http/client/reactive/RxNettyClientHttpResponse.java

@ -16,6 +16,8 @@ @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -64,4 +91,10 @@ public class RxNettyClientHttpResponse implements ClientHttpResponse {
public HttpHeaders getHeaders() {
return this.headers;
}
@Override
public MultiValueMap<String, ResponseCookie> getCookies() {
return this.cookies;
}
}

2
spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServerHttpResponse.java

@ -37,7 +37,7 @@ public interface ServerHttpResponse extends ReactiveHttpOutputMessage { @@ -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();

2
spring-web-reactive/src/test/java/org/springframework/http/server/reactive/CookieIntegrationTests.java

@ -103,7 +103,7 @@ public class CookieIntegrationTests extends AbstractHttpHandlerIntegrationTests @@ -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…
Cancel
Save