use of com.linkedin.r2.util.Timeout 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.util.Timeout 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.util.Timeout in project rest.li by linkedin.
the class TestChannelPoolStreamHandler method getChannel.
private static EmbeddedChannel getChannel() {
EmbeddedChannel ch = new EmbeddedChannel(new RAPResponseDecoder(1000), new RAPStreamResponseHandler(), new ChannelPoolStreamHandler());
ch.attr(RAPResponseDecoder.TIMEOUT_ATTR_KEY).set(new Timeout<None>(Executors.newSingleThreadScheduledExecutor(), 1000, TimeUnit.MILLISECONDS, None.none()));
ch.attr(RAPStreamResponseHandler.CALLBACK_ATTR_KEY).set(response -> {
StreamResponse streamResponse = response.getResponse();
streamResponse.getEntityStream().setReader(new DrainReader());
});
return ch;
}
use of com.linkedin.r2.util.Timeout in project rest.li by linkedin.
the class SyncIOHandler method loop.
public void loop() throws ServletException, IOException {
final long startTime = System.currentTimeMillis();
byte[] buf = new byte[DEFAULT_DATA_CHUNK_SIZE];
while (shouldContinue() && !_forceExit) {
Event event;
try {
long timeSpent = System.currentTimeMillis() - startTime;
long maxWaitTime = timeSpent < _timeout ? _timeout - timeSpent : 0;
event = _eventQueue.poll(maxWaitTime, TimeUnit.MILLISECONDS);
if (event == null) {
throw new TimeoutException("Timeout after " + _timeout + " milliseconds.");
}
} catch (Exception ex) {
throw new ServletException(ex);
}
switch(event.getEventType()) {
case ResponseDataAvailable:
{
ByteString data = (ByteString) event.getData();
data.write(_os);
_rh.request(1);
break;
}
case WriteRequestPossible:
{
while (_wh.remaining() > 0) {
int actualLen = _is.read(buf);
if (actualLen < 0) {
_wh.done();
_requestReadFinished = true;
break;
}
_wh.write(ByteString.copy(buf, 0, actualLen));
}
break;
}
case FullResponseReceived:
{
_os.close();
_responseWriteFinished = true;
break;
}
case ResponseDataError:
{
_os.close();
_responseWriteFinished = true;
break;
}
case WriteRequestAborted:
{
if (event.getData() instanceof AbortedException) {
// reader cancels, we'll drain the stream on behalf of reader
// we don't directly drain it here because we'd like to give other events
// some opportunities to be executed; e.g. return an error response
_eventQueue.add(Event.DrainRequestEvent);
} else {
// TODO: do we want to be smarter and return server error response?
throw new ServletException((Throwable) event.getData());
}
break;
}
case DrainRequest:
{
for (int i = 0; i < 10; i++) {
int actualLen = _is.read(buf);
if (actualLen < 0) {
_requestReadFinished = true;
break;
}
}
if (!_requestReadFinished) {
// add self back to event queue and give others a chance to run
_eventQueue.add(Event.DrainRequestEvent);
}
break;
}
case ForceExit:
{
_forceExit = true;
break;
}
default:
throw new IllegalStateException("Unknown event type:" + event.getEventType());
}
}
}
use of com.linkedin.r2.util.Timeout in project rest.li by linkedin.
the class TestClientShutdown method testShutdown.
@Test(dataProvider = "configs")
public void testShutdown(boolean clientROS, boolean serverROS, String protocolVersion) throws Exception {
_clientFactory = new HttpClientFactory();
Map<String, String> clientProperties = new HashMap<String, String>();
// very long shutdown timeout
clientProperties.put(HttpClientFactory.HTTP_SHUTDOWN_TIMEOUT, "60000");
clientProperties.put(HttpClientFactory.HTTP_PROTOCOL_VERSION, protocolVersion);
_client = new TransportClientAdapter(_clientFactory.getClient(clientProperties), clientROS);
TransportDispatcher dispatcher = new TransportDispatcherBuilder().addRestHandler(ECHO_URI, new EchoHandler()).build();
_server = new HttpServerFactory(HttpJettyServer.ServletType.RAP).createH2cServer(PORT, dispatcher, serverROS);
try {
_server.start();
RestRequestBuilder builder = new RestRequestBuilder(URI.create("http://localhost:" + PORT + ECHO_URI));
byte[] content = new byte[100];
builder.setEntity(content);
Future<RestResponse> future = _client.restRequest(builder.build());
RestResponse response = future.get(30, TimeUnit.SECONDS);
Assert.assertEquals(response.getEntity().copyBytes(), content);
final FutureCallback<None> clientShutdownCallback = new FutureCallback<None>();
_client.shutdown(clientShutdownCallback);
// we should catch those clients that do not shutdown properly in 5 seconds
clientShutdownCallback.get(5000, TimeUnit.MILLISECONDS);
final FutureCallback<None> factoryShutdownCallback = new FutureCallback<None>();
_clientFactory.shutdown(factoryShutdownCallback);
factoryShutdownCallback.get();
} finally {
if (_server != null) {
_server.stop();
_server.waitForStop();
}
}
}
Aggregations