use of com.linkedin.r2.transport.common.Server in project rest.li by linkedin.
the class SimpleLoadBalancerStateTest method testClientsShutdownAfterPropertyUpdatesStreamRequest.
@Test(groups = { "small", "back-end" })
public void testClientsShutdownAfterPropertyUpdatesStreamRequest() throws URISyntaxException, InterruptedException {
reset();
URI uri = URI.create("http://cluster-1/test");
List<String> schemes = new ArrayList<>();
Map<Integer, PartitionData> partitionData = new HashMap<>(1);
partitionData.put(DefaultPartitionAccessor.DEFAULT_PARTITION_ID, new PartitionData(1d));
Map<URI, Map<Integer, PartitionData>> uriData = new HashMap<>();
uriData.put(uri, partitionData);
schemes.add("http");
// set up state
_state.listenToService("service-1", new NullStateListenerCallback());
_state.listenToCluster("cluster-1", new NullStateListenerCallback());
_state.setDelayedExecution(0);
_serviceRegistry.put("service-1", new ServiceProperties("service-1", "cluster-1", "/test", Arrays.asList("random"), Collections.<String, Object>emptyMap(), Collections.<String, Object>emptyMap(), Collections.<String, String>emptyMap(), schemes, Collections.<URI>emptySet()));
_clusterRegistry.put("cluster-1", new ClusterProperties("cluster-1"));
_uriRegistry.put("cluster-1", new UriProperties("cluster-1", uriData));
URI uri1 = URI.create("http://partition-cluster-1/test1");
URI uri2 = URI.create("http://partition-cluster-1/test2");
_state.listenToCluster("partition-cluster-1", new NullStateListenerCallback());
_clusterRegistry.put("partition-cluster-1", new ClusterProperties("partition-cluster-1", null, new HashMap<>(), new HashSet<>(), new RangeBasedPartitionProperties("id=(\\d+)", 0, 100, 2)));
_state.listenToService("partition-service-1", new NullStateListenerCallback());
_serviceRegistry.put("partition-service-1", new ServiceProperties("partition-service-1", "partition-cluster-1", "/partition-test", Arrays.asList("degraderV3"), Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), schemes, Collections.emptySet()));
Map<Integer, PartitionData> partitionWeight = new HashMap<>();
partitionWeight.put(0, new PartitionData(1d));
partitionWeight.put(1, new PartitionData(2d));
Map<URI, Map<Integer, PartitionData>> partitionDesc = new HashMap<>();
partitionDesc.put(uri1, partitionWeight);
partitionWeight.remove(0);
partitionWeight.put(2, new PartitionData(1d));
partitionDesc.put(uri2, partitionWeight);
_uriRegistry.put("partition-cluster-1", new UriProperties("partition-cluster-1", partitionDesc));
TrackerClient client1 = _state.getClient("partition-service-1", uri1);
TrackerClient client2 = _state.getClient("partition-service-1", uri2);
assertEquals(client2.getPartitionWeight(1), 2d);
assertEquals(client2.getPartitionWeight(2), 1d);
assertEquals(client1.getPartitionWeight(1), 2d);
// Get client, then refresh cluster
TrackerClient client = _state.getClient("service-1", uri);
client.streamRequest(new StreamRequestBuilder(URI.create("d2://service-1/foo")).build(EntityStreams.emptyStream()), new RequestContext(), Collections.emptyMap(), new TransportCallbackAdapter<>(Callbacks.empty()));
// now force a refresh by adding cluster
_clusterRegistry.put("cluster-1", new ClusterProperties("cluster-1"));
// Get client, then refresh service
client = _state.getClient("service-1", uri);
client.streamRequest(new StreamRequestBuilder(URI.create("d2://service-1/foo")).build(EntityStreams.emptyStream()), new RequestContext(), Collections.emptyMap(), new TransportCallbackAdapter<>(Callbacks.empty()));
// refresh by adding service
_serviceRegistry.put("service-1", new ServiceProperties("service-1", "cluster-1", "/test", Arrays.asList("random"), Collections.<String, Object>emptyMap(), null, null, schemes, null));
// Get client, then mark server up/down
client = _state.getClient("service-1", uri);
client.streamRequest(new StreamRequestBuilder(URI.create("d2://service-1/foo")).build(EntityStreams.emptyStream()), new RequestContext(), Collections.emptyMap(), new TransportCallbackAdapter<>(Callbacks.empty()));
_uriRegistry.put("cluster-1", new UriProperties("cluster-1", Collections.<URI, Map<Integer, PartitionData>>emptyMap()));
_uriRegistry.put("cluster-1", new UriProperties("cluster-1", uriData));
// Get the client one last time
client = _state.getClient("service-1", uri);
client.streamRequest(new StreamRequestBuilder(URI.create("d2://service-1/foo")).build(EntityStreams.emptyStream()), new RequestContext(), Collections.emptyMap(), new TransportCallbackAdapter<>(Callbacks.empty()));
TestShutdownCallback callback = new TestShutdownCallback();
_state.shutdown(callback);
assertTrue(callback.await(10, TimeUnit.SECONDS), "Failed to shut down state");
for (TransportClientFactory factory : _clientFactories.values()) {
SimpleLoadBalancerTest.DoNothingClientFactory f = (SimpleLoadBalancerTest.DoNothingClientFactory) factory;
assertEquals(f.getRunningClientCount(), 0, "not all clients were shut down");
}
}
use of com.linkedin.r2.transport.common.Server in project rest.li by linkedin.
the class RestClient method getProtocolVersionForService.
/*package private*/
void getProtocolVersionForService(final Request<?> request, final RequestContext requestContext, Callback<ProtocolVersion> callback) {
TimingContextUtil.beginTiming(requestContext, FrameworkTimingKeys.CLIENT_REQUEST_RESTLI_GET_PROTOCOL.key());
ProtocolVersionOption versionOption = request.getRequestOptions().getProtocolVersionOption();
ProtocolVersion announcedProtocolVersion = null;
// fetch server announced version only for 'ProtocolVersionOption.USE_LATEST_IF_AVAILABLE'
if (versionOption == ProtocolVersionOption.USE_LATEST_IF_AVAILABLE) {
final String serviceName = request.getServiceName();
// check cache first.
announcedProtocolVersion = _announcedProtocolVersionCache.getIfPresent(serviceName);
if (announcedProtocolVersion == null) {
// if announcedProtocolVersion is not available in cache find and cache it.
try {
_client.getMetadata(new URI(_uriPrefix + serviceName), Callbacks.handle(metadata -> {
ProtocolVersion announcedVersion = getAnnouncedVersion(metadata);
_announcedProtocolVersionCache.put(serviceName, announcedVersion);
final ProtocolVersion protocolVersion = getProtocolVersion(AllProtocolVersions.BASELINE_PROTOCOL_VERSION, AllProtocolVersions.PREVIOUS_PROTOCOL_VERSION, AllProtocolVersions.LATEST_PROTOCOL_VERSION, AllProtocolVersions.NEXT_PROTOCOL_VERSION, announcedVersion, versionOption, _forceUseNextVersionOverride);
TimingContextUtil.endTiming(requestContext, FrameworkTimingKeys.CLIENT_REQUEST_RESTLI_GET_PROTOCOL.key());
callback.onSuccess(protocolVersion);
}, callback));
} catch (URISyntaxException e) {
throw new RuntimeException("Failed to create a valid URI to fetch properties for!");
}
return;
}
}
callback.onSuccess(getProtocolVersion(AllProtocolVersions.BASELINE_PROTOCOL_VERSION, AllProtocolVersions.PREVIOUS_PROTOCOL_VERSION, AllProtocolVersions.LATEST_PROTOCOL_VERSION, AllProtocolVersions.NEXT_PROTOCOL_VERSION, announcedProtocolVersion, versionOption, _forceUseNextVersionOverride));
}
use of com.linkedin.r2.transport.common.Server in project rest.li by linkedin.
the class LatencyInstrumentationResource method create.
/**
* This is the "upstream endpoint" which is queried directly by the integration test.
* This endpoint makes a call to {@link #batchPartialUpdate(BatchPatchRequest)} (the "downstream endpoint"),
* then packs all the client-side timing data into the original server-side request context.
*/
@ReturnEntity
@RestMethod.Create
public CreateKVResponse<Long, InstrumentationControl> create(InstrumentationControl control) {
final boolean forceException = control.isForceException();
final boolean useScatterGather = control.isUseScatterGather();
final String uriPrefix = control.getServiceUriPrefix();
// Build the downstream request
final BatchPartialUpdateEntityRequestBuilder<Long, InstrumentationControl> builder = new LatencyInstrumentationBuilders().batchPartialUpdateAndGet();
final PatchRequest<InstrumentationControl> patch = PatchGenerator.diffEmpty(control);
for (long i = 0; i < DOWNSTREAM_BATCH_SIZE; i++) {
builder.input(i, patch);
}
final BatchPartialUpdateEntityRequest<Long, InstrumentationControl> request = builder.build();
// Set up the Rest.li client config
final RestLiClientConfig clientConfig = new RestLiClientConfig();
clientConfig.setUseStreaming(control.isUseStreaming());
if (useScatterGather) {
clientConfig.setScatterGatherStrategy(new DefaultScatterGatherStrategy(new DummyUriMapper()));
}
final TransportClient transportClient = new HttpClientFactory.Builder().build().getClient(Collections.emptyMap());
final RestClient restClient = new ForceScatterGatherRestClient(new TransportClientAdapter(transportClient), uriPrefix, clientConfig);
final RequestContext serverRequestContext = getContext().getRawRequestContext();
final RequestContext clientRequestContext = new RequestContext();
// Load the timing importance threshold from the server context into the client context
clientRequestContext.putLocalAttr(TimingContextUtil.TIMING_IMPORTANCE_THRESHOLD_KEY_NAME, serverRequestContext.getLocalAttr(TimingContextUtil.TIMING_IMPORTANCE_THRESHOLD_KEY_NAME));
try {
// Make the request, then assert that the returned errors (if any) are as expected
BatchKVResponse<Long, UpdateEntityStatus<InstrumentationControl>> response = restClient.sendRequest(request, clientRequestContext).getResponseEntity();
final Map<Long, ErrorResponse> errors = response.getErrors();
if (forceException && errors.isEmpty()) {
throw new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR, "Expected failures for the downstream batch request, but found none.");
}
if (!forceException && !errors.isEmpty()) {
throw new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR, "Expected no failures for the downstream batch request, but found some.");
}
for (ErrorResponse errorResponse : errors.values()) {
if (!DOWNSTREAM_ERROR_CODE.equals(errorResponse.getCode())) {
throw new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR, "Encountered a downstream failure with an unexpected or missing error code.");
}
}
} catch (RemoteInvocationException e) {
throw new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR, "Downstream failures should be batch entry failures, but encountered a top-level request failure.", e);
}
Map<TimingKey, TimingContextUtil.TimingContext> clientTimingsMap = TimingContextUtil.getTimingsMap(clientRequestContext);
Map<TimingKey, TimingContextUtil.TimingContext> serverTimingsMap = TimingContextUtil.getTimingsMap(serverRequestContext);
// Load all client timings into the server timings map
serverTimingsMap.putAll(clientTimingsMap);
getContext().setResponseHeader(HAS_CLIENT_TIMINGS_HEADER, Boolean.TRUE.toString());
if (forceException) {
throw new RestLiServiceException(HttpStatus.S_400_BAD_REQUEST, "You wanted me to fail, so I failed.").setCode(UPSTREAM_ERROR_CODE);
}
return new CreateKVResponse<>(1L, control);
}
use of com.linkedin.r2.transport.common.Server in project rest.li by linkedin.
the class AbstractAsyncR2StreamServlet method service.
@Override
public void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
final AsyncContext ctx = req.startAsync(req, resp);
ctx.setTimeout(_timeout);
final WrappedAsyncContext wrappedCtx = new WrappedAsyncContext(ctx);
final AsyncEventIOHandler ioHandler = new AsyncEventIOHandler(req.getInputStream(), resp.getOutputStream(), req.getRemoteAddr(), wrappedCtx, MAX_BUFFERED_CHUNKS, _logServletExceptions);
final RequestContext requestContext = ServletHelper.readRequestContext(req);
final StreamRequest streamRequest;
try {
streamRequest = ServletHelper.readFromServletRequest(req, ioHandler);
} catch (URISyntaxException e) {
ServletHelper.writeToServletError(resp, RestStatus.BAD_REQUEST, e.toString());
wrappedCtx.complete();
return;
}
final AtomicBoolean startedResponding = new AtomicBoolean(false);
ctx.addListener(new AsyncListener() {
@Override
public void onTimeout(AsyncEvent event) throws IOException {
LOG.error("Server timeout for request: " + formatURI(req.getRequestURI()));
if (startedResponding.compareAndSet(false, true)) {
LOG.info("Returning server timeout response");
ServletHelper.writeToServletError(resp, RestStatus.INTERNAL_SERVER_ERROR, "Server timeout");
} else {
req.setAttribute(ASYNC_IOEXCEPTION, new ServletException("Server timeout"));
}
ioHandler.exitLoop();
wrappedCtx.complete();
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
// Nothing to do here
}
@Override
public void onError(AsyncEvent event) throws IOException {
LOG.error("Server error for request: " + formatURI(req.getRequestURI()));
if (startedResponding.compareAndSet(false, true)) {
LOG.info("Returning server error response");
ServletHelper.writeToServletError(resp, RestStatus.INTERNAL_SERVER_ERROR, "Server error");
} else {
req.setAttribute(ASYNC_IOEXCEPTION, new ServletException("Server error"));
}
ioHandler.exitLoop();
wrappedCtx.complete();
}
@Override
public void onComplete(AsyncEvent event) throws IOException {
Object exception = req.getAttribute(ASYNC_IOEXCEPTION);
if (exception != null) {
throw new IOException((Throwable) exception);
}
}
});
final TransportCallback<StreamResponse> callback = new TransportCallback<StreamResponse>() {
@Override
public void onResponse(final TransportResponse<StreamResponse> response) {
if (startedResponding.compareAndSet(false, true)) {
ctx.start(new Runnable() {
@Override
public void run() {
try {
StreamResponse streamResponse = ServletHelper.writeResponseHeadersToServletResponse(response, resp);
streamResponse.getEntityStream().setReader(ioHandler);
ioHandler.loop();
} catch (Exception e) {
req.setAttribute(ASYNC_IOEXCEPTION, e);
wrappedCtx.complete();
}
}
});
} else {
LOG.error("Dropped a response; this is mostly like because that AsyncContext timeout or error had already happened");
}
}
};
// we have to use a new thread and let this thread return to pool. otherwise the timeout won't start
ctx.start(new Runnable() {
@Override
public void run() {
try {
getDispatcher().handleRequest(streamRequest, requestContext, callback);
ioHandler.loop();
} catch (Exception e) {
req.setAttribute(ASYNC_IOEXCEPTION, e);
wrappedCtx.complete();
}
}
});
}
use of com.linkedin.r2.transport.common.Server in project rest.li by linkedin.
the class TestHttpsEcho method testHttpEcho.
/**
* Test that https-enabled server and client can speak plain HTTP as well.
*/
@Test
public void testHttpEcho() throws Exception {
final EchoService client = new RestEchoClient(Bootstrap.createURI(SslContextUtil.getHttpPortFromHttps(_port), Bootstrap.getEchoURI(), false), createClient());
final String msg = "This is a simple http echo message";
final FutureCallback<String> callback = new FutureCallback<>();
client.echo(msg, callback);
Assert.assertEquals(callback.get(), msg);
}
Aggregations