@Override public void exchangeTerminated(HttpExchange exchange, Result result) { super.exchangeTerminated(exchange, result); Response response = result.getResponse(); HttpFields responseHeaders = response.getHeaders(); String closeReason = null; if (result.isFailed()) closeReason = "failure"; else if (receiver.isShutdown()) closeReason = "server close"; if (closeReason == null) { if (response.getVersion().compareTo(HttpVersion.HTTP_1_1) < 0) { // HTTP 1.0 must close the connection unless it has // an explicit keep alive or it's a CONNECT method. boolean keepAlive = responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString()); boolean connect = HttpMethod.CONNECT.is(exchange.getRequest().getMethod()); if (!keepAlive && !connect) closeReason = "http/1.0"; } else { // HTTP 1.1 or greater closes only if it has an explicit close. if (responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())) closeReason = "http/1.1"; } } if (closeReason != null) { if (LOG.isDebugEnabled()) LOG.debug("Closing, reason: {} - {}", closeReason, connection); connection.close(); } else { if (response.getStatus() == HttpStatus.SWITCHING_PROTOCOLS_101) connection.remove(); else release(); } }
private static ClientResponse translateResponse( final ClientRequest jerseyRequest, final org.eclipse.jetty.client.api.Response jettyResponse, final NonBlockingInputStream entityStream) { final ClientResponse jerseyResponse = new ClientResponse(Statuses.from(jettyResponse.getStatus()), jerseyRequest); processResponseHeaders(jettyResponse.getHeaders(), jerseyResponse); jerseyResponse.setEntityStream(entityStream); return jerseyResponse; }
private InBoundHeaders getInBoundHeaders(Response response) { final InBoundHeaders headers = new InBoundHeaders(); for (HttpField header : response.getHeaders()) { headers.add(header.getName(), header.getValue()); } return headers; }
@Slow @Test public void testProxyLongPoll() throws Exception { prepareProxy(); final long timeout = 1000; prepareServer( new HttpServlet() { @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { if (!request.isAsyncStarted()) { final AsyncContext asyncContext = request.startAsync(); asyncContext.setTimeout(timeout); asyncContext.addListener( new AsyncListener() { @Override public void onComplete(AsyncEvent event) throws IOException {} @Override public void onTimeout(AsyncEvent event) throws IOException { if (request.getHeader("Via") != null) response.addHeader(PROXIED_HEADER, "true"); asyncContext.complete(); } @Override public void onError(AsyncEvent event) throws IOException {} @Override public void onStartAsync(AsyncEvent event) throws IOException {} }); } } }); Response response = client .newRequest("localhost", serverConnector.getLocalPort()) .timeout(2 * timeout, TimeUnit.MILLISECONDS) .send(); Assert.assertEquals(200, response.getStatus()); Assert.assertTrue(response.getHeaders().containsKey(PROXIED_HEADER)); }
@Override public void onHeaders(Response response) { HttpFields headers = response.getHeaders(); long length = headers.getLongField(HttpHeader.CONTENT_LENGTH.asString()); if (length > maxLength) response.abort(new IllegalArgumentException("Buffering capacity exceeded")); String contentType = headers.get(HttpHeader.CONTENT_TYPE); if (contentType != null) { String charset = "charset="; int index = contentType.toLowerCase(Locale.ENGLISH).indexOf(charset); if (index > 0) { String encoding = contentType.substring(index + charset.length()); // Sometimes charsets arrive with an ending semicolon index = encoding.indexOf(';'); if (index > 0) encoding = encoding.substring(0, index); this.encoding = encoding; } } }
@Override public boolean abort(Throwable cause) { return response.abort(cause); }
@Override public HttpFields getHeaders() { return response.getHeaders(); }
@Override public String getReason() { return response.getReason(); }
@Override public int getStatus() { return response.getStatus(); }
@Override public HttpVersion getVersion() { return response.getVersion(); }
@Override public <T extends ResponseListener> List<T> getListeners(Class<T> listenerClass) { return response.getListeners(listenerClass); }
@Override public long getConversationID() { return response.getConversationID(); }
@Test public void testProxyRequestFailureInTheMiddleOfProxyingBigContent() throws Exception { final long proxyTimeout = 1000; int outputBufferSize = 1024; Map<String, String> proxyParams = new HashMap<>(); proxyParams.put("timeout", String.valueOf(proxyTimeout)); proxyParams.put("outputBufferSize", String.valueOf(outputBufferSize)); prepareProxy(proxyParams); final CountDownLatch chunk1Latch = new CountDownLatch(1); final byte[] chunk1 = new byte[outputBufferSize]; new Random().nextBytes(chunk1); final int chunk2 = 'w'; prepareServer( new HttpServlet() { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletOutputStream output = response.getOutputStream(); output.write(chunk1); response.flushBuffer(); // Wait for the client to receive this chunk. await(chunk1Latch, 5000); // Send second chunk, must not be received by proxy. output.write(chunk2); } private boolean await(CountDownLatch latch, long ms) throws IOException { try { return latch.await(ms, TimeUnit.MILLISECONDS); } catch (InterruptedException x) { throw new InterruptedIOException(); } } }); HttpClient client = prepareClient(); InputStreamResponseListener listener = new InputStreamResponseListener(); int port = serverConnector.getLocalPort(); client.newRequest("localhost", port).send(listener); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertEquals(200, response.getStatus()); InputStream input = listener.getInputStream(); for (int i = 0; i < chunk1.length; ++i) Assert.assertEquals(chunk1[i] & 0xFF, input.read()); TimeUnit.MILLISECONDS.sleep(2 * proxyTimeout); chunk1Latch.countDown(); try { // Make sure the proxy does not receive chunk2. input.read(); Assert.fail(); } catch (EOFException x) { // Expected } HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP) client.getDestination("http", "localhost", port); Assert.assertEquals(0, destination.getConnectionPool().getIdleConnections().size()); }
@Test public void testProxyRequestFailureInTheMiddleOfProxyingSmallContent() throws Exception { final long proxyTimeout = 1000; Map<String, String> proxyParams = new HashMap<>(); proxyParams.put("timeout", String.valueOf(proxyTimeout)); prepareProxy(proxyParams); final CountDownLatch chunk1Latch = new CountDownLatch(1); final int chunk1 = 'q'; final int chunk2 = 'w'; prepareServer( new HttpServlet() { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletOutputStream output = response.getOutputStream(); output.write(chunk1); response.flushBuffer(); // Wait for the client to receive this chunk. await(chunk1Latch, 5000); // Send second chunk, must not be received by proxy. output.write(chunk2); } private boolean await(CountDownLatch latch, long ms) throws IOException { try { return latch.await(ms, TimeUnit.MILLISECONDS); } catch (InterruptedException x) { throw new InterruptedIOException(); } } }); HttpClient client = prepareClient(); InputStreamResponseListener listener = new InputStreamResponseListener(); int port = serverConnector.getLocalPort(); client.newRequest("localhost", port).send(listener); // Make the proxy request fail; given the small content, the // proxy-to-client response is not committed yet so it will be reset. TimeUnit.MILLISECONDS.sleep(2 * proxyTimeout); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertEquals(504, response.getStatus()); // Make sure there is no content, as the proxy-to-client response has been reset. InputStream input = listener.getInputStream(); Assert.assertEquals(-1, input.read()); chunk1Latch.countDown(); // Result succeeds because a 504 is a valid HTTP response. Result result = listener.await(5, TimeUnit.SECONDS); Assert.assertTrue(result.isSucceeded()); // Make sure the proxy does not receive chunk2. Assert.assertEquals(-1, input.read()); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP) client.getDestination("http", "localhost", port); Assert.assertEquals(0, destination.getConnectionPool().getIdleConnections().size()); }