|
|
|
@ -35,26 +35,25 @@ import org.springframework.core.io.buffer.DataBuffer; |
|
|
|
* Servlet 3.1 and Undertow support. |
|
|
|
* Servlet 3.1 and Undertow support. |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Arjen Poutsma |
|
|
|
* @author Arjen Poutsma |
|
|
|
|
|
|
|
* @author Violeta Georgieva |
|
|
|
* @since 5.0 |
|
|
|
* @since 5.0 |
|
|
|
* @see ServletServerHttpRequest |
|
|
|
* @see ServletServerHttpRequest |
|
|
|
* @see UndertowHttpHandlerAdapter |
|
|
|
* @see UndertowHttpHandlerAdapter |
|
|
|
* @see ServerHttpResponse#writeAndFlushWith(Publisher) |
|
|
|
* @see ServerHttpResponse#writeAndFlushWith(Publisher) |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
abstract class AbstractResponseBodyFlushProcessor |
|
|
|
abstract class AbstractResponseBodyFlushProcessor implements Processor<Publisher<DataBuffer>, Void> { |
|
|
|
implements Processor<Publisher<DataBuffer>, Void> { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected final Log logger = LogFactory.getLog(getClass()); |
|
|
|
protected final Log logger = LogFactory.getLog(getClass()); |
|
|
|
|
|
|
|
|
|
|
|
private final ResponseBodyWriteResultPublisher publisherDelegate = |
|
|
|
private final ResponseBodyWriteResultPublisher resultPublisher = new ResponseBodyWriteResultPublisher(); |
|
|
|
new ResponseBodyWriteResultPublisher(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final AtomicReference<State> state = |
|
|
|
private final AtomicReference<State> state = new AtomicReference<>(State.UNSUBSCRIBED); |
|
|
|
new AtomicReference<>(State.UNSUBSCRIBED); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private volatile boolean subscriberCompleted; |
|
|
|
private volatile boolean subscriberCompleted; |
|
|
|
|
|
|
|
|
|
|
|
private Subscription subscription; |
|
|
|
private Subscription subscription; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Subscriber
|
|
|
|
// Subscriber
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
@ -89,13 +88,15 @@ abstract class AbstractResponseBodyFlushProcessor |
|
|
|
this.state.get().onComplete(this); |
|
|
|
this.state.get().onComplete(this); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Publisher
|
|
|
|
// Publisher
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public final void subscribe(Subscriber<? super Void> subscriber) { |
|
|
|
public final void subscribe(Subscriber<? super Void> subscriber) { |
|
|
|
this.publisherDelegate.subscribe(subscriber); |
|
|
|
this.resultPublisher.subscribe(subscriber); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Creates a new processor for subscribing to a body chunk. |
|
|
|
* Creates a new processor for subscribing to a body chunk. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@ -106,6 +107,11 @@ abstract class AbstractResponseBodyFlushProcessor |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected abstract void flush() throws IOException; |
|
|
|
protected abstract void flush() throws IOException; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean changeState(State oldState, State newState) { |
|
|
|
|
|
|
|
return this.state.compareAndSet(oldState, newState); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void writeComplete() { |
|
|
|
private void writeComplete() { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
logger.trace(this.state + " writeComplete"); |
|
|
|
logger.trace(this.state + " writeComplete"); |
|
|
|
@ -114,17 +120,19 @@ abstract class AbstractResponseBodyFlushProcessor |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean changeState(State oldState, State newState) { |
|
|
|
private void cancel() { |
|
|
|
return this.state.compareAndSet(oldState, newState); |
|
|
|
this.subscription.cancel(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private enum State { |
|
|
|
private enum State { |
|
|
|
|
|
|
|
|
|
|
|
UNSUBSCRIBED { |
|
|
|
UNSUBSCRIBED { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onSubscribe(AbstractResponseBodyFlushProcessor processor, |
|
|
|
public void onSubscribe(AbstractResponseBodyFlushProcessor processor, Subscription subscription) { |
|
|
|
Subscription subscription) { |
|
|
|
|
|
|
|
Objects.requireNonNull(subscription, "Subscription cannot be null"); |
|
|
|
Objects.requireNonNull(subscription, "Subscription cannot be null"); |
|
|
|
if (processor.changeState(this, SUBSCRIBED)) { |
|
|
|
if (processor.changeState(this, REQUESTED)) { |
|
|
|
processor.subscription = subscription; |
|
|
|
processor.subscription = subscription; |
|
|
|
subscription.request(1); |
|
|
|
subscription.request(1); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -132,86 +140,97 @@ abstract class AbstractResponseBodyFlushProcessor |
|
|
|
super.onSubscribe(processor, subscription); |
|
|
|
super.onSubscribe(processor, subscription); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, SUBSCRIBED { |
|
|
|
}, |
|
|
|
|
|
|
|
REQUESTED { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onNext(AbstractResponseBodyFlushProcessor processor, |
|
|
|
public void onNext(AbstractResponseBodyFlushProcessor processor, Publisher<DataBuffer> chunk) { |
|
|
|
Publisher<DataBuffer> chunk) { |
|
|
|
if (processor.changeState(this, RECEIVED)) { |
|
|
|
Processor<DataBuffer, Void> chunkProcessor = |
|
|
|
Processor<DataBuffer, Void> chunkProcessor = processor.createBodyProcessor(); |
|
|
|
processor.createBodyProcessor(); |
|
|
|
chunk.subscribe(chunkProcessor); |
|
|
|
chunk.subscribe(chunkProcessor); |
|
|
|
chunkProcessor.subscribe(new WriteSubscriber(processor)); |
|
|
|
chunkProcessor.subscribe(new WriteSubscriber(processor)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
void onComplete(AbstractResponseBodyFlushProcessor processor) { |
|
|
|
public void onComplete(AbstractResponseBodyFlushProcessor processor) { |
|
|
|
processor.subscriberCompleted = true; |
|
|
|
if (processor.changeState(this, COMPLETED)) { |
|
|
|
|
|
|
|
processor.resultPublisher.publishComplete(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
RECEIVED { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void writeComplete(AbstractResponseBodyFlushProcessor processor) { |
|
|
|
public void writeComplete(AbstractResponseBodyFlushProcessor processor) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
processor.flush(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (IOException ex) { |
|
|
|
|
|
|
|
processor.cancel(); |
|
|
|
|
|
|
|
processor.onError(ex); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (processor.subscriberCompleted) { |
|
|
|
if (processor.subscriberCompleted) { |
|
|
|
if (processor.changeState(this, COMPLETED)) { |
|
|
|
if (processor.changeState(this, COMPLETED)) { |
|
|
|
processor.publisherDelegate.publishComplete(); |
|
|
|
processor.resultPublisher.publishComplete(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
try { |
|
|
|
if (processor.changeState(this, REQUESTED)) { |
|
|
|
processor.flush(); |
|
|
|
processor.subscription.request(1); |
|
|
|
} |
|
|
|
|
|
|
|
catch (IOException ex) { |
|
|
|
|
|
|
|
processor.onError(ex); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
processor.subscription.request(1); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, COMPLETED { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void onNext(AbstractResponseBodyFlushProcessor processor, |
|
|
|
|
|
|
|
Publisher<DataBuffer> publisher) { |
|
|
|
|
|
|
|
// ignore
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void onComplete(AbstractResponseBodyFlushProcessor processor) { |
|
|
|
|
|
|
|
processor.subscriberCompleted = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
COMPLETED { |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
void onError(AbstractResponseBodyFlushProcessor processor, Throwable t) { |
|
|
|
public void onNext(AbstractResponseBodyFlushProcessor processor, |
|
|
|
|
|
|
|
Publisher<DataBuffer> publisher) { |
|
|
|
// ignore
|
|
|
|
// ignore
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
void onComplete(AbstractResponseBodyFlushProcessor processor) { |
|
|
|
public void onError(AbstractResponseBodyFlushProcessor processor, Throwable t) { |
|
|
|
// ignore
|
|
|
|
// ignore
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void writeComplete(AbstractResponseBodyFlushProcessor processor) { |
|
|
|
public void onComplete(AbstractResponseBodyFlushProcessor processor) { |
|
|
|
// ignore
|
|
|
|
// ignore
|
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
public void onSubscribe(AbstractResponseBodyFlushProcessor processor, |
|
|
|
public void onSubscribe(AbstractResponseBodyFlushProcessor processor, Subscription subscription) { |
|
|
|
Subscription subscription) { |
|
|
|
|
|
|
|
subscription.cancel(); |
|
|
|
subscription.cancel(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void onNext(AbstractResponseBodyFlushProcessor processor, |
|
|
|
public void onNext(AbstractResponseBodyFlushProcessor processor, Publisher<DataBuffer> publisher) { |
|
|
|
Publisher<DataBuffer> publisher) { |
|
|
|
|
|
|
|
throw new IllegalStateException(toString()); |
|
|
|
throw new IllegalStateException(toString()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void onError(AbstractResponseBodyFlushProcessor processor, Throwable t) { |
|
|
|
public void onError(AbstractResponseBodyFlushProcessor processor, Throwable ex) { |
|
|
|
if (processor.changeState(this, COMPLETED)) { |
|
|
|
if (processor.changeState(this, COMPLETED)) { |
|
|
|
processor.publisherDelegate.publishError(t); |
|
|
|
processor.resultPublisher.publishError(ex); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void onComplete(AbstractResponseBodyFlushProcessor processor) { |
|
|
|
public void onComplete(AbstractResponseBodyFlushProcessor processor) { |
|
|
|
throw new IllegalStateException(toString()); |
|
|
|
throw new IllegalStateException(toString()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void writeComplete(AbstractResponseBodyFlushProcessor processor) { |
|
|
|
public void writeComplete(AbstractResponseBodyFlushProcessor processor) { |
|
|
|
throw new IllegalStateException(toString()); |
|
|
|
// ignore
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static class WriteSubscriber implements Subscriber<Void> { |
|
|
|
private static class WriteSubscriber implements Subscriber<Void> { |
|
|
|
|
|
|
|
|
|
|
|
private final AbstractResponseBodyFlushProcessor processor; |
|
|
|
private final AbstractResponseBodyFlushProcessor processor; |
|
|
|
@ -221,8 +240,8 @@ abstract class AbstractResponseBodyFlushProcessor |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onSubscribe(Subscription s) { |
|
|
|
public void onSubscribe(Subscription subscription) { |
|
|
|
s.request(Long.MAX_VALUE); |
|
|
|
subscription.request(Long.MAX_VALUE); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
@ -230,13 +249,14 @@ abstract class AbstractResponseBodyFlushProcessor |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onError(Throwable t) { |
|
|
|
public void onError(Throwable ex) { |
|
|
|
processor.onError(t); |
|
|
|
this.processor.cancel(); |
|
|
|
|
|
|
|
this.processor.onError(ex); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onComplete() { |
|
|
|
public void onComplete() { |
|
|
|
processor.writeComplete(); |
|
|
|
this.processor.writeComplete(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|