use of com.linkedin.r2.message.Response in project rest.li by linkedin.
the class Http2StreamResponseHandler method channelRead.
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ResponseWithCallback) {
@SuppressWarnings("unchecked") ResponseWithCallback<StreamResponse, TransportCallback<StreamResponse>> responseWithCallback = (ResponseWithCallback<StreamResponse, TransportCallback<StreamResponse>>) msg;
StreamResponse response = responseWithCallback.response();
TransportCallback<StreamResponse> callback = responseWithCallback.callback();
Map<String, String> headers = new HashMap<>(response.getHeaders());
Map<String, String> wireAttrs = new HashMap<>(WireAttributeHelper.removeWireAttributes(headers));
StreamResponse newResponse = new StreamResponseBuilder(response).unsafeSetHeaders(headers).build(response.getEntityStream());
LOG.debug("{}: handling a response", ctx.channel().remoteAddress());
callback.onResponse(TransportResponseImpl.success(newResponse, wireAttrs));
}
ctx.fireChannelRead(msg);
}
use of com.linkedin.r2.message.Response in project rest.li by linkedin.
the class HttpClientFactory method getClient.
/**
* Create a new {@link TransportClient} with the specified properties,
* {@link SSLContext} and {@link SSLParameters}
*
* @param properties map of properties for the {@link TransportClient}
* @param sslContext {@link SSLContext} to be used for requests over SSL/TLS.
* @param sslParameters {@link SSLParameters} to configure secure connections.
* @return an appropriate {@link TransportClient} instance, as specified by the properties.
*/
private TransportClient getClient(Map<String, ? extends Object> properties, SSLContext sslContext, SSLParameters sslParameters) {
LOG.info("Getting a client with configuration {} and SSLContext {}", properties, sslContext);
TransportClient client = getRawClient(properties, sslContext, sslParameters);
List<String> httpRequestServerSupportedEncodings = ConfigValueExtractor.buildList(properties.remove(HTTP_REQUEST_CONTENT_ENCODINGS), LIST_SEPARATOR);
// In the old model, responses were compressed according to the type of method, so clients would send
// the Accept-Encoding header if the method was in HTTP_RESPONSE_COMPRESSION_OPERATIONS.
// In the new model, responses are compressed according to its size, so clients send the Accept-Encoding header
// if the server enabled response compression by setting HTTP_USE_RESPONSE_COMPRESSION to true.
// Until all servers migrate to the new model, clients will understand both models,
// and send the Accept-Encoding header if either the old or the new criterion is satisfied.
List<String> httpResponseCompressionOperations = ConfigValueExtractor.buildList(properties.remove(HTTP_RESPONSE_COMPRESSION_OPERATIONS), LIST_SEPARATOR);
String useResponseCompressionProperty = (String) properties.get(HTTP_USE_RESPONSE_COMPRESSION);
if (useResponseCompressionProperty != null && Boolean.parseBoolean(useResponseCompressionProperty)) {
httpResponseCompressionOperations.add(ClientCompressionHelper.COMPRESS_ALL_RESPONSES_INDICATOR);
}
FilterChain filters = _filters;
if (_useClientCompression) {
List<String> responseEncodings = null;
if (properties.containsKey(HTTP_RESPONSE_CONTENT_ENCODINGS)) {
responseEncodings = ConfigValueExtractor.buildList(properties.remove(HTTP_RESPONSE_CONTENT_ENCODINGS), LIST_SEPARATOR);
}
String httpServiceName = (String) properties.get(HTTP_SERVICE_NAME);
EncodingType restRequestContentEncoding = getRestRequestContentEncoding(httpRequestServerSupportedEncodings);
StreamEncodingType streamRequestContentEncoding = getStreamRequestContentEncoding(httpRequestServerSupportedEncodings);
if (restRequestContentEncoding != EncodingType.IDENTITY || !httpResponseCompressionOperations.isEmpty()) {
filters = filters.addLastRest(new ClientCompressionFilter(restRequestContentEncoding, getRestRequestCompressionConfig(httpServiceName, restRequestContentEncoding), buildRestAcceptEncodingSchemaNames(responseEncodings), _responseCompressionConfigs.get(httpServiceName), httpResponseCompressionOperations));
}
if (streamRequestContentEncoding != StreamEncodingType.IDENTITY || !httpResponseCompressionOperations.isEmpty()) {
CompressionConfig compressionConfig = getStreamRequestCompressionConfig(httpServiceName, streamRequestContentEncoding);
filters = filters.addLast(new ClientStreamCompressionFilter(streamRequestContentEncoding, compressionConfig, buildStreamAcceptEncodingSchemas(responseEncodings), _responseCompressionConfigs.get(httpServiceName), httpResponseCompressionOperations, _compressionExecutor));
}
}
Integer queryPostThreshold = chooseNewOverDefault(getIntValue(properties, HTTP_QUERY_POST_THRESHOLD), Integer.MAX_VALUE);
ClientQueryTunnelFilter clientQueryTunnelFilter = new ClientQueryTunnelFilter(queryPostThreshold);
filters = filters.addLastRest(clientQueryTunnelFilter);
filters = filters.addLast(clientQueryTunnelFilter);
// Add the disruptor filter to the end of the filter chain to get the most accurate simulation of disrupt
Integer requestTimeout = chooseNewOverDefault(getIntValue(properties, HTTP_REQUEST_TIMEOUT), DEFAULT_REQUEST_TIMEOUT);
DisruptFilter disruptFilter = new DisruptFilter(_executor, _eventLoopGroup, requestTimeout);
filters = filters.addLastRest(disruptFilter);
filters = filters.addLast(disruptFilter);
client = new FilterChainClient(client, filters);
client = new FactoryClient(client);
synchronized (_mutex) {
if (!_running) {
throw new IllegalStateException("Factory is shutting down");
}
_clientsOutstanding++;
return client;
}
}
use of com.linkedin.r2.message.Response in project rest.li by linkedin.
the class TestHttpNettyStreamClient method testRestRequests.
@Test(dataProvider = "requestResponseParameters", expectedExceptions = UnsupportedOperationException.class)
public void testRestRequests(AbstractNettyStreamClient client, String method, int requestSize, int responseSize, boolean isFullRequest) throws Exception {
Server server = new HttpServerBuilder().responseSize(responseSize).build();
try {
server.start();
for (int i = 0; i < REQUEST_COUNT; i++) {
RestRequest request = new RestRequestBuilder(new URI(URL)).setMethod(method).setHeader(HttpHeaderNames.HOST.toString(), HOST_NAME.toString()).setEntity(ByteString.copy(new byte[requestSize])).build();
RequestContext context = new RequestContext();
context.putLocalAttr(R2Constants.IS_FULL_REQUEST, isFullRequest);
client.restRequest(request, context, new HashMap<>(), new TransportCallbackAdapter<>(new Callback<RestResponse>() {
@Override
public void onSuccess(RestResponse response) {
}
@Override
public void onError(Throwable e) {
}
}));
}
} finally {
server.stop();
}
}
use of com.linkedin.r2.message.Response in project rest.li by linkedin.
the class TestHttpNettyStreamClient method testCancelStreamRequests.
@Test(dataProvider = "requestResponseParameters", groups = TestGroupNames.TESTNG_GROUP_KNOWN_ISSUE)
public void testCancelStreamRequests(AbstractNettyStreamClient client, String method, int requestSize, int responseSize, boolean isFullRequest) throws Exception {
AtomicInteger succeeded = new AtomicInteger(0);
AtomicInteger failed = new AtomicInteger(0);
Server server = new HttpServerBuilder().responseSize(responseSize).build();
try {
server.start();
CountDownLatch latch = new CountDownLatch(REQUEST_COUNT);
for (int i = 0; i < REQUEST_COUNT; i++) {
StreamRequest request = new StreamRequestBuilder(new URI(URL)).setMethod(method).setHeader(HttpHeaderNames.HOST.toString(), HOST_NAME.toString()).build(EntityStreams.newEntityStream(new ByteStringWriter(ByteString.copy(new byte[requestSize]))));
RequestContext context = new RequestContext();
context.putLocalAttr(R2Constants.IS_FULL_REQUEST, isFullRequest);
client.streamRequest(request, context, new HashMap<>(), new TransportCallbackAdapter<>(new Callback<StreamResponse>() {
@Override
public void onSuccess(StreamResponse response) {
response.getEntityStream().setReader(new Reader() {
@Override
public void onDataAvailable(ByteString data) {
}
@Override
public void onDone() {
failed.incrementAndGet();
latch.countDown();
}
@Override
public void onError(Throwable e) {
failed.incrementAndGet();
latch.countDown();
}
@Override
public void onInit(ReadHandle rh) {
rh.cancel();
succeeded.incrementAndGet();
latch.countDown();
}
});
}
@Override
public void onError(Throwable e) {
failed.incrementAndGet();
latch.countDown();
}
}));
}
if (!latch.await(30, TimeUnit.SECONDS)) {
Assert.fail("Timeout waiting for responses. " + succeeded + " requests succeeded and " + failed + " requests failed out of total " + REQUEST_COUNT + " requests");
}
Assert.assertEquals(latch.getCount(), 0);
Assert.assertEquals(failed.get(), 0);
Assert.assertEquals(succeeded.get(), REQUEST_COUNT);
FutureCallback<None> shutdownCallback = new FutureCallback<>();
client.shutdown(shutdownCallback);
shutdownCallback.get(30, TimeUnit.SECONDS);
} finally {
server.stop();
}
}
use of com.linkedin.r2.message.Response 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();
}
});
}
}
Aggregations