Search in sources :

Example 6 with TransportResponse

use of com.linkedin.r2.transport.common.bridge.common.TransportResponse in project rest.li by linkedin.

the class TestHttp2NettyStreamClient method testChannelReusedAfterRequestTimeout.

/**
 * When a request fails due to {@link TimeoutException}, connection should not be destroyed.
 * @throws Exception
 */
@Test(timeOut = TEST_TIMEOUT)
public void testChannelReusedAfterRequestTimeout() throws Exception {
    final HttpServerBuilder.HttpServerStatsProvider statsProvider = new HttpServerBuilder.HttpServerStatsProvider();
    final HttpServerBuilder serverBuilder = new HttpServerBuilder();
    final Server server = serverBuilder.serverStatsProvider(statsProvider).stopTimeout(0).build();
    final HttpClientBuilder clientBuilder = new HttpClientBuilder(_eventLoop, _scheduler);
    final Http2NettyStreamClient client = clientBuilder.setRequestTimeout(1000).buildHttp2StreamClient();
    final TransportResponse<StreamResponse> response1;
    final TransportResponse<StreamResponse> response2;
    try {
        server.start();
        final StreamRequestBuilder builder1 = new StreamRequestBuilder(new URI(URL));
        final StreamRequest request1 = builder1.setMethod(METHOD).setHeader(HttpHeaderNames.HOST.toString(), HOST_NAME.toString()).build(EntityStreams.newEntityStream(new TimeoutWriter()));
        final FutureTransportCallback<StreamResponse> callback1 = new FutureTransportCallback<>();
        client.streamRequest(request1, new RequestContext(), new HashMap<>(), callback1);
        response1 = callback1.get();
        final StreamRequestBuilder builder2 = new StreamRequestBuilder(new URI(URL));
        final StreamRequest request2 = builder2.setMethod(METHOD).setHeader(HttpHeaderNames.HOST.toString(), HOST_NAME.toString()).build(EntityStreams.newEntityStream(new ByteStringWriter(ByteString.copy(new byte[REQUEST_SIZE]))));
        final FutureTransportCallback<StreamResponse> callback2 = new FutureTransportCallback<>();
        client.streamRequest(request2, new RequestContext(), new HashMap<>(), callback2);
        response2 = callback2.get();
    } finally {
        server.stop();
    }
    // The 1st request should be failed with timeout
    Assert.assertNotNull(response1);
    Assert.assertTrue(response1.hasError());
    Assert.assertNotNull(response1.getError());
    ExceptionTestUtil.verifyCauseChain(response1.getError(), TimeoutException.class);
    // The 2nd request should succeed
    Assert.assertNotNull(response2);
    Assert.assertFalse(response2.hasError());
    response2.getResponse().getEntityStream().setReader(new DrainReader());
    // The server should have seen 2 requests but establishes only 1 connection with the client
    Assert.assertEquals(statsProvider.requestCount(), 3);
    Assert.assertEquals(statsProvider.clientConnections().size(), 1);
}
Also used : Server(org.eclipse.jetty.server.Server) FutureTransportCallback(com.linkedin.r2.transport.common.bridge.common.FutureTransportCallback) StreamResponse(com.linkedin.r2.message.stream.StreamResponse) HttpServerBuilder(com.linkedin.r2.testutils.server.HttpServerBuilder) HttpClientBuilder(com.linkedin.r2.transport.http.client.HttpClientBuilder) StreamRequestBuilder(com.linkedin.r2.message.stream.StreamRequestBuilder) URI(java.net.URI) DrainReader(com.linkedin.r2.message.stream.entitystream.DrainReader) StreamRequest(com.linkedin.r2.message.stream.StreamRequest) RequestContext(com.linkedin.r2.message.RequestContext) ByteStringWriter(com.linkedin.r2.message.stream.entitystream.ByteStringWriter) Test(org.testng.annotations.Test)

