|
|
|
@ -13,17 +13,19 @@ |
|
|
|
* See the License for the specific language governing permissions and |
|
|
|
* See the License for the specific language governing permissions and |
|
|
|
* limitations under the License. |
|
|
|
* limitations under the License. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
package org.springframework.http.server.reactive; |
|
|
|
package org.springframework.http.server.reactive; |
|
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
import java.util.concurrent.atomic.AtomicReference; |
|
|
|
import java.util.concurrent.atomic.AtomicInteger; |
|
|
|
import java.util.function.Supplier; |
|
|
|
import java.util.function.Supplier; |
|
|
|
|
|
|
|
|
|
|
|
import org.reactivestreams.Publisher; |
|
|
|
import org.reactivestreams.Publisher; |
|
|
|
import reactor.core.publisher.Mono; |
|
|
|
import reactor.core.publisher.Mono; |
|
|
|
|
|
|
|
|
|
|
|
import org.springframework.core.io.buffer.DataBuffer; |
|
|
|
import org.springframework.core.io.buffer.DataBuffer; |
|
|
|
|
|
|
|
import org.springframework.core.io.buffer.DataBufferAllocator; |
|
|
|
import org.springframework.http.HttpHeaders; |
|
|
|
import org.springframework.http.HttpHeaders; |
|
|
|
import org.springframework.http.ResponseCookie; |
|
|
|
import org.springframework.http.ResponseCookie; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
@ -39,32 +41,48 @@ import org.springframework.util.MultiValueMap; |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public abstract class AbstractServerHttpResponse implements ServerHttpResponse { |
|
|
|
public abstract class AbstractServerHttpResponse implements ServerHttpResponse { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final int STATE_NEW = 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final int STATE_COMMITTING = 2; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final int STATE_COMMITTED = 3; |
|
|
|
|
|
|
|
|
|
|
|
private final HttpHeaders headers; |
|
|
|
private final HttpHeaders headers; |
|
|
|
|
|
|
|
|
|
|
|
private final MultiValueMap<String, ResponseCookie> cookies; |
|
|
|
private final MultiValueMap<String, ResponseCookie> cookies; |
|
|
|
|
|
|
|
|
|
|
|
private AtomicReference<State> state = new AtomicReference<>(State.NEW); |
|
|
|
private final AtomicInteger state = new AtomicInteger(STATE_NEW); |
|
|
|
|
|
|
|
|
|
|
|
private final List<Supplier<? extends Mono<Void>>> beforeCommitActions = new ArrayList<>(4); |
|
|
|
private final List<Supplier<? extends Mono<Void>>> beforeCommitActions = new ArrayList<>(4); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final DataBufferAllocator allocator; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public AbstractServerHttpResponse(DataBufferAllocator allocator) { |
|
|
|
|
|
|
|
Assert.notNull(allocator, "'allocator' must not be null"); |
|
|
|
|
|
|
|
|
|
|
|
protected AbstractServerHttpResponse() { |
|
|
|
this.allocator = allocator; |
|
|
|
this.headers = new HttpHeaders(); |
|
|
|
this.headers = new HttpHeaders(); |
|
|
|
this.cookies = new LinkedMultiValueMap<String, ResponseCookie>(); |
|
|
|
this.cookies = new LinkedMultiValueMap<String, ResponseCookie>(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public final DataBufferAllocator allocator() { |
|
|
|
|
|
|
|
return this.allocator; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public HttpHeaders getHeaders() { |
|
|
|
public HttpHeaders getHeaders() { |
|
|
|
if (State.COMITTED.equals(this.state.get())) { |
|
|
|
if (STATE_COMMITTED == this.state.get()) { |
|
|
|
return HttpHeaders.readOnlyHttpHeaders(this.headers); |
|
|
|
return HttpHeaders.readOnlyHttpHeaders(this.headers); |
|
|
|
} |
|
|
|
} |
|
|
|
return this.headers; |
|
|
|
else { |
|
|
|
|
|
|
|
return this.headers; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public MultiValueMap<String, ResponseCookie> getCookies() { |
|
|
|
public MultiValueMap<String, ResponseCookie> getCookies() { |
|
|
|
if (State.COMITTED.equals(this.state.get())) { |
|
|
|
if (STATE_COMMITTED == this.state.get()) { |
|
|
|
return CollectionUtils.unmodifiableMultiValueMap(this.cookies); |
|
|
|
return CollectionUtils.unmodifiableMultiValueMap(this.cookies); |
|
|
|
} |
|
|
|
} |
|
|
|
return this.cookies; |
|
|
|
return this.cookies; |
|
|
|
@ -78,16 +96,16 @@ public abstract class AbstractServerHttpResponse implements ServerHttpResponse { |
|
|
|
|
|
|
|
|
|
|
|
private Mono<Void> applyBeforeCommit() { |
|
|
|
private Mono<Void> applyBeforeCommit() { |
|
|
|
Mono<Void> mono = Mono.empty(); |
|
|
|
Mono<Void> mono = Mono.empty(); |
|
|
|
if (this.state.compareAndSet(State.NEW, State.COMMITTING)) { |
|
|
|
if (this.state.compareAndSet(STATE_NEW, STATE_COMMITTING)) { |
|
|
|
for (Supplier<? extends Mono<Void>> action : this.beforeCommitActions) { |
|
|
|
for (Supplier<? extends Mono<Void>> action : this.beforeCommitActions) { |
|
|
|
mono = mono.after(() -> action.get()); |
|
|
|
mono = mono.after(action); |
|
|
|
} |
|
|
|
} |
|
|
|
mono = mono.otherwise(ex -> { |
|
|
|
mono = mono.otherwise(ex -> { |
|
|
|
// Ignore errors from beforeCommit actions
|
|
|
|
// Ignore errors from beforeCommit actions
|
|
|
|
return Mono.empty(); |
|
|
|
return Mono.empty(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
mono = mono.after(() -> { |
|
|
|
mono = mono.after(() -> { |
|
|
|
this.state.set(State.COMITTED); |
|
|
|
this.state.set(STATE_COMMITTED); |
|
|
|
writeHeaders(); |
|
|
|
writeHeaders(); |
|
|
|
writeCookies(); |
|
|
|
writeCookies(); |
|
|
|
return Mono.empty(); |
|
|
|
return Mono.empty(); |
|
|
|
@ -125,7 +143,4 @@ public abstract class AbstractServerHttpResponse implements ServerHttpResponse { |
|
|
|
return applyBeforeCommit(); |
|
|
|
return applyBeforeCommit(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private enum State { NEW, COMMITTING, COMITTED } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|