use of com.linkedin.r2.transport.http.client.TimeoutTransportCallback in project rest.li by linkedin.
the class HttpNettyClient method writeRequest.
private void writeRequest(RestRequest request, RequestContext requestContext, Map<String, String> wireAttrs, final TimeoutTransportCallback<RestResponse> callback) {
State state = _state.get();
if (state != State.RUNNING) {
errorResponse(callback, new IllegalStateException("Client is " + state));
return;
}
URI uri = request.getURI();
String scheme = uri.getScheme();
if (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)) {
errorResponse(callback, new IllegalArgumentException("Unknown scheme: " + scheme + " (only http/https is supported)"));
return;
}
String host = uri.getHost();
int port = uri.getPort();
if (port == -1) {
port = "http".equalsIgnoreCase(scheme) ? HTTP_DEFAULT_PORT : HTTPS_DEFAULT_PORT;
}
final RestRequest newRequest = new RestRequestBuilder(request).overwriteHeaders(WireAttributeHelper.toWireAttributes(wireAttrs)).build();
final SocketAddress address;
try {
// TODO investigate DNS resolution and timing
InetAddress inetAddress = InetAddress.getByName(host);
address = new InetSocketAddress(inetAddress, port);
requestContext.putLocalAttr(R2Constants.REMOTE_SERVER_ADDR, inetAddress.getHostAddress());
} catch (UnknownHostException e) {
errorResponse(callback, e);
return;
}
requestContext.putLocalAttr(R2Constants.HTTP_PROTOCOL_VERSION, HttpProtocolVersion.HTTP_1_1);
final AsyncPool<Channel> pool;
try {
pool = _channelPoolManager.getPoolForAddress(address);
} catch (IllegalStateException e) {
errorResponse(callback, e);
return;
}
final Cancellable pendingGet = pool.get(new Callback<Channel>() {
@Override
public void onSuccess(final Channel channel) {
// This handler ensures the channel is returned to the pool at the end of the
// Netty pipeline.
channel.attr(ChannelPoolHandler.CHANNEL_POOL_ATTR_KEY).set(pool);
callback.addTimeoutTask(new Runnable() {
@Override
public void run() {
AsyncPool<Channel> pool = channel.attr(ChannelPoolHandler.CHANNEL_POOL_ATTR_KEY).getAndRemove();
if (pool != null) {
pool.dispose(channel);
}
}
});
// This handler invokes the callback with the response once it arrives.
channel.attr(RAPResponseHandler.CALLBACK_ATTR_KEY).set(callback);
final State state = _state.get();
if (state == State.REQUESTS_STOPPING || state == State.SHUTDOWN) {
// In this case, we acquired a channel from the pool as request processing is halting.
// The shutdown task might not timeout this callback, since it may already have scanned
// all the channels for pending requests before we set the callback as the channel
// attachment. The TimeoutTransportCallback ensures the user callback in never
// invoked more than once, so it is safe to invoke it unconditionally.
errorResponse(callback, new TimeoutException("Operation did not complete before shutdown"));
return;
}
// here we want the exception in outbound operations to be passed back through pipeline so that
// the user callback would be invoked with the exception and the channel can be put back into the pool
channel.writeAndFlush(newRequest).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
}
@Override
public void onError(Throwable e) {
errorResponse(callback, e);
}
});
if (pendingGet != null) {
callback.addTimeoutTask(new Runnable() {
@Override
public void run() {
pendingGet.cancel();
}
});
}
}
use of com.linkedin.r2.transport.http.client.TimeoutTransportCallback in project rest.li by linkedin.
the class HttpNettyStreamClient method doWriteRequest.
@Override
protected void doWriteRequest(Request request, RequestContext context, SocketAddress address, TimeoutTransportCallback<StreamResponse> callback) {
final AsyncPool<Channel> pool;
try {
pool = _channelPoolManager.getPoolForAddress(address);
} catch (IllegalStateException e) {
errorResponse(callback, e);
return;
}
context.putLocalAttr(R2Constants.HTTP_PROTOCOL_VERSION, HttpProtocolVersion.HTTP_1_1);
Callback<Channel> getCallback = new ChannelPoolGetCallback(pool, request, callback);
final Cancellable pendingGet = pool.get(getCallback);
if (pendingGet != null) {
callback.addTimeoutTask(() -> pendingGet.cancel());
}
}
use of com.linkedin.r2.transport.http.client.TimeoutTransportCallback in project rest.li by linkedin.
the class AbstractNettyStreamClient method writeRequestWithTimeout.
private void writeRequestWithTimeout(final StreamRequest request, RequestContext requestContext, Map<String, String> wireAttrs, TransportCallback<StreamResponse> callback) {
StreamExecutionCallback executionCallback = new StreamExecutionCallback(_callbackExecutors, callback);
// By wrapping the callback in a Timeout callback before passing it along, we deny the rest
// of the code access to the unwrapped callback. This ensures two things:
// 1. The user callback will always be invoked, since the Timeout will eventually expire
// 2. The user callback is never invoked more than once
final TimeoutTransportCallback<StreamResponse> timeoutCallback = new TimeoutTransportCallback<StreamResponse>(_scheduler, _requestTimeout, TimeUnit.MILLISECONDS, executionCallback, _requestTimeoutMessage);
final StreamRequest requestWithWireAttrHeaders = request.builder().overwriteHeaders(WireAttributeHelper.toWireAttributes(wireAttrs)).build(request.getEntityStream());
// talk to legacy R2 servers without problem if they're just using restRequest (full request).
if (isFullRequest(requestContext)) {
Messages.toRestRequest(requestWithWireAttrHeaders, new Callback<RestRequest>() {
@Override
public void onError(Throwable e) {
errorResponse(timeoutCallback, e);
}
@Override
public void onSuccess(RestRequest restRequest) {
writeRequest(restRequest, requestContext, timeoutCallback);
}
});
} else {
writeRequest(requestWithWireAttrHeaders, requestContext, timeoutCallback);
}
}
use of com.linkedin.r2.transport.http.client.TimeoutTransportCallback in project rest.li by linkedin.
the class TestHttp2ProtocolUpgradeHandler method testChannelCloseBeforeUpgrade.
@Test(timeOut = 10000)
@SuppressWarnings("unchecked")
public void testChannelCloseBeforeUpgrade() throws Exception {
Http2UpgradeHandler handler = new Http2UpgradeHandler();
EmbeddedChannel channel = new EmbeddedChannel(handler);
// Reads the upgrade request from the outbound buffer to ensure nothing in the buffer
Assert.assertEquals(channel.outboundMessages().size(), 1);
Assert.assertNotNull(channel.readOutbound());
Assert.assertTrue(channel.outboundMessages().isEmpty());
RequestWithCallback request = Mockito.mock(RequestWithCallback.class);
TimeoutAsyncPoolHandle handle = Mockito.mock(TimeoutAsyncPoolHandle.class);
TimeoutTransportCallback callback = Mockito.mock(TimeoutTransportCallback.class);
Mockito.when(request.handle()).thenReturn(handle);
Mockito.when(request.callback()).thenReturn(callback);
// Write should not succeed before upgrade completes
Assert.assertFalse(channel.writeOutbound(request));
Assert.assertFalse(channel.finish());
// Synchronously waiting for channel to close
channel.close().sync();
Mockito.verify(request).handle();
Mockito.verify(request).callback();
Mockito.verify(handle).dispose();
Mockito.verify(callback).onResponse(Mockito.any(TransportResponse.class));
}
use of com.linkedin.r2.transport.http.client.TimeoutTransportCallback in project rest.li by linkedin.
the class AbstractNettyClient method writeRequest.
/**
* This method calls the user defined method {@link AbstractNettyClient#doWriteRequest(Request, RequestContext, SocketAddress, Map, TimeoutTransportCallback, long)}
* after having checked that the client is still running and resolved the DNS
*/
private void writeRequest(Req request, RequestContext requestContext, Map<String, String> wireAttrs, TransportCallback<Res> callback) {
// Decorates callback
TransportCallback<Res> executionCallback = getExecutionCallback(callback);
TransportCallback<Res> shutdownAwareCallback = getShutdownAwareCallback(executionCallback);
// Resolves request timeout
long requestTimeout = HttpNettyClient.resolveRequestTimeout(requestContext, _requestTimeout);
// By wrapping the callback in a Timeout callback before passing it along, we deny the rest
// of the code access to the unwrapped callback. This ensures two things:
// 1. The user callback will always be invoked, since the Timeout will eventually expire
// 2. The user callback is never invoked more than once
TimeoutTransportCallback<Res> timeoutCallback = new TimeoutTransportCallback<>(_scheduler, requestTimeout, TimeUnit.MILLISECONDS, shutdownAwareCallback, "Exceeded request timeout of " + requestTimeout + "ms");
// check lifecycle
NettyClientState state = _state.get();
if (state != NettyClientState.RUNNING) {
errorResponse(callback, new IllegalStateException("Client is " + state));
return;
}
// resolve address
final SocketAddress address;
try {
TimingContextUtil.markTiming(requestContext, TIMING_KEY);
address = HttpNettyClient.resolveAddress(request, requestContext);
TimingContextUtil.markTiming(requestContext, TIMING_KEY);
} catch (UnknownHostException | UnknownSchemeException e) {
errorResponse(callback, e);
return;
}
doWriteRequest(request, requestContext, address, wireAttrs, timeoutCallback, requestTimeout);
}
Aggregations