// ======================================================================== // Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.client; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.matchers.JUnitMatchers.containsString; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.client.helperClasses.HttpServerAndClientCreator; import org.eclipse.jetty.client.helperClasses.ServerAndClientCreator; import org.eclipse.jetty.client.security.ProxyAuthorization; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpMethods; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.nio.DirectNIOBuffer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.Stress; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.junit.After; import org.junit.Before; import org.junit.Test; /* ------------------------------------------------------------ */ /** * Functional testing for HttpExchange. */ public class HttpExchangeTest { final static boolean verbose=HttpExchange.LOG.isDebugEnabled(); protected static int _maxConnectionsPerAddress = 2; protected static String _scheme = "http"; protected static Server _server; protected static int _port; protected static HttpClient _httpClient; protected static AtomicInteger _count = new AtomicInteger(); protected static ServerAndClientCreator serverAndClientCreator = new HttpServerAndClientCreator(); protected static URI getBaseURI() { return URI.create(_scheme + "://localhost:" + _port + "/"); } /* ------------------------------------------------------------ */ // TODO work out why BeforeClass does not work here? @Before public void setUpOnce() throws Exception { _scheme = "http"; _server = serverAndClientCreator.createServer(); _httpClient = serverAndClientCreator.createClient(3000L,3500L,2000); _port = _server.getConnectors()[0].getLocalPort(); } /* ------------------------------------------------------------ */ @After public void tearDownOnce() throws Exception { _httpClient.stop(); long startTime = System.currentTimeMillis(); while (!_httpClient.getState().equals(AbstractLifeCycle.STOPPED)) { if (System.currentTimeMillis() - startTime > 1000) break; Thread.sleep(5); } _server.stop(); } /* ------------------------------------------------------------ */ @Test public void testResetNewExchange() throws Exception { HttpExchange exchange = new HttpExchange(); exchange.reset(); } /* ------------------------------------------------------------ */ @Test public void testPerf() throws Exception { sender(1,false); sender(1,true); sender(10,false); sender(10,true); if (Stress.isEnabled()) { sender(100,false); sender(100,true); sender(10000,false); sender(10000,true); } } /* ------------------------------------------------------------ */ /** * Test sending data through the exchange. * * @throws IOException */ public void sender(final int nb, final boolean close) throws Exception { // System.err.printf("%nSENDER %d %s%n",nb,close); _count.set(0); final CountDownLatch complete = new CountDownLatch(nb); final AtomicInteger allcontent = new AtomicInteger(nb); HttpExchange[] httpExchange = new HttpExchange[nb]; long start = System.currentTimeMillis(); for (int i = 0; i < nb; i++) { final int n = i; httpExchange[n] = new HttpExchange() { String result = "pending"; int len = 0; /* ------------------------------------------------------------ */ @Override protected void onRequestCommitted() { if (verbose) System.err.println(n+" [ "+this); result = "committed"; } /* ------------------------------------------------------------ */ @Override protected void onRequestComplete() throws IOException { if (verbose) System.err.println(n+" [ =="); result = "sent"; } @Override /* ------------------------------------------------------------ */ protected void onResponseStatus(Buffer version, int status, Buffer reason) { if (verbose) System.err.println(n+" ] "+version+" "+status+" "+reason); result = "status"; } /* ------------------------------------------------------------ */ @Override protected void onResponseHeader(Buffer name, Buffer value) { if (verbose) System.err.println(n+" ] "+name+": "+value); } /* ------------------------------------------------------------ */ @Override protected void onResponseHeaderComplete() throws IOException { if (verbose) System.err.println(n+" ] -"); result = "content"; super.onResponseHeaderComplete(); } /* ------------------------------------------------------------ */ @Override protected void onResponseContent(Buffer content) { len += content.length(); if (verbose) System.err.println(n+" ] "+content.length()+" -> "+len); } /* ------------------------------------------------------------ */ @Override protected void onResponseComplete() { if (verbose) System.err.println(n+" ] == "+len+" "+complete.getCount()+"/"+nb); result = "complete"; if (len == 2009) allcontent.decrementAndGet(); else System.err.println(n+ " ONLY " + len+ "/2009"); complete.countDown(); } /* ------------------------------------------------------------ */ @Override protected void onConnectionFailed(Throwable ex) { if (verbose) System.err.println(n+" ] "+ex); complete.countDown(); result = "failed"; System.err.println(n+ " FAILED " + ex); super.onConnectionFailed(ex); } /* ------------------------------------------------------------ */ @Override protected void onException(Throwable ex) { if (verbose) System.err.println(n+" ] "+ex); complete.countDown(); result = "excepted"; System.err.println(n+ " EXCEPTED " + ex); super.onException(ex); } /* ------------------------------------------------------------ */ @Override protected void onExpire() { if (verbose) System.err.println(n+" ] expired"); complete.countDown(); result = "expired"; System.err.println(n + " EXPIRED " + len); super.onExpire(); } /* ------------------------------------------------------------ */ @Override public String toString() { return n+"/"+result+"/"+len+"/"+super.toString(); } }; httpExchange[n].setURI(getBaseURI().resolve("/" + n)); httpExchange[n].addRequestHeader("arbitrary","value"); if (close) httpExchange[n].setRequestHeader("Connection","close"); _httpClient.send(httpExchange[n]); } if (!complete.await(2,TimeUnit.SECONDS)) System.err.println(_httpClient.dump()); assertTrue(complete.await(20,TimeUnit.SECONDS)); assertEquals("nb="+nb+" close="+close,0,allcontent.get()); } /* ------------------------------------------------------------ */ @Test public void testPostWithContentExchange() throws Exception { for (int i=0;i<20;i++) { ContentExchange httpExchange=new ContentExchange(); httpExchange.setURI(getBaseURI()); httpExchange.setMethod(HttpMethods.POST); httpExchange.setRequestContent(new ByteArrayBuffer("<hello />")); _httpClient.send(httpExchange); int status = httpExchange.waitForDone(); //httpExchange.waitForStatus(HttpExchange.STATUS_COMPLETED); String result=httpExchange.getResponseContent(); assertEquals(HttpExchange.STATUS_COMPLETED, status); assertEquals("i="+i,"<hello />",result); } } /* ------------------------------------------------------------ */ @Test public void testGetWithContentExchange() throws Exception { for (int i=0;i<10;i++) { ContentExchange httpExchange=new ContentExchange(); URI uri = getBaseURI().resolve("?i=" + i); httpExchange.setURI(uri); httpExchange.setMethod(HttpMethods.GET); _httpClient.send(httpExchange); int status = httpExchange.waitForDone(); //httpExchange.waitForStatus(HttpExchange.STATUS_COMPLETED); String result=httpExchange.getResponseContent(); assertNotNull("Should have received response content", result); assertEquals("i="+i,0,result.indexOf("<hello>")); assertEquals("i="+i,result.length()-10,result.indexOf("</hello>")); assertEquals(HttpExchange.STATUS_COMPLETED, status); Thread.sleep(5); } } /* ------------------------------------------------------------ */ @Test public void testLocalAddressAvailabilityWithContentExchange() throws Exception { for (int i=0;i<10;i++) { ContentExchange httpExchange=new ContentExchange(); URI uri = getBaseURI().resolve("?i=" + i); httpExchange.setURI(uri); httpExchange.setMethod(HttpMethods.GET); _httpClient.send(httpExchange); int status = httpExchange.waitForDone(); assertNotNull(httpExchange.getLocalAddress()); String result=httpExchange.getResponseContent(); assertNotNull("Should have received response content", result); assertEquals("i="+i,0,result.indexOf("<hello>")); assertEquals("i="+i,result.length()-10,result.indexOf("</hello>")); assertEquals(HttpExchange.STATUS_COMPLETED, status); Thread.sleep(5); } } /* ------------------------------------------------------------ */ @Test public void testShutdownWithExchange() throws Exception { final AtomicReference<Throwable> throwable=new AtomicReference<Throwable>(); HttpExchange httpExchange=new HttpExchange() { /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.client.HttpExchange#onException(java.lang.Throwable) */ @Override protected void onException(Throwable x) { throwable.set(x); } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.client.HttpExchange#onConnectionFailed(java.lang.Throwable) */ @Override protected void onConnectionFailed(Throwable x) { throwable.set(x); } }; httpExchange.setURI(getBaseURI()); httpExchange.setMethod("SLEEP"); _httpClient.send(httpExchange); new Thread() { @Override public void run() { try { Thread.sleep(500); _httpClient.stop(); } catch(Exception e) {e.printStackTrace();} } }.start(); int status = httpExchange.waitForDone(); System.err.println(throwable.get()); assertTrue(throwable.get().toString().indexOf("close")>=0); assertEquals(HttpExchange.STATUS_EXCEPTED, status); _httpClient.start(); } /* ------------------------------------------------------------ */ @Test public void testBigPostWithContentExchange() throws Exception { int size =32; ContentExchange httpExchange=new ContentExchange() { int total; @Override protected synchronized void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException { if (verbose) System.err.println("] "+version+" "+status+" "+reason); super.onResponseStatus(version,status,reason); } @Override protected synchronized void onResponseHeader(Buffer name, Buffer value) throws IOException { if (verbose) System.err.println("] "+name+": "+value); super.onResponseHeader(name,value); } @Override protected synchronized void onResponseContent(Buffer content) throws IOException { if (verbose) { total+=content.length(); System.err.println("] "+content.length()+" -> "+total); } super.onResponseContent(content); } @Override protected void onRequestComplete() throws IOException { if (verbose) System.err.println("] =="); super.onRequestComplete(); } @Override protected void onResponseHeaderComplete() throws IOException { if (verbose) System.err.println("] --"); super.onResponseHeaderComplete(); } }; Buffer babuf = new ByteArrayBuffer(size*36*1024); Buffer niobuf = new DirectNIOBuffer(size*36*1024); byte[] bytes="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes(); for (int i=0;i<size*1024;i++) { babuf.put(bytes); niobuf.put(bytes); } httpExchange.setURI(getBaseURI()); httpExchange.setMethod(HttpMethods.POST); httpExchange.setRequestContentType("application/data"); httpExchange.setRequestContent(babuf); _httpClient.send(httpExchange); long start=System.currentTimeMillis(); while(!httpExchange.isDone()) { long now=System.currentTimeMillis(); if ((now-start)>=10000) { System.err.println("TEST IS TAKING TOOOOO LONG!!!!!!!!!!!!!!!!!!!!"); System.err.println("CLIENT:"); System.err.println(_httpClient.dump()); System.err.println("SERVER:"); _server.dumpStdErr(); break; } Thread.sleep(100); } int status = httpExchange.waitForDone(); assertEquals(HttpExchange.STATUS_COMPLETED,status); String result=httpExchange.getResponseContent(); assertEquals(babuf.length(),result.length()); httpExchange.reset(); httpExchange.setURI(getBaseURI()); httpExchange.setMethod(HttpMethods.POST); httpExchange.setRequestContentType("application/data"); httpExchange.setRequestContent(niobuf); _httpClient.send(httpExchange); start=System.currentTimeMillis(); while(!httpExchange.isDone()) { long now=System.currentTimeMillis(); if ((now-start)>=10000) { System.err.println("TEST IS TAKING TOOOOO LONG!!!!!!!!!!!!!!!!!!!!"); System.err.println("CLIENT:"); System.err.println(_httpClient.dump()); System.err.println("SERVER:"); _server.dumpStdErr(); break; } Thread.sleep(100); } status = httpExchange.waitForDone(); assertEquals(HttpExchange.STATUS_COMPLETED, status); result=httpExchange.getResponseContent(); assertEquals(niobuf.length(),result.length()); } /* ------------------------------------------------------------ */ @Test public void testSlowPost() throws Exception { ContentExchange httpExchange=new ContentExchange(); httpExchange.setURI(getBaseURI()); httpExchange.setMethod(HttpMethods.POST); final String data="012345678901234567890123456789012345678901234567890123456789"; InputStream content = new InputStream() { int _index=0; @Override public int read() throws IOException { // System.err.printf("reading 1 of %d/%d%n",_index,data.length()); if (_index>=data.length()) return -1; try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } // System.err.printf("read 1%n"); return data.charAt(_index++); } @Override public int read(byte[] b, int off, int len) throws IOException { // System.err.printf("reading %d of %d/%d%n",len,_index,data.length()); if (_index >= data.length()) return -1; try { Thread.sleep(25); } catch (InterruptedException e) { e.printStackTrace(); } int l = 0; while (l < 5 && _index < data.length() && l < len) b[off + l++] = (byte)data.charAt(_index++); // System.err.printf("read %d%n",l); return l; } }; httpExchange.setRequestContentSource(content); _httpClient.send(httpExchange); int status = httpExchange.waitForDone(); String result = httpExchange.getResponseContent(); assertEquals(HttpExchange.STATUS_COMPLETED,status); assertEquals(data,result); } /* ------------------------------------------------------------ */ @Test public void testProxy() throws Exception { if (_scheme.equals("https")) return; try { _httpClient.setProxy(new Address("127.0.0.1",_port)); _httpClient.setProxyAuthentication(new ProxyAuthorization("user","password")); ContentExchange httpExchange=new ContentExchange(); httpExchange.setAddress(new Address("jetty.eclipse.org",8080)); httpExchange.setMethod(HttpMethods.GET); httpExchange.setRequestURI("/jetty-6"); _httpClient.send(httpExchange); int status = httpExchange.waitForDone(); //httpExchange.waitForStatus(HttpExchange.STATUS_COMPLETED); String result=httpExchange.getResponseContent(); assertNotNull("Should have received response content", result); result=result.trim(); assertEquals(HttpExchange.STATUS_COMPLETED, status); assertTrue(result.startsWith("Proxy request: http://jetty.eclipse.org:8080/jetty-6")); assertTrue(result.endsWith("Basic dXNlcjpwYXNzd29yZA==")); } finally { _httpClient.setProxy(null); } } /* ------------------------------------------------------------ */ @Test public void testReserveConnections () throws Exception { _httpClient = serverAndClientCreator.createClient(3000L,3500L,2000); final HttpDestination destination = _httpClient.getDestination(new Address("localhost",_port),_scheme.equalsIgnoreCase("https")); final org.eclipse.jetty.client.AbstractHttpConnection[] connections = new org.eclipse.jetty.client.AbstractHttpConnection[_maxConnectionsPerAddress]; for (int i = 0; i < _maxConnectionsPerAddress; i++) { connections[i] = destination.reserveConnection(200); assertNotNull(connections[i]); HttpExchange ex = new ContentExchange(); ex.setURI(getBaseURI().resolve("?i=" + i)); ex.setMethod(HttpMethods.GET); connections[i].send(ex); } // try to get a connection, and only wait 500ms, as we have // already reserved the max, should return null Connection c = destination.reserveConnection(500); assertNull(c); // unreserve first connection destination.returnConnection(connections[0],false); // reserving one should now work c = destination.reserveConnection(500); assertNotNull(c); // release connections for (AbstractHttpConnection httpConnection : connections){ destination.returnConnection(httpConnection,false); } } @Test public void testOptionsWithExchange() throws Exception { ContentExchange httpExchange = new ContentExchange(true); httpExchange.setURL(getBaseURI().toASCIIString()); httpExchange.setRequestURI("*"); httpExchange.setMethod(HttpMethods.OPTIONS); // httpExchange.setRequestHeader("Connection","close"); _httpClient.send(httpExchange); int state = httpExchange.waitForDone(); assertEquals(HttpExchange.STATUS_COMPLETED, state); assertEquals(HttpStatus.OK_200,httpExchange.getResponseStatus()); HttpFields headers = httpExchange.getResponseFields(); HttpAsserts.assertContainsHeaderKey("Content-Length", headers); assertEquals("Content-Length header value", 0, headers.getLongField("Content-Length")); HttpAsserts.assertContainsHeaderKey("Allow",headers); String allow = headers.getStringField("Allow"); String expectedMethods[] = { "GET", "HEAD", "POST", "PUT", "DELETE", "MOVE", "OPTIONS", "TRACE" }; for (String expectedMethod : expectedMethods) { assertThat(allow,containsString(expectedMethod)); } } /* ------------------------------------------------------------ */ public static void copyStream(InputStream in, OutputStream out) { try { byte[] buffer = new byte[1024]; int len; while ((len = in.read(buffer)) >= 0) { out.write(buffer,0,len); } } catch (EofException e) { System.err.println("HttpExchangeTest#copyStream: " + e); } catch (IOException e) { e.printStackTrace(); } } }