package com.limegroup.gnutella; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.Socket; import java.util.HashMap; import java.util.Map; import java.util.Vector; import junit.framework.Test; import com.limegroup.gnutella.auth.ContentManager; import com.limegroup.gnutella.auth.StubContentAuthority; import com.limegroup.gnutella.downloader.ConnectionStatus; import com.limegroup.gnutella.downloader.HTTPDownloader; import com.limegroup.gnutella.downloader.QueuedException; import com.limegroup.gnutella.downloader.TryAgainLaterException; import com.limegroup.gnutella.downloader.UnknownCodeException; import com.limegroup.gnutella.downloader.VerifyingFile; import com.limegroup.gnutella.http.HTTPRequestMethod; import com.limegroup.gnutella.messages.vendor.ContentRequest; import com.limegroup.gnutella.messages.vendor.ContentResponse; import com.limegroup.gnutella.settings.ContentSettings; import com.limegroup.gnutella.settings.UploadSettings; import com.limegroup.gnutella.stubs.ActivityCallbackStub; import com.limegroup.gnutella.stubs.FileDescStub; import com.limegroup.gnutella.stubs.FileManagerStub; import com.limegroup.gnutella.uploader.StalledUploadWatchdog; import com.limegroup.gnutella.util.CommonUtils; import com.limegroup.gnutella.util.PipedSocketFactory; import com.limegroup.gnutella.util.PrivilegedAccessor; public class UploaderTest extends com.limegroup.gnutella.util.BaseTestCase { private ActivityCallback ac; private static FileManager fm; private RouterService rs; private UploadManager upManager; private RemoteFileDesc rfd1; private RemoteFileDesc rfd2; private RemoteFileDesc rfd3; private RemoteFileDesc rfd4; private RemoteFileDesc rfd5; private RemoteFileDesc defaultRfd; private static FileDescStub defaultStub; private URN urn1,urn2,urn3,urn4,urn5; public void setUp() throws Exception { ac = new ActivityCallbackStub(); Map urns = new HashMap(); Vector descs = new Vector(); urn1 = URN.createSHA1Urn("urn:sha1:PLSTHIPQGSSZTS5FJUPAKUZWUGYQYPFG"); urn2 = URN.createSHA1Urn("urn:sha1:PLSTHIPQGSSZTS5FJUPAKUZWUGYQYPFF"); urn3 = URN.createSHA1Urn("urn:sha1:PLSTHIPQGSSZTS5FJUPAKUZWUGYQYPFE"); urn4 = URN.createSHA1Urn("urn:sha1:PLSTHIPQGSSZTS5FJUPAKUZWUGYQYPFD"); urn5 = URN.createSHA1Urn("urn:sha1:PLSTHIPQGSSZTS5FJUPAKUZWUGYQYPFC"); defaultStub = new FileDescStub(); defaultRfd = new RemoteFileDesc("1.1.1.1",1,10,"abc.txt",FileDescStub.DEFAULT_SIZE, new byte[16], 56, false, 3, false, null, FileDescStub.DEFAULT_SET, false, false,"",0, null, -1); FileDescStub descStub = new FileDescStub("abc1.txt",urn1,0); urns.put(urn1,descStub); descs.add(descStub); rfd1 = new RemoteFileDesc("1.1.1.1",1,0,"abc1.txt",FileDescStub.DEFAULT_SIZE, new byte[16], 56, false, 3, false, null, descStub.getUrns(), false, false,"",0, null, -1); descStub = new FileDescStub("abc2.txt",urn2,1); urns.put(urn2,descStub); descs.add(descStub); rfd2 = new RemoteFileDesc("1.1.1.2",1,1,"abc2.txt",FileDescStub.DEFAULT_SIZE, new byte[16], 56, false, 3, false, null, descStub.getUrns(), false, false,"",0, null, -1); descStub = new FileDescStub("abc3.txt",urn3,2); urns.put(urn3,descStub); descs.add(descStub); rfd3 = new RemoteFileDesc("1.1.1.3",1,2,"abc3.txt",FileDescStub.DEFAULT_SIZE, new byte[16], 56, false, 3, false, null, descStub.getUrns(), false, false,"",0, null, -1); descStub = new FileDescStub("abc4.txt",urn4,3); urns.put(urn4,descStub); descs.add(descStub); rfd4 = new RemoteFileDesc("1.1.1.4",1,3,"abc4.txt",FileDescStub.DEFAULT_SIZE, new byte[16], 56, false, 3, false, null, descStub.getUrns(), false, false,"",0, null, -1); descStub = new FileDescStub("abc5.txt",urn5,4); urns.put(urn5,descStub); descs.add(descStub); rfd5 = new RemoteFileDesc("1.1.1.5",1,4,"abc5.txt",FileDescStub.DEFAULT_SIZE, new byte[16], 56, false, 3, false, null, descStub.getUrns(), false, false,"",0, null, -1); // we don't want the tests confused by the stalled // watchdog killing stuff. // note that the testStalledUploads sets this to the // correct time. StalledUploadWatchdog.DELAY_TIME = Integer.MAX_VALUE; fm = new FileManagerStub(urns,descs); rs = new RouterService(ac); RouterService.getContentManager().initialize(); upManager = new UploadManager(); PrivilegedAccessor.setValue(rs,"fileManager",fm); PrivilegedAccessor.setValue(rs,"uploadManager", upManager); fm.get(0); } public UploaderTest(String name) { super(name); } public void tearDown() { ac = null; fm = null; rs = null; upManager = null; rfd1 = null; rfd2 = null; rfd3 = null; rfd4 = null; rfd5 = null; } public static Test suite() { return buildTestSuite(UploaderTest.class); } /** * - Bandwidth tracker works properly. */ public void testLegacy() { UploadManager.tBandwidthTracker(new UploadManager()); } /** * Tests that an upload triggers a validation. */ public void testContentVerified() throws Exception { ContentSettings.CONTENT_MANAGEMENT_ACTIVE.setValue(true); ContentSettings.USER_WANTS_MANAGEMENTS.setValue(true); UploadSettings.HARD_MAX_UPLOADS.setValue(2); UploadSettings.SOFT_MAX_UPLOADS.setValue(9999); UploadSettings.UPLOADS_PER_PERSON.setValue(99999); UploadSettings.UPLOAD_QUEUE_SIZE.setValue(2); StubContentAuthority auth = new StubContentAuthority(); RouterService.getContentManager().setContentAuthority(auth); assertEquals(0, auth.getSent().size()); HTTPDownloader d1 = addUploader(upManager,rfd1,"1.1.1.1",true); connectDloader(d1,true,rfd1,true); assertEquals(1, auth.getSent().size()); assertEquals(urn1, ((ContentRequest)auth.getSent().get(0)).getURN()); kill(d1); d1 = addUploader(upManager,rfd1,"1.1.1.1",true); connectDloader(d1, true, rfd1, true); assertEquals(1, auth.getSent().size()); // didn't send again. } /** * Tests that: * - uploads upto maxUploads get slots * - uploader after that but upto uploadQueueSize get queued * - uploads beyond that get try again later * - when an uploade with slot terminates first uploader gets slot * - when an uploader with slot terminates, everyone in queue advances. * - uploads not in slot one, but uploader has available for all get slot */ public void testNormalQueueing() throws Exception { UploadSettings.HARD_MAX_UPLOADS.setValue(2); UploadSettings.SOFT_MAX_UPLOADS.setValue(9999); UploadSettings.UPLOADS_PER_PERSON.setValue(99999); UploadSettings.UPLOAD_QUEUE_SIZE.setValue(2); HTTPDownloader d3 = null; //first two uploads to get slots HTTPDownloader d1 = addUploader(upManager,rfd1,"1.1.1.1",true); connectDloader(d1,true,rfd1,true); HTTPDownloader d2 = addUploader(upManager,rfd2,"1.1.1.2",true); connectDloader(d2,true,rfd2,true); assertEquals("should have two active uploaders", 2, upManager.uploadsInProgress()); try { //queued at 1st position d3 = addUploader(upManager,rfd3,"1.1.1.3",true); connectDloader(d3,true,rfd3,true); fail("uploader should have been queued, but was given slot"); } catch(QueuedException qx) { assertEquals(1, qx.getQueuePosition()); } catch (Exception ioe) { fail("not queued", ioe); } assertEquals("should have 1 queued uploader", 1, upManager.getNumQueuedUploads()); HTTPDownloader d4 = null; try { //queued at 2nd position d4 = addUploader(upManager,rfd4,"1.1.1.4",true); connectDloader(d4,true,rfd4,true); fail("uploader should have been queued, but was given slot"); } catch(QueuedException qx) { assertEquals(2,qx.getQueuePosition()); } catch (Exception ioe) { fail("not queued", ioe); } assertEquals("should have 2 queued uploaders", 2, upManager.getNumQueuedUploads()); HTTPDownloader d5 = null; try { //rejected d5 = addUploader(upManager,rfd5,"1.1.1.5",true); connectDloader(d5,true,rfd5,true); fail("downloader should have been rejected"); } catch (QueuedException qx) { fail("queued after capacity ", qx); } catch (TryAgainLaterException tax) {//correct } catch (IOException ioe) {//TODO1: Is this really acceptable?? //this is OK(see TODO) for now, since the uploader //may close the socket } //free up a slot kill(d1);//now the queue should free up, but we need to request assertEquals("should have 1 active uploader", 1, upManager.uploadsInProgress()); //wait till min Poll time + 2 seconds to be safe, we have been //burned by this before - if d4 responds too fast it gets //disconnected Thread.sleep((UploadManager.MIN_POLL_TIME+ UploadManager.MAX_POLL_TIME)/2); //test that uploaders cannot jump the line try { //still queued - cannot jump line. connectDloader(d4,false, rfd4,true); fail("uploader allowed to jump the line"); } catch (QueuedException qx) { assertEquals("should be queued", 2, qx.getQueuePosition()); } catch (Exception e) {//any other is bad fail("wrong exception thrown", e); } assertEquals("should have 2 queued uploaders", 2, upManager.getNumQueuedUploads()); //test that first uploader in queue is given the slot try { //should get the slot connectDloader(d3,false,rfd3,true); } catch (QueuedException e) { fail("downloader should have got slot, but was queued at: " + e.getQueuePosition(), e); } assertEquals("should have 1 queued upload", 1, upManager.getNumQueuedUploads()); assertEquals("should have 2 active uploads", 2, upManager.uploadsInProgress()); //Test that uploads in queue advance. d4 should have 0th position Thread.sleep((UploadManager.MIN_POLL_TIME+ UploadManager.MAX_POLL_TIME)/2); try { connectDloader(d4,false,rfd4,true); } catch(QueuedException qx) { assertEquals(1,qx.getQueuePosition()); } assertEquals("should have 1 queued upload", 1, upManager.getNumQueuedUploads()); assertEquals("should have 2 active uploads", 2, upManager.uploadsInProgress()); //Add another queued guy try { //queued at 2nd position d5 = addUploader(upManager,rfd5,"1.1.1.5",true); connectDloader(d5,true,rfd5,true); fail("uploader should have been queued, but was given slot"); } catch(QueuedException qx) { assertEquals(2,qx.getQueuePosition()); } catch (Exception ioe) { fail("not queued", ioe); } assertEquals("should have 2 queued uploads", 2, upManager.getNumQueuedUploads()); assertEquals("should have 2 active uploads", 2, upManager.uploadsInProgress()); // Now kill both uploads in progress and see if // the second queued guy can get his slot before // the first one polls (he should). // D4 & D5 are queued // D3 & D2 are active kill(d3); kill(d2); assertEquals("should have 2 queued uploads", 2, upManager.getNumQueuedUploads()); assertEquals("should have 0 active uploads", 0, upManager.uploadsInProgress()); // Sleep so we don't hammer with requests. Thread.sleep((UploadManager.MIN_POLL_TIME+ UploadManager.MAX_POLL_TIME)/2); //test that second uploader is given a slot. try { connectDloader(d5,false,rfd5,true); } catch (QueuedException e) { fail("downloader should have got slot, but was queued at: " + e.getQueuePosition(), e); } //test that the first uploader is also given a slot. try { connectDloader(d4,false,rfd4,true); } catch (QueuedException e) { fail("downloader should have got slot, but was queued at: " + e.getQueuePosition(), e); } assertEquals("should have 0 queued uploads", 0, upManager.getNumQueuedUploads()); assertEquals("should have 2 active uploads", 2, upManager.uploadsInProgress()); } public void testThexQueuing() throws Exception { UploadSettings.HARD_MAX_UPLOADS.setValue(2); UploadSettings.SOFT_MAX_UPLOADS.setValue(9999); UploadSettings.UPLOADS_PER_PERSON.setValue(99999); UploadSettings.UPLOAD_QUEUE_SIZE.setValue(2); for(int i = 0;i<5;i++) fm.get(i).getHashTree(); Thread.sleep(5000); HTTPDownloader d3 = null; //first two uploads to get slots HTTPDownloader d1 = addUploader(upManager,rfd1,"1.1.1.1",true); connectDloader(d1,true,rfd1,true); HTTPDownloader d2 = addUploader(upManager,rfd2,"1.1.1.2",true); connectDloader(d2,true,rfd2,true); try { //queued at 0th position d3 = addUploader(upManager,rfd3,"1.1.1.3",true); connectDloader(d3,true,rfd3,true); fail("uploader should have been queued, but was given slot"); } catch(QueuedException qx) { assertEquals(1, qx.getQueuePosition()); } assertEquals("should have 1 queued uploads", 1, upManager.getNumQueuedUploads()); assertEquals("should have 2 active uploads", 2, upManager.uploadsInProgress()); // should queue next guy, but 'cause its thex we wont. HTTPDownloader d4 = addUploader(upManager,rfd4,"1.1.1.4",true); ConnectionStatus status = connectThex(d4, true); assertTrue(status.isThexResponse()); // d5 will connect and get queued. HTTPDownloader d5 = addUploader(upManager, rfd2, "1.1.1.5", true); try { connectDloader(d5, true, rfd2, true); fail("should have been queued"); } catch(QueuedException qx) { assertEquals(2, qx.getQueuePosition()); } assertEquals("should have 1 queued uploads", 2, upManager.getNumQueuedUploads()); assertEquals("should have 2 active uploads", 2, upManager.uploadsInProgress()); //even though 5 is queued, he should be able to get the thex tree. status = connectThex(d5, false); assertTrue(status.isThexResponse()); // no need to check the tree, is checked in lots of other tests. //but, when he tries to get the file again, he stays queued. assertEquals("should have 1 queued uploads", 2, upManager.getNumQueuedUploads()); assertEquals("should have 2 active uploads", 2, upManager.uploadsInProgress()); Thread.sleep((UploadManager.MIN_POLL_TIME+ UploadManager.MAX_POLL_TIME)/2); try { connectDloader(d5, false, rfd2, true); fail("should have been queued"); } catch(QueuedException qx) { assertEquals(2, qx.getQueuePosition()); } } /** * Tests that two requests for the same file on the same connection, does * not cause the second request to be queued. */ public void testSameFileSameHostGivenSlot() throws Exception { UploadSettings.HARD_MAX_UPLOADS.setValue(1); UploadSettings.SOFT_MAX_UPLOADS.setValue(1); UploadSettings.UPLOADS_PER_PERSON.setValue(99999); UploadSettings.UPLOAD_QUEUE_SIZE.setValue(2); //connect the first downloader. PipedSocketFactory psf = null; try { psf = new PipedSocketFactory("127.0.0.1", "1.1.1.1"); } catch (Exception e) { fail("unable to create piped socket factory", e); } final Socket sa = psf.getSocketA(); final UploadManager upman = upManager; Thread t = new Thread() { public void run() { try { upman.acceptUpload(HTTPRequestMethod.GET, sa, false); } catch(Throwable e) { ErrorService.error(e); } } }; t.setDaemon(true); t.start(); BufferedWriter out = null; ByteReader byteReader = null; String s = null; try { Socket sb = psf.getSocketB(); OutputStream os = sb.getOutputStream(); out = new BufferedWriter(new OutputStreamWriter(os)); out.write("GET /get/0/abc1.txt HTTP/1.1\r\n"); out.write("User-Agent: "+CommonUtils.getHttpServer()+"\r\n"); out.write("X-Queue: 0.1\r\n");//we support remote queueing out.write("Range: bytes=0-12000 \r\n"); out.write("\r\n"); out.flush(); byteReader = new ByteReader(sb.getInputStream()); s = byteReader.readLine(); assertEquals("HTTP/1.1 206 Partial Content",s); } catch (Exception e) { fail("problem with first downloader", e); } //second request. This one will be queued at position 1 try { HTTPDownloader d2 = addUploader(upManager,rfd2,"1.1.1.2",true); connectDloader(d2,true,rfd2,true); fail("d2 should not have been given slot"); } catch (QueuedException qEx) { assertEquals("should be first in queue", 1,qEx.getQueuePosition()); } catch (Exception other) { fail("download should have been queued", other); } //second request from the uploader which already has the slot try { while(!s.equals("")) s = byteReader.readLine(); byte[] buf = new byte[1024]; int d=0; while(true) { int u = byteReader.read(buf,0,1024); d+=u; if(d>11999) break; } out.write("GET /get/0/abc1.txt HTTP/1.1\r\n"); out.write("User-Agent: "+CommonUtils.getHttpServer()+"\r\n"); out.write("X-Queue: 0.1\r\n");//we support remote queueing out.write("Range: bytes=1200-2400 \r\n"); out.write("\r\n"); out.flush(); s = byteReader.readLine(); assertEquals("HTTP/1.1 206 Partial Content",s); } catch(Exception e) { fail("exception thrown while trying HTTP 1.1 pipling", e); } //third uploader must be queued at position 2. try { HTTPDownloader d3 = addUploader(upManager,rfd3,"1.1.1.3",true); connectDloader(d3,true,rfd2,true); } catch (QueuedException q) { assertEquals("should be second in queue", 2,q.getQueuePosition()); } catch (Exception other) { fail("download should have been queued", other); } //System.out.println("Passed"); } public void testBanning() throws Exception { UploadSettings.HARD_MAX_UPLOADS.setValue(2); UploadSettings.SOFT_MAX_UPLOADS.setValue(9999); UploadSettings.UPLOADS_PER_PERSON.setValue(2); UploadSettings.UPLOAD_QUEUE_SIZE.setValue(10); Class cache = PrivilegedAccessor.getClass(UploadManager.class, "RequestCache"); PrivilegedAccessor.setValue(cache, "WAIT_TIME", new Long(20*1000)); PrivilegedAccessor.setValue(cache, "FIRST_CHECK_TIME", new Long(10*1000)); HTTPDownloader d1 = addUploader(upManager,rfd1,"1.1.1.1",true); connectDloader(d1,true,rfd1,true); HTTPDownloader d2 = addUploader(upManager,rfd2,"1.1.1.1",true); connectDloader(d2,true,rfd2,true); HTTPDownloader d3 = addUploader(upManager,rfd3,"1.1.1.1",true); try { connectDloader(d3,true,rfd3,true); fail("Host limit reached, should not have accepted d3"); } catch (QueuedException qx) { fail("host limit reached should not queue", qx); } catch (TryAgainLaterException expectedException) { } catch (IOException ioe) {//This is similar to d5 being rejected //in testNormalQueueing, IOE may be thrown if uploader //closes the socket. } HTTPDownloader d4 = addUploader(upManager,rfd4,"1.1.1.4",true); try { connectDloader(d4,true,rfd4,true); fail("Host limit reached, should not have accepted d4"); } catch (TryAgainLaterException tx) { fail("d4 should have been queued", tx); } catch (QueuedException expectedException) { } catch (IOException ioe) { fail("d4 should have been queued", ioe); } assertEquals("should have 1 queued uploads", 1, upManager.getNumQueuedUploads()); assertEquals("should have 2 active uploads", 2, upManager.uploadsInProgress()); kill(d1); kill(d2); assertEquals("should have 1 queued uploads", 1, upManager.getNumQueuedUploads()); assertEquals("should have 0 active uploads", 0, upManager.uploadsInProgress()); // We now have room for d3 to connect again, but it shouldn't // be allowed because the 15 minute alloted timespan isn't up // yet. try { d3 = addUploader(upManager,rfd3,"1.1.1.1",true); connectDloader(d3,true,rfd1,true); fail("d3 is greedy, should have sent limit reached"); } catch (QueuedException qx) { fail("host limit reached should not queue", qx); } catch (TryAgainLaterException expectedException) { } catch (IOException ioe) {//This is similar to d5 being rejected //in testNormalQueueing, IOE may be thrown if uploader //closes the socket. } assertEquals("should have 1 queued uploads", 1, upManager.getNumQueuedUploads()); assertEquals("should have 0 active uploads", 0, upManager.uploadsInProgress()); int i = 0; try { for(; i < 20; i++) { Thread.sleep(1000); try { d3 = addUploader(upManager, rfd3, "1.1.1.1", true); connectDloader(d3, true, rfd1, true); fail("should have thrown limit reached"); } catch(TryAgainLaterException expected) {} catch(IOException expectedToo) { if(expectedToo instanceof UnknownCodeException) throw expectedToo; } } fail("should have thrown exception"); } catch(UnknownCodeException expected) { assertEquals("wrong code", 403, expected.getCode()); } assertEquals("should have 1 queued uploads", 1, upManager.getNumQueuedUploads()); assertEquals("should have 0 active uploads", 0, upManager.uploadsInProgress()); // now wait awhile and we should be allowed back in. Thread.sleep(60000); d3 = addUploader(upManager, rfd3, "1.1.1.1", true); connectDloader(d3, true, rfd1, true); // now wait awhile and we should be allowed back in. assertEquals("should have 1 active uploads", 1, upManager.uploadsInProgress()); } public void testUniqueUploads() throws Exception { UploadSettings.HARD_MAX_UPLOADS.setValue(2); UploadSettings.SOFT_MAX_UPLOADS.setValue(9999); UploadSettings.UPLOADS_PER_PERSON.setValue(2); UploadSettings.UPLOAD_QUEUE_SIZE.setValue(2); HTTPDownloader d1 = addUploader(upManager,rfd1,"1.1.1.1",true); connectDloader(d1,true,rfd1,true); HTTPDownloader d2 = addUploader(upManager,rfd1,"1.1.1.1",true); try{ connectDloader(d2,true,rfd2,true); fail("Duplicate upload, should not have accepted d2"); }catch (QueuedException qx) { fail("duplicate upload should not queue", qx); } catch (TryAgainLaterException expectedException) { }catch(IOException ioe){} d2 = addUploader(upManager,rfd2,"1.1.1.2",true); connectDloader(d2,true,rfd2,true); //upload slots are full, queue is empty HTTPDownloader d3 = addUploader(upManager,rfd2,"1.1.1.2",true); try { connectDloader(d3,true,rfd2,true); fail("duplicate upload, should not have accepted d3"); } catch (QueuedException qx) { fail("duplicate upload should not queue", qx); } catch (TryAgainLaterException expectedException) { } catch (IOException ioe) {//This is similar to d5 being rejected //in testNormalQueueing, IOE may be thrown if uploader //closes the socket. } //upload slots are still full, queue is still empty HTTPDownloader d4 = addUploader(upManager,rfd4,"1.1.1.4",true); try { connectDloader(d4,true,rfd4,true); fail("slots are full, d4 should have been queued"); } catch (TryAgainLaterException tx) { fail("d4 should have been queued", tx); } catch (QueuedException expectedException) { } catch (IOException ioe) { fail("d4 should have been queued", ioe); } //System.out.println("passed"); } /** * Makes sure a stalled uploader is disconnected. * (We need to have a queue size of 1 because 0 isn't allowed, so * the test is slightly more complicated than it needs to be.) */ public void testStalledUploader() throws Exception { StalledUploadWatchdog.DELAY_TIME = 1000 * 30; // 30 seconds. UploadSettings.HARD_MAX_UPLOADS.setValue(2); UploadSettings.SOFT_MAX_UPLOADS.setValue(9999); UploadSettings.UPLOADS_PER_PERSON.setValue(99999); UploadSettings.UPLOAD_QUEUE_SIZE.setValue(1); HTTPDownloader d1, d2, d3, d4; d1 = addUploader(upManager, rfd1, "1.1.1.1", true); connectDloader(d1,true,rfd1,true); d2 = addUploader(upManager, rfd2, "1.1.1.2", true); connectDloader(d2,true,rfd2,true); assertEquals("should have 2 active uploaders", 2, upManager.uploadsInProgress() ); // assert that we can reach the limit d3 = addUploader(upManager, rfd3, "1.1.1.3", true); try { connectDloader(d3,true,rfd3,true); fail("expected queued later."); } catch(QueuedException e) { assertEquals(1, e.getQueuePosition()); } assertEquals("should have 1 queued uploader", 1, upManager.getNumQueuedUploads()); // okay, we know its full, now drop the guy from the queue. try { connectDloader(d3, false, rfd3, true); fail("should have thrown ioexception"); } catch(IOException e) { // expected behaviour. } assertEquals("should have no queued uploaders", 0, upManager.getNumQueuedUploads()); //sleep a little more than needed for the stall to die. Thread.sleep(StalledUploadWatchdog.DELAY_TIME+5*1000); assertEquals("should have no active uploaders", 0, upManager.uploadsInProgress()); // should be able to connect now. d3 = addUploader(upManager, rfd3, "1.1.1.3", false); connectDloader(d3, true, rfd3, true); d4 = addUploader(upManager, rfd4, "1.1.1.4", false); connectDloader(d4,true,rfd4,true); kill(d3); kill(d4); } /** * Makes sure that if downloaders reconnect too soon they are dropped * also if uploaders respond too late they should be dropped. Downloader * MUST respond bewteen MIN_POLL_TIME and MAX_POLL_TIME */ public void testQueueTiming() throws Exception { UploadSettings.HARD_MAX_UPLOADS.setValue(2); UploadSettings.SOFT_MAX_UPLOADS.setValue(9999); UploadSettings.UPLOADS_PER_PERSON.setValue(99999); UploadSettings.UPLOAD_QUEUE_SIZE.setValue(2); HTTPDownloader d3 = null; //first two uploads to get slots HTTPDownloader d1 = addUploader(upManager,rfd1,"1.1.1.1",true); connectDloader(d1,true,rfd1,true); HTTPDownloader d2 = addUploader(upManager,rfd2,"1.1.1.2",true); connectDloader(d2,true,rfd2,true); try { //queued at 0th position d3 = addUploader(upManager,rfd3,"1.1.1.3",true); connectDloader(d3,true,rfd3,true); fail("uploader should have been queued, but was given slot"); } catch(QueuedException qx) { assertEquals(1, qx.getQueuePosition()); } catch (Exception ioe) { fail("not queued", ioe); } HTTPDownloader d4 = null; try { //queued at 1st position d4 = addUploader(upManager,rfd4,"1.1.1.4",true); connectDloader(d4,true,rfd4,true); fail("uploader should have been queued, but was given slot"); } catch(QueuedException qx) { assertEquals(2,qx.getQueuePosition()); } catch (Exception ioe) { fail("not queued", ioe); } //OK. we have two uploaders queued. one is going to respond //too soon and the other too late - both will be dropped. try { //too soon. connectDloader(d3,false,rfd3,true); fail("downloader should have been dropped"); } catch (QueuedException qx) { fail("Should have been dropped, not queued", qx); } catch (IOException ioe) {//correct behavoiour } //TODO3: The test below does not work because we are not using // real sockets, so the setting the soTimeout does not work. //reading after soTimeout does not cause an exception to be thrown // for now, we will have to test this manually // Thread.sleep(UploadManager.MAX_POLL_TIME+10);//Now we are too late // try { // connectDloader(d4,false,rfd4,true); // fail("downloader should have been dropped"); // } catch(QueuedException qx) { // fail("downloader should have been dropped, its queued"); // } catch(TryAgainLaterException talx) { // fail("downloader should have been dropped talx thrown"); // } catch (IOException iox) { //correct behaviour // } catch (Exception other) { // other.printStackTrace(); // fail("unknown exception thrown"); // } //System.out.println("Passed"); } /** * Tests that Uploader only queues downloaders that specify that they * support queueing */ public void testNotQueuedUnlessHeaderSent() throws Exception { UploadSettings.HARD_MAX_UPLOADS.setValue(1); UploadSettings.SOFT_MAX_UPLOADS.setValue(9999); UploadSettings.UPLOADS_PER_PERSON.setValue(99999); UploadSettings.UPLOAD_QUEUE_SIZE.setValue(1); //take the only available slto HTTPDownloader d1 = addUploader(upManager,rfd1,"1.1.1.1",true); connectDloader(d1,true,rfd1,true); //now there are no slots HTTPDownloader d2 = addUploader(upManager,rfd2,"1.1.1.2",true); try { connectDloader(d2,true,rfd2,false);//dont queue fail("d2 was accepted after slot limit reached"); } catch(QueuedException qe) { fail("Downloader d2 not send x-queue, but was queued", qe); } catch (TryAgainLaterException expectedException) { } catch (IOException ioe) {//This is similar to d5 being rejected //in testNormalQueueing, IOE may be thrown if uploader //closes the socket. } HTTPDownloader d3 = addUploader(upManager,rfd3,"1.1.1.3",true); try { connectDloader(d3,true,rfd3,true); fail("d3 was accepted instead of being queueud"); } catch(TryAgainLaterException tx) { fail("d3 should have been queued TALX thrown", tx); } catch (QueuedException expectedException) { assertEquals("should be first in queue", 1,expectedException.getQueuePosition()); } //System.out.println("Passed"); } public void testPerHostLimitedNotQueued() throws Exception { UploadSettings.HARD_MAX_UPLOADS.setValue(2); UploadSettings.SOFT_MAX_UPLOADS.setValue(9999); UploadSettings.UPLOADS_PER_PERSON.setValue(2); UploadSettings.UPLOAD_QUEUE_SIZE.setValue(2); HTTPDownloader d1 = addUploader(upManager,rfd1,"1.1.1.1",true); connectDloader(d1,true,rfd1,true); HTTPDownloader d2 = addUploader(upManager,rfd2,"1.1.1.1",true); connectDloader(d2,true,rfd2,true); HTTPDownloader d3 = addUploader(upManager,rfd3,"1.1.1.1",true); try { connectDloader(d3,true,rfd3,true); fail("Host limit reached, should not have accepted d3"); } catch (QueuedException qx) { fail("host limit reached should not queue", qx); } catch (TryAgainLaterException expectedException) { } catch (IOException ioe) {//This is similar to d5 being rejected //in testNormalQueueing, IOE may be thrown if uploader //closes the socket. } HTTPDownloader d4 = addUploader(upManager,rfd4,"1.1.1.4",true); try { connectDloader(d4,true,rfd1,true); fail("Host limit reached, should not have accepted d4"); } catch (TryAgainLaterException tx) { fail("d4 should have been queued", tx); } catch (QueuedException expectedException) { } catch (IOException ioe) { fail("d4 should have been queued", ioe); } //System.out.println("passed"); } public void testGreedyLimitReached() throws Exception { UploadSettings.HARD_MAX_UPLOADS.setValue(2); UploadSettings.SOFT_MAX_UPLOADS.setValue(9999); UploadSettings.UPLOADS_PER_PERSON.setValue(2); UploadSettings.UPLOAD_QUEUE_SIZE.setValue(10); HTTPDownloader d1 = addUploader(upManager,rfd1,"1.1.1.1",true); connectDloader(d1,true,rfd1,true); HTTPDownloader d2 = addUploader(upManager,rfd2,"1.1.1.1",true); connectDloader(d2,true,rfd1,true); HTTPDownloader d3 = addUploader(upManager,rfd3,"1.1.1.1",true); try { connectDloader(d3,true,rfd1,true); fail("Host limit reached, should not have accepted d3"); } catch (QueuedException qx) { fail("host limit reached should not queue", qx); } catch (TryAgainLaterException expectedException) { } catch (IOException ioe) {//This is similar to d5 being rejected //in testNormalQueueing, IOE may be thrown if uploader //closes the socket. } HTTPDownloader d4 = addUploader(upManager,rfd4,"1.1.1.2",true); try { connectDloader(d4,true,rfd1,true); fail("Host limit reached, should not have accepted d4"); } catch (TryAgainLaterException tx) { fail("d4 should have been queued", tx); } catch (QueuedException expectedException) { } catch (IOException ioe) { fail("d4 should have been queued", ioe); } assertEquals("should have 1 queued uploads", 1, upManager.getNumQueuedUploads()); assertEquals("should have 2 active uploads", 2, upManager.uploadsInProgress()); kill(d1); kill(d2); assertEquals("should have 1 queued uploads", 1, upManager.getNumQueuedUploads()); assertEquals("should have 0 active uploads", 0, upManager.uploadsInProgress()); // We now have room for d3 to connect again, but it shouldn't // be allowed because the 15 minute alloted timespan isn't up // yet. try { d3 = addUploader(upManager,rfd3,"1.1.1.1",true); connectDloader(d3,true,rfd1,true); fail("d3 is greedy, should have sent limit reached"); } catch (QueuedException qx) { fail("host limit reached should not queue", qx); } catch (TryAgainLaterException expectedException) { } catch (IOException ioe) {//This is similar to d5 being rejected //in testNormalQueueing, IOE may be thrown if uploader //closes the socket. } assertEquals("should have 1 queued uploads", 1, upManager.getNumQueuedUploads()); assertEquals("should have 0 active uploads", 0, upManager.uploadsInProgress()); } public void testSoftMax() throws Exception { UploadSettings.HARD_MAX_UPLOADS.setValue(9999); UploadSettings.SOFT_MAX_UPLOADS.setValue(2); UploadSettings.UPLOADS_PER_PERSON.setValue(99999); UploadSettings.UPLOAD_QUEUE_SIZE.setValue(2); HTTPDownloader d3 = null; //first two uploads to get slots HTTPDownloader d1 = addUploader(upManager,rfd1,"1.1.1.1",true); connectDloader(d1,true,rfd1,true); HTTPDownloader d2 = addUploader(upManager,rfd2,"1.1.1.2",true); connectDloader(d2,true,rfd2,true); try { //queued at 1st position d3 = addUploader(upManager,rfd3,"1.1.1.3",true); connectDloader(d3,true,rfd3,true); fail("uploader should have been queued, but was given slot"); } catch(QueuedException qx) { assertEquals("should be first in queue", 1, qx.getQueuePosition()); } catch (Exception ioe) { fail("not queued", ioe); } kill(d1); Thread.sleep((UploadManager.MIN_POLL_TIME+ UploadManager.MAX_POLL_TIME)/2); try { //should get the slot connectDloader(d3,false,rfd3,true); } catch (QueuedException e) { fail("downloader should have got slot "+e.getQueuePosition(), e); } catch (TryAgainLaterException tlx) { fail("exception thrown in connectHTTP", tlx); } //System.out.println("passed"); } /** * We should count the number of uploads in progress AND the number of * uploads the upload queue before deciding the upload per host limit. * Tests this. */ public void testUploadLimtIncludesQueue() throws Exception { UploadSettings.HARD_MAX_UPLOADS.setValue(1); UploadSettings.SOFT_MAX_UPLOADS.setValue(1); UploadSettings.UPLOADS_PER_PERSON.setValue(1); UploadSettings.UPLOAD_QUEUE_SIZE.setValue(10); //first two uploads to get slots HTTPDownloader d1 = addUploader(upManager,rfd1,"1.1.1.1",true); connectDloader(d1,true,rfd1,true); try { //queued at 1st position HTTPDownloader d2 = addUploader(upManager,rfd2,"1.1.1.2",true); connectDloader(d2,true,rfd2,true); fail("uploader should have been queued"); } catch (QueuedException qx) { assertEquals("should be first in queue", 1,qx.getQueuePosition()); } try { HTTPDownloader d3 = addUploader(upManager,rfd2,"1.1.1.2",true); connectDloader(d3,true,rfd2,true); fail("uploader should have been rejected "); } catch (QueuedException qx) { fail("uploader should have been rejected not queued ", qx); } catch (TryAgainLaterException talx) { //expected behaviour } catch (IOException e) { //IOException is OK because we are using pipedSocketFactory //which does not allow the downloader to read the bytes in //the buffer if the uploader closes the socket first, rather //it throws an IOException. } //System.out.println("passed"); } /** * Adds a downloader to upman, returning the downloader. The downloader * is connected but doesn't actually read the contents. * * @param upman the UploadManager responsible for accepting or denying * the upload * @param rfd the file requested by the downloader * @param ip the address reported by the downloader. This need * not be a connectable address, though it must be resolvable * @param block if true, force the uploader to block after writing * hundredth byte -- UNUSED. * @exception TryAgainLaterException the downloader was denied * because upman was busy * @exception IOException some other exception */ private static HTTPDownloader addUploader(final UploadManager upman, RemoteFileDesc rfd, String ip, boolean block) throws IOException{ //Allow some fudging to prevent race conditons. try { Thread.sleep(1000); } catch (InterruptedException e) { } //PipedSocketFactory psf=new PipedSocketFactory( //"127.0.0.1", ip, block ? 5000 : -1, -1); PipedSocketFactory psf = new PipedSocketFactory("127.0.0.1", ip); //Socket A either has no limit or a limit of 1000 bytes to write. //Socket B has no limit final Socket sa=psf.getSocketA(); Thread runner=new Thread() { public void run() { try { InputStream ia = sa.getInputStream(); assertEquals('G', ia.read()); assertEquals('E', ia.read()); assertEquals('T', ia.read()); assertEquals(' ', ia.read()); upman.acceptUpload(HTTPRequestMethod.GET,sa, false); } catch(Throwable t) { // make sure we know about errors. ErrorService.error(t); } } }; runner.setDaemon(true); runner.start(); Socket sb=psf.getSocketB(); File tmp=File.createTempFile("UploadManager_Test", "dat"); VerifyingFile vf = new VerifyingFile(0); vf.open(tmp); HTTPDownloader downloader = new HTTPDownloader(sb, rfd, vf,true); tmp.delete(); return downloader; } private static void connectDloader(HTTPDownloader dloader, boolean tcp, RemoteFileDesc rfd,boolean queue) throws TryAgainLaterException, IOException { if(tcp) dloader.connectTCP(0); //may throw TryAgainLater, etc. dloader.connectHTTP(0,rfd.getSize(),queue); } private static ConnectionStatus connectThex(HTTPDownloader dloader, boolean tcp) throws Exception { if(tcp) dloader.connectTCP(0); addThexHeader(dloader); return dloader.requestHashTree(dloader.getRemoteFileDesc().getSHA1Urn()); } private static void addThexHeader(HTTPDownloader dl) throws Exception { PrivilegedAccessor.invokeMethod(dl, "parseTHEXHeader", "X-Thex-URI: " + fm.get((int)dl.getIndex()).getHashTree().httpStringValue()); } private static void kill(HTTPDownloader downloader) { downloader.stop(); try { Thread.sleep(1000); } catch (InterruptedException ignored) { } } }