Compare commits
9 Commits
connection
...
muwire-0.3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4e2a530a13 | ||
![]() |
03646e2b90 | ||
![]() |
3dce228bbb | ||
![]() |
15a49ad550 | ||
![]() |
3d91c0f4c7 | ||
![]() |
2825a8d9a4 | ||
![]() |
8dcce9bda6 | ||
![]() |
d8d3e2cd58 | ||
![]() |
51d5dbe47e |
@@ -11,12 +11,12 @@ The current stable release - 0.2.5 is avaiable for download at http://muwire.com
|
||||
You need JRE 8 or newer. After installing that and setting up the appropriate paths, just type
|
||||
|
||||
```
|
||||
./gradlew assemble
|
||||
./gradlew clean assemble
|
||||
```
|
||||
|
||||
If you want to run the unit tests, type
|
||||
```
|
||||
./gradlew build
|
||||
./gradlew clean build
|
||||
```
|
||||
|
||||
Some of the UI tests will fail because they haven't been written yet :-/
|
||||
|
@@ -35,7 +35,7 @@ class Cli {
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home, "0.2.9")
|
||||
core = new Core(props, home, "0.3.0")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
|
@@ -53,7 +53,7 @@ class CliDownloader {
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home, "0.2.9")
|
||||
core = new Core(props, home, "0.3.0")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
|
@@ -104,9 +104,9 @@ public class Core {
|
||||
i2pOptions["inbound.nickname"] = "MuWire"
|
||||
i2pOptions["outbound.nickname"] = "MuWire"
|
||||
i2pOptions["inbound.length"] = "3"
|
||||
i2pOptions["inbound.quantity"] = "2"
|
||||
i2pOptions["inbound.quantity"] = "4"
|
||||
i2pOptions["outbound.length"] = "3"
|
||||
i2pOptions["outbound.quantity"] = "2"
|
||||
i2pOptions["outbound.quantity"] = "4"
|
||||
i2pOptions["i2cp.tcp.host"] = "127.0.0.1"
|
||||
i2pOptions["i2cp.tcp.port"] = "7654"
|
||||
}
|
||||
@@ -277,7 +277,7 @@ public class Core {
|
||||
}
|
||||
}
|
||||
|
||||
Core core = new Core(props, home, "0.2.9")
|
||||
Core core = new Core(props, home, "0.3.0")
|
||||
core.startServices()
|
||||
|
||||
// ... at the end, sleep or execute script
|
||||
|
@@ -15,7 +15,7 @@ class DownloadSessionTest {
|
||||
private File source, target
|
||||
private InfoHash infoHash
|
||||
private Endpoint endpoint
|
||||
private Pieces pieces, claimed
|
||||
private Pieces pieces
|
||||
private String rootBase64
|
||||
|
||||
private DownloadSession session
|
||||
@@ -48,8 +48,7 @@ class DownloadSessionTest {
|
||||
else
|
||||
nPieces = size / pieceSize + 1
|
||||
pieces = new Pieces(nPieces)
|
||||
claimed = new Pieces(nPieces)
|
||||
claimedPieces.each {claimed.markDownloaded(it)}
|
||||
claimedPieces.each {pieces.claimed.set(it)}
|
||||
|
||||
fromDownloader = new PipedInputStream()
|
||||
fromUploader = new PipedInputStream()
|
||||
@@ -57,7 +56,7 @@ class DownloadSessionTest {
|
||||
toUploader = new PipedOutputStream(fromDownloader)
|
||||
endpoint = new Endpoint(null, fromUploader, toUploader, null)
|
||||
|
||||
session = new DownloadSession("",pieces, claimed, infoHash, endpoint, target, pieceSize, size)
|
||||
session = new DownloadSession("",pieces, infoHash, endpoint, target, pieceSize, size)
|
||||
downloadThread = new Thread( { session.request() } as Runnable)
|
||||
downloadThread.setDaemon(true)
|
||||
downloadThread.start()
|
||||
@@ -154,7 +153,7 @@ class DownloadSessionTest {
|
||||
int pieceSize = FileHasher.getPieceSize(1)
|
||||
int size = (1 << pieceSize) * 10
|
||||
initSession(size, [1,2,3,4,5,6,7,8,9])
|
||||
assert !claimed.isMarked(0)
|
||||
assert !pieces.claimed.get(0)
|
||||
|
||||
assert "GET $rootBase64" == readTillRN(fromDownloader)
|
||||
String range = readTillRN(fromDownloader)
|
||||
@@ -162,7 +161,7 @@ class DownloadSessionTest {
|
||||
int start = Integer.parseInt(matcher[0][1])
|
||||
int end = Integer.parseInt(matcher[0][2])
|
||||
|
||||
assert claimed.isMarked(0)
|
||||
assert pieces.claimed.get(0)
|
||||
assert start == 0 && end == (1 << pieceSize) - 1
|
||||
}
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ class PiecesTest {
|
||||
public void testSinglePiece() {
|
||||
pieces = new Pieces(1)
|
||||
assert !pieces.isComplete()
|
||||
assert pieces.getRandomPiece() == 0
|
||||
assert pieces.claim() == 0
|
||||
pieces.markDownloaded(0)
|
||||
assert pieces.isComplete()
|
||||
}
|
||||
@@ -25,11 +25,11 @@ class PiecesTest {
|
||||
public void testTwoPieces() {
|
||||
pieces = new Pieces(2)
|
||||
assert !pieces.isComplete()
|
||||
int piece = pieces.getRandomPiece()
|
||||
int piece = pieces.claim()
|
||||
assert piece == 0 || piece == 1
|
||||
pieces.markDownloaded(piece)
|
||||
assert !pieces.isComplete()
|
||||
int piece2 = pieces.getRandomPiece()
|
||||
int piece2 = pieces.claim()
|
||||
assert piece != piece2
|
||||
pieces.markDownloaded(piece2)
|
||||
assert pieces.isComplete()
|
||||
|
@@ -26,7 +26,7 @@ class FileHasherTest extends GroovyTestCase {
|
||||
void testPieceSize() {
|
||||
assert 17 == FileHasher.getPieceSize(1000000)
|
||||
assert 17 == FileHasher.getPieceSize(100000000)
|
||||
assert 27 == FileHasher.getPieceSize(FileHasher.MAX_SIZE)
|
||||
assert 24 == FileHasher.getPieceSize(FileHasher.MAX_SIZE)
|
||||
shouldFail IllegalArgumentException, {
|
||||
FileHasher.getPieceSize(Long.MAX_VALUE)
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ class HasherServiceTest {
|
||||
hasher = new FileHasher()
|
||||
service = new HasherService(hasher, eventBus, new FileManager(eventBus, new MuWireSettings()))
|
||||
eventBus.register(FileHashedEvent.class, listener)
|
||||
eventBus.register(FileSharedEvent.class, service)
|
||||
service.start()
|
||||
}
|
||||
|
||||
|
@@ -78,7 +78,7 @@ class PersisterServiceLoadingTest {
|
||||
persisted.write json
|
||||
|
||||
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
|
||||
ps.start()
|
||||
ps.onUILoadedEvent(null)
|
||||
Thread.sleep(2000)
|
||||
|
||||
assert listener.publishedFiles.size() == 1
|
||||
@@ -121,7 +121,7 @@ class PersisterServiceLoadingTest {
|
||||
persisted.write json
|
||||
|
||||
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
|
||||
ps.start()
|
||||
ps.onUILoadedEvent(null)
|
||||
Thread.sleep(2000)
|
||||
|
||||
assert listener.publishedFiles.size() == 1
|
||||
@@ -163,7 +163,7 @@ class PersisterServiceLoadingTest {
|
||||
persisted.append "$json2\n"
|
||||
|
||||
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
|
||||
ps.start()
|
||||
ps.onUILoadedEvent(null)
|
||||
Thread.sleep(2000)
|
||||
|
||||
assert listener.publishedFiles.size() == 2
|
||||
@@ -195,7 +195,7 @@ class PersisterServiceLoadingTest {
|
||||
persisted.write json1
|
||||
|
||||
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
|
||||
ps.start()
|
||||
ps.onUILoadedEvent(null)
|
||||
Thread.sleep(2000)
|
||||
|
||||
assert listener.publishedFiles.size() == 1
|
||||
|
@@ -58,7 +58,7 @@ class PersisterServiceSavingTest {
|
||||
sf = new SharedFile(f, ih, 0)
|
||||
|
||||
ps = new PersisterService(persisted, eventBus, 100, fileSource)
|
||||
ps.start()
|
||||
ps.onUILoadedEvent(null)
|
||||
Thread.sleep(1500)
|
||||
|
||||
JsonSlurper jsonSlurper = new JsonSlurper()
|
||||
@@ -77,7 +77,7 @@ class PersisterServiceSavingTest {
|
||||
sf = new DownloadedFile(f, ih, 0, new HashSet([dests.dest1, dests.dest2]))
|
||||
|
||||
ps = new PersisterService(persisted, eventBus, 100, fileSource)
|
||||
ps.start()
|
||||
ps.onUILoadedEvent(null)
|
||||
Thread.sleep(1500)
|
||||
|
||||
JsonSlurper jsonSlurper = new JsonSlurper()
|
||||
|
@@ -9,18 +9,18 @@ import com.muwire.core.InfoHash
|
||||
|
||||
class RequestParsingTest {
|
||||
|
||||
Request request
|
||||
ContentRequest request
|
||||
|
||||
private void fromString(String requestString) {
|
||||
def is = new ByteArrayInputStream(requestString.getBytes(StandardCharsets.US_ASCII))
|
||||
request = Request.parse(new InfoHash(new byte[InfoHash.SIZE]), is)
|
||||
request = Request.parseContentRequest(new InfoHash(new byte[InfoHash.SIZE]), is)
|
||||
}
|
||||
|
||||
|
||||
private static void failed(String requestString) {
|
||||
try {
|
||||
def is = new ByteArrayInputStream(requestString.getBytes(StandardCharsets.US_ASCII))
|
||||
Request.parse(new InfoHash(new byte[InfoHash.SIZE]), is)
|
||||
Request.parseContentRequest(new InfoHash(new byte[InfoHash.SIZE]), is)
|
||||
assert false
|
||||
} catch (IOException expected) {}
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ class UploaderTest {
|
||||
InputStream is
|
||||
OutputStream os
|
||||
|
||||
Request request
|
||||
ContentRequest request
|
||||
Uploader uploader
|
||||
|
||||
byte[] inFile
|
||||
@@ -52,7 +52,7 @@ class UploaderTest {
|
||||
}
|
||||
|
||||
private void startUpload() {
|
||||
uploader = new Uploader(file, request, endpoint)
|
||||
uploader = new ContentUploader(file, request, endpoint)
|
||||
uploadThread = new Thread(uploader.respond() as Runnable)
|
||||
uploadThread.setDaemon(true)
|
||||
uploadThread.start()
|
||||
@@ -77,7 +77,7 @@ class UploaderTest {
|
||||
@Test
|
||||
public void testSmallFile() {
|
||||
fillFile(20)
|
||||
request = new Request(range : new Range(0,19))
|
||||
request = new ContentRequest(range : new Range(0,19))
|
||||
startUpload()
|
||||
assert "200 OK" == readUntilRN()
|
||||
assert "Content-Range: 0-19" == readUntilRN()
|
||||
@@ -92,7 +92,7 @@ class UploaderTest {
|
||||
@Test
|
||||
public void testRequestMiddle() {
|
||||
fillFile(20)
|
||||
request = new Request(range : new Range(5,15))
|
||||
request = new ContentRequest(range : new Range(5,15))
|
||||
startUpload()
|
||||
assert "200 OK" == readUntilRN()
|
||||
assert "Content-Range: 5-15" == readUntilRN()
|
||||
@@ -108,7 +108,7 @@ class UploaderTest {
|
||||
@Test
|
||||
public void testOutOfRange() {
|
||||
fillFile(20)
|
||||
request = new Request(range : new Range(0,20))
|
||||
request = new ContentRequest(range : new Range(0,20))
|
||||
startUpload()
|
||||
assert "416 Range Not Satisfiable" == readUntilRN()
|
||||
assert "" == readUntilRN()
|
||||
@@ -118,7 +118,7 @@ class UploaderTest {
|
||||
public void testLargeFile() {
|
||||
final int length = 0x1 << 14
|
||||
fillFile(length)
|
||||
request = new Request(range : new Range(0, length - 1))
|
||||
request = new ContentRequest(range : new Range(0, length - 1))
|
||||
startUpload()
|
||||
readUntilRN()
|
||||
readUntilRN()
|
||||
|
@@ -49,7 +49,7 @@ Files are transferred over HTTP1.1 protocol with some custom headers added for d
|
||||
|
||||
### Mesh management
|
||||
|
||||
Download mesh management is identical to Gnutella, except instead of ip addresses MuWire personas are used. [More information](http://rfc-gnutella.sourceforge.net/developer/tmp/download-mesh.html)
|
||||
Download mesh management is a simplified version of Gnutella's "Alternate Location" system. For more information see the "download-mesh" document.
|
||||
|
||||
### In-Network updates
|
||||
|
||||
|
15
doc/download-mesh.md
Normal file
15
doc/download-mesh.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Download Mesh / Partial Sharing
|
||||
|
||||
MuWire uses a system similar to Gnutella's "Alternate Location" download mesh management system, however it is simplified to account for I2P's strengths and borrows a bit from BitTorrent's "Have" message.
|
||||
|
||||
### "X-Have" header
|
||||
|
||||
With every request a downloader makes it sends an "X-Have" header containing the Base64-encoded representation of a bitfield where bits set to 1 represent pieces of the file that the downloader already has. To make partial file sharing possible, if the uploader does not have the complete file it also sends this header in every response. If the header is missing it is assumed the uploader has the complete file.
|
||||
|
||||
### "X-Alt" header
|
||||
|
||||
The uploader can recommend other uploaders to the downloader via the "X-Alt" header. The format of this header is a comma-separated list of Base64-encoded Personas that have previously reported having at least one piece of the file to the uploader via the "X-Have" header.
|
||||
|
||||
### Differences from Gnutella
|
||||
|
||||
Unlike Gnutella the uploader is the sole repository where possible sources of the file are tracked. There is no negative "X-Nalt" header to prevent attacking the download mesh by mass downvoting of sources.
|
@@ -1,5 +1,5 @@
|
||||
group = com.muwire
|
||||
version = 0.2.9
|
||||
version = 0.3.0
|
||||
groovyVersion = 2.4.15
|
||||
slf4jVersion = 1.7.25
|
||||
spockVersion = 1.1-groovy-2.4
|
||||
|
@@ -282,8 +282,10 @@ class MainFrameModel {
|
||||
updateTablePreservingSelection("trusted-table")
|
||||
updateTablePreservingSelection("distrusted-table")
|
||||
|
||||
results.values().each {
|
||||
it.view.pane.getClientProperty("results-table")?.model.fireTableDataChanged()
|
||||
results.values().each { MVCGroup group ->
|
||||
if (group.alive) {
|
||||
group.view.pane.getClientProperty("results-table")?.model.fireTableDataChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -58,7 +58,8 @@ class MainFrameView {
|
||||
builder.with {
|
||||
application(size : [1024,768], id: 'main-frame',
|
||||
locationRelativeTo : null,
|
||||
title: application.configuration['application.title'] + " " + metadata["application.version"],
|
||||
title: application.configuration['application.title'] + " " +
|
||||
metadata["application.version"] + " revision " + metadata["build.revision"],
|
||||
iconImage: imageIcon('/griffon-icon-48x48.png').image,
|
||||
iconImages: [imageIcon('/griffon-icon-48x48.png').image,
|
||||
imageIcon('/griffon-icon-32x32.png').image,
|
||||
|
Reference in New Issue
Block a user