use of io.servicetalk.concurrent.SingleSource.Subscriber in project servicetalk by apple.
the class ServiceTalkThreadContextMapTest method testAsyncExecution.
@Test
void testAsyncExecution() throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
MDC.clear();
MDC.put("a", "1");
MDC.put("b", "2");
// human inspection as sanity check
logger.info("expected a=1 b=2");
Thread original = Thread.currentThread();
Single<String> single = new Single<String>() {
@Override
protected void handleSubscribe(Subscriber<? super String> singleSubscriber) {
executor.execute(() -> {
singleSubscriber.onSubscribe(IGNORE_CANCEL);
singleSubscriber.onSuccess("1");
});
}
}.map(v -> {
assertNotEquals(Thread.currentThread(), original);
assertEquals("1", MDC.get("a"));
assertEquals("2", MDC.get("b"));
MDC.put("b", "22");
return v;
}).beforeFinally(() -> {
// human inspection as sanity check
logger.info("expected a=1 b=22");
assertEquals("1", MDC.get("a"));
assertEquals("22", MDC.get("b"));
});
single.toFuture().get();
} finally {
executor.shutdown();
}
}
use of io.servicetalk.concurrent.SingleSource.Subscriber in project servicetalk by apple.
the class SingleProcessorTest method multiThreadedAddAlwaysTerminates.
private static void multiThreadedAddAlwaysTerminates(@Nullable String value, @Nullable Throwable cause) throws Exception {
final int subscriberCount = 1000;
CyclicBarrier barrier = new CyclicBarrier(subscriberCount + 1);
List<Single<Subscriber<String>>> subscriberSingles = new ArrayList<>(subscriberCount);
SingleProcessor<String> processor = new SingleProcessor<>();
for (int i = 0; i < subscriberCount; ++i) {
subscriberSingles.add(EXECUTOR_RULE.executor().submit(() -> {
@SuppressWarnings("unchecked") Subscriber<String> subscriber = mock(Subscriber.class);
barrier.await();
processor.subscribe(subscriber);
return subscriber;
}));
}
Future<Collection<Subscriber<String>>> future = collectUnordered(subscriberSingles, subscriberCount).toFuture();
barrier.await();
if (cause != null) {
processor.onError(cause);
Collection<Subscriber<String>> subscribers = future.get();
for (Subscriber<String> s : subscribers) {
verify(s).onError(cause);
}
} else {
processor.onSuccess(value);
Collection<Subscriber<String>> subscribers = future.get();
for (Subscriber<String> s : subscribers) {
verify(s).onSuccess(value);
}
}
}
use of io.servicetalk.concurrent.SingleSource.Subscriber in project servicetalk by apple.
the class BlockingStreamingToStreamingService method handle.
@Override
public Single<StreamingHttpResponse> handle(final HttpServiceContext ctx, final StreamingHttpRequest request, final StreamingHttpResponseFactory responseFactory) {
return new Single<StreamingHttpResponse>() {
@Override
protected void handleSubscribe(final Subscriber<? super StreamingHttpResponse> subscriber) {
final ThreadInterruptingCancellable tiCancellable = new ThreadInterruptingCancellable(currentThread());
try {
subscriber.onSubscribe(tiCancellable);
} catch (Throwable cause) {
handleExceptionFromOnSubscribe(subscriber, cause);
return;
}
// This exists to help users with error propagation. If the user closes the payloadWriter and they throw
// (e.g. try-with-resources) this processor is merged with the payloadWriter Publisher so the error will
// still be propagated.
final Processor exceptionProcessor = newCompletableProcessor();
final BufferHttpPayloadWriter payloadWriter = new BufferHttpPayloadWriter(ctx.headersFactory().newTrailers());
DefaultBlockingStreamingHttpServerResponse response = null;
try {
final Consumer<DefaultHttpResponseMetaData> sendMeta = (metaData) -> {
final DefaultStreamingHttpResponse result;
try {
// transfer-encoding takes precedence over content-length.
// > When a message does not have a Transfer-Encoding header field, a
// Content-Length header field can provide the anticipated size.
// https://tools.ietf.org/html/rfc7230#section-3.3.2
final HttpHeaders headers = metaData.headers();
final HttpProtocolVersion version = metaData.version();
boolean addTrailers = version.major() > 1 || isTransferEncodingChunked(headers);
if (!addTrailers && h1TrailersSupported(version) && !hasContentLength(headers) && // breaks our HttpResponseDecoder
!HEAD.equals(request.method())) {
// this is likely not supported in http/1.0 and it is possible that a response has
// neither header and the connection close indicates the end of the response.
// https://tools.ietf.org/html/rfc7230#section-3.3.3
headers.add(TRANSFER_ENCODING, CHUNKED);
addTrailers = true;
}
Publisher<Object> messageBody = fromSource(exceptionProcessor).merge(payloadWriter.connect());
if (addTrailers) {
messageBody = messageBody.concat(succeeded(payloadWriter.trailers()));
}
messageBody = messageBody.beforeSubscription(() -> new Subscription() {
@Override
public void request(final long n) {
}
@Override
public void cancel() {
tiCancellable.cancel();
}
});
result = new DefaultStreamingHttpResponse(metaData.status(), version, headers, metaData.context0(), ctx.executionContext().bufferAllocator(), messageBody, forTransportReceive(false, version, headers), ctx.headersFactory());
} catch (Throwable t) {
subscriber.onError(t);
throw t;
}
subscriber.onSuccess(result);
};
response = new DefaultBlockingStreamingHttpServerResponse(OK, request.version(), ctx.headersFactory().newHeaders(), payloadWriter, ctx.executionContext().bufferAllocator(), sendMeta);
original.handle(ctx, request.toBlockingStreamingRequest(), response);
// The user code has returned successfully, complete the processor so the response stream can
// complete. If the user handles the request asynchronously (e.g. on another thread) they are
// responsible for closing the payloadWriter.
exceptionProcessor.onComplete();
} catch (Throwable cause) {
tiCancellable.setDone(cause);
if (response == null || response.markMetaSent()) {
safeOnError(subscriber, cause);
} else {
try {
exceptionProcessor.onError(cause);
} finally {
safeClose(payloadWriter, cause);
}
}
return;
}
tiCancellable.setDone();
}
};
}
use of io.servicetalk.concurrent.SingleSource.Subscriber in project servicetalk by apple.
the class ResponseTimeoutTest method setUp.
private void setUp(Duration clientTimeout, Duration serverTimeout) throws Exception {
ctx = forAddress(localAddress(0)).appendServiceFilter(new TimeoutHttpServiceFilter((req, ts) -> serverTimeout, true)).listenAndAwait((__, ___, factory) -> {
Single<HttpResponse> resp = Single.never();
serverResponses.add(resp);
return resp;
});
client = forSingleAddress(serverHostAndPort(ctx)).appendClientFilter(client -> new StreamingHttpClientFilter(client) {
@Override
protected Single<StreamingHttpResponse> request(final StreamingHttpRequester delegate, final StreamingHttpRequest request) {
return Single.succeeded(null).afterOnSubscribe(delayedClientCancels::add).concat(delegate().request(request).liftSync(target -> new Subscriber<StreamingHttpResponse>() {
@Override
public void onSubscribe(final Cancellable cancellable) {
target.onSubscribe(() -> {
delayedClientCancels.add(cancellable);
cancellable.cancel();
});
}
@Override
public void onSuccess(final StreamingHttpResponse result) {
ClientTerminationSignal signal = OK.equals(result.status()) ? new ClientTerminationSignal(target, result) : new ClientTerminationSignal(target, new HttpResponseStatusException(result.status()));
delayedClientTermination.add(signal);
target.onSuccess(result);
}
@Override
public void onError(final Throwable t) {
delayedClientTermination.add(new ClientTerminationSignal(target, t));
target.onError(t);
}
})).filter(Objects::nonNull).firstOrError().map(thing -> (StreamingHttpResponse) thing);
}
}).appendConnectionFactoryFilter(original -> new CountingConnectionFactory(original, connectionCount)).appendClientFilter(new TimeoutHttpRequesterFilter((req, ts) -> clientTimeout, true)).build();
}
use of io.servicetalk.concurrent.SingleSource.Subscriber in project servicetalk by apple.
the class DefaultJerseyStreamingHttpRouter method handle.
@Override
public Single<StreamingHttpResponse> handle(final HttpServiceContext serviceCtx, final StreamingHttpRequest req, final StreamingHttpResponseFactory factory) {
return new SubscribableSingle<StreamingHttpResponse>() {
@Override
protected void handleSubscribe(final Subscriber<? super StreamingHttpResponse> subscriber) {
final DelayedCancellable delayedCancellable = new DelayedCancellable();
DuplicateTerminateDetectorSingle<? super StreamingHttpResponse> dupSub = new DuplicateTerminateDetectorSingle<>(subscriber);
try {
dupSub.onSubscribe(delayedCancellable);
} catch (Throwable cause) {
handleExceptionFromOnSubscribe(dupSub, cause);
return;
}
try {
handle0(serviceCtx, req, factory, dupSub, delayedCancellable);
} catch (final Throwable t) {
safeOnError(dupSub, t);
}
}
};
}
Aggregations