Example 7 with TransportResponse

use of com.linkedin.r2.transport.common.bridge.common.TransportResponse in project rest.li by linkedin.

the class AbstractAsyncR2Servlet method service.

@Override
public void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
    RequestContext requestContext = ServletHelper.readRequestContext(req);
    RestRequest restRequest;
    try {
        restRequest = readFromServletRequest(req);
    } catch (URISyntaxException e) {
        writeToServletError(resp, RestStatus.BAD_REQUEST, e.toString());
        return;
    }
    final AsyncContext ctx = req.startAsync(req, resp);
    ctx.setTimeout(_timeout);
    ctx.addListener(new AsyncListener() {

        @Override
        public void onTimeout(AsyncEvent event) throws IOException {
            AsyncContext ctx = event.getAsyncContext();
            writeToServletError((HttpServletResponse) ctx.getResponse(), RestStatus.INTERNAL_SERVER_ERROR, "Server Timeout");
            ctx.complete();
        }

        @Override
        public void onStartAsync(AsyncEvent event) throws IOException {
        // Nothing to do here
        }

        @Override
        public void onError(AsyncEvent event) throws IOException {
            writeToServletError((HttpServletResponse) event.getSuppliedResponse(), RestStatus.INTERNAL_SERVER_ERROR, "Server Error");
            ctx.complete();
        }

        @Override
        public void onComplete(AsyncEvent event) throws IOException {
            Object exception = req.getAttribute(TRANSPORT_CALLBACK_IOEXCEPTION);
            if (exception != null)
                throw new IOException((IOException) exception);
        }
    });
    TransportCallback<RestResponse> callback = new TransportCallback<RestResponse>() {

        @Override
        public void onResponse(final TransportResponse<RestResponse> response) {
            // TransportCallback is usually invoked by non-servlet threads; hence we cannot assume that it's ok to
            // do blocking IO there. As a result, we should use AsyncContext.start() to do blocking IO using the
            // container/servlet threads. This still maintains the advantage of Async, meaning servlet thread is not
            // blocking-wait when the response is not ready.
            ctx.start(new Runnable() {

                @Override
                public void run() {
                    try {
                        writeToServletResponse(response, (HttpServletResponse) ctx.getResponse());
                    } catch (IOException e) {
                        req.setAttribute(TRANSPORT_CALLBACK_IOEXCEPTION, e);
                    } finally {
                        ctx.complete();
                    }
                }
            });
        }
    };
    getDispatcher().handleRequest(restRequest, requestContext, callback);
}
Also used : TransportCallback(com.linkedin.r2.transport.common.bridge.common.TransportCallback) RestResponse(com.linkedin.r2.message.rest.RestResponse) HttpServletResponse(javax.servlet.http.HttpServletResponse) AsyncContext(javax.servlet.AsyncContext) URISyntaxException(java.net.URISyntaxException) IOException(java.io.IOException) AsyncEvent(javax.servlet.AsyncEvent) TransportResponse(com.linkedin.r2.transport.common.bridge.common.TransportResponse) RestRequest(com.linkedin.r2.message.rest.RestRequest) AsyncListener(javax.servlet.AsyncListener) RequestContext(com.linkedin.r2.message.RequestContext)

Example 8 with TransportResponse

use of com.linkedin.r2.transport.common.bridge.common.TransportResponse in project rest.li by linkedin.

the class AbstractR2Servlet method writeToServletResponse.

