package com.limegroup.gnutella.uploader; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.InterruptedIOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import java.util.TreeSet; import junit.framework.AssertionFailedError; import junit.framework.Test; import com.limegroup.gnutella.ByteReader; import com.limegroup.gnutella.Connection; import com.limegroup.gnutella.ConnectionManager; import com.limegroup.gnutella.CreationTimeCache; import com.limegroup.gnutella.FileDesc; import com.limegroup.gnutella.FileManager; import com.limegroup.gnutella.GUID; import com.limegroup.gnutella.ManagedConnectionStub; import com.limegroup.gnutella.ReplyHandler; import com.limegroup.gnutella.Response; import com.limegroup.gnutella.RouterService; import com.limegroup.gnutella.URN; import com.limegroup.gnutella.UploadManager; import com.limegroup.gnutella.altlocs.AlternateLocation; import com.limegroup.gnutella.altlocs.AlternateLocationCollection; import com.limegroup.gnutella.altlocs.PushAltLoc; import com.limegroup.gnutella.dime.DIMEParser; import com.limegroup.gnutella.dime.DIMERecord; import com.limegroup.gnutella.downloader.Interval; import com.limegroup.gnutella.downloader.VerifyingFile; import com.limegroup.gnutella.filters.IPFilter; import com.limegroup.gnutella.handshaking.HandshakeResponder; import com.limegroup.gnutella.handshaking.HandshakeResponse; import com.limegroup.gnutella.handshaking.UltrapeerHeaders; import com.limegroup.gnutella.http.ConstantHTTPHeaderValue; import com.limegroup.gnutella.http.HTTPConstants; import com.limegroup.gnutella.http.HTTPHeaderName; import com.limegroup.gnutella.messages.BadPacketException; import com.limegroup.gnutella.messages.Message; import com.limegroup.gnutella.messages.PushRequest; import com.limegroup.gnutella.messages.QueryReply; import com.limegroup.gnutella.messages.QueryRequest; import com.limegroup.gnutella.messages.vendor.HeadPing; import com.limegroup.gnutella.messages.vendor.HeadPong; import com.limegroup.gnutella.settings.ChatSettings; import com.limegroup.gnutella.settings.ConnectionSettings; import com.limegroup.gnutella.settings.FilterSettings; import com.limegroup.gnutella.settings.SharingSettings; import com.limegroup.gnutella.settings.UltrapeerSettings; import com.limegroup.gnutella.settings.UploadSettings; import com.limegroup.gnutella.stubs.ActivityCallbackStub; import com.limegroup.gnutella.stubs.ConnectionManagerStub; import com.limegroup.gnutella.util.BaseTestCase; import com.limegroup.gnutella.util.CommonUtils; import com.limegroup.gnutella.util.IntervalSet; import com.limegroup.gnutella.util.IpPort; import com.limegroup.gnutella.util.IpPortImpl; import com.limegroup.gnutella.util.PrivilegedAccessor; /** * Test that a client uploads a file correctly. Depends on a file * containing the lowercase characters a-z. */ public class UploadTest extends BaseTestCase { private static final int PORT = 6668; /** The file name, plain and encoded. */ private static String testDirName = "com/limegroup/gnutella/uploader/data"; private static String incName = "partial alphabet.txt"; private static String fileName="alphabet test file#2.txt"; private static String encodedFile="alphabet%20test+file%232.txt"; /** The file contents. */ private static final String alphabet="abcdefghijklmnopqrstuvwxyz"; /** The hash of the file contents. */ private static final String baseHash = "GLIQY64M7FSXBSQEZY37FIM5QQSA2OUJ"; private static final String hash= "urn:sha1:" + baseHash; private static final String badHash="urn:sha1:GLIQY64M7FSXBSQEZY37FIM5QQSA2SAM"; private static final String incompleteHash = "urn:sha1:INCOMCPLETEXBSQEZY37FIM5QQSA2OUJ"; private static final int index=0; /** Our listening port for pushes. */ private static final int callbackPort = 6671; private UploadManager upMan; /** The verifying file for the shared incomplete file */ private static VerifyingFile vf; /** The filedesc of the shared file. */ private FileDesc FD; /** The root32 of the shared file. */ private String ROOT32; /** * Features for push loc testing. */ private static String FALTFeatures, FWALTFeatures; private static RouterService ROUTER_SERVICE; private static TestUploadManager UPLOAD_MANAGER; private static final Object loaded = new Object(); /** * Creates a new UploadTest with the specified name. */ public UploadTest(String name) { super(name); } /** * Allows this test to be run as a set of suites. */ public static Test suite() { return buildTestSuite(UploadTest.class); } public static void main(String args[]) { junit.textui.TestRunner.run(suite()); } /** * Simple copy. Horrible performance for large files. * Good performance for alphabets. */ private static void copyFile(File source, File dest) throws Exception { FileInputStream fis = new FileInputStream(source); FileOutputStream fos = new FileOutputStream(dest); int read = fis.read(); while(read != -1) { fos.write(read); read = fis.read(); } fis.close(); fos.close(); } public static void globalSetUp() { vf = new VerifyingFile(252450); ROUTER_SERVICE = new RouterService(new FManCallback()); UPLOAD_MANAGER = new TestUploadManager(); // Overwrite the original UploadManager with // our custom TestUploadManager. See latter // for more Info! try { PrivilegedAccessor.setValue(ROUTER_SERVICE, "uploadManager", UPLOAD_MANAGER); } catch (Exception e) { fail(e); } } protected void setUp() throws Exception { SharingSettings.ADD_ALTERNATE_FOR_SELF.setValue(false); FilterSettings.BLACK_LISTED_IP_ADDRESSES.setValue( new String[] {"*.*.*.*"}); FilterSettings.WHITE_LISTED_IP_ADDRESSES.setValue( new String[] {"127.*.*.*",InetAddress.getLocalHost().getHostAddress()}); IPFilter.refreshIPFilter(); ConnectionSettings.PORT.setValue(PORT); SharingSettings.EXTENSIONS_TO_SHARE.setValue("txt"); UploadSettings.HARD_MAX_UPLOADS.setValue(10); UploadSettings.UPLOADS_PER_PERSON.setValue(10); UploadSettings.MAX_PUSHES_PER_HOST.setValue(9999); FilterSettings.FILTER_DUPLICATES.setValue(false); ConnectionSettings.NUM_CONNECTIONS.setValue(8); ConnectionSettings.CONNECT_ON_STARTUP.setValue(true); ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false); ConnectionSettings.EVER_ACCEPTED_INCOMING.setValue(true); UltrapeerSettings.FORCE_ULTRAPEER_MODE.setValue(true); UltrapeerSettings.EVER_ULTRAPEER_CAPABLE.setValue(true); File testDir = CommonUtils.getResourceFile(testDirName); assertTrue("test directory could not be found", testDir.isDirectory()); File testFile = new File(testDir, fileName); assertTrue("test file should exist", testFile.exists()); File sharedFile = new File(_sharedDir, fileName); // we must use a seperate copy method // because the filename has a # in it which can't be a resource. copyFile(testFile, sharedFile); assertTrue("should exist", new File(_sharedDir, fileName).exists()); assertGreaterThan("should have data", 0, new File(_sharedDir, fileName).length()); if ( !RouterService.isStarted() ) { startAndWaitForLoad(); Thread.sleep(2000); } assertEquals("ports should be equal", PORT, ConnectionSettings.PORT.getValue()); upMan = RouterService.getUploadManager(); // Make sure our customized UploadManager is set in // RouterService and clear its activeUploads // cache. See TestUploadManager for more Info! assertTrue(upMan == UPLOAD_MANAGER); UPLOAD_MANAGER.clearUploads(); FileManager fm = RouterService.getFileManager(); File incFile = new File(_incompleteDir, incName); CommonUtils.copyResourceFile(testDirName + "/" + incName, incFile); URN urn = URN.createSHA1Urn(incompleteHash); Set urns = new HashSet(); urns.add(urn); fm.addIncompleteFile(incFile, urns, incName, 1981, vf); assertEquals( 1, fm.getNumIncompleteFiles() ); assertEquals( 1, fm.getNumFiles() ); FD = fm.getFileDescForFile(new File(_sharedDir, fileName)); while(FD.getHashTree() == null) Thread.sleep(300); ROOT32 = FD.getHashTree().getRootHash(); // remove all alts for clarity. RouterService.getAltlocManager().purge(); try {Thread.sleep(300); } catch (InterruptedException e) { } assertEquals("unexpected uploads in progress", 0, upMan.uploadsInProgress() ); assertEquals("unexpected queued uploads", 0, upMan.getNumQueuedUploads() ); // clear the cache history so no banning occurs. Map requests = (Map)PrivilegedAccessor.getValue(upMan, "REQUESTS"); requests.clear(); FALTFeatures= HTTPHeaderName.FEATURES+ ": "+ConstantHTTPHeaderValue.PUSH_LOCS_FEATURE.httpStringValue(); FWALTFeatures= HTTPHeaderName.FEATURES+ ": "+ConstantHTTPHeaderValue.FWT_PUSH_LOCS_FEATURE.httpStringValue(); } //public void testAll() { //UploadTest works fine in isolation, but this sleep seems to be //needed to work as part of AllTests. I'm not sure why. //try {Thread.sleep(200); } catch (InterruptedException e) { } //} ///////////////////push downloads with HTTP1.0/////////// public void testHTTP10Push() throws Exception { boolean passed = false; passed = downloadPush(fileName, null,alphabet); assertTrue("Push download",passed); } public void testHTTP10PushEncodedFile() throws Exception { boolean passed = false; passed=downloadPush(encodedFile, null,alphabet); assertTrue("Push download, encoded file name",passed); } public void testHTTP10PushRange() throws Exception { boolean passed = false; passed =downloadPush(fileName, "Range: bytes=2-5","cdef"); assertTrue("Push download, middle range, inclusive",passed); } ///////////////////push downloads with HTTP1.1/////////////// public void testHTTP11Push() throws Exception { boolean passed = false; passed = downloadPush1(fileName, null, alphabet); assertTrue("Push download with HTTP1.1",passed); } /** * tests the scenario where we receive the same push request message * more than once */ public void testDuplicatePushes() throws Exception { Connection c = createConnection(); c.initialize(new UltrapeerHeaders(null), new EmptyResponder()); QueryRequest query=QueryRequest.createQuery("txt", (byte)3); c.send(query); c.flush(); QueryReply reply=null; for(int i = 0; i < 10; i++) { Message m=c.receive(2000); if (m instanceof QueryReply) { reply=(QueryReply)m; break; } } if(reply == null) throw new IOException("didn't get query reply in time"); PushRequest push = new PushRequest(GUID.makeGuid(), (byte)3, reply.getClientGUID(), 0, new byte[] {(byte)127, (byte)0, (byte)0, (byte)1}, callbackPort); //Create listening socket, then send the push a few times ServerSocket ss=new ServerSocket(callbackPort); c.send(push); c.send(push); c.send(push); c.flush(); ss.setSoTimeout(2000); Socket s = ss.accept(); try { s = ss.accept(); fail("node replied to duplicate push request"); }catch(IOException expected){} try { s = ss.accept(); fail("node replied to duplicate push request"); }catch(IOException expected){} ss.close(); c.close(); } public void testHTTP11PushEncodedFile() throws Exception { boolean passed = false; passed =downloadPush1(encodedFile, null, "abcdefghijklmnopqrstuvwxyz"); assertTrue("Push download, encoded file name with HTTP1.1",passed); } public void testHTTP11PushRange() throws Exception { boolean passed = false; passed =downloadPush1(fileName, "Range: bytes=2-5","cdef"); assertTrue("Push download, middle range, inclusive with HTTP1.1",passed); } public void testHTTP11Head() throws Exception { assertTrue("Persistent push HEAD requests", downloadPush1("HEAD", "/get/"+index+"/"+encodedFile, null, "")); } //////////////normal downloads with HTTP 1.0////////////// public void testHTTP10Download() throws Exception { boolean passed = false; passed =download(fileName, null,"abcdefghijklmnopqrstuvwxyz"); assertTrue("No range header",passed); } public void testHTTP10DownloadRange() throws Exception { boolean passed = false; passed =download(fileName, "Range: bytes=2-", "cdefghijklmnopqrstuvwxyz"); assertTrue("Standard range header",passed); } public void testHTTP10DownloadMissingRange() throws Exception { boolean passed = false; passed =download(fileName, "Range: bytes 2-", "cdefghijklmnopqrstuvwxyz"); assertTrue("Range missing \"=\". (Not legal HTTP, but common.)", passed); } public void testHTTP10DownloadMiddleRange() throws Exception { boolean passed = false; passed =download(fileName, "Range: bytes=2-5","cdef", "Content-range: bytes 2-5/26"); assertTrue("Middle range, inclusive",passed); } public void testHTTP10DownloadRangeNoSpace() throws Exception { boolean passed = false; passed =download(fileName, "Range:bytes 2-", "cdefghijklmnopqrstuvwxyz", "Content-length:24"); assertTrue("No space after \":\". (Legal HTTP.)",passed); } public void testHTTP10DownloadRangeLastByte() throws Exception { boolean passed = false; passed =download(fileName, "Range: bytes=-5","vwxyz"); assertTrue("Last bytes of file",passed); } public void testHTTP10DownloadRangeTooBigNegative() throws Exception { boolean passed = false; passed =download(fileName, "Range: bytes=-30", "abcdefghijklmnopqrstuvwxyz"); assertTrue("Too big negative range request",passed); } public void testHTTP10DownloadRangeExtraSpace() throws Exception { boolean passed = false; passed =download(fileName, "Range: bytes= 2 - 5 ", "cdef"); assertTrue("Lots of extra space",passed); } public void testHTTP10DownloadURLEncoding() throws Exception { assertEquals("Unexpected: "+java.net.URLDecoder.decode(encodedFile), fileName, java.net.URLDecoder.decode(encodedFile)); boolean passed = false; passed =download(encodedFile, null,"abcdefghijklmnopqrstuvwxyz"); assertTrue("URL encoded",passed); } ////////////normal download with HTTP 1.1//////////////// public void testHTTP11DownloadNoRangeHeader() throws Exception { boolean passed = false; passed =download1(fileName, null,"abcdefghijklmnopqrstuvwxyz", false); assertTrue("No range header with HTTP1.1",passed); } public void testHTTP11DownloadStandardRangeHeader() throws Exception { boolean passed = false; passed =download1(fileName, "Range: bytes=2-", "cdefghijklmnopqrstuvwxyz", false); assertTrue("Standard range header with HTTP1.1",passed); } public void testHTTP11DownloadRangeMissingEquals() throws Exception { boolean passed = false; passed =download1(fileName, "Range: bytes 2-", "cdefghijklmnopqrstuvwxyz", false); assertTrue("Range missing \"=\". (Not legal HTTP, but common.)"+ "with HTTP1.1", passed); } public void testHTTP11DownloadMiddleRange() throws Exception { boolean passed = false; passed =download1(fileName, "Range: bytes=2-5","cdef", false); assertTrue("Middle range, inclusive with HTTP1.1",passed); } public void testHTTP11DownloadRangeNoSpaceAfterColon() throws Exception { boolean passed = false; passed =download1(fileName, "Range:bytes 2-", "cdefghijklmnopqrstuvwxyz", false); assertTrue("No space after \":\". (Legal HTTP.) with HTTP1.1",passed); } public void testHTTP11DownloadRangeLastByte() throws Exception { boolean passed = false; passed =download1(fileName, "Range: bytes=-5","vwxyz", false); assertTrue("Last bytes of file with HTTP1.1",passed); } public void testHTTP11DownloadRangeLotsOfExtraSpace() throws Exception { boolean passed = false; passed =download1(fileName, "Range: bytes= 2 - 5 ", "cdef", false); assertTrue("Lots of extra space with HTTP1.1",passed); } public void testHTTP11IncompleteRange() throws Exception { boolean passed = false; // add the range. Interval iv = new Interval(2, 6); IntervalSet vb = (IntervalSet) PrivilegedAccessor.getValue(vf,"verifiedBlocks"); vb.add(iv); passed = download1(incompleteHash, "Range: bytes 2-5", "cdef", true); assertTrue("incomplete range did not work", passed); passed = download1(incompleteHash, "Range: bytes 1-3", "cd", true); assertTrue("didn't shrink wanted ranges, low", passed); passed = download1(incompleteHash, "Range: bytes 3-10", "defg", true); assertTrue("didn't shrink wanted ranges, high", passed); passed = download1(incompleteHash, "Range: bytes 0-20", "cdefg", true); assertTrue("didn't shrink wanted ranges, both", passed); // failures checked in testIncompleteXXX later on down. } public void testHTTP11DownloadURLEncoding() throws Exception { boolean passed = false; assertEquals("URLDecoder broken", fileName, java.net.URLDecoder.decode(encodedFile)); passed =download1(encodedFile, null,"abcdefghijklmnopqrstuvwxyz", false); assertTrue("URL encoded with HTTP1.1",passed); } //////////////////Pipelining tests with HTTP1.1////////////// public void testHTTP11PipeliningDownload() throws Exception { boolean passed = false; passed = pipelineDownloadNormal(fileName, null, "abcdefghijklmnopqrstuvwxyz"); assertTrue("piplining with normal download",passed); } public void testHTTP11PipeliningDownloadPush() throws Exception { boolean passed = false; passed = pipelineDownloadPush(fileName,null, "abcdefghijklmnopqrstuvwxyz"); assertTrue("piplining with push download",passed); } public void testHTTP11DownloadMixedPersistent() throws Exception { tMixedPersistentRequests(); } public void testHTTP11DownloadPersistentURI() throws Exception { tPersistentURIRequests(); } public void testFALTNotRequested() throws Exception { Thread.currentThread().setPriority(Thread.MAX_PRIORITY); URN sha1 = URN.createSHA1Urn(hash); GUID clientGUID = new GUID(GUID.makeGuid()); GUID clientGUID2 = new GUID(GUID.makeGuid()); AlternateLocation direct = AlternateLocation.create("1.2.3.4:5",sha1); AlternateLocation push = AlternateLocation.create( clientGUID.toHexString()+";1.2.3.4:5",sha1); ((PushAltLoc)push).updateProxies(true); PushAltLoc pushFwt = (PushAltLoc) AlternateLocation.create( clientGUID2.toHexString()+";fwt/1.0;1.2.3.4:6",sha1); pushFwt.updateProxies(true); RouterService.getAltlocManager().add(direct, null); RouterService.getAltlocManager().add(push, null); RouterService.getAltlocManager().add(pushFwt, null); assertEquals(0,((PushAltLoc)push).supportsFWTVersion()); assertEquals(1,pushFwt.supportsFWTVersion()); assertEquals(3,RouterService.getAltlocManager().getNumLocs(FD.getSHA1Urn())); boolean passed = download(fileName, "X-Alt:", "abcdefghijklmnopqrstuvwxyz", new Applyable() { public void apply(String line) throws Exception { if(line.toLowerCase().startsWith("x-falt:")) fail("had line: " + line); else if(!line.startsWith("HTTP")) // check only the first time. return; UploadManager umanager = RouterService.getUploadManager(); List l; HTTPUploader u; synchronized(umanager){ // See TestUploadManager for more info! l = UPLOAD_MANAGER.activeUploads; assertEquals(1,l.size()); u = (HTTPUploader)l.get(0); } assertFalse(u.wantsFAlts()); assertEquals(0,u.wantsFWTAlts()); } } ); assertTrue(passed); Thread.currentThread().setPriority(Thread.NORM_PRIORITY); } public void testFALTWhenRequested() throws Exception { Thread.currentThread().setPriority(Thread.MAX_PRIORITY); URN sha1 = URN.createSHA1Urn(hash); GUID clientGUID = new GUID(GUID.makeGuid()); GUID clientGUID2 = new GUID(GUID.makeGuid()); AlternateLocation direct = AlternateLocation.create("1.2.3.4:5",sha1); final AlternateLocation push = AlternateLocation.create( clientGUID.toHexString()+";1.2.3.4:5",sha1); ((PushAltLoc)push).updateProxies(true); final PushAltLoc pushFwt = (PushAltLoc) AlternateLocation.create( clientGUID2.toHexString()+";fwt/1.0;1.2.3.4:6",sha1); pushFwt.updateProxies(true); RouterService.getAltlocManager().add(direct, null); RouterService.getAltlocManager().add(push, null); RouterService.getAltlocManager().add(pushFwt, null); assertEquals(0,((PushAltLoc)push).supportsFWTVersion()); assertEquals(1,pushFwt.supportsFWTVersion()); assertEquals(3,RouterService.getAltlocManager().getNumLocs(FD.getSHA1Urn())); boolean passed = download(fileName, FALTFeatures, "abcdefghijklmnopqrstuvwxyz", "X-FAlt: " + push.httpStringValue() + ", " + pushFwt.httpStringValue(), new Applyable() { public void apply(String line) throws Exception { if(!line.startsWith("HTTP")) // check only the first time return; UploadManager umanager = RouterService.getUploadManager(); List l; HTTPUploader u; synchronized(umanager){ // See TestUploadManager for more info! l = UPLOAD_MANAGER.activeUploads; assertEquals(1,l.size()); u = (HTTPUploader)l.get(0); } assertTrue(u.wantsFAlts()); assertEquals(0,u.wantsFWTAlts()); } } ); assertTrue(passed); Thread.currentThread().setPriority(Thread.NORM_PRIORITY); } public void testFWALTWhenRequested() throws Exception { Thread.currentThread().setPriority(Thread.MAX_PRIORITY); URN sha1 = URN.createSHA1Urn(hash); GUID clientGUID = new GUID(GUID.makeGuid()); GUID clientGUID2 = new GUID(GUID.makeGuid()); AlternateLocation direct = AlternateLocation.create("1.2.3.4:5",sha1); final AlternateLocation push = AlternateLocation.create( clientGUID.toHexString()+";1.2.3.4:5",sha1); ((PushAltLoc)push).updateProxies(true); final PushAltLoc pushFwt = (PushAltLoc) AlternateLocation.create( clientGUID2.toHexString()+";fwt/1.0;1.2.3.4:6",sha1); pushFwt.updateProxies(true); RouterService.getAltlocManager().add(direct, null); RouterService.getAltlocManager().add(push, null); RouterService.getAltlocManager().add(pushFwt, null); assertEquals(0,((PushAltLoc)push).supportsFWTVersion()); assertEquals(1,pushFwt.supportsFWTVersion()); assertEquals(3,RouterService.getAltlocManager().getNumLocs(FD.getSHA1Urn())); boolean passed = download(fileName, FWALTFeatures, "abcdefghijklmnopqrstuvwxyz", "X-FAlt: " + pushFwt.httpStringValue(), new Applyable() { public void apply(String line) throws Exception { if(!line.startsWith("HTTP")) // check only the first time. return; UploadManager umanager = RouterService.getUploadManager(); List l; HTTPUploader u; synchronized(umanager) { // See TestUploadManager for more info! l = UPLOAD_MANAGER.activeUploads; assertEquals(1,l.size()); u = (HTTPUploader)l.get(0); } assertTrue(u.wantsFAlts()); assertEquals((int)HTTPConstants.FWT_TRANSFER_VERSION,u.wantsFWTAlts()); } } ); assertTrue(passed); Thread.currentThread().setPriority(Thread.NORM_PRIORITY); } public void testUploaderStoresAllAlts() throws Exception { URN sha1 = URN.createSHA1Urn(hash); GUID clientGUID = new GUID(GUID.makeGuid()); AlternateLocation direct = AlternateLocation.create("1.2.3.4:5",sha1); AlternateLocation push = AlternateLocation.create( clientGUID.toHexString()+";1.2.3.4:5",sha1); ((PushAltLoc)push).updateProxies(true); assertEquals(0, RouterService.getAltlocManager().getNumLocs(FD.getSHA1Urn())); boolean passed = download(fileName, "X-Alt: " + direct.httpStringValue() + "\r\n" + "X-FAlt: " + push.httpStringValue(), "abcdefghijklmnopqrstuvwxyz" ); assertTrue(passed); assertEquals(2,RouterService.getAltlocManager().getNumLocs(FD.getSHA1Urn())); assertEquals(1,RouterService.getAltlocManager().getPush(FD.getSHA1Urn(),false).getAltLocsSize()); assertEquals(1,RouterService.getAltlocManager().getDirect(FD.getSHA1Urn()).getAltLocsSize()); assertTrue(RouterService.getAltlocManager().getPush(FD.getSHA1Urn(),false).contains(push)); assertTrue(RouterService.getAltlocManager().getDirect(FD.getSHA1Urn()).contains(direct)); } // Tests for alternate locations public void testAlternateLocationAddAndRemove() throws Exception { // Add a simple marker alt so we know it only contains that String loc = "http://1.1.1.1:1/uri-res/N2R?" + hash; AlternateLocation al = AlternateLocation.create(loc); RouterService.getAltlocManager().add(al, null); boolean passed = false; passed = download("/uri-res/N2R?" + hash,null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // Ensure that one removal doesn't stop it. RouterService.getAltlocManager().remove(al, null); passed = download("/uri-res/N2R?" + hash,null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); //Add a second one, so we can check to make sure //another removal removes the first one. String loc2 = "http://2.2.2.2:2/uri-res/N2R?" + hash; AlternateLocation al2 = AlternateLocation.create(loc2); RouterService.getAltlocManager().add(al2, null); passed = download("/uri-res/N2R?" + hash,null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 2.2.2.2:2, 1.1.1.1:1"); assertTrue("alt failed", passed); //Remove the first guy again, should only have loc2 left. RouterService.getAltlocManager().remove(al, null); passed = download("/uri-res/N2R?" + hash,null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 2.2.2.2:2"); assertTrue("alt failed", passed); } //Tests that headers the downloader gives are used. public void testSentHeaderIsUsed() throws Exception { // Add a simple marker alt so we know it only contains that String loc = "http://1.1.1.1:1/uri-res/N2R?" + hash; AlternateLocation al = AlternateLocation.create(loc); RouterService.getAltlocManager().add(al, null); boolean passed = false; passed = download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // Add a header that gives a new location. String sendLoc = "http://2.2.2.2:2/uri-res/N2R?" + hash; AlternateLocation sendAl = AlternateLocation.create(sendLoc); passed = download("/uri-res/N2R?" + hash, "X-Alt: " + sendLoc, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // Make sure the FD has that loc now. AlternateLocationCollection alc = RouterService.getAltlocManager().getDirect(FD.getSHA1Urn()); assertEquals("wrong # locs", 2, alc.getAltLocsSize()); List alts = new LinkedList(); for(Iterator i = alc.iterator(); i.hasNext(); ) alts.add(i.next()); assertTrue( alts.contains(al) ); assertTrue( alts.contains(sendAl) ); //Make sure a request will give us both locs now. passed = download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 2.2.2.2:2, 1.1.1.1:1"); assertTrue("alt failed", passed); //Demote the location (don't remove) passed = download("/uri-res/N2R?" + hash, "X-NAlt: " + sendLoc, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // Should still have it. assertEquals("wrong # locs", 2, alc.getAltLocsSize()); alts = new LinkedList(); for(Iterator i = alc.iterator(); i.hasNext(); ) alts.add(i.next()); assertTrue( alts.contains(al) ); assertTrue( alts.contains(sendAl) ); //Now remove. passed = download("/uri-res/N2R?" + hash, "X-NAlt: " + sendLoc, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // Now is removed. assertEquals("wrong # locs", 1, alc.getAltLocsSize()); assertEquals(al, alc.iterator().next()); } //Tests that headers the downloader gives are used. public void testMiniNewHeaderIsUsed() throws Exception { // Add a simple marker alt so we know it only contains that String loc = "http://1.1.1.1:1/uri-res/N2R?" + hash; AlternateLocation al = AlternateLocation.create(loc); RouterService.getAltlocManager().add(al, null); boolean passed = false; passed = download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // Add a header that gives a new location. String sendLoc = "http://2.2.2.2:2/uri-res/N2R?" + hash; AlternateLocation sendAl = AlternateLocation.create(sendLoc); passed = download("/uri-res/N2R?" + hash, "X-Alt: 2.2.2.2:2", "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // Make sure the FD has that loc now. AlternateLocationCollection alc = RouterService.getAltlocManager().getDirect(FD.getSHA1Urn()); assertEquals("wrong # locs", 2, alc.getAltLocsSize()); List alts = new LinkedList(); for(Iterator i = alc.iterator(); i.hasNext(); ) alts.add(i.next()); assertTrue( alts.contains(al) ); assertTrue( alts.contains(sendAl) ); //Make sure a request will give us both locs now. passed = download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 2.2.2.2:2, 1.1.1.1:1"); assertTrue("alt failed", passed); //Demote the location (don't remove) passed = download("/uri-res/N2R?" + hash, "X-NAlt: 2.2.2.2:2", "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // Should still have it. assertEquals("wrong # locs", 2, alc.getAltLocsSize()); alts = new LinkedList(); for(Iterator i = alc.iterator(); i.hasNext(); ) alts.add(i.next()); assertTrue( alts.contains(al) ); assertTrue( alts.contains(sendAl) ); //Now remove (try interchanging with old header) passed = download("/uri-res/N2R?" + hash, "X-NAlt: " + sendLoc, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // Now is removed. assertEquals("wrong # locs", 1, alc.getAltLocsSize()); assertEquals(al, alc.iterator().next()); //Now try a header without a port, should be 6346. sendLoc = "http://2.3.4.5:6346/uri-res/N2R?" + hash; sendAl = AlternateLocation.create(sendLoc); passed = download("/uri-res/N2R?" + hash, "X-Alt: 2.3.4.5", "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // Make sure the FD has that loc now. assertEquals("wrong # locs", 2, alc.getAltLocsSize()); alts = new LinkedList(); for(Iterator i = alc.iterator(); i.hasNext(); ) alts.add(i.next()); assertTrue( alts.contains(al) ); assertTrue( alts.contains(sendAl) ); } // Tests that headers with multiple values in them are // read correctly public void testMultipleAlternates() throws Exception { // Add a simple marker alt so we know it only contains that String loc = "http://1.1.1.1:1/uri-res/N2R?" + hash; AlternateLocation al = AlternateLocation.create(loc); RouterService.getAltlocManager().add(al, null); boolean passed = false; passed = download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // Add a header that gives a new location. String send1 = "http://1.2.3.1:1/uri-res/N2R?" + hash; AlternateLocation al1 = AlternateLocation.create(send1); String send2 = "http://1.2.3.2:2/uri-res/N2R?" + hash; AlternateLocation al2 = AlternateLocation.create(send2); String send3 = "http://1.2.3.4:6346/uri-res/N2R?" + hash; AlternateLocation al3 = AlternateLocation.create(send3); passed = download("/uri-res/N2R?" + hash, "X-Alt: " + send1 + ", " + send2 + ", 1.2.3.4", "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // Make sure the FD has that loc now. AlternateLocationCollection alc = RouterService.getAltlocManager().getDirect(FD.getSHA1Urn()); assertEquals("wrong # locs", 4, alc.getAltLocsSize()); List alts = new LinkedList(); for(Iterator i = alc.iterator(); i.hasNext(); ) alts.add(i.next()); assertTrue( alts.contains(al) ); assertTrue( alts.contains(al1) ); assertTrue( alts.contains(al2) ); assertTrue( alts.contains(al3) ); //Demote. passed = download("/uri-res/N2R?" + hash, "X-NAlt: 1.2.3.1:1, " + send2 + ", " + send3, "abcdefghijklmnopqrstuvwxyz"); assertTrue("alt failed", passed); // Should still have it. assertEquals("wrong # locs", 4, alc.getAltLocsSize()); //Remove passed = download("/uri-res/N2R?" + hash, "X-NAlt: " + send1 + ", 1.2.3.2:2, " + send3, "abcdefghijklmnopqrstuvwxyz"); assertTrue("alt failed", passed); // Now is removed. assertEquals("wrong # locs", 1, alc.getAltLocsSize()); assertEquals(al, alc.iterator().next()); } /** * tests that when reading the NFAlt header we only remove proxies */ public void testRemovingNFAlt() throws Exception { boolean passed; GUID g = new GUID(GUID.makeGuid()); URN urn = URN.createSHA1Urn(hash); PushAltLoc abc = (PushAltLoc)AlternateLocation.create( g.toHexString()+";1.1.1.1:1;2.2.2.2:2;3.3.3.3:3", urn); String abcHttp = abc.httpStringValue(); PushAltLoc bcd=(PushAltLoc)AlternateLocation.create( g.toHexString()+";2.2.2.2:2;3.3.3.3:3;4.4.4.4:4", urn); bcd.updateProxies(true); String bcdHttp = bcd.httpStringValue(); RouterService.getAltlocManager().add(bcd, null); assertEquals(1,RouterService.getAltlocManager().getNumLocs(urn)); passed=download("/uri-res/N2R?" + hash, "X-NFAlt: " + abcHttp, "abcdefghijklmnopqrstuvwxyz"); assertTrue("alt failed", passed); //two of the proxies of bcd should be gone assertEquals("wrong # locs", 1, RouterService.getAltlocManager().getPush(FD.getSHA1Urn(),false).getAltLocsSize()); assertEquals("wrong # proxies",1,bcd.getPushAddress().getProxies().size()); //now repeat, sending all three original proxies of bce as NFAlts Thread.sleep(1000); passed=download("/uri-res/N2R?" + hash, "X-NFAlt: " + bcdHttp, "abcdefghijklmnopqrstuvwxyz"); assertTrue("alt failed", passed); // all proxies should be gone, and bcd should be removed from // the filedesc assertEquals("wrong # locs", 0, RouterService.getAltlocManager().getPush(FD.getSHA1Urn(),false).getAltLocsSize()); assertEquals("wrong # proxies",0,bcd.getPushAddress().getProxies().size()); } // unfortunately we can't test with private addresses // because all these connections require that local_is_private // is false, which turns off isPrivateAddress checking. public void testInvalidAltsAreIgnored() throws Exception { // Add a simple marker alt so we know it only contains that String loc = "http://1.1.1.1:1/uri-res/N2R?" + hash; AlternateLocation al = AlternateLocation.create(loc); RouterService.getAltlocManager().add(al, null); boolean passed = false; passed = download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // Add an invalid alt String invalidAddr = "http://0.0.0.0:6346/uri-res/N2R?" + hash; passed = download("/uri-res/N2R?" + hash, "X-Alt: " + invalidAddr, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // FD should still only have 1 AlternateLocationCollection alc = RouterService.getAltlocManager().getDirect(FD.getSHA1Urn()); assertEquals("wrong # locs: " + alc, 1, alc.getAltLocsSize()); assertEquals(al, alc.iterator().next()); invalidAddr = "http://255.255.255.255:6346/uri-res/N2R?" + hash; passed = download("/uri-res/N2R?" + hash, "X-Alt: " + invalidAddr, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // FD should still only have 1 assertEquals("wrong # locs: " + alc, 1, alc.getAltLocsSize()); assertEquals(al, alc.iterator().next()); // Add an invalid port String invalidPort = "http://1.2.3.4:0/uri-res/N2R?" + hash; passed = download("/uri-res/N2R?" + hash, "X-Alt: " + invalidPort, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // FD should still only have 1 assertEquals("wrong # locs: " + alc, 1, alc.getAltLocsSize()); assertEquals(al, alc.iterator().next()); invalidPort = "http://1.2.3.4:-2/uri-res/N2R?" + hash; passed = download("/uri-res/N2R?" + hash, "X-Alt: " + invalidPort, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // FD should still only have 1 assertEquals("wrong # locs: " + alc, 1, alc.getAltLocsSize()); assertEquals(al, alc.iterator().next()); } public void test10AltsAreSent() throws Exception { // Add a simple marker alt so we know it only contains that String loc = "http://1.1.1.1:1/uri-res/N2R?" + hash; AlternateLocation al = AlternateLocation.create(loc); RouterService.getAltlocManager().add(al, null); boolean passed = false; passed = download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); for(int i = 0; i < 20; i++) { RouterService.getAltlocManager().add( AlternateLocation.create( "http://1.1.1." + i + ":6346/uri-res/N2R?" + hash), null); } assertEquals(21, RouterService.getAltlocManager().getDirect(FD.getSHA1Urn()).getAltLocsSize()); String pre = "1.1.1."; String post = ""; String comma = ", "; // note that this value can change depending on iterators, // so this is a very flaky test. String required = "X-Alt: " + pre + 16 + post + comma + pre + 13 + post + comma + pre + 10 + post + comma + pre + 15 + post + comma + pre + 12 + post + comma + pre + 1 + post + comma + pre + 14 + post + comma + pre + 11 + post + comma + pre + 0 + post + comma + pre + "1:1"; passed = download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", required); assertTrue(passed); } public void testAltsExpire() throws Exception { UploadSettings.LEGACY_EXPIRATION_DAMPER.setValue((float)Math.E - 0.2f); // test that an altloc will expire if given out too often String loc = "http://1.1.1.1:1/uri-res/N2R?" + hash; AlternateLocation al = AlternateLocation.create(loc); assertTrue(al.canBeSent(AlternateLocation.MESH_LEGACY)); RouterService.getAltlocManager().add(al, null); // send it out several times int i = 0; try { for (i = 0; i < 10; i++) { download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); } fail("altloc didn't expire"); } catch (AssertionFailedError expected) {} assertLessThan(10, i); assertFalse(al.canBeSent(AlternateLocation.MESH_LEGACY)); // now add the altloc again, it will be reset RouterService.getAltlocManager().add(al, null); assertTrue(al.canBeSent(AlternateLocation.MESH_LEGACY)); } public void testAltsDontExpire() throws Exception { UploadSettings.LEGACY_EXPIRATION_DAMPER.setValue((float)Math.E/4); // test that an altloc will not expire if given out less often String loc = "http://1.1.1.1:1/uri-res/N2R?" + hash; AlternateLocation al = AlternateLocation.create(loc); assertTrue(al.canBeSent(AlternateLocation.MESH_LEGACY)); RouterService.getAltlocManager().add(al, null); for (int i = 0; i < 10; i++) { assertTrue(download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1")); Thread.sleep(8*1000); } assertTrue(al.canBeSent(AlternateLocation.MESH_LEGACY)); } /** * tests that when an altloc has expired from all the meshes it is removed. */ public void testExpiredAltsRemoved() throws Exception { FilterSettings.WHITE_LISTED_IP_ADDRESSES.setValue(new String[]{"*.*.*.*"}); IPFilter.refreshIPFilter(); // set the expiration values to the bare minimum UploadSettings.LEGACY_BIAS.setValue(0f); UploadSettings.PING_BIAS.setValue(0f); UploadSettings.RESPONSE_BIAS.setValue(0f); // create an altloc String loc = "http://1.1.1.1:1/uri-res/N2R?" + hash; AlternateLocation al = AlternateLocation.create(loc); assertTrue(al.canBeSent(AlternateLocation.MESH_LEGACY)); assertTrue(al.canBeSent(AlternateLocation.MESH_PING)); assertTrue(al.canBeSent(AlternateLocation.MESH_RESPONSE)); RouterService.getAltlocManager().add(al, null); // drain the meshes in various orders drainLegacy(); drainPing(); drainResponse(); assertFalse(RouterService.getAltlocManager().hasAltlocs(al.getSHA1Urn())); // and re-add the altloc al = AlternateLocation.create(loc); RouterService.getAltlocManager().add(al, null); // repeat drainResponse(); drainLegacy(); drainPing(); assertFalse(RouterService.getAltlocManager().hasAltlocs(al.getSHA1Urn())); al = AlternateLocation.create(loc); RouterService.getAltlocManager().add(al, null); // repeat 2 drainPing(); drainResponse(); drainLegacy(); assertFalse(RouterService.getAltlocManager().hasAltlocs(al.getSHA1Urn())); UploadSettings.LEGACY_BIAS.revertToDefault(); UploadSettings.PING_BIAS.revertToDefault(); UploadSettings.RESPONSE_BIAS.revertToDefault(); } private void drainLegacy() throws Exception { int i = 0; try { for (; i < 20; i++) { download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); } fail("altloc didn't expire"); } catch (AssertionFailedError expected) {} assertGreaterThan(1,i); assertLessThan(20,i); } private void drainPing() throws Exception { HeadPing ping = new HeadPing(new GUID(GUID.makeGuid()),FD.getSHA1Urn(),HeadPing.ALT_LOCS); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ping.write(baos); byte [] data = baos.toByteArray(); DatagramPacket toSend = new DatagramPacket(data,data.length, new InetSocketAddress(InetAddress.getLocalHost(),PORT)); int i = 0; for (; i < 20; i++) { DatagramSocket sock = null; try { sock = new DatagramSocket(10000+i); sock.setSoTimeout(2000); sock.send(toSend); byte [] recv = new byte[1000]; DatagramPacket rcv = new DatagramPacket(recv,recv.length); sock.receive(rcv); ByteArrayInputStream bais = new ByteArrayInputStream(recv,0,rcv.getLength()); HeadPong pong = (HeadPong) Message.read(bais); if (pong.getAltLocs().isEmpty()) break; } finally { if (sock != null) sock.close(); } } assertGreaterThan(1,i); assertLessThan(20,i); } private void drainResponse() throws Exception { FilterSettings.FILTER_HASH_QUERIES.setValue(false); // easier with hash MyReplyHandler handler = new MyReplyHandler(); assertTrue(RouterService.getAltlocManager().hasAltlocs(FD.getSHA1Urn())); int i = 0; for (; i < 20; i++) { QueryRequest request = QueryRequest.createQuery(FD.getSHA1Urn()); RouterService.getMessageRouter().handleMessage(request, handler); assertNotNull(handler.received); ByteArrayOutputStream baos = new ByteArrayOutputStream(); handler.received.write(baos); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); QueryReply reply = (QueryReply) Message.read(bais); Response resp = reply.getResultsArray()[0]; if (resp.getLocations().isEmpty()) break; handler.received = null; } assertGreaterThan(1,i); assertLessThan(20,i); FilterSettings.FILTER_HASH_QUERIES.revertToDefault(); } private static class MyReplyHandler extends ManagedConnectionStub { public QueryReply received; public void handleQueryReply(QueryReply queryReply, ReplyHandler receivingConnection) { received = queryReply; } } public void testChunksGiveDifferentLocs() throws Exception { // Add a simple marker alt so we know it only contains that String loc = "http://1.1.1.1:1/uri-res/N2R?" + hash; AlternateLocation al = AlternateLocation.create(loc); RouterService.getAltlocManager().add(al, null); boolean passed = false; passed = download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); for(int i = 0; i < 20; i++) { RouterService.getAltlocManager().add( AlternateLocation.create( "http://1.1.1." + i + ":6346/uri-res/N2R?" + hash), null); } assertEquals(21, RouterService.getAltlocManager().getNumLocs(FD.getSHA1Urn())); //1. Write request Socket s = new Socket("localhost", PORT); BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); String reqFile = "/uri-res/N2R?" + hash; String pre = "1.1.1."; String post = ""; String comma = ", "; // note that this value can change depending on iterators, // so this is a very flaky test. String required = null; required = "X-Alt: " + pre + 16 + post + comma + pre + 13 + post + comma + pre + 10 + post + comma + pre + 15 + post + comma + pre + 12 + post + comma + pre + 1 + post + comma + pre + 14 + post + comma + pre + 11 + post + comma + pre + 0 + post + comma + pre + "1:1"; downloadInternal11(reqFile, "Range: bytes=0-1", out, in, required); required = "X-Alt: " + pre + 5 + post + comma + pre + 2 + post + comma + pre + 18 + post + comma + pre + 7 + post + comma + pre + 4 + post + comma + pre + 17 + post + comma + pre + 6 + post + comma + pre + 3 + post + comma + pre + 19 + post + comma + pre + 8 + post; downloadInternal11(reqFile, "Range: bytes=2-3", out, in, required); required = "X-Alt: " + pre + 9 + post; downloadInternal11(reqFile, "Range: bytes=4-5", out, in, required); // Now if some more are added to file desc, make sure they're reported. RouterService.getAltlocManager().add( AlternateLocation.create( "http://1.1.1.99:6346/uri-res/N2R?" + hash), null); required = "X-Alt: " + pre + 99 + post; downloadInternal11(reqFile, "Range: bytes=6-7", out, in, required); in.close(); out.close(); s.close(); } public void testPrioritizingAlternates() throws Exception { // Add a simple marker alt so we know it only contains that String loc = "http://1.1.1.1:1/uri-res/N2R?" + hash; AlternateLocation al = AlternateLocation.create(loc); RouterService.getAltlocManager().add(al, null); boolean passed = false; passed = download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", "X-Alt: 1.1.1.1:1"); assertTrue("alt failed", passed); // get rid of it. RouterService.getAltlocManager().remove(al, null); RouterService.getAltlocManager().remove(al, null); for(int i = 0; i < 50; i++) { al = AlternateLocation.create( "http://1.1.1." + i + ":6346/uri-res/N2R?" + hash); RouterService.getAltlocManager().add(al,null); //0-9, make as demoted. if( i < 10 ) { RouterService.getAltlocManager().remove(al,null); // should demote. } // 10-19, increment once. else if( i < 20 ) { RouterService.getAltlocManager().add(al, null); // should increment. } // 20-29, increment & demote. else if( i < 30 ) { RouterService.getAltlocManager().add(al, null); // increment RouterService.getAltlocManager().remove(al, null); // demote } // 30-39, increment twice. else if( i < 40 ) { RouterService.getAltlocManager().add(al, null); //increment RouterService.getAltlocManager().add(al, null); //increment } // 40-49, leave normal. } AlternateLocationCollection alc = RouterService.getAltlocManager().getDirect(FD.getSHA1Urn()); assertEquals(50, alc.getAltLocsSize()); //Order of return should be: // 40-49 returned first // 10-19 returned next // 30-39 returned next // 0-9 returned next // 20-29 returned next //1. Write request Socket s = new Socket("localhost", PORT); BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); String reqFile = "/uri-res/N2R?" + hash; String pre = "1.1.1."; String post = ""; String comma = ", "; // note that this value can change depending on iterators, // so this is a very flaky test. String required = null; required = "X-Alt: " + pre + 40 + post + comma + pre + 41 + post + comma + pre + 42 + post + comma + pre + 43 + post + comma + pre + 44 + post + comma + pre + 45 + post + comma + pre + 46 + post + comma + pre + 47 + post + comma + pre + 48 + post + comma + pre + 49 + post; downloadInternal11(reqFile, "Range: bytes=0-1", out, in, required); required = "X-Alt: " + pre + 16 + post + comma + pre + 13 + post + comma + pre + 10 + post + comma + pre + 18 + post + comma + pre + 15 + post + comma + pre + 12 + post + comma + pre + 17 + post + comma + pre + 14 + post + comma + pre + 11 + post + comma + pre + 19 + post; downloadInternal11(reqFile, "Range: bytes=2-3", out, in, required); required = "X-Alt: " + pre + 35 + post + comma + pre + 32 + post + comma + pre + 37 + post + comma + pre + 34 + post + comma + pre + 31 + post + comma + pre + 39 + post + comma + pre + 36 + post + comma + pre + 33 + post + comma + pre + 30 + post + comma + pre + 38 + post; downloadInternal11(reqFile, "Range: bytes=4-5", out, in, required); required = "X-Alt: " + pre + 5 + post + comma + pre + 2 + post + comma + pre + 7 + post + comma + pre + 4 + post + comma + pre + 1 + post + comma + pre + 9 + post + comma + pre + 6 + post + comma + pre + 3 + post + comma + pre + 0 + post + comma + pre + 8 + post; downloadInternal11(reqFile, "Range: bytes=6-7", out, in, required); required = "X-Alt: " + pre + 24 + post + comma + pre + 21 + post + comma + pre + 29 + post + comma + pre + 26 + post + comma + pre + 23 + post + comma + pre + 20 + post + comma + pre + 28 + post + comma + pre + 25 + post + comma + pre + 22 + post + comma + pre + 27 + post; downloadInternal11(reqFile, "Range: bytes=8-9", out, in, required); in.close(); out.close(); s.close(); } /////////////////Miscellaneous tests for acceptable failure behaviour////////// public void testIncompleteFileUpload() throws Exception { tFailureHeaderRequired( "/uri-res/N2R?" + incompleteHash, null, true, true, "HTTP/1.1 416 Requested Range Unavailable"); } public void testIncompleteFileWithRanges() throws Exception { // add a range to the incomplete file. Interval iv = new Interval(50, 102500); IntervalSet vb = (IntervalSet) PrivilegedAccessor.getValue(vf,"verifiedBlocks"); vb.add(iv); tFailureHeaderRequired( "/uri-res/N2R?" + incompleteHash, null, true, true, "X-Available-Ranges: bytes 50-102499"); // add another range and make sure we display it. iv = new Interval(150050, 252450); vb.add(iv); tFailureHeaderRequired( "/uri-res/N2R?" + incompleteHash, null, true, true, "X-Available-Ranges: bytes 50-102499, 150050-252449"); // add an interval too small to report and make sure we don't report iv = new Interval(102505, 150000); vb.add(iv); tFailureHeaderRequired( "/uri-res/N2R?" + incompleteHash, null, true, true, "X-Available-Ranges: bytes 50-102499, 150050-252449"); // add the glue between the other intervals and make sure we condense // the ranges into a single larger range. iv = new Interval(102500, 102505); vb.add(iv); iv = new Interval(150000, 150050); vb.add(iv); tFailureHeaderRequired( "/uri-res/N2R?" + incompleteHash, null, true, true, "X-Available-Ranges: bytes 50-252449"); } public void testIncompleteFileWithRangeRequest() throws Exception { String header = "Range: bytes 20-40"; tFailureHeaderRequired( "/uri-res/N2R?" + incompleteHash, header, true, true, "HTTP/1.1 416 Requested Range Unavailable"); } public void testHTTP11WrongURI() throws Exception { tFailureHeaderRequired( "/uri-res/N2R?" + badHash, null, true, true, "HTTP/1.1 404 Not Found"); } public void testHTTP10WrongURI() throws Exception { // note that the header will be returned with 1.1 // even though we sent with 1.0 tFailureHeaderRequired( "/uri-res/N2R?" + badHash, null, false, false, "HTTP/1.1 404 Not Found"); } public void testHTTP11MalformedURI() throws Exception { tFailureHeaderRequired( "/uri-res/N2R?" + "no more school", null, true, false, "HTTP/1.1 400 Malformed Request"); } public void testHTTP10MalformedURI() throws Exception { tFailureHeaderRequired( "/uri-res/N2R?" + "no more school", null, false, false, "HTTP/1.1 400 Malformed Request"); } public void testHTTP11MalformedGet() throws Exception { tFailureHeaderRequired( "/get/some/dr/pepper", null, true, false, "HTTP/1.1 400 Malformed Request"); } public void testHTTP11MalformedHeader() throws Exception { tFailureHeaderRequired( "/uri-res/N2R?" + hash, "Range: 2-5", // expects "Range: bytes 2-5" true, false, "HTTP/1.1 400 Malformed Request"); } ///////////////////test that creation time is given/////////// public void testCreationTimeGiven() throws Exception { //0. Confirm creation time exists URN urn = URN.createSHA1Urn(hash); Long cTime = CreationTimeCache.instance().getCreationTime(urn); assertNotNull(cTime); assertTrue(cTime.longValue() > 0); assertTrue(download("/uri-res/N2R?" + hash, null, "abcdefghijklmnopqrstuvwxyz", "X-Create-Time: " + cTime)); } public void testCreationTimeGivenForPartial() throws Exception { //0. make creatio time URN urn = URN.createSHA1Urn(incompleteHash); Long cTime = new Long("10776"); CreationTimeCache.instance().addTime(urn, cTime.longValue()); // successful interval 2-5 was set above assertTrue(download("/uri-res/N2R?" + incompleteHash, "Range: bytes 2-5", "cdef", "X-Create-Time: " + cTime)); } /////////////// test the feature header is used correctly /////////// public void testFeatureHeader() throws Exception { ChatSettings.CHAT_ENABLED.setValue(true); assertTrue(download(fileName, null, "abcdefghijklmnopqrstuvwxyz", "X-Features: fwalt/0.1, browse/1.0, chat/0.1")); ChatSettings.CHAT_ENABLED.setValue(false); assertTrue(download(fileName, null, "abcdefghijklmnopqrstuvwxyz", "X-Features: fwalt/0.1, browse/1.0")); } /** * tests that the node sends a proper proxies header */ public void testProxiesHeaderNotSent() throws Exception { // try when we are not firewalled PrivilegedAccessor.setValue( RouterService.getAcceptor(), "_acceptedIncoming", new Boolean(true)); assertTrue(RouterService.acceptedIncomingConnection()); Socket s = new Socket("localhost", PORT); BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); assertFalse( containsHeader("GET",fileName,null,out,in,"X-Push-Proxy: 1.2.3.4:5")); try{in.close();}catch(IOException ignored){} try{out.close();}catch(IOException ignored){} Thread.sleep(1000); // now try with an empty set of proxies s = getSocketFromPush(); in = new BufferedReader(new InputStreamReader( s.getInputStream())); out = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); in.readLine(); //skip GIV in.readLine(); //skip blank line PrivilegedAccessor.setValue( RouterService.getAcceptor(), "_acceptedIncoming", new Boolean(false)); assertFalse(RouterService.acceptedIncomingConnection()); assertFalse( containsHeader("GET",fileName,null,out,in,"X-Push-Proxy: 1.2.3.4:5")); try{in.close();}catch(IOException ignored){} try{out.close();}catch(IOException ignored){} } public void testProxiesHeaderSent() throws Exception{ Socket s = getSocketFromPush(); BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream())); Writer out = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); in.readLine(); //skip GIV in.readLine(); //skip blank line // now try with some proxies ConnectionManager original = RouterService.getConnectionManager(); final Set proxies = new TreeSet(IpPort.COMPARATOR); IpPort ppi = new IpPortImpl("1.2.3.4",5); proxies.add(ppi); ConnectionManagerStub cmStub = new ConnectionManagerStub() { public Set getPushProxies() { return proxies; } }; PrivilegedAccessor.setValue(RouterService.class,"manager",cmStub); PrivilegedAccessor.setValue( RouterService.getAcceptor(), "_acceptedIncoming", new Boolean(false)); assertFalse(RouterService.acceptedIncomingConnection()); assertTrue( containsHeader("GET",fileName,null,out,in,"X-Push-Proxy: 1.2.3.4:5")); try{in.close();}catch(IOException ignored){} try{out.close();}catch(IOException ignored){} PrivilegedAccessor.setValue(RouterService.class,"manager",original); } ////////// test thex works ///////////// public void testThexHeader() throws Exception { assertTrue(download(fileName, null, "abcdefghijklmnopqrstuvwxyz", "X-Thex-URI: " + "/uri-res/N2X?" + hash + ";" + ROOT32) ); } public void testUploadFromBitprint() throws Exception { assertTrue(download("/uri-res/N2R?urn:bitprint:" + baseHash + "." + ROOT32, null, "abcdefghijklmnopqrstuvwxyz")); // we check for a valid bitprint length. tFailureHeaderRequired("/uri-res/N2R?urn:bitprint:" + baseHash + "." + "asdoihffd", null, true, false, "HTTP/1.1 400 Malformed Request"); // but not for the valid base32 root -- in the future we may // and this test will break assertTrue(download("/uri-res/N2R?urn:bitprint:" + baseHash + "." + "SAMUWJUUSPLMMDUQZOWX32R6AEOT7NCCBX6AGBI", null, "abcdefghijklmnopqrstuvwxyz")); // make sure "bitprint:" is required for bitprint uploading. tFailureHeaderRequired("/uri-res/N2R?urn:sha1:" + baseHash + "." + ROOT32, null, true, false, "HTTP/1.1 400 Malformed Request"); } public void testBadGetTreeRequest() throws Exception { tFailureHeaderRequired("/uri-res/N2X?" + badHash, null, true, true, "HTTP/1.1 404 Not Found"); tFailureHeaderRequired("/uri-res/N2X?" + "no hash", null, true, false, "HTTP/1.1 400 Malformed Request"); } public void testGetTree() throws Exception { byte[] dl = getDownloadBytes("/uri-res/N2X?" + hash, null, null); // assertEquals(FD.getHashTree().getOutputLength(), dl.length); DIMEParser parser = new DIMEParser(new ByteArrayInputStream(dl)); DIMERecord xml = parser.nextRecord(); DIMERecord tree = parser.nextRecord(); assertFalse(parser.hasNext()); List allNodes = FD.getHashTree().getAllNodes(); byte[] data = tree.getData(); int offset = 0; for(Iterator genIter = allNodes.iterator(); genIter.hasNext(); ) { for(Iterator i = ((List)genIter.next()).iterator(); i.hasNext();) { byte[] current = (byte[])i.next(); for(int j = 0; j < current.length; j++) { assertEquals("offset: " + offset + ", idx: " + j, current[j], data[offset++]); } } } assertEquals(data.length, offset); // more extensive validity checks are in HashTreeTest // this is just checking to make sure we sent the right tree. } /** * Downloads file (stored in slot index) from address:port, returning the * content as a string. If header!=null, includes it as a request header. * Do not include new line or carriage return in header. Throws IOException * if there is a problem, without cleaning up. */ private static boolean download1(String file,String header,String expResp, boolean uri) throws IOException { //Unfortunately we can't use URLConnection because we need to test //malformed and slightly malformed headers //1. Write request boolean ret = true; Socket s = new Socket("localhost", PORT); BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); String requestName; if( uri ) { requestName = "/uri-res/N2R?" + file; } else { requestName = "/get/" + index + "/" + file; } //first request with the socket String value=downloadInternal1("GET", requestName, header,out,in,expResp.length()); ret = value.equals(expResp);//first request seccessful? //make second requst on same socket value = "";//reset value = downloadInternal1("GET", requestName, header, out, in, expResp.length()); ret = ret && value.equals(expResp);//both reqests successful? in.close(); out.close(); s.close(); return ret; } private static boolean download(String file,String header,String expResp) throws IOException { return download(file, header, expResp, null, null); } private static boolean download(String file, String header, String expResp, Applyable f) throws IOException { return download(file, header, expResp, null, f); } private static byte[] getDownloadBytes(String file, String header, String expheader) throws IOException { //1. Write request Socket s = new Socket("localhost", PORT); InputStream in = s.getInputStream(); OutputStream out = s.getOutputStream(); String req = makeRequest(file); byte[] ret = getBytes("GET", req, header, out, in, expheader, false, true); in.close(); out.close(); s.close(); return ret; } private static boolean download(String file,String header, String expResp, String expHeader) throws IOException { return download(file, header, expResp, expHeader, null); } private static boolean download(String file, String header, String expResp, String expHeader, Applyable f) throws IOException { //Unfortunately we can't use URLConnection because we need to test //malformed and slightly malformed headers //1. Write request Socket s = new Socket("localhost", PORT); BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); String ret=downloadInternal(file, header, out, in, expHeader, f); in.close(); out.close(); s.close(); try {Thread.sleep(100); }catch(InterruptedException e){} return ret.equals(expResp); } /** Does a simple push GET download. */ private static boolean downloadPush1(String indexedFile, String header, String expResp) throws IOException, BadPacketException{ return downloadPush1("GET", makeRequest(indexedFile), header, expResp); } /** * Does a push & gets a socket from the incoming connection. */ private static Socket getSocketFromPush() throws IOException, BadPacketException { Connection c = createConnection(); c.initialize(new UltrapeerHeaders(null), new EmptyResponder()); QueryRequest query=QueryRequest.createQuery("txt", (byte)3); c.send(query); c.flush(); QueryReply reply=null; for(int i = 0; i < 10; i++) { Message m=c.receive(2000); if (m instanceof QueryReply) { reply=(QueryReply)m; break; } } if(reply == null) throw new IOException("didn't get query reply in time"); PushRequest push = new PushRequest(GUID.makeGuid(), (byte)3, reply.getClientGUID(), 0, new byte[] {(byte)127, (byte)0, (byte)0, (byte)1}, callbackPort); //Create listening socket, then send push. ServerSocket ss=new ServerSocket(callbackPort); c.send(push); c.flush(); ss.setSoTimeout(2000); Socket s = ss.accept(); c.close(); ss.close(); return s; } /** * Does an arbitrary push download. * @param request an HTTP request such as "GET" or "HEAD * @param file the full filename, e.g., "/get/0/file.txt" */ private static boolean downloadPush1(String request, String file, String header, String expResp) throws IOException, BadPacketException { //Establish push route Socket s = getSocketFromPush(); BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); in.readLine(); //skip GIV in.readLine(); //skip blank line //Download from the (incoming) TCP connection. String retStr=downloadInternal1(request, file, header, out, in,expResp.length()); assertEquals("unexpected HTTP response message body", expResp, retStr); boolean ret = retStr.equals(expResp); // reset string variable retStr = downloadInternal1(request, file, header, out, in,expResp.length()); assertEquals("unexpected HTTP response message body in second request", expResp, retStr); ret = ret && retStr.equals(expResp); //Cleanup s.close(); return ret; } private static boolean downloadPush(String file, String header, String expResp) throws IOException, BadPacketException { Socket s = getSocketFromPush(); BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); in.readLine(); //skip GIV in.readLine(); //skip blank line //Download from the (incoming) TCP connection. String retStr=downloadInternal(file, header, out, in); assertNotNull("string returned from download should not be null", retStr); //Cleanup s.close(); assertEquals("wrong response", expResp, retStr); return retStr.equals(expResp); } /** * Sends a get request to out, reads the response from in, and returns the * content. Doesn't close in or out. * @param requiredHeader a header to look for, or null if we don't care */ private static String downloadInternal(String file, String header, BufferedWriter out, BufferedReader in) throws IOException { return downloadInternal(file, header, out, in, null, null); } /** * Sends a get request to out, reads the response from in, and returns the * content. Doesn't close in or out. * @param requestMethod the method to request ('GET', 'HEAD') * @param file the actual request (/get/ ...) * @param header the header to send, possibly null * @param out the writer * @param in the reader * @param requiredHeader the header we want to receive * if null, then no header is expected. * @param http11 whether or not to write http 1.1 or 1.0 * @return the contents of what we read */ private static String downloadInternal(String requestMethod, String file, String header, BufferedWriter out, BufferedReader in, String requiredHeader, boolean http11, boolean require11Response, Applyable f) throws IOException { return new String( request(requestMethod, file, header, out, in, requiredHeader, http11, require11Response, f) ); } private static byte[] getBytes(String requestMethod, String file, String header, OutputStream out, InputStream in, String requiredHeader, boolean http11, boolean require11Response) throws IOException { int length = readToContent(requestMethod, file, header, new OutputStreamWriter(out), in, requiredHeader, http11, require11Response, null); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); //3. Read content. Obviously this is designed for small files. for(int i = 0; i < length; i++) { int c = in.read(); if (c < 0) break; bytes.write(c); } return bytes.toByteArray(); } private static byte[] request(String requestMethod, String file, String header, BufferedWriter out, BufferedReader in, String requiredHeader, boolean http11, boolean require11Response, Applyable f) throws IOException { int length = readToContent(requestMethod, file, header, out, in, requiredHeader, http11, require11Response, f); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); //3. Read content. Obviously this is designed for small files. for(int i = 0; i < length; i++) { int c = in.read(); if (c < 0) break; bytes.write(c); } return bytes.toByteArray(); } private static int readToContent(String requestMethod, String file, String header, Writer out, Object in, String requiredHeader, boolean http11, boolean require11Response, Applyable f) throws IOException { // send request out.write( requestMethod + " " + file + " " + (http11 ? "HTTP/1.1" : "HTTP/1.0") + "\r\n"); if (header != null) out.write(header + "\r\n"); if(http11) out.write("Connection: Keep-Alive\r\n"); out.write("\r\n"); out.flush(); //2. Read response code and headers, remember the content-length. boolean foundHeader = false; Header expectedHeader = null; int length = -1; if( requiredHeader != null ) expectedHeader = new Header(requiredHeader); boolean firstLine = true; while (true) { String line; if(in instanceof Reader) line = readLine((Reader)in); else line = readLine((InputStream)in); if(require11Response && firstLine && (line == null || !line.startsWith("HTTP/1.1"))) fail("bad first response line: " + line); firstLine = false; if( line == null) throw new InterruptedIOException("connection closed"); if (line.equals("")) break; if(f != null) { try { f.apply(line); } catch(Exception e) { fail(e); return -1; } } if (requiredHeader != null) { Header found = new Header(line); if( found.equals(expectedHeader)) { foundHeader = true; } } if( line.startsWith("Content-Length: ") ) length = Integer.valueOf(line.substring(15).trim()).intValue(); } //2A. If a header was required, make sure it was there. if (requiredHeader != null) { assertTrue("Didn't find header: " + requiredHeader, foundHeader); } //System.out.println("Download returns: " + System.currentTimeMillis()); return length; } private static boolean containsHeader(String requestMethod, String file, String header, Writer out, Reader in, String requiredHeader) throws IOException { // send request out.write( requestMethod + " " + makeRequest(file) + " " + "HTTP/1.1\r\n"); if (header != null) out.write(header + "\r\n"); out.write("Connection: Keep-Alive\r\n"); out.write("\r\n"); out.flush(); //2. Read response code and headers, remember the content-length. boolean foundHeader = false; Header expectedHeader = null; if( requiredHeader != null ) expectedHeader = new Header(requiredHeader); while (true) { String line = readLine(in); if( line == null) throw new InterruptedIOException("connection closed"); //System.out.println("<< " + line); if (line.equals("")) break; if (requiredHeader != null) { Header found = new Header(line); if( found.title.equals(expectedHeader.title)) { return true; } } } return false; } private static interface Applyable { public void apply(String line) throws Exception; } private static class Header { final String title; final List contents; public Header(String data) { contents = new LinkedList(); int colon = data.indexOf(":"); if( colon == -1 ) { title = data; } else { title = data.substring(0, colon); StringTokenizer st = new StringTokenizer(data.substring(colon+1), ","); while(st.hasMoreTokens()) { String info = st.nextToken().trim(); contents.add(info); } } } public boolean equals(Object o) { if(o == this) return true; if(!(o instanceof Header)) return false; Header other = (Header)o; if(!title.toLowerCase().equals(other.title.toLowerCase())) return false; return listEquals(contents, other.contents); } public boolean listEquals(List one, List two) { if( one.size() != two.size() ) return false; boolean found; for(Iterator i = one.iterator(); i.hasNext(); ) { found = false; String a = (String)i.next(); for(Iterator j = two.iterator(); j.hasNext(); ) { String b = (String)j.next(); if(a.equalsIgnoreCase(b)) found = true; } if(!found) return false; } for(Iterator i = two.iterator(); i.hasNext(); ) { found = false; String a = (String)i.next(); for(Iterator j = two.iterator(); j.hasNext(); ) { String b = (String)j.next(); if(a.equalsIgnoreCase(b)) found = true; } if(!found) return false; } return true; } public String toString() { return title + " : " + contents; } } /** * Sends a get request to out, reads the response from in, and returns the * content. Doesn't close in or out. * @param requiredHeader a header to look for, or null if we don't care */ private static String downloadInternal11(String file, String header, BufferedWriter out, BufferedReader in, String requiredHeader) throws IOException { return downloadInternal("GET", makeRequest(file), header, out, in, requiredHeader, true, true, null); } /** * Sends a get request to out, reads the response from in, and returns the * content. Doesn't close in or out. * @param requiredHeader a header to look for, or null if we don't care */ private static String downloadInternal(String file, String header, BufferedWriter out, BufferedReader in, String requiredHeader, Applyable f) throws IOException { return downloadInternal("GET", makeRequest(file), header, out, in, requiredHeader, false, true, f); } /** * Sends an arbitrary request, returning the result. * @param file the full filename, e.g., "/get/0/file.txt" * @param request an HTTP request such as "GET" or "HEAD" */ private static String downloadInternal1(String request, String file, String header, BufferedWriter out, BufferedReader in, int expectedSize) throws IOException { //Assert.that(out!=null && in!=null,"socket closed my server"); //1. Send request out.write(request+" "+file+" HTTP/1.1\r\n"); if (header!=null) out.write(header+"\r\n"); out.write("Connection: Keep-Alive\r\n"); out.write("\r\n"); out.flush(); //2. Read (and ignore!) response code and headers. TODO: verify. String firstLine = in.readLine(); if(firstLine == null || !firstLine.startsWith("HTTP/1.1")) fail("bad first response line: " + firstLine); while(!in.readLine().equals("")){ } //3. Read content. Obviously this is designed for small files. StringBuffer buf=new StringBuffer(); for(int i=0; i<expectedSize; i++) { int c = in.read(); buf.append((char)c); } return buf.toString(); } private static boolean pipelineDownloadNormal(String file, String header, String expResp) throws IOException { boolean ret = true; Socket s = new Socket("localhost",PORT); BufferedReader in = new BufferedReader(new InputStreamReader (s.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter (s.getOutputStream())); //write first request out.write("GET " + makeRequest(file)+" HTTP/1.1\r\n"); if (header!=null) out.write(header+"\r\n"); out.write("Connection:Keep-Alive\r\n"); out.write("\r\n"); out.flush(); //write second request out.write("GET " + makeRequest(file) +" HTTP/1.1\r\n"); if (header!=null) out.write(header+"\r\n"); out.write("Connection:Keep-Alive\r\n"); out.write("\r\n"); out.flush(); int expectedSize = expResp.length(); //read...ignore response headers String firstLine = in.readLine(); if(firstLine == null || !firstLine.startsWith("HTTP/1.1")) fail("bad first response line: " + firstLine); while(!in.readLine().equals("")){ } //read first response StringBuffer buf=new StringBuffer(); for(int i=0; i<expectedSize; i++){ int c = in.read(); buf.append((char)c); } ret = buf.toString().equals(expResp); //ingore second header buf = new StringBuffer(); while(!in.readLine().equals("")){ } //read Second response for(int i=0; i<expectedSize; i++){ int c = in.read(); buf.append((char)c); } // close all the appropriate streams & sockets. in.close(); out.close(); s.close(); return ret && buf.toString().equals(expResp); } private static boolean pipelineDownloadPush(String file, String header, String expResp) throws IOException , BadPacketException { boolean ret = true; Socket s = getSocketFromPush(); BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); in.readLine(); //skip GIV in.readLine(); //skip blank line //write first request out.write("GET " + makeRequest(file) +" HTTP/1.1\r\n"); if (header!=null) out.write(header+"\r\n"); out.write("Connection:Keep-Alive\r\n"); out.write("\r\n"); out.flush(); //write second request out.write("GET " + makeRequest(file) +" HTTP/1.1\r\n"); if (header!=null) out.write(header+"\r\n"); out.write("Connection:Keep-Alive\r\n"); out.write("\r\n"); out.flush(); int expectedSize = expResp.length(); //read...ignore response headers String firstLine = in.readLine(); if(firstLine == null || !firstLine.startsWith("HTTP/1.1")) fail("bad first response line: " + firstLine); while(!in.readLine().equals("")){ } //read first response StringBuffer buf=new StringBuffer(); for(int i=0; i<expectedSize; i++){ int c1 = in.read(); buf.append((char)c1); } ret = buf.toString().equals(expResp); buf = new StringBuffer(); //ingore second header firstLine = in.readLine(); if(firstLine == null || !firstLine.startsWith("HTTP/1.1")) fail("bad first response line: " + firstLine); while(!in.readLine().equals("")){ } //read Second response for(int i=0; i<expectedSize; i++){ int c1 = in.read(); buf.append((char)c1); } // close all the appropriate streams & sockets. in.close(); out.close(); s.close(); return ret && buf.toString().equals(expResp); } /** Makes sure that a HEAD request followed by a GET request does the right * thing. */ public void tMixedPersistentRequests() throws Exception { Socket s = null; try { //1. Establish connection. s = new Socket("localhost", PORT); BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); //2. Send HEAD request assertEquals("", downloadInternal1("HEAD", "/get/"+index+"/"+encodedFile, null, out, in, 0)); //3. Send GET request, make sure data ok. assertEquals(alphabet, downloadInternal1("GET", "/get/"+index+"/"+encodedFile, null, out, in, alphabet.length())); } finally { if (s!=null) try { s.close(); } catch (IOException ignore) { } } } /** Tests persistent connections with URI requests. (Raphael Manfredi claimed this * was broken.) */ public void tPersistentURIRequests() throws Exception { Socket s = null; try { //1. Establish connection. s = new Socket("localhost", PORT); BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); //2. Send GET request in URI form assertEquals(alphabet, downloadInternal1("GET", "/uri-res/N2R?"+hash, null, out, in, alphabet.length())); //3. Send another GET request in URI form assertEquals(alphabet, downloadInternal1("GET", "/uri-res/N2R?"+hash, null, out, in, alphabet.length())); } finally { if (s!=null) try { s.close(); } catch (IOException ignore) { } } } /** * Tests various cases for failed downloads, ensuring * that the correct header is sent back. */ public void tFailureHeaderRequired(String file, String sendHeader, boolean http11, boolean repeat, String requiredHeader) throws Exception { Socket s = null; try { //1. Establish connection. s = new Socket("localhost", PORT); BufferedReader in = new BufferedReader(new InputStreamReader( s.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter( s.getOutputStream())); //2. Send GET request in URI form downloadInternal("GET", file, sendHeader, out, in, requiredHeader, http11, true, null); //3. If the connection should remain open, make sure we // can request again. if( repeat ) { downloadInternal("GET", file, sendHeader, out, in, requiredHeader, http11, true, null); } else { try { downloadInternal("GET", file, sendHeader, out, in, requiredHeader, http11, false, null); fail("Connection should be closed"); } catch(InterruptedIOException good) { // good. } catch(SocketException good) { // good too. } } } finally { if (s!=null) try { s.close(); } catch (IOException ignore) { } } } /** * Creates an Ultrapeer connection. */ private static Connection createConnection() { return new Connection("localhost", PORT); } private static class EmptyResponder implements HandshakeResponder { public HandshakeResponse respond(HandshakeResponse response, boolean outgoing) { return HandshakeResponse.createResponse(new Properties()); } public void setLocalePreferencing(boolean b) {} } private static class FManCallback extends ActivityCallbackStub { public void fileManagerLoaded() { synchronized(loaded) { loaded.notify(); } } } private static void startAndWaitForLoad() { synchronized(loaded) { try { ROUTER_SERVICE.start(); loaded.wait(); } catch (InterruptedException e) { //good. } } } private static String makeRequest(String req) { if(req.startsWith("/uri-res")) return req; else return "/get/" + index + "/" + req; } /** * Reads a new line WITHOUT end of line characters. A line is * defined as a minimal sequence of character ending with "\n", with * all "\r"'s thrown away. Hence calling readLine on a stream * containing "abc\r\n" or "a\rbc\n" will return "abc". * * Throws IOException if there is an IO error. Returns null if * there are no more lines to read, i.e., EOF has been reached. * Note that calling readLine on "ab<EOF>" returns null. */ public static String readLine(Reader _istream) throws IOException { if (_istream == null) return ""; StringBuffer sBuffer = new StringBuffer(); int c = -1; //the character just read boolean keepReading = true; do { try { c = _istream.read(); } catch(ArrayIndexOutOfBoundsException aiooe) { // this is apparently thrown under strange circumstances. // interpret as an IOException. throw new IOException("aiooe."); } switch(c) { // if this was a \n character, break out of the reading loop case '\n': keepReading = false; break; // if this was a \r character, ignore it. case '\r': continue; // if we reached an EOF ... case -1: return null; // if it was any other character, append it to the buffer. default: sBuffer.append((char)c); } } while(keepReading); // return the string we have read. return sBuffer.toString(); } public static String readLine(InputStream in) throws IOException { return new ByteReader(in).readLine(); } /** * testFALTNotRequested(), testFALTWhenRequested() and testFWALTWhenRequested() * fail if the server processes the entire request before we start reading * from the InputStreams. That means: our HTTPUploader is added to UploadManagers * private _activeUploadsList, the request is processed and the HTTPUploader is * removed from the List. We start reading from the InputStream and the assertions * in the mentioned tests fail because our HTTPUploader is no longer in that * List. So, we have to cache the HTTPUploader somehow what this extension does. */ private static class TestUploadManager extends UploadManager { private List activeUploads = new ArrayList(); protected synchronized void addAcceptedUploader(HTTPUploader uploader) { activeUploads.add(uploader); super.addAcceptedUploader(uploader); } public void clearUploads() { activeUploads.clear(); } } }