Skip to content

Commit b50ad1b

Browse files
committed
AbstractServerHttpResponse skips commit actions on 2nd pass
Closes gh-25753
1 parent 0db3f2b commit b50ad1b

File tree

2 files changed

+29
-15
lines changed

2 files changed

+29
-15
lines changed

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

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public abstract class AbstractServerHttpResponse implements ServerHttpResponse {
5757
* response during which time pre-commit actions can still make changes to
5858
* the response status and headers.
5959
*/
60-
private enum State {NEW, COMMITTING, COMMITTED}
60+
private enum State {NEW, COMMITTING, COMMIT_ACTION_FAILED, COMMITTED}
6161

6262
protected final Log logger = HttpLogging.forLogName(getClass());
6363

@@ -204,7 +204,8 @@ public void beforeCommit(Supplier<? extends Mono<Void>> action) {
204204

205205
@Override
206206
public boolean isCommitted() {
207-
return this.state.get() != State.NEW;
207+
State state = this.state.get();
208+
return (state != State.NEW && state != State.COMMIT_ACTION_FAILED);
208209
}
209210

210211
@Override
@@ -251,19 +252,22 @@ protected Mono<Void> doCommit() {
251252
* @return a completion publisher
252253
*/
253254
protected Mono<Void> doCommit(@Nullable Supplier<? extends Mono<Void>> writeAction) {
254-
if (!this.state.compareAndSet(State.NEW, State.COMMITTING)) {
255-
return Mono.empty();
256-
}
257-
258255
Flux<Void> allActions = Flux.empty();
259-
260-
if (!this.commitActions.isEmpty()) {
261-
allActions = Flux.concat(Flux.fromIterable(this.commitActions).map(Supplier::get))
262-
.doOnError(ex -> {
263-
if (this.state.compareAndSet(State.COMMITTING, State.NEW)) {
264-
getHeaders().clearContentHeaders();
265-
}
266-
});
256+
if (this.state.compareAndSet(State.NEW, State.COMMITTING)) {
257+
if (!this.commitActions.isEmpty()) {
258+
allActions = Flux.concat(Flux.fromIterable(this.commitActions).map(Supplier::get))
259+
.doOnError(ex -> {
260+
if (this.state.compareAndSet(State.COMMITTING, State.COMMIT_ACTION_FAILED)) {
261+
getHeaders().clearContentHeaders();
262+
}
263+
});
264+
}
265+
}
266+
else if (this.state.compareAndSet(State.COMMIT_ACTION_FAILED, State.COMMITTING)) {
267+
// Skip commit actions
268+
}
269+
else {
270+
return Mono.empty();
267271
}
268272

269273
allActions = allActions.concatWith(Mono.fromRunnable(() -> {

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.core.io.buffer.DefaultDataBuffer;
3434
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
3535
import org.springframework.http.HttpHeaders;
36+
import org.springframework.http.HttpStatus;
3637
import org.springframework.http.MediaType;
3738
import org.springframework.http.ResponseCookie;
3839

@@ -150,7 +151,7 @@ void beforeCommitActionWithSetComplete() {
150151
assertThat(response.getCookies().getFirst("ID")).isSameAs(cookie);
151152
}
152153

153-
@Test // gh-24186
154+
@Test // gh-24186, gh-25753
154155
void beforeCommitErrorShouldLeaveResponseNotCommitted() {
155156

156157
Consumer<Supplier<Mono<Void>>> tester = preCommitAction -> {
@@ -168,6 +169,15 @@ void beforeCommitErrorShouldLeaveResponseNotCommitted() {
168169
assertThat(response.cookiesWritten).isFalse();
169170
assertThat(response.isCommitted()).isFalse();
170171
assertThat(response.getHeaders()).isEmpty();
172+
173+
// Handle the error
174+
response.setStatusCode(HttpStatus.SERVICE_UNAVAILABLE);
175+
StepVerifier.create(response.setComplete()).verifyComplete();
176+
177+
assertThat(response.statusCodeWritten).isTrue();
178+
assertThat(response.headersWritten).isTrue();
179+
assertThat(response.cookiesWritten).isTrue();
180+
assertThat(response.isCommitted()).isTrue();
171181
};
172182

173183
tester.accept(() -> Mono.error(new IllegalStateException("Max sessions")));

0 commit comments

Comments
 (0)