protected void writeToServletResponse(TransportResponse<RestResponse> response, HttpServletResponse resp) throws IOException {
    Map<String, String> wireAttrs = response.getWireAttributes();
    for (Map.Entry<String, String> e : WireAttributeHelper.toWireAttributes(wireAttrs).entrySet()) {
        resp.setHeader(e.getKey(), e.getValue());
    }
    RestResponse restResponse = null;
    if (response.hasError()) {
        Throwable e = response.getError();
        if (e instanceof RestException) {
            restResponse = ((RestException) e).getResponse();
        }
        if (restResponse == null) {
            restResponse = RestStatus.responseForError(RestStatus.INTERNAL_SERVER_ERROR, e);
        }
    } else {
        restResponse = response.getResponse();
    }
    resp.setStatus(restResponse.getStatus());
    Map<String, String> headers = restResponse.getHeaders();
    for (Map.Entry<String, String> e : headers.entrySet()) {
        // TODO multi-valued headers
        resp.setHeader(e.getKey(), e.getValue());
    }
    for (String cookie : restResponse.getCookies()) {
        resp.addHeader(HttpConstants.RESPONSE_COOKIE_HEADER_NAME, cookie);
    }
    final ByteString entity = restResponse.getEntity();
    entity.write(resp.getOutputStream());
    resp.getOutputStream().close();
}
Also used : RestResponse(com.linkedin.r2.message.rest.RestResponse) ByteString(com.linkedin.data.ByteString) RestException(com.linkedin.r2.message.rest.RestException) ByteString(com.linkedin.data.ByteString) Map(java.util.Map)

Example 9 with TransportResponse

use of com.linkedin.r2.transport.common.bridge.common.TransportResponse in project rest.li by linkedin.

the class AbstractR2Servlet method service.

@Override
protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
    RequestContext requestContext = ServletHelper.readRequestContext(req);
    RestRequest restRequest;
    try {
        restRequest = readFromServletRequest(req);
    } catch (URISyntaxException e) {
        writeToServletError(resp, RestStatus.BAD_REQUEST, e.toString());
        return;
    }
    final AtomicReference<TransportResponse<RestResponse>> result = new AtomicReference<>();
    final CountDownLatch latch = new CountDownLatch(1);
    TransportCallback<RestResponse> callback = new TransportCallback<RestResponse>() {

        @Override
        public void onResponse(TransportResponse<RestResponse> response) {
            result.set(response);
            latch.countDown();
        }
    };
    getDispatcher().handleRequest(restRequest, requestContext, callback);
    try {
        if (latch.await(_timeout, TimeUnit.MILLISECONDS)) {
            writeToServletResponse(result.get(), resp);
        } else {
            writeToServletError(resp, RestStatus.INTERNAL_SERVER_ERROR, "Server Timeout after " + _timeout + "ms.");
        }
    } catch (InterruptedException e) {
        throw new ServletException("Interrupted!", e);
    }
}
Also used : ServletException(javax.servlet.ServletException) TransportCallback(com.linkedin.r2.transport.common.bridge.common.TransportCallback) RestRequest(com.linkedin.r2.message.rest.RestRequest) RestResponse(com.linkedin.r2.message.rest.RestResponse) AtomicReference(java.util.concurrent.atomic.AtomicReference) RequestContext(com.linkedin.r2.message.RequestContext) URISyntaxException(java.net.URISyntaxException) CountDownLatch(java.util.concurrent.CountDownLatch) TransportResponse(com.linkedin.r2.transport.common.bridge.common.TransportResponse)

Example 10 with TransportResponse

use of com.linkedin.r2.transport.common.bridge.common.TransportResponse in project rest.li by linkedin.

the class HttpDispatcher method handleRequest.

/**
 * handle a {@link com.linkedin.r2.message.stream.StreamRequest} using the given request context.
 * @see com.linkedin.r2.transport.common.bridge.server.TransportDispatcher#handleStreamRequest
 *
 * @param req the request to be handled.
 * @param context the request context.
 * @param callback the callback to be invoked with the response or error.
 */
public void handleRequest(StreamRequest req, RequestContext context, final TransportCallback<StreamResponse> callback) {
    markOnRequestTimings(context);
    final Map<String, String> headers = new HashMap<>(req.getHeaders());
    final Map<String, String> wireAttrs = WireAttributeHelper.removeWireAttributes(headers);
    final BaseConnector connector = new BaseConnector();
    try {
        MessageType.Type msgType = MessageType.getMessageType(wireAttrs, MessageType.Type.REST);
        switch(msgType) {
            default:
            case REST:
                req.getEntityStream().setReader(connector);
                StreamRequest newReq = req.builder().build(EntityStreams.newEntityStream(connector));
                // decorate the call back so that if response is error or response finishes streaming,
                // we cancel the request stream
                TransportCallback<StreamResponse> decorateCallback = new TransportCallback<StreamResponse>() {

                    @Override
                    public void onResponse(TransportResponse<StreamResponse> response) {
                        // no need to check StreamException because that's handled by HttpBridge.httpToStreamCallback
                        if (response.hasError()) {
                            connector.cancel();
                        } else {
                            Observer observer = new Observer() {

                                @Override
                                public void onDataAvailable(ByteString data) {
                                // do nothing
                                }

                                @Override
                                public void onDone() {
                                    connector.cancel();
                                }

                                @Override
                                public void onError(Throwable e) {
                                    connector.cancel();
                                }
                            };
                            response.getResponse().getEntityStream().addObserver(observer);
                        }
                        callback.onResponse(response);
                    }
                };
                _dispatcher.handleStreamRequest(HttpBridge.toStreamRequest(newReq, headers), wireAttrs, context, HttpBridge.httpToStreamCallback(decorateCallback));
        }
    } catch (Exception e) {
        connector.cancel();
        callback.onResponse(TransportResponseImpl.<StreamResponse>error(e, Collections.<String, String>emptyMap()));
    }
}
Also used : TransportCallback(com.linkedin.r2.transport.common.bridge.common.TransportCallback) HashMap(java.util.HashMap) BaseConnector(com.linkedin.r2.message.stream.entitystream.BaseConnector) ByteString(com.linkedin.data.ByteString) StreamResponse(com.linkedin.r2.message.stream.StreamResponse) ByteString(com.linkedin.data.ByteString) TransportResponse(com.linkedin.r2.transport.common.bridge.common.TransportResponse) StreamRequest(com.linkedin.r2.message.stream.StreamRequest) Observer(com.linkedin.r2.message.stream.entitystream.Observer) MessageType(com.linkedin.r2.transport.common.MessageType)

Aggregations

StreamResponse (com.linkedin.r2.message.stream.StreamResponse)11 RequestContext (com.linkedin.r2.message.RequestContext)8 TransportResponse (com.linkedin.r2.transport.common.bridge.common.TransportResponse)8 StreamRequest (com.linkedin.r2.message.stream.StreamRequest)7 TransportCallback (com.linkedin.r2.transport.common.bridge.common.TransportCallback)7 RestResponse (com.linkedin.r2.message.rest.RestResponse)6 ByteString (com.linkedin.data.ByteString)4 RestResponseBuilder (com.linkedin.r2.message.rest.RestResponseBuilder)4 StreamRequestBuilder (com.linkedin.r2.message.stream.StreamRequestBuilder)4 HttpServerBuilder (com.linkedin.r2.testutils.server.HttpServerBuilder)4 URI (java.net.URI)4 URISyntaxException (java.net.URISyntaxException)4 Server (org.eclipse.jetty.server.Server)4 Test (org.testng.annotations.Test)4 ByteStringWriter (com.linkedin.r2.message.stream.entitystream.ByteStringWriter)3 FutureTransportCallback (com.linkedin.r2.transport.common.bridge.common.FutureTransportCallback)3 HttpClientBuilder (com.linkedin.r2.transport.http.client.HttpClientBuilder)3 RetriableRequestException (com.linkedin.r2.RetriableRequestException)2 RestRequest (com.linkedin.r2.message.rest.RestRequest)2 DrainReader (com.linkedin.r2.message.stream.entitystream.DrainReader)2