use of com.linkedin.r2.transport.http.client.TimeoutAsyncPoolHandle in project rest.li by linkedin.
the class Http2StreamCodec method write.
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (!(msg instanceof RequestWithCallback)) {
ctx.write(msg, promise);
return;
}
Request request = ((RequestWithCallback) msg).request();
Http2ConnectionEncoder encoder = encoder();
int streamId = connection().local().incrementAndGetNextStreamId();
final ChannelFuture headersFuture;
if (request instanceof StreamRequest) {
final StreamRequest streamRequest = (StreamRequest) request;
final Http2Headers http2Headers = NettyRequestAdapter.toHttp2Headers(streamRequest);
final BufferedReader bufferedReader = new BufferedReader(ctx, encoder, streamId, ((RequestWithCallback) msg).handle());
final OrderedEntityStreamReader reader = new OrderedEntityStreamReader(ctx, bufferedReader);
streamRequest.getEntityStream().setReader(reader);
LOG.debug("Sent HTTP/2 HEADERS frame, stream={}, end={}, headers={}, padding={}bytes", new Object[] { streamId, NOT_END_STREAM, http2Headers.size(), NO_PADDING });
headersFuture = encoder.writeHeaders(ctx, streamId, http2Headers, NO_PADDING, NOT_END_STREAM, promise);
headersFuture.addListener(future -> {
if (future.isSuccess()) {
reader.request(BufferedReader.MAX_BUFFERED_CHUNKS);
}
});
} else if (request instanceof RestRequest) {
final RestRequest restRequest = (RestRequest) request;
final Http2Headers headers = NettyRequestAdapter.toHttp2Headers(restRequest);
LOG.debug("Sent HTTP/2 HEADERS frame, stream={}, end={}, headers={}, padding={}bytes", new Object[] { streamId, NOT_END_STREAM, headers.size(), NO_PADDING });
headersFuture = encoder.writeHeaders(ctx, streamId, headers, NO_PADDING, NOT_END_STREAM, promise);
headersFuture.addListener(future -> {
if (future.isSuccess()) {
final ByteBuf data = Unpooled.wrappedBuffer(restRequest.getEntity().asByteBuffer());
LOG.debug("Sent HTTP/2 DATA frame, stream={}, end={}, data={}bytes, padding={}bytes", new Object[] { streamId, END_STREAM, data.readableBytes(), NO_PADDING });
encoder.writeData(ctx, streamId, data, NO_PADDING, END_STREAM, ctx.newPromise());
ctx.channel().flush();
}
});
} else {
// Release the handle to put the channel back to the pool
((RequestWithCallback) msg).handle().release();
throw new IllegalArgumentException("Request is neither StreamRequest or RestRequest");
}
final TransportCallback<?> callback = ((RequestWithCallback) msg).callback();
@SuppressWarnings("unchecked") final TimeoutAsyncPoolHandle<Channel> handle = (TimeoutAsyncPoolHandle<Channel>) ((RequestWithCallback) msg).handle();
headersFuture.addListener(future -> {
if (future.isSuccess()) {
// Sets TransportCallback as a stream property to be retrieved later
Http2PipelinePropertyUtil.set(ctx, connection(), streamId, Http2ClientPipelineInitializer.CALLBACK_ATTR_KEY, callback);
// Sets AsyncPoolHandle as a stream property to be retrieved later
Http2PipelinePropertyUtil.set(ctx, connection(), streamId, Http2ClientPipelineInitializer.CHANNEL_POOL_HANDLE_ATTR_KEY, handle);
// Sets a timeout task to reset stream
// Channel pool handle is also released at timeout
handle.addTimeoutTask(() -> {
LOG.debug("Reset stream upon timeout, stream={}", streamId);
resetStream(ctx, streamId, Http2Error.CANCEL.code(), ctx.newPromise());
ctx.flush();
});
} else {
// Invokes callback onResponse with the error thrown during write header or data
callback.onResponse(TransportResponseImpl.error(future.cause()));
// Releases the handle to put the channel back to the pool
handle.release();
// Resets the stream if a stream is created after we sent header
if (connection().stream(streamId) != null) {
LOG.debug("Reset stream upon timeout, stream={}", streamId);
resetStream(ctx, streamId, Http2Error.CANCEL.code(), ctx.newPromise());
ctx.flush();
}
}
});
}
use of com.linkedin.r2.transport.http.client.TimeoutAsyncPoolHandle in project rest.li by linkedin.
the class Http2UpgradeHandler method write.
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (!(msg instanceof RequestWithCallback)) {
ctx.write(msg, promise);
return;
}
_upgradePromise.addListener(f -> {
ChannelFuture future = (ChannelFuture) f;
if (future.isSuccess()) {
ctx.write(msg, promise);
} else {
// Releases the async pool handle
@SuppressWarnings("unchecked") TimeoutAsyncPoolHandle<?> handle = ((RequestWithCallback<?, ?, TimeoutAsyncPoolHandle<?>>) msg).handle();
handle.dispose();
// Invokes user specified callback with error
TransportCallback<?> callback = ((RequestWithCallback) msg).callback();
callback.onResponse(TransportResponseImpl.error(future.cause()));
}
});
}
use of com.linkedin.r2.transport.http.client.TimeoutAsyncPoolHandle in project rest.li by linkedin.
the class Http2AlpnHandler method write.
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (!(msg instanceof RequestWithCallback)) {
ctx.write(msg, promise);
return;
}
_alpnPromise.addListener(f -> {
ChannelFuture future = (ChannelFuture) f;
if (future.isSuccess()) {
ctx.write(msg, promise);
} else {
// Releases the async pool handle
@SuppressWarnings("unchecked") TimeoutAsyncPoolHandle<?> handle = ((RequestWithCallback<?, ?, TimeoutAsyncPoolHandle<?>>) msg).handle();
handle.dispose();
// Invokes user specified callback with error
TransportCallback<?> callback = ((RequestWithCallback) msg).callback();
callback.onResponse(TransportResponseImpl.error(future.cause()));
}
});
}
use of com.linkedin.r2.transport.http.client.TimeoutAsyncPoolHandle in project rest.li by linkedin.
the class Http2UpgradeHandler method write.
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (!(msg instanceof RequestWithCallback)) {
ctx.write(msg, promise);
return;
}
_upgradePromise.addListener(f -> {
ChannelFuture future = (ChannelFuture) f;
if (future.isSuccess()) {
ctx.write(msg, promise);
} else {
@SuppressWarnings("unchecked") TimeoutAsyncPoolHandle<?> handle = ((RequestWithCallback<?, ?, TimeoutAsyncPoolHandle<?>>) msg).handle();
handle.error().release();
TransportCallback<?> callback = ((RequestWithCallback) msg).callback();
callback.onResponse(TransportResponseImpl.error(future.cause()));
}
});
}
use of com.linkedin.r2.transport.http.client.TimeoutAsyncPoolHandle in project rest.li by linkedin.
the class Http2FrameListener method onHeadersRead.
@Override
public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, boolean endOfStream) throws Http2Exception {
LOG.debug("Received HTTP/2 HEADERS frame, stream={}, end={}, headers={}, padding={}bytes", new Object[] { streamId, endOfStream, headers.size(), padding });
// Ignores response for the upgrade request
if (streamId == Http2CodecUtil.HTTP_UPGRADE_STREAM_ID) {
return;
}
final StreamResponseBuilder builder = new StreamResponseBuilder();
// Process HTTP/2 pseudo headers
if (headers.status() != null) {
builder.setStatus(Integer.parseInt(headers.status().toString()));
}
if (headers.authority() != null) {
builder.addHeaderValue(HttpHeaderNames.HOST.toString(), headers.authority().toString());
}
// Process other HTTP headers
for (Map.Entry<CharSequence, CharSequence> header : headers) {
if (Http2Headers.PseudoHeaderName.isPseudoHeader(header.getKey())) {
// Do no set HTTP/2 pseudo headers to response
continue;
}
final String key = header.getKey().toString();
final String value = header.getValue().toString();
if (key.equalsIgnoreCase(HttpConstants.RESPONSE_COOKIE_HEADER_NAME)) {
builder.addCookie(value);
} else {
builder.unsafeAddHeaderValue(key, value);
}
}
// Gets async pool handle from stream properties
Http2Connection.PropertyKey handleKey = ctx.channel().attr(Http2ClientPipelineInitializer.CHANNEL_POOL_HANDLE_ATTR_KEY).get();
TimeoutAsyncPoolHandle<?> handle = _connection.stream(streamId).removeProperty(handleKey);
if (handle == null) {
_lifecycleManager.onError(ctx, Http2Exception.connectionError(Http2Error.PROTOCOL_ERROR, "No channel pool handle is associated with this stream", streamId));
return;
}
final StreamResponse response;
if (endOfStream) {
response = builder.build(EntityStreams.emptyStream());
ctx.fireChannelRead(handle);
} else {
// Associate an entity stream writer to the HTTP/2 stream
final TimeoutBufferedWriter writer = new TimeoutBufferedWriter(ctx, streamId, _maxContentLength, handle);
if (_connection.stream(streamId).setProperty(_writerKey, writer) != null) {
_lifecycleManager.onError(ctx, Http2Exception.connectionError(Http2Error.PROTOCOL_ERROR, "Another writer has already been associated with current stream ID", streamId));
return;
}
// Prepares StreamResponse for the channel pipeline
EntityStream entityStream = EntityStreams.newEntityStream(writer);
response = builder.build(entityStream);
}
// Gets callback from stream properties
Http2Connection.PropertyKey callbackKey = ctx.channel().attr(Http2ClientPipelineInitializer.CALLBACK_ATTR_KEY).get();
TransportCallback<?> callback = _connection.stream(streamId).removeProperty(callbackKey);
if (callback != null) {
ctx.fireChannelRead(new ResponseWithCallback<Response, TransportCallback<?>>(response, callback));
}
}
Aggregations