Skip to content

Commit 0241a02

Browse files
committed
Fix guard against multiple subscriptions
This commit changes the guard against multiple subscriptions, as the previously used doOnSubscribe hook could not function as guard in certain scenarios. Closes gh-32727
1 parent cbda469 commit 0241a02

File tree

1 file changed

+40
-10
lines changed

1 file changed

+40
-10
lines changed

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

+40-10
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616

1717
package org.springframework.http.client.reactive;
1818

19+
import java.util.Objects;
1920
import java.util.concurrent.atomic.AtomicBoolean;
2021

22+
import org.reactivestreams.Publisher;
23+
import org.reactivestreams.Subscriber;
24+
import org.reactivestreams.Subscription;
2125
import reactor.core.publisher.Flux;
2226

2327
import org.springframework.core.io.buffer.DataBuffer;
@@ -55,16 +59,7 @@ protected AbstractClientHttpResponse(HttpStatusCode statusCode, HttpHeaders head
5559
this.statusCode = statusCode;
5660
this.headers = headers;
5761
this.cookies = cookies;
58-
this.body = singleSubscription(body);
59-
}
60-
61-
private static Flux<DataBuffer> singleSubscription(Flux<DataBuffer> body) {
62-
AtomicBoolean subscribed = new AtomicBoolean();
63-
return body.doOnSubscribe(s -> {
64-
if (!subscribed.compareAndSet(false, true)) {
65-
throw new IllegalStateException("The client response body can only be consumed once");
66-
}
67-
});
62+
this.body = Flux.from(new SingleSubscriberPublisher<>(body));
6863
}
6964

7065

@@ -87,4 +82,39 @@ public MultiValueMap<String, ResponseCookie> getCookies() {
8782
public Flux<DataBuffer> getBody() {
8883
return this.body;
8984
}
85+
86+
87+
private static final class SingleSubscriberPublisher<T> implements Publisher<T> {
88+
89+
private static final Subscription NO_OP_SUBSCRIPTION = new Subscription() {
90+
@Override
91+
public void request(long l) {
92+
}
93+
94+
@Override
95+
public void cancel() {
96+
}
97+
};
98+
99+
private final Publisher<T> delegate;
100+
101+
private final AtomicBoolean subscribed = new AtomicBoolean();
102+
103+
104+
public SingleSubscriberPublisher(Publisher<T> delegate) {
105+
this.delegate = delegate;
106+
}
107+
108+
@Override
109+
public void subscribe(Subscriber<? super T> subscriber) {
110+
Objects.requireNonNull(subscriber, "Subscriber must not be null");
111+
if (this.subscribed.compareAndSet(false, true)) {
112+
this.delegate.subscribe(subscriber);
113+
}
114+
else {
115+
subscriber.onSubscribe(NO_OP_SUBSCRIPTION);
116+
subscriber.onError(new IllegalStateException("The client response body can only be consumed once"));
117+
}
118+
}
119+
}
90120
}

0 commit comments

Comments
 (0)