Compare commits

..

54 Commits

Author SHA1 Message Date
Zlatin Balevsky
6bad67c1bf Release 0.4.8 2019-07-08 18:30:19 +01:00
Zlatin Balevsky
c76e6dc99f Merge pull request #9 from zetok/backticks
Replace deprecated backticks with $() for command substitution
2019-07-08 08:24:37 +01:00
Zetok Zalbavar
acf9db0db3 Replace deprecated backticks with $() for command substitution
Although it's a Bash FAQ, the point also applies to POSIX-compatible
shells: https://mywiki.wooledge.org/BashFAQ/082
2019-07-08 06:29:33 +01:00
Zlatin Balevsky
69b4f0b547 Add trust/distrust action from monitor window. Thanks Aegon 2019-07-07 15:31:21 +01:00
Zlatin Balevsky
80e165b505 fix download size in renderer, thanks Aegon 2019-07-07 11:17:56 +01:00
Zlatin Balevsky
bcce55b873 fix integer overflow 2019-07-07 10:58:39 +01:00
Zlatin Balevsky
d5c92560db fix integer overflow 2019-07-07 10:56:14 +01:00
Zlatin Balevsky
f827c1c9bf Home directories for different OSes 2019-07-07 09:14:13 +01:00
Zlatin Balevsky
88c5f1a02d Add GPG key link 2019-07-07 09:04:52 +01:00
Zlatin Balevsky
d8e44f5f39 kill other workers if download is finished 2019-07-06 22:21:13 +01:00
Zlatin Balevsky
72ff47ffe5 use custom renderer and comparator for download progress 2019-07-06 12:53:49 +01:00
Zlatin Balevsky
066ee2c96d wrong list 2019-07-06 11:28:04 +01:00
Zlatin Balevsky
0a8016dea7 enable stealing of pieces from other download workers 2019-07-06 11:26:18 +01:00
Zlatin Balevsky
db36367b11 avoid AIOOBE 2019-07-06 11:00:31 +01:00
Zlatin Balevsky
b6c9ccb7f6 return up to 9 X-Alts 2019-07-06 09:03:27 +01:00
Zlatin Balevsky
a9dc636bce write pieces every time a downloader finishes 2019-07-06 00:52:49 +01:00
Zlatin Balevsky
3cc0574d11 working partial pieces 2019-07-06 00:47:45 +01:00
Zlatin Balevsky
20fab9b16d work on partial piece persistence 2019-07-06 00:17:46 +01:00
Zlatin Balevsky
4015818323 center buttons 2019-07-05 17:15:50 +01:00
Zlatin Balevsky
f569d45c8c reallign tables 2019-07-05 17:07:14 +01:00
Zlatin Balevsky
3773647869 remove diff rejects 2019-07-05 16:24:57 +01:00
Zlatin Balevsky
29cdbf018c remove trailing spaces 2019-07-05 16:24:19 +01:00
Zlatin Balevsky
94bb7022eb tabs -> spaces 2019-07-05 16:22:34 +01:00
Zlatin Balevsky
39808302df Show which file is hashing, thanks to Aegon 2019-07-05 16:20:03 +01:00
Zlatin Balevsky
2d22f9c39e override router log manager 2019-07-05 12:32:23 +01:00
Zlatin Balevsky
ee8f80bab6 up i2p to 0.9.41 2019-07-05 12:26:48 +01:00
Zlatin Balevsky
3e6242e583 break when matching search is found 2019-07-04 18:12:22 +01:00
Zlatin Balevsky
41181616ee compact display of incoming searches, thanks Aegon 2019-07-04 17:59:53 +01:00
Zlatin Balevsky
eb2530ca32 fix sorting of download/upload tables thanks Aegon 2019-07-04 17:58:06 +01:00
Zlatin Balevsky
b5233780ef Release 0.4.7 2019-07-03 20:36:54 +01:00
Zlatin Balevsky
78753d7538 shut down cache client on shutdown 2019-07-03 19:50:00 +01:00
Zlatin Balevsky
4740e8b4f5 log hostcache stats 2019-07-03 19:46:24 +01:00
Zlatin Balevsky
ad5b00fc90 prettier progress status thanks to Aegon 2019-07-03 12:50:24 +01:00
Zlatin Balevsky
d6c6880848 update readme 2019-07-03 07:27:48 +01:00
Zlatin Balevsky
4f948c1b9e Release 0.4.6 2019-07-03 07:11:59 +01:00
Zlatin Balevsky
2b68c24f9c use switch 2019-07-03 07:01:27 +01:00
Zlatin Balevsky
bcdf0422db update for embedded router 2019-07-03 07:00:04 +01:00
Zlatin Balevsky
f6434b478d remove FAQ 2019-07-03 06:56:20 +01:00
Zlatin Balevsky
e979fdd26f update list view tables 2019-07-03 06:51:21 +01:00
Zlatin Balevsky
e6bfcaaab9 size columns, center integers 2019-07-03 06:11:02 +01:00
Zlatin Balevsky
9780108e8a disable trust buttons on action 2019-07-03 06:00:09 +01:00
Zlatin Balevsky
697c7d2d6d enable/disable trust panel buttons 2019-07-03 05:41:17 +01:00
Zlatin Balevsky
887d10c8bf move buttons around 2019-07-03 05:30:39 +01:00
Zlatin Balevsky
ef6b8fe458 add a state for failed updates 2019-07-03 05:12:00 +01:00
Zlatin Balevsky
20ab55d763 update todo 2019-07-03 00:23:21 +01:00
Zlatin Balevsky
eda58c9e0d Merge branch 'trust-lists' 2019-07-03 00:04:50 +01:00
Zlatin Balevsky
1ccf6fbdfa participating bandwidth grid cell 2019-07-02 15:35:42 +01:00
Zlatin Balevsky
5711979272 Release 0.4.5 2019-07-02 15:01:51 +01:00
Zlatin Balevsky
9a5e2b1fa3 speed smoothing patch courtesy of Aegon 2019-07-02 14:46:40 +01:00
Zlatin Balevsky
a89b423dfc simpler speed calculation 2019-07-02 13:05:06 +01:00
Zlatin Balevsky
79e8438941 always assume interval is at least 1 second 2019-07-02 12:49:00 +01:00
Zlatin Balevsky
19c2c46491 prevent NPE on startup 2019-07-02 12:27:15 +01:00
Zlatin Balevsky
78f1d54b69 add new host cache 2019-07-02 10:04:24 +01:00
Zlatin Balevsky
9461649ed4 change sig type 2019-07-02 09:49:13 +01:00
144 changed files with 5095 additions and 4790 deletions

View File

@@ -4,7 +4,7 @@ MuWire is an easy to use file-sharing program which offers anonymity using [I2P
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
The current stable release - 0.4.0 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
The current stable release - 0.4.6 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
### Building
@@ -23,34 +23,13 @@ Some of the UI tests will fail because they haven't been written yet :-/
### Running
You need to have an I2P router up and running on the same machine. After you build the application, look inside `gui/build/distributions`. Untar/unzip one of the `shadow` files and then run the jar contained inside by typing `java -jar MuWire-x.y.z.jar` in a terminal or command prompt. If you use a custom I2CP host and port, create a file `$HOME/.MuWire/i2p.properties` and put `i2cp.tcp.host=<host>` and `i2cp.tcp.port=<port>` in there.
After you build the application, look inside `gui/build/distributions`. Untar/unzip one of the `shadow` files and then run the jar contained inside by typing `java -jar MuWire-x.y.z.jar` in a terminal or command prompt.
The first time you run MuWire it will ask you to select a nickname. This nickname will be displayed with search results, so that others can verify the file was shared by you. It is best to leave MuWire running all the time, just like I2P.
If you have an I2P router running on the same machine that is all you need to do. If you use a custom I2CP host and port, create a file `i2p.properties` and put `i2cp.tcp.host=<host>` and `i2cp.tcp.port=<port>` in there. On Windows that file should go into `%HOME%\AppData\Roaming\MuWire`, on Mac into `$HOME/Library/Application Support/MuWire` and on Linux `$HOME/.MuWire`
If you do not have an I2P router, pass the following switch to the Java process: `-DembeddedRouter=true`. This will launch MuWire's embedded router. Be aware that this causes startup to take a lot longer.
### Known bugs and limitations
### GPG Fingerprint
471B 9FD4 5517 A5ED 101F C57D A728 3207 2D52 5E41
* Many UI features you would expect are not there yet
### Quick FAQ
* why is MuWire slow ?
- too few sources you're downloading from
- you can increase the number of tunnels by using more tunnels via Options->I2P Inbound/Outbound Quantity
the default is 4 and you could raise up to as high as 16 ( Caution !!!!)
* my search is not returning (enough) results !
- search is keyword or hash based
- keywords and hash(es) are NOT regexed or wildcarded so they have to be complete
so searching for 'musi' will not return results with 'music' - you have to search for 'music'
- ALL keywords have to match
- only use space for keyword separation
- if you already have the file in question it is not displayed ( can be changed via Options )
* what's this right click -> 'Copy hash to clipboard' for ?
- if you have a specific file you wish to share or download you can use the hash as a unique identifier
to make sure you have exactly the right file.
- you can share this hash with others to ensure they are getting the right file
You can find the full key at https://keybase.io/zlatinb

View File

@@ -12,10 +12,6 @@ This reduces query traffic by not sending last hop queries to peers that definit
This helps with scalability
##### Trust List Sharing
For helping users make better decisions whom to trust
##### Content Control Panel
To allow every user to not route queries for content they do not like. This is mostly GUI work, the backend part is simple

View File

@@ -2,7 +2,7 @@ subprojects {
apply plugin: 'groovy'
dependencies {
compile 'net.i2p:i2p:0.9.40'
compile 'net.i2p:i2p:0.9.41'
compile 'org.codehaus.groovy:groovy-all:2.4.15'
}

View File

@@ -35,7 +35,7 @@ class Cli {
Core core
try {
core = new Core(props, home, "0.4.4")
core = new Core(props, home, "0.4.8")
} catch (Exception bad) {
bad.printStackTrace(System.out)
println "Failed to initialize core, exiting"

View File

@@ -53,7 +53,7 @@ class CliDownloader {
Core core
try {
core = new Core(props, home, "0.4.4")
core = new Core(props, home, "0.4.8")
} catch (Exception bad) {
bad.printStackTrace(System.out)
println "Failed to initialize core, exiting"

View File

@@ -2,9 +2,9 @@ apply plugin : 'application'
mainClassName = 'com.muwire.core.Core'
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
dependencies {
compile 'net.i2p:router:0.9.40'
compile 'net.i2p.client:mstreaming:0.9.40'
compile 'net.i2p.client:streaming:0.9.40'
compile 'net.i2p:router:0.9.41'
compile 'net.i2p.client:mstreaming:0.9.41'
compile 'net.i2p.client:streaming:0.9.41'
testCompile 'org.junit.jupiter:junit-jupiter-api:5.4.2'
testCompile 'junit:junit:4.12'

View File

@@ -20,6 +20,7 @@ import com.muwire.core.download.UIDownloadPausedEvent
import com.muwire.core.download.UIDownloadResumedEvent
import com.muwire.core.files.FileDownloadedEvent
import com.muwire.core.files.FileHashedEvent
import com.muwire.core.files.FileHashingEvent
import com.muwire.core.files.FileHasher
import com.muwire.core.files.FileLoadedEvent
import com.muwire.core.files.FileManager
@@ -140,33 +141,33 @@ public class Core {
routerProps.setProperty("i2np.udp.port", i2pOptions["i2np.udp.port"])
routerProps.setProperty("i2np.udp.internalPort", i2pOptions["i2np.udp.port"])
router = new Router(routerProps)
I2PAppContext.getGlobalContext().metaClass = new RouterContextMetaClass()
router.getContext().setLogManager(new MuWireLogManager())
router.runRouter()
while(!router.isRunning())
Thread.sleep(100)
}
log.info("initializing I2P socket manager")
def i2pClient = new I2PClientFactory().createClient()
File keyDat = new File(home, "key.dat")
if (!keyDat.exists()) {
log.info("Creating new key.dat")
keyDat.withOutputStream {
i2pClient.createDestination(it, Constants.SIG_TYPE)
}
}
log.info("initializing I2P socket manager")
def i2pClient = new I2PClientFactory().createClient()
File keyDat = new File(home, "key.dat")
if (!keyDat.exists()) {
log.info("Creating new key.dat")
keyDat.withOutputStream {
i2pClient.createDestination(it, Constants.SIG_TYPE)
}
}
// options like tunnel length and quantity
I2PSession i2pSession
I2PSocketManager socketManager
keyDat.withInputStream {
socketManager = new I2PSocketManagerFactory().createManager(it, i2pOptions["i2cp.tcp.host"], i2pOptions["i2cp.tcp.port"].toInteger(), i2pOptions)
}
socketManager.getDefaultOptions().setReadTimeout(60000)
socketManager.getDefaultOptions().setConnectTimeout(30000)
I2PSession i2pSession
I2PSocketManager socketManager
keyDat.withInputStream {
socketManager = new I2PSocketManagerFactory().createManager(it, i2pOptions["i2cp.tcp.host"], i2pOptions["i2cp.tcp.port"].toInteger(), i2pOptions)
}
socketManager.getDefaultOptions().setReadTimeout(60000)
socketManager.getDefaultOptions().setConnectTimeout(30000)
socketManager.addDisconnectListener({eventBus.publish(new RouterDisconnectedEvent())} as DisconnectListener)
i2pSession = socketManager.getSession()
i2pSession = socketManager.getSession()
def destination = new Destination()
def spk = new SigningPrivateKey(Constants.SIG_TYPE)
@@ -175,7 +176,7 @@ public class Core {
def privateKey = new PrivateKey()
privateKey.readBytes(it)
spk.readBytes(it)
}
}
def baos = new ByteArrayOutputStream()
def daos = new DataOutputStream(baos)
@@ -193,65 +194,65 @@ public class Core {
me = new Persona(new ByteArrayInputStream(baos.toByteArray()))
log.info("Loaded myself as "+me.getHumanReadableName())
eventBus = new EventBus()
eventBus = new EventBus()
log.info("initializing trust service")
File goodTrust = new File(home, "trusted")
File badTrust = new File(home, "distrusted")
trustService = new TrustService(goodTrust, badTrust, 5000)
eventBus.register(TrustEvent.class, trustService)
log.info("initializing trust service")
File goodTrust = new File(home, "trusted")
File badTrust = new File(home, "distrusted")
trustService = new TrustService(goodTrust, badTrust, 5000)
eventBus.register(TrustEvent.class, trustService)
log.info "initializing file manager"
fileManager = new FileManager(eventBus, props)
eventBus.register(FileHashedEvent.class, fileManager)
eventBus.register(FileLoadedEvent.class, fileManager)
eventBus.register(FileDownloadedEvent.class, fileManager)
eventBus.register(FileUnsharedEvent.class, fileManager)
eventBus.register(SearchEvent.class, fileManager)
log.info "initializing file manager"
fileManager = new FileManager(eventBus, props)
eventBus.register(FileHashedEvent.class, fileManager)
eventBus.register(FileLoadedEvent.class, fileManager)
eventBus.register(FileDownloadedEvent.class, fileManager)
eventBus.register(FileUnsharedEvent.class, fileManager)
eventBus.register(SearchEvent.class, fileManager)
eventBus.register(DirectoryUnsharedEvent.class, fileManager)
log.info("initializing mesh manager")
MeshManager meshManager = new MeshManager(fileManager, home, props)
eventBus.register(SourceDiscoveredEvent.class, meshManager)
log.info "initializing persistence service"
persisterService = new PersisterService(new File(home, "files.json"), eventBus, 15000, fileManager)
log.info "initializing persistence service"
persisterService = new PersisterService(new File(home, "files.json"), eventBus, 15000, fileManager)
eventBus.register(UILoadedEvent.class, persisterService)
log.info("initializing host cache")
File hostStorage = new File(home, "hosts.json")
log.info("initializing host cache")
File hostStorage = new File(home, "hosts.json")
hostCache = new HostCache(trustService,hostStorage, 30000, props, i2pSession.getMyDestination())
eventBus.register(HostDiscoveredEvent.class, hostCache)
eventBus.register(ConnectionEvent.class, hostCache)
eventBus.register(HostDiscoveredEvent.class, hostCache)
eventBus.register(ConnectionEvent.class, hostCache)
log.info("initializing connection manager")
connectionManager = props.isLeaf() ?
new LeafConnectionManager(eventBus, me, 3, hostCache, props) :
log.info("initializing connection manager")
connectionManager = props.isLeaf() ?
new LeafConnectionManager(eventBus, me, 3, hostCache, props) :
new UltrapeerConnectionManager(eventBus, me, 512, 512, hostCache, trustService, props)
eventBus.register(TrustEvent.class, connectionManager)
eventBus.register(ConnectionEvent.class, connectionManager)
eventBus.register(DisconnectionEvent.class, connectionManager)
eventBus.register(TrustEvent.class, connectionManager)
eventBus.register(ConnectionEvent.class, connectionManager)
eventBus.register(DisconnectionEvent.class, connectionManager)
eventBus.register(QueryEvent.class, connectionManager)
log.info("initializing cache client")
cacheClient = new CacheClient(eventBus,hostCache, connectionManager, i2pSession, props, 10000)
log.info("initializing cache client")
cacheClient = new CacheClient(eventBus,hostCache, connectionManager, i2pSession, props, 10000)
log.info("initializing update client")
updateClient = new UpdateClient(eventBus, i2pSession, myVersion, props, fileManager, me)
eventBus.register(FileDownloadedEvent.class, updateClient)
eventBus.register(UIResultBatchEvent.class, updateClient)
log.info("initializing connector")
I2PConnector i2pConnector = new I2PConnector(socketManager)
log.info("initializing connector")
I2PConnector i2pConnector = new I2PConnector(socketManager)
log.info "initializing results sender"
ResultsSender resultsSender = new ResultsSender(eventBus, i2pConnector, me)
log.info "initializing results sender"
ResultsSender resultsSender = new ResultsSender(eventBus, i2pConnector, me)
log.info "initializing search manager"
SearchManager searchManager = new SearchManager(eventBus, me, resultsSender)
eventBus.register(QueryEvent.class, searchManager)
eventBus.register(ResultsEvent.class, searchManager)
log.info "initializing search manager"
SearchManager searchManager = new SearchManager(eventBus, me, resultsSender)
eventBus.register(QueryEvent.class, searchManager)
eventBus.register(ResultsEvent.class, searchManager)
log.info("initializing download manager")
downloadManager = new DownloadManager(eventBus, trustService, meshManager, props, i2pConnector, home, me)
@@ -269,9 +270,9 @@ public class Core {
log.info("initializing connection establisher")
connectionEstablisher = new ConnectionEstablisher(eventBus, i2pConnector, props, connectionManager, hostCache)
log.info("initializing acceptor")
I2PAcceptor i2pAcceptor = new I2PAcceptor(socketManager)
connectionAcceptor = new ConnectionAcceptor(eventBus, connectionManager, props,
log.info("initializing acceptor")
I2PAcceptor i2pAcceptor = new I2PAcceptor(socketManager)
connectionAcceptor = new ConnectionAcceptor(eventBus, connectionManager, props,
i2pAcceptor, hostCache, trustService, searchManager, uploadManager, connectionEstablisher)
log.info("initializing directory watcher")
@@ -288,7 +289,7 @@ public class Core {
trustSubscriber = new TrustSubscriber(eventBus, i2pConnector, props)
eventBus.register(UILoadedEvent.class, trustSubscriber)
eventBus.register(TrustSubscriptionEvent.class, trustSubscriber)
}
}
public void startServices() {
hasherService.start()
@@ -318,6 +319,8 @@ public class Core {
connectionEstablisher.stop()
log.info("shutting down directory watcher")
directoryWatcher.stop()
log.info("shutting down cache client")
cacheClient.stop()
log.info("shutting down connection manager")
connectionManager.shutdown()
if (router != null) {
@@ -326,19 +329,6 @@ public class Core {
}
}
static class RouterContextMetaClass extends DelegatingMetaClass {
private final Object logManager = new MuWireLogManager()
RouterContextMetaClass() {
super(RouterContext.class)
}
Object invokeMethod(Object object, String name, Object[] args) {
if (name == "logManager")
return logManager
super.invokeMethod(object, name, args)
}
}
static main(args) {
def home = System.getProperty("user.home") + File.separator + ".MuWire"
home = new File(home)
@@ -363,7 +353,7 @@ public class Core {
}
}
Core core = new Core(props, home, "0.4.4")
Core core = new Core(props, home, "0.4.8")
core.startServices()
// ... at the end, sleep or execute script

View File

@@ -4,17 +4,17 @@ import java.util.concurrent.atomic.AtomicLong
class Event {
private static final AtomicLong SEQ_NO = new AtomicLong();
final long seqNo
final long timestamp
private static final AtomicLong SEQ_NO = new AtomicLong();
final long seqNo
final long timestamp
Event() {
seqNo = SEQ_NO.getAndIncrement()
timestamp = System.currentTimeMillis()
}
Event() {
seqNo = SEQ_NO.getAndIncrement()
timestamp = System.currentTimeMillis()
}
@Override
public String toString() {
"seqNo $seqNo timestamp $timestamp"
}
@Override
public String toString() {
"seqNo $seqNo timestamp $timestamp"
}
}

View File

@@ -11,41 +11,41 @@ import groovy.util.logging.Log
@Log
class EventBus {
private Map handlers = new HashMap()
private final Executor executor = Executors.newSingleThreadExecutor {r ->
def rv = new Thread(r)
rv.setDaemon(true)
rv.setName("event-bus")
rv
}
private Map handlers = new HashMap()
private final Executor executor = Executors.newSingleThreadExecutor {r ->
def rv = new Thread(r)
rv.setDaemon(true)
rv.setName("event-bus")
rv
}
void publish(Event e) {
executor.execute({publishInternal(e)} as Runnable)
}
void publish(Event e) {
executor.execute({publishInternal(e)} as Runnable)
}
private void publishInternal(Event e) {
log.fine "publishing event $e of type ${e.getClass().getSimpleName()} event $e"
def currentHandlers
final def clazz = e.getClass()
synchronized(this) {
currentHandlers = handlers.getOrDefault(clazz, [])
}
currentHandlers.each {
private void publishInternal(Event e) {
log.fine "publishing event $e of type ${e.getClass().getSimpleName()} event $e"
def currentHandlers
final def clazz = e.getClass()
synchronized(this) {
currentHandlers = handlers.getOrDefault(clazz, [])
}
currentHandlers.each {
try {
it."on${clazz.getSimpleName()}"(e)
} catch (Exception bad) {
log.log(Level.SEVERE, "exception dispatching event",bad)
}
}
}
}
}
synchronized void register(Class<? extends Event> eventType, def handler) {
log.info "Registering $handler for type $eventType"
def currentHandlers = handlers.get(eventType)
if (currentHandlers == null) {
currentHandlers = new CopyOnWriteArrayList()
handlers.put(eventType, currentHandlers)
}
currentHandlers.add handler
}
synchronized void register(Class<? extends Event> eventType, def handler) {
log.info "Registering $handler for type $eventType"
def currentHandlers = handlers.get(eventType)
if (currentHandlers == null) {
currentHandlers = new CopyOnWriteArrayList()
handlers.put(eventType, currentHandlers)
}
currentHandlers.add handler
}
}

View File

@@ -29,16 +29,16 @@ class MuWireSettings {
boolean embeddedRouter
int inBw, outBw
MuWireSettings() {
MuWireSettings() {
this(new Properties())
}
MuWireSettings(Properties props) {
isLeaf = Boolean.valueOf(props.get("leaf","false"))
allowUntrusted = Boolean.valueOf(props.getProperty("allowUntrusted","true"))
MuWireSettings(Properties props) {
isLeaf = Boolean.valueOf(props.get("leaf","false"))
allowUntrusted = Boolean.valueOf(props.getProperty("allowUntrusted","true"))
allowTrustLists = Boolean.valueOf(props.getProperty("allowTrustLists","true"))
trustListInterval = Integer.valueOf(props.getProperty("trustListInterval","1"))
crawlerResponse = CrawlerResponse.valueOf(props.get("crawlerResponse","REGISTERED"))
crawlerResponse = CrawlerResponse.valueOf(props.get("crawlerResponse","REGISTERED"))
nickname = props.getProperty("nickname","MuWireUser")
downloadLocation = new File((String)props.getProperty("downloadLocation",
System.getProperty("user.home")))
@@ -66,7 +66,7 @@ class MuWireSettings {
trustSubscriptions.add(new Persona(new ByteArrayInputStream(Base64.decode(it))))
}
}
}
}
void write(OutputStream out) throws IOException {
Properties props = new Properties()
@@ -80,7 +80,7 @@ class MuWireSettings {
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
props.setProperty("autoDownloadUpdate", String.valueOf(autoDownloadUpdate))
props.setProperty("updateType",updateType)
props.setProperty("updateType",String.valueOf(updateType))
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
props.setProperty("downloadSequentialRatio", String.valueOf(downloadSequentialRatio))
props.setProperty("hostClearInterval", String.valueOf(hostClearInterval))
@@ -106,25 +106,25 @@ class MuWireSettings {
props.store(out, "")
}
boolean isLeaf() {
isLeaf
}
boolean isLeaf() {
isLeaf
}
boolean allowUntrusted() {
allowUntrusted
}
boolean allowUntrusted() {
allowUntrusted
}
void setAllowUntrusted(boolean allowUntrusted) {
this.allowUntrusted = allowUntrusted
}
void setAllowUntrusted(boolean allowUntrusted) {
this.allowUntrusted = allowUntrusted
}
CrawlerResponse getCrawlerResponse() {
crawlerResponse
}
CrawlerResponse getCrawlerResponse() {
crawlerResponse
}
void setCrawlerResponse(CrawlerResponse crawlerResponse) {
this.crawlerResponse = crawlerResponse
}
void setCrawlerResponse(CrawlerResponse crawlerResponse) {
this.crawlerResponse = crawlerResponse
}
String getNickname() {
nickname

View File

@@ -2,12 +2,12 @@ package com.muwire.core
abstract class Service {
volatile boolean loaded
volatile boolean loaded
abstract void load()
abstract void load()
void waitForLoad() {
while (!loaded)
Thread.sleep(10)
}
void waitForLoad() {
while (!loaded)
Thread.sleep(10)
}
}

View File

@@ -25,104 +25,104 @@ abstract class Connection implements Closeable {
private static final int SEARCHES = 10
private static final long INTERVAL = 1000
final EventBus eventBus
final Endpoint endpoint
final boolean incoming
final HostCache hostCache
final EventBus eventBus
final Endpoint endpoint
final boolean incoming
final HostCache hostCache
final TrustService trustService
final MuWireSettings settings
private final AtomicBoolean running = new AtomicBoolean()
private final BlockingQueue messages = new LinkedBlockingQueue()
private final Thread reader, writer
private final AtomicBoolean running = new AtomicBoolean()
private final BlockingQueue messages = new LinkedBlockingQueue()
private final Thread reader, writer
private final LinkedList<Long> searchTimestamps = new LinkedList<>()
protected final String name
protected final String name
long lastPingSentTime, lastPongReceivedTime
long lastPingSentTime, lastPongReceivedTime
Connection(EventBus eventBus, Endpoint endpoint, boolean incoming,
Connection(EventBus eventBus, Endpoint endpoint, boolean incoming,
HostCache hostCache, TrustService trustService, MuWireSettings settings) {
this.eventBus = eventBus
this.incoming = incoming
this.endpoint = endpoint
this.hostCache = hostCache
this.eventBus = eventBus
this.incoming = incoming
this.endpoint = endpoint
this.hostCache = hostCache
this.trustService = trustService
this.settings = settings
this.name = endpoint.destination.toBase32().substring(0,8)
this.name = endpoint.destination.toBase32().substring(0,8)
this.reader = new Thread({readLoop()} as Runnable)
this.reader.setName("reader-$name")
this.reader.setDaemon(true)
this.reader = new Thread({readLoop()} as Runnable)
this.reader.setName("reader-$name")
this.reader.setDaemon(true)
this.writer = new Thread({writeLoop()} as Runnable)
this.writer.setName("writer-$name")
this.writer.setDaemon(true)
}
this.writer = new Thread({writeLoop()} as Runnable)
this.writer.setName("writer-$name")
this.writer.setDaemon(true)
}
/**
* starts the connection threads
*/
void start() {
if (!running.compareAndSet(false, true)) {
log.log(Level.WARNING,"$name already running", new Exception())
return
}
reader.start()
writer.start()
}
/**
* starts the connection threads
*/
void start() {
if (!running.compareAndSet(false, true)) {
log.log(Level.WARNING,"$name already running", new Exception())
return
}
reader.start()
writer.start()
}
@Override
public void close() {
if (!running.compareAndSet(true, false)) {
log.log(Level.WARNING, "$name already closed", new Exception() )
return
}
@Override
public void close() {
if (!running.compareAndSet(true, false)) {
log.log(Level.WARNING, "$name already closed", new Exception() )
return
}
log.info("closing $name")
reader.interrupt()
writer.interrupt()
endpoint.close()
eventBus.publish(new DisconnectionEvent(destination: endpoint.destination))
}
reader.interrupt()
writer.interrupt()
endpoint.close()
eventBus.publish(new DisconnectionEvent(destination: endpoint.destination))
}
protected void readLoop() {
try {
while(running.get()) {
read()
}
} catch (SocketTimeoutException e) {
protected void readLoop() {
try {
while(running.get()) {
read()
}
} catch (SocketTimeoutException e) {
} catch (Exception e) {
log.log(Level.WARNING,"unhandled exception in reader",e)
} finally {
close()
}
}
}
protected abstract void read()
protected abstract void read()
protected void writeLoop() {
try {
while(running.get()) {
def message = messages.take()
write(message)
}
} catch (Exception e) {
protected void writeLoop() {
try {
while(running.get()) {
def message = messages.take()
write(message)
}
} catch (Exception e) {
log.log(Level.WARNING, "unhandled exception in writer",e)
} finally {
close()
}
}
}
protected abstract void write(def message);
protected abstract void write(def message);
void sendPing() {
def ping = [:]
ping.type = "Ping"
ping.version = 1
messages.put(ping)
lastPingSentTime = System.currentTimeMillis()
}
void sendPing() {
def ping = [:]
ping.type = "Ping"
ping.version = 1
messages.put(ping)
lastPingSentTime = System.currentTimeMillis()
}
void sendQuery(QueryEvent e) {
def query = [:]
@@ -140,25 +140,25 @@ abstract class Connection implements Closeable {
messages.put(query)
}
protected void handlePing() {
log.fine("$name received ping")
def pong = [:]
pong.type = "Pong"
pong.version = 1
pong.pongs = hostCache.getGoodHosts(10).collect { d -> d.toBase64() }
messages.put(pong)
}
protected void handlePing() {
log.fine("$name received ping")
def pong = [:]
pong.type = "Pong"
pong.version = 1
pong.pongs = hostCache.getGoodHosts(10).collect { d -> d.toBase64() }
messages.put(pong)
}
protected void handlePong(def pong) {
log.fine("$name received pong")
lastPongReceivedTime = System.currentTimeMillis()
if (pong.pongs == null)
throw new Exception("Pong doesn't have pongs")
pong.pongs.each {
def dest = new Destination(it)
eventBus.publish(new HostDiscoveredEvent(destination: dest))
}
}
protected void handlePong(def pong) {
log.fine("$name received pong")
lastPongReceivedTime = System.currentTimeMillis()
if (pong.pongs == null)
throw new Exception("Pong doesn't have pongs")
pong.pongs.each {
def dest = new Destination(it)
eventBus.publish(new HostDiscoveredEvent(destination: dest))
}
}
private boolean throttleSearch() {
final long now = System.currentTimeMillis()

View File

@@ -29,97 +29,97 @@ import groovy.util.logging.Log
@Log
class ConnectionAcceptor {
final EventBus eventBus
final UltrapeerConnectionManager manager
final MuWireSettings settings
final I2PAcceptor acceptor
final HostCache hostCache
final TrustService trustService
final EventBus eventBus
final UltrapeerConnectionManager manager
final MuWireSettings settings
final I2PAcceptor acceptor
final HostCache hostCache
final TrustService trustService
final SearchManager searchManager
final UploadManager uploadManager
final ConnectionEstablisher establisher
final ExecutorService acceptorThread
final ExecutorService handshakerThreads
final ExecutorService acceptorThread
final ExecutorService handshakerThreads
private volatile shutdown
ConnectionAcceptor(EventBus eventBus, UltrapeerConnectionManager manager,
MuWireSettings settings, I2PAcceptor acceptor, HostCache hostCache,
TrustService trustService, SearchManager searchManager, UploadManager uploadManager,
ConnectionAcceptor(EventBus eventBus, UltrapeerConnectionManager manager,
MuWireSettings settings, I2PAcceptor acceptor, HostCache hostCache,
TrustService trustService, SearchManager searchManager, UploadManager uploadManager,
ConnectionEstablisher establisher) {
this.eventBus = eventBus
this.manager = manager
this.settings = settings
this.acceptor = acceptor
this.hostCache = hostCache
this.trustService = trustService
this.eventBus = eventBus
this.manager = manager
this.settings = settings
this.acceptor = acceptor
this.hostCache = hostCache
this.trustService = trustService
this.searchManager = searchManager
this.uploadManager = uploadManager
this.establisher = establisher
this.establisher = establisher
acceptorThread = Executors.newSingleThreadExecutor { r ->
def rv = new Thread(r)
rv.setDaemon(true)
rv.setName("acceptor")
rv
}
acceptorThread = Executors.newSingleThreadExecutor { r ->
def rv = new Thread(r)
rv.setDaemon(true)
rv.setName("acceptor")
rv
}
handshakerThreads = Executors.newCachedThreadPool { r ->
def rv = new Thread(r)
rv.setDaemon(true)
rv.setName("acceptor-processor-${System.currentTimeMillis()}")
rv
}
}
handshakerThreads = Executors.newCachedThreadPool { r ->
def rv = new Thread(r)
rv.setDaemon(true)
rv.setName("acceptor-processor-${System.currentTimeMillis()}")
rv
}
}
void start() {
acceptorThread.execute({acceptLoop()} as Runnable)
}
void start() {
acceptorThread.execute({acceptLoop()} as Runnable)
}
void stop() {
void stop() {
shutdown = true
acceptorThread.shutdownNow()
handshakerThreads.shutdownNow()
}
acceptorThread.shutdownNow()
handshakerThreads.shutdownNow()
}
private void acceptLoop() {
private void acceptLoop() {
try {
while(true) {
def incoming = acceptor.accept()
log.info("accepted connection from ${incoming.destination.toBase32()}")
switch(trustService.getLevel(incoming.destination)) {
case TrustLevel.TRUSTED : break
case TrustLevel.NEUTRAL :
if (settings.allowUntrusted())
break
case TrustLevel.DISTRUSTED :
log.info("Disallowing distrusted connection")
incoming.close()
continue
}
handshakerThreads.execute({processIncoming(incoming)} as Runnable)
}
while(true) {
def incoming = acceptor.accept()
log.info("accepted connection from ${incoming.destination.toBase32()}")
switch(trustService.getLevel(incoming.destination)) {
case TrustLevel.TRUSTED : break
case TrustLevel.NEUTRAL :
if (settings.allowUntrusted())
break
case TrustLevel.DISTRUSTED :
log.info("Disallowing distrusted connection")
incoming.close()
continue
}
handshakerThreads.execute({processIncoming(incoming)} as Runnable)
}
} catch (Exception e) {
log.log(Level.WARNING, "exception in accept loop",e)
if (!shutdown)
throw e
}
}
}
private void processIncoming(Endpoint e) {
InputStream is = e.inputStream
try {
int read = is.read()
switch(read) {
case (byte)'M':
private void processIncoming(Endpoint e) {
InputStream is = e.inputStream
try {
int read = is.read()
switch(read) {
case (byte)'M':
if (settings.isLeaf())
throw new IOException("Incoming connection as leaf")
processMuWire(e)
break
case (byte)'G':
processGET(e)
break
processMuWire(e)
break
case (byte)'G':
processGET(e)
break
case (byte)'H':
processHashList(e)
break
@@ -129,28 +129,28 @@ class ConnectionAcceptor {
case (byte)'T':
processTRUST(e)
break
default:
throw new Exception("Invalid read $read")
}
} catch (Exception ex) {
log.log(Level.WARNING, "incoming connection failed",ex)
e.close()
eventBus.publish new ConnectionEvent(endpoint: e, incoming: true, leaf: null, status: ConnectionAttemptStatus.FAILED)
}
}
default:
throw new Exception("Invalid read $read")
}
} catch (Exception ex) {
log.log(Level.WARNING, "incoming connection failed",ex)
e.close()
eventBus.publish new ConnectionEvent(endpoint: e, incoming: true, leaf: null, status: ConnectionAttemptStatus.FAILED)
}
}
private void processMuWire(Endpoint e) {
byte[] uWire = "uWire ".bytes
for (int i = 0; i < uWire.length; i++) {
int read = e.inputStream.read()
if (read != uWire[i]) {
throw new IOException("unexpected value $read at position $i")
}
}
private void processMuWire(Endpoint e) {
byte[] uWire = "uWire ".bytes
for (int i = 0; i < uWire.length; i++) {
int read = e.inputStream.read()
if (read != uWire[i]) {
throw new IOException("unexpected value $read at position $i")
}
}
byte[] type = new byte[4]
DataInputStream dis = new DataInputStream(e.inputStream)
dis.readFully(type)
byte[] type = new byte[4]
DataInputStream dis = new DataInputStream(e.inputStream)
dis.readFully(type)
if (type == "leaf".bytes)
handleIncoming(e, true)
@@ -160,44 +160,44 @@ class ConnectionAcceptor {
throw new IOException("unknown connection type $type")
}
private void handleIncoming(Endpoint e, boolean leaf) {
boolean accept = !manager.isConnected(e.destination) &&
private void handleIncoming(Endpoint e, boolean leaf) {
boolean accept = !manager.isConnected(e.destination) &&
!establisher.isInProgress(e.destination) &&
(leaf ? manager.hasLeafSlots() : manager.hasPeerSlots())
if (accept) {
log.info("accepting connection, leaf:$leaf")
e.outputStream.write("OK".bytes)
e.outputStream.flush()
def wrapped = new Endpoint(e.destination, new InflaterInputStream(e.inputStream), new DeflaterOutputStream(e.outputStream, true), e.toClose)
eventBus.publish(new ConnectionEvent(endpoint: wrapped, incoming: true, leaf: leaf, status: ConnectionAttemptStatus.SUCCESSFUL))
} else {
log.info("rejecting connection, leaf:$leaf")
e.outputStream.write("REJECT".bytes)
def hosts = hostCache.getGoodHosts(10)
if (!hosts.isEmpty()) {
def json = [:]
json.tryHosts = hosts.collect { d -> d.toBase64() }
json = JsonOutput.toJson(json)
def os = new DataOutputStream(e.outputStream)
os.writeShort(json.bytes.length)
os.write(json.bytes)
}
e.outputStream.flush()
e.close()
eventBus.publish(new ConnectionEvent(endpoint: e, incoming: true, leaf: leaf, status: ConnectionAttemptStatus.REJECTED))
}
}
if (accept) {
log.info("accepting connection, leaf:$leaf")
e.outputStream.write("OK".bytes)
e.outputStream.flush()
def wrapped = new Endpoint(e.destination, new InflaterInputStream(e.inputStream), new DeflaterOutputStream(e.outputStream, true), e.toClose)
eventBus.publish(new ConnectionEvent(endpoint: wrapped, incoming: true, leaf: leaf, status: ConnectionAttemptStatus.SUCCESSFUL))
} else {
log.info("rejecting connection, leaf:$leaf")
e.outputStream.write("REJECT".bytes)
def hosts = hostCache.getGoodHosts(10)
if (!hosts.isEmpty()) {
def json = [:]
json.tryHosts = hosts.collect { d -> d.toBase64() }
json = JsonOutput.toJson(json)
def os = new DataOutputStream(e.outputStream)
os.writeShort(json.bytes.length)
os.write(json.bytes)
}
e.outputStream.flush()
e.close()
eventBus.publish(new ConnectionEvent(endpoint: e, incoming: true, leaf: leaf, status: ConnectionAttemptStatus.REJECTED))
}
}
private void processGET(Endpoint e) {
private void processGET(Endpoint e) {
byte[] et = new byte[3]
final DataInputStream dis = new DataInputStream(e.getInputStream())
dis.readFully(et)
if (et != "ET ".getBytes(StandardCharsets.US_ASCII))
throw new IOException("Invalid GET connection")
uploadManager.processGET(e)
}
}
private void processHashList(Endpoint e) {
byte[] ashList = new byte[8]

View File

@@ -22,162 +22,162 @@ import net.i2p.util.ConcurrentHashSet
@Log
class ConnectionEstablisher {
private static final int CONCURRENT = 4
private static final int CONCURRENT = 4
final EventBus eventBus
final I2PConnector i2pConnector
final MuWireSettings settings
final ConnectionManager connectionManager
final HostCache hostCache
final EventBus eventBus
final I2PConnector i2pConnector
final MuWireSettings settings
final ConnectionManager connectionManager
final HostCache hostCache
final Timer timer
final ExecutorService executor
final Timer timer
final ExecutorService executor
final Set inProgress = new ConcurrentHashSet()
final Set inProgress = new ConcurrentHashSet()
ConnectionEstablisher(){}
ConnectionEstablisher(EventBus eventBus, I2PConnector i2pConnector, MuWireSettings settings,
ConnectionManager connectionManager, HostCache hostCache) {
this.eventBus = eventBus
this.i2pConnector = i2pConnector
this.settings = settings
this.connectionManager = connectionManager
this.hostCache = hostCache
timer = new Timer("connection-timer",true)
executor = Executors.newFixedThreadPool(CONCURRENT, { r ->
def rv = new Thread(r)
rv.setDaemon(true)
rv.setName("connector-${System.currentTimeMillis()}")
rv
} as ThreadFactory)
}
ConnectionEstablisher(EventBus eventBus, I2PConnector i2pConnector, MuWireSettings settings,
ConnectionManager connectionManager, HostCache hostCache) {
this.eventBus = eventBus
this.i2pConnector = i2pConnector
this.settings = settings
this.connectionManager = connectionManager
this.hostCache = hostCache
timer = new Timer("connection-timer",true)
executor = Executors.newFixedThreadPool(CONCURRENT, { r ->
def rv = new Thread(r)
rv.setDaemon(true)
rv.setName("connector-${System.currentTimeMillis()}")
rv
} as ThreadFactory)
}
void start() {
timer.schedule({connectIfNeeded()} as TimerTask, 100, 1000)
}
void start() {
timer.schedule({connectIfNeeded()} as TimerTask, 100, 1000)
}
void stop() {
timer.cancel()
executor.shutdownNow()
}
void stop() {
timer.cancel()
executor.shutdownNow()
}
private void connectIfNeeded() {
if (!connectionManager.needsConnections())
return
if (inProgress.size() >= CONCURRENT)
return
private void connectIfNeeded() {
if (!connectionManager.needsConnections())
return
if (inProgress.size() >= CONCURRENT)
return
def toTry = null
for (int i = 0; i < 5; i++) {
toTry = hostCache.getHosts(1)
if (toTry.isEmpty())
return
toTry = toTry[0]
if (!connectionManager.isConnected(toTry) &&
!inProgress.contains(toTry)) {
break
}
}
if (toTry == null)
return
if (!connectionManager.isConnected(toTry) && inProgress.add(toTry))
executor.execute({connect(toTry)} as Runnable)
}
def toTry = null
for (int i = 0; i < 5; i++) {
toTry = hostCache.getHosts(1)
if (toTry.isEmpty())
return
toTry = toTry[0]
if (!connectionManager.isConnected(toTry) &&
!inProgress.contains(toTry)) {
break
}
}
if (toTry == null)
return
if (!connectionManager.isConnected(toTry) && inProgress.add(toTry))
executor.execute({connect(toTry)} as Runnable)
}
private void connect(Destination toTry) {
log.info("starting connect to ${toTry.toBase32()}")
try {
def endpoint = i2pConnector.connect(toTry)
log.info("successful transport connect to ${toTry.toBase32()}")
private void connect(Destination toTry) {
log.info("starting connect to ${toTry.toBase32()}")
try {
def endpoint = i2pConnector.connect(toTry)
log.info("successful transport connect to ${toTry.toBase32()}")
// outgoing handshake
endpoint.outputStream.write("MuWire ".bytes)
def type = settings.isLeaf() ? "leaf" : "peer"
endpoint.outputStream.write(type.bytes)
endpoint.outputStream.flush()
// outgoing handshake
endpoint.outputStream.write("MuWire ".bytes)
def type = settings.isLeaf() ? "leaf" : "peer"
endpoint.outputStream.write(type.bytes)
endpoint.outputStream.flush()
InputStream is = endpoint.inputStream
int read = is.read()
if (read == -1) {
fail endpoint
return
}
switch(read) {
case (byte)'O': readK(endpoint); break
case (byte)'R': readEJECT(endpoint); break
default :
log.warning("unknown response $read")
fail endpoint
}
} catch (Exception e) {
log.log(Level.WARNING, "Couldn't connect to ${toTry.toBase32()}", e)
def endpoint = new Endpoint(toTry, null, null, null)
fail(endpoint)
} finally {
inProgress.remove(toTry)
}
}
InputStream is = endpoint.inputStream
int read = is.read()
if (read == -1) {
fail endpoint
return
}
switch(read) {
case (byte)'O': readK(endpoint); break
case (byte)'R': readEJECT(endpoint); break
default :
log.warning("unknown response $read")
fail endpoint
}
} catch (Exception e) {
log.log(Level.WARNING, "Couldn't connect to ${toTry.toBase32()}", e)
def endpoint = new Endpoint(toTry, null, null, null)
fail(endpoint)
} finally {
inProgress.remove(toTry)
}
}
private void fail(Endpoint endpoint) {
endpoint.close()
eventBus.publish(new ConnectionEvent(endpoint: endpoint, incoming: false, leaf: false, status: ConnectionAttemptStatus.FAILED))
}
private void fail(Endpoint endpoint) {
endpoint.close()
eventBus.publish(new ConnectionEvent(endpoint: endpoint, incoming: false, leaf: false, status: ConnectionAttemptStatus.FAILED))
}
private void readK(Endpoint e) {
int read = e.inputStream.read()
if (read != 'K') {
log.warning("unknown response after O: $read")
fail e
return
}
private void readK(Endpoint e) {
int read = e.inputStream.read()
if (read != 'K') {
log.warning("unknown response after O: $read")
fail e
return
}
log.info("connection to ${e.destination.toBase32()} established")
log.info("connection to ${e.destination.toBase32()} established")
// wrap into deflater / inflater streams and publish
def wrapped = new Endpoint(e.destination, new InflaterInputStream(e.inputStream), new DeflaterOutputStream(e.outputStream, true), e.toClose)
eventBus.publish(new ConnectionEvent(endpoint: wrapped, incoming: false, leaf: false, status: ConnectionAttemptStatus.SUCCESSFUL))
}
// wrap into deflater / inflater streams and publish
def wrapped = new Endpoint(e.destination, new InflaterInputStream(e.inputStream), new DeflaterOutputStream(e.outputStream, true), e.toClose)
eventBus.publish(new ConnectionEvent(endpoint: wrapped, incoming: false, leaf: false, status: ConnectionAttemptStatus.SUCCESSFUL))
}
private void readEJECT(Endpoint e) {
byte[] eject = "EJECT".bytes
for (int i = 0; i < eject.length; i++) {
int read = e.inputStream.read()
if (read != eject[i]) {
log.warning("Unknown response after R at position $i")
fail e
return
}
}
log.info("connection to ${e.destination.toBase32()} rejected")
private void readEJECT(Endpoint e) {
byte[] eject = "EJECT".bytes
for (int i = 0; i < eject.length; i++) {
int read = e.inputStream.read()
if (read != eject[i]) {
log.warning("Unknown response after R at position $i")
fail e
return
}
}
log.info("connection to ${e.destination.toBase32()} rejected")
eventBus.publish(new ConnectionEvent(endpoint: e, incoming: false, leaf: false, status: ConnectionAttemptStatus.REJECTED))
try {
DataInputStream dais = new DataInputStream(e.inputStream)
int payloadSize = dais.readUnsignedShort()
byte[] payload = new byte[payloadSize]
dais.readFully(payload)
eventBus.publish(new ConnectionEvent(endpoint: e, incoming: false, leaf: false, status: ConnectionAttemptStatus.REJECTED))
try {
DataInputStream dais = new DataInputStream(e.inputStream)
int payloadSize = dais.readUnsignedShort()
byte[] payload = new byte[payloadSize]
dais.readFully(payload)
def json = new JsonSlurper()
json = json.parse(payload)
def json = new JsonSlurper()
json = json.parse(payload)
if (json.tryHosts == null) {
log.warning("post-rejection json didn't contain hosts to try")
return
}
if (json.tryHosts == null) {
log.warning("post-rejection json didn't contain hosts to try")
return
}
json.tryHosts.asList().each {
Destination suggested = new Destination(it)
eventBus.publish(new HostDiscoveredEvent(destination: suggested))
}
} catch (Exception ignore) {
log.log(Level.WARNING,"Problem parsing post-rejection payload",ignore)
} finally {
// the end
e.close()
}
}
json.tryHosts.asList().each {
Destination suggested = new Destination(it)
eventBus.publish(new HostDiscoveredEvent(destination: suggested))
}
} catch (Exception ignore) {
log.log(Level.WARNING,"Problem parsing post-rejection payload",ignore)
} finally {
// the end
e.close()
}
}
public boolean isInProgress(Destination d) {
inProgress.contains(d)

View File

@@ -6,14 +6,14 @@ import net.i2p.data.Destination
class ConnectionEvent extends Event {
Endpoint endpoint
boolean incoming
Boolean leaf // can be null if uknown
ConnectionAttemptStatus status
Endpoint endpoint
boolean incoming
Boolean leaf // can be null if uknown
ConnectionAttemptStatus status
@Override
public String toString() {
"ConnectionEvent ${super.toString()} endpoint: $endpoint incoming: $incoming leaf : $leaf status : $status"
}
@Override
public String toString() {
"ConnectionEvent ${super.toString()} endpoint: $endpoint incoming: $incoming leaf : $leaf status : $status"
}
}

View File

@@ -12,63 +12,63 @@ import net.i2p.data.Destination
abstract class ConnectionManager {
private static final int PING_TIME = 20000
private static final int PING_TIME = 20000
final EventBus eventBus
final EventBus eventBus
private final Timer timer
private final Timer timer
protected final HostCache hostCache
protected final HostCache hostCache
protected final Persona me
protected final MuWireSettings settings
ConnectionManager() {}
ConnectionManager() {}
ConnectionManager(EventBus eventBus, Persona me, HostCache hostCache, MuWireSettings settings) {
this.eventBus = eventBus
ConnectionManager(EventBus eventBus, Persona me, HostCache hostCache, MuWireSettings settings) {
this.eventBus = eventBus
this.me = me
this.hostCache = hostCache
this.hostCache = hostCache
this.settings = settings
this.timer = new Timer("connections-pinger",true)
}
this.timer = new Timer("connections-pinger",true)
}
void start() {
timer.schedule({sendPings()} as TimerTask, 1000,1000)
}
void start() {
timer.schedule({sendPings()} as TimerTask, 1000,1000)
}
void stop() {
timer.cancel()
getConnections().each { it.close() }
}
void stop() {
timer.cancel()
getConnections().each { it.close() }
}
void onTrustEvent(TrustEvent e) {
if (e.level == TrustLevel.DISTRUSTED)
drop(e.persona.destination)
}
void onTrustEvent(TrustEvent e) {
if (e.level == TrustLevel.DISTRUSTED)
drop(e.persona.destination)
}
abstract void drop(Destination d)
abstract void drop(Destination d)
abstract Collection<Connection> getConnections()
abstract Collection<Connection> getConnections()
protected abstract int getDesiredConnections()
protected abstract int getDesiredConnections()
boolean needsConnections() {
return getConnections().size() < getDesiredConnections()
}
boolean needsConnections() {
return getConnections().size() < getDesiredConnections()
}
abstract boolean isConnected(Destination d)
abstract boolean isConnected(Destination d)
abstract void onConnectionEvent(ConnectionEvent e)
abstract void onConnectionEvent(ConnectionEvent e)
abstract void onDisconnectionEvent(DisconnectionEvent e)
abstract void onDisconnectionEvent(DisconnectionEvent e)
abstract void shutdown()
protected void sendPings() {
final long now = System.currentTimeMillis()
getConnections().each {
if (now - it.lastPingSentTime > PING_TIME)
it.sendPing()
}
}
protected void sendPings() {
final long now = System.currentTimeMillis()
getConnections().each {
if (now - it.lastPingSentTime > PING_TIME)
it.sendPing()
}
}
}

View File

@@ -6,10 +6,10 @@ import net.i2p.data.Destination
class DisconnectionEvent extends Event {
Destination destination
Destination destination
@Override
public String toString() {
"DisconnectionEvent ${super.toString()} destination:${destination.toBase32()}"
}
@Override
public String toString() {
"DisconnectionEvent ${super.toString()} destination:${destination.toBase32()}"
}
}

View File

@@ -8,39 +8,39 @@ import net.i2p.data.Destination
@Log
class Endpoint implements Closeable {
final Destination destination
final InputStream inputStream
final OutputStream outputStream
final Destination destination
final InputStream inputStream
final OutputStream outputStream
final def toClose
private final AtomicBoolean closed = new AtomicBoolean()
private final AtomicBoolean closed = new AtomicBoolean()
Endpoint(Destination destination, InputStream inputStream, OutputStream outputStream, def toClose) {
this.destination = destination
this.inputStream = inputStream
this.outputStream = outputStream
Endpoint(Destination destination, InputStream inputStream, OutputStream outputStream, def toClose) {
this.destination = destination
this.inputStream = inputStream
this.outputStream = outputStream
this.toClose = toClose
}
}
@Override
public void close() {
if (!closed.compareAndSet(false, true)) {
log.log(Level.WARNING,"Close loop detected for ${destination.toBase32()}", new Exception())
return
}
if (inputStream != null) {
try {inputStream.close()} catch (Exception ignore) {}
}
if (outputStream != null) {
try {outputStream.close()} catch (Exception ignore) {}
}
@Override
public void close() {
if (!closed.compareAndSet(false, true)) {
log.log(Level.WARNING,"Close loop detected for ${destination.toBase32()}", new Exception())
return
}
if (inputStream != null) {
try {inputStream.close()} catch (Exception ignore) {}
}
if (outputStream != null) {
try {outputStream.close()} catch (Exception ignore) {}
}
if (toClose != null) {
try {toClose.reset()} catch (Exception ignore) {}
}
}
}
@Override
public String toString() {
"destination: ${destination.toBase32()}"
}
@Override
public String toString() {
"destination: ${destination.toBase32()}"
}
}

View File

@@ -5,18 +5,18 @@ import net.i2p.client.streaming.I2PSocketManager
class I2PAcceptor {
final I2PSocketManager socketManager
final I2PServerSocket serverSocket
final I2PSocketManager socketManager
final I2PServerSocket serverSocket
I2PAcceptor() {}
I2PAcceptor() {}
I2PAcceptor(I2PSocketManager socketManager) {
this.socketManager = socketManager
this.serverSocket = socketManager.getServerSocket()
}
I2PAcceptor(I2PSocketManager socketManager) {
this.socketManager = socketManager
this.serverSocket = socketManager.getServerSocket()
}
Endpoint accept() {
def socket = serverSocket.accept()
new Endpoint(socket.getPeerDestination(), socket.getInputStream(), socket.getOutputStream(), socket)
}
Endpoint accept() {
def socket = serverSocket.accept()
new Endpoint(socket.getPeerDestination(), socket.getInputStream(), socket.getOutputStream(), socket)
}
}

View File

@@ -5,17 +5,17 @@ import net.i2p.data.Destination
class I2PConnector {
final I2PSocketManager socketManager
final I2PSocketManager socketManager
I2PConnector() {}
I2PConnector() {}
I2PConnector(I2PSocketManager socketManager) {
this.socketManager = socketManager
}
I2PConnector(I2PSocketManager socketManager) {
this.socketManager = socketManager
}
Endpoint connect(Destination dest) {
def socket = socketManager.connect(dest)
new Endpoint(dest, socket.getInputStream(), socket.getOutputStream(), socket)
}
Endpoint connect(Destination dest) {
def socket = socketManager.connect(dest)
new Endpoint(dest, socket.getInputStream(), socket.getOutputStream(), socket)
}
}

View File

@@ -17,21 +17,21 @@ import net.i2p.data.Destination
*/
class LeafConnection extends Connection {
public LeafConnection(EventBus eventBus, Endpoint endpoint, HostCache hostCache,
public LeafConnection(EventBus eventBus, Endpoint endpoint, HostCache hostCache,
TrustService trustService, MuWireSettings settings) {
super(eventBus, endpoint, true, hostCache, trustService, settings);
}
super(eventBus, endpoint, true, hostCache, trustService, settings);
}
@Override
protected void read() {
// TODO Auto-generated method stub
@Override
protected void read() {
// TODO Auto-generated method stub
}
}
@Override
protected void write(Object message) {
// TODO Auto-generated method stub
@Override
protected void write(Object message) {
// TODO Auto-generated method stub
}
}
}

View File

@@ -14,21 +14,21 @@ import net.i2p.data.Destination
@Log
class LeafConnectionManager extends ConnectionManager {
final int maxConnections
final int maxConnections
final Map<Destination, UltrapeerConnection> connections = new ConcurrentHashMap()
final Map<Destination, UltrapeerConnection> connections = new ConcurrentHashMap()
public LeafConnectionManager(EventBus eventBus, Persona me, int maxConnections,
public LeafConnectionManager(EventBus eventBus, Persona me, int maxConnections,
HostCache hostCache, MuWireSettings settings) {
super(eventBus, me, hostCache, settings)
this.maxConnections = maxConnections
}
super(eventBus, me, hostCache, settings)
this.maxConnections = maxConnections
}
@Override
public void drop(Destination d) {
// TODO Auto-generated method stub
@Override
public void drop(Destination d) {
// TODO Auto-generated method stub
}
}
void onQueryEvent(QueryEvent e) {
if (me.destination == e.receivedOn) {
@@ -37,41 +37,41 @@ class LeafConnectionManager extends ConnectionManager {
}
@Override
public Collection<Connection> getConnections() {
connections.values()
}
@Override
public Collection<Connection> getConnections() {
connections.values()
}
@Override
protected int getDesiredConnections() {
return maxConnections;
}
@Override
protected int getDesiredConnections() {
return maxConnections;
}
@Override
public boolean isConnected(Destination d) {
connections.containsKey(d)
}
@Override
public boolean isConnected(Destination d) {
connections.containsKey(d)
}
@Override
public void onConnectionEvent(ConnectionEvent e) {
if (e.incoming || e.leaf) {
log.severe("Got inconsistent event as a leaf! $e")
return
}
if (e.status != ConnectionAttemptStatus.SUCCESSFUL)
return
@Override
public void onConnectionEvent(ConnectionEvent e) {
if (e.incoming || e.leaf) {
log.severe("Got inconsistent event as a leaf! $e")
return
}
if (e.status != ConnectionAttemptStatus.SUCCESSFUL)
return
Connection c = new UltrapeerConnection(eventBus, e.endpoint)
connections.put(e.endpoint.destination, c)
c.start()
}
Connection c = new UltrapeerConnection(eventBus, e.endpoint)
connections.put(e.endpoint.destination, c)
c.start()
}
@Override
public void onDisconnectionEvent(DisconnectionEvent e) {
def removed = connections.remove(e.destination)
if (removed == null)
log.severe("removed destination not present in connection manager ${e.destination.toBase32()}")
}
@Override
public void onDisconnectionEvent(DisconnectionEvent e) {
def removed = connections.remove(e.destination)
if (removed == null)
log.severe("removed destination not present in connection manager ${e.destination.toBase32()}")
}
@Override
void shutdown() {

View File

@@ -21,62 +21,62 @@ import net.i2p.data.Destination
@Log
class PeerConnection extends Connection {
private final DataInputStream dis
private final DataOutputStream dos
private final DataInputStream dis
private final DataOutputStream dos
private final byte[] readHeader = new byte[3]
private final byte[] writeHeader = new byte[3]
private final byte[] readHeader = new byte[3]
private final byte[] writeHeader = new byte[3]
private final JsonSlurper slurper = new JsonSlurper()
private final JsonSlurper slurper = new JsonSlurper()
public PeerConnection(EventBus eventBus, Endpoint endpoint,
boolean incoming, HostCache hostCache, TrustService trustService,
public PeerConnection(EventBus eventBus, Endpoint endpoint,
boolean incoming, HostCache hostCache, TrustService trustService,
MuWireSettings settings) {
super(eventBus, endpoint, incoming, hostCache, trustService, settings)
this.dis = new DataInputStream(endpoint.inputStream)
this.dos = new DataOutputStream(endpoint.outputStream)
}
super(eventBus, endpoint, incoming, hostCache, trustService, settings)
this.dis = new DataInputStream(endpoint.inputStream)
this.dos = new DataOutputStream(endpoint.outputStream)
}
@Override
protected void read() {
dis.readFully(readHeader)
int length = DataUtil.readLength(readHeader)
log.fine("$name read length $length")
@Override
protected void read() {
dis.readFully(readHeader)
int length = DataUtil.readLength(readHeader)
log.fine("$name read length $length")
byte[] payload = new byte[length]
dis.readFully(payload)
byte[] payload = new byte[length]
dis.readFully(payload)
if ((readHeader[0] & (byte)0x80) == 0x80) {
// TODO process binary
} else {
def json = slurper.parse(payload)
if (json.type == null)
throw new Exception("missing json type")
switch(json.type) {
case "Ping" : handlePing(); break;
case "Pong" : handlePong(json); break;
if ((readHeader[0] & (byte)0x80) == 0x80) {
// TODO process binary
} else {
def json = slurper.parse(payload)
if (json.type == null)
throw new Exception("missing json type")
switch(json.type) {
case "Ping" : handlePing(); break;
case "Pong" : handlePong(json); break;
case "Search": handleSearch(json); break
default :
throw new Exception("unknown json type ${json.type}")
}
}
}
default :
throw new Exception("unknown json type ${json.type}")
}
}
}
@Override
protected void write(Object message) {
byte[] payload
if (message instanceof Map) {
payload = JsonOutput.toJson(message).bytes
DataUtil.packHeader(payload.length, writeHeader)
log.fine "$name writing message type ${message.type} length $payload.length"
writeHeader[0] &= (byte)0x7F
} else {
// TODO: write binary
}
@Override
protected void write(Object message) {
byte[] payload
if (message instanceof Map) {
payload = JsonOutput.toJson(message).bytes
DataUtil.packHeader(payload.length, writeHeader)
log.fine "$name writing message type ${message.type} length $payload.length"
writeHeader[0] &= (byte)0x7F
} else {
// TODO: write binary
}
dos.write(writeHeader)
dos.write(payload)
dos.flush()
}
dos.write(writeHeader)
dos.write(payload)
dos.flush()
}
}

View File

@@ -17,30 +17,30 @@ import net.i2p.data.Destination
*/
class UltrapeerConnection extends Connection {
public UltrapeerConnection(EventBus eventBus, Endpoint endpoint, HostCache hostCache, TrustService trustService) {
super(eventBus, endpoint, false, hostCache, trustService)
}
public UltrapeerConnection(EventBus eventBus, Endpoint endpoint, HostCache hostCache, TrustService trustService) {
super(eventBus, endpoint, false, hostCache, trustService)
}
@Override
protected void read() {
// TODO Auto-generated method stub
@Override
protected void read() {
// TODO Auto-generated method stub
}
}
@Override
protected void write(Object message) {
if (message instanceof Map) {
writeJsonMessage(message)
} else {
writeBinaryMessage(message)
}
}
@Override
protected void write(Object message) {
if (message instanceof Map) {
writeJsonMessage(message)
} else {
writeBinaryMessage(message)
}
}
private void writeJsonMessage(def message) {
private void writeJsonMessage(def message) {
}
}
private void writeBinaryMessage(def message) {
private void writeBinaryMessage(def message) {
}
}
}

View File

@@ -16,26 +16,26 @@ import net.i2p.data.Destination
@Log
class UltrapeerConnectionManager extends ConnectionManager {
final int maxPeers, maxLeafs
final int maxPeers, maxLeafs
final TrustService trustService
final Map<Destination, PeerConnection> peerConnections = new ConcurrentHashMap()
final Map<Destination, LeafConnection> leafConnections = new ConcurrentHashMap()
final Map<Destination, PeerConnection> peerConnections = new ConcurrentHashMap()
final Map<Destination, LeafConnection> leafConnections = new ConcurrentHashMap()
UltrapeerConnectionManager() {}
UltrapeerConnectionManager() {}
public UltrapeerConnectionManager(EventBus eventBus, Persona me, int maxPeers, int maxLeafs,
public UltrapeerConnectionManager(EventBus eventBus, Persona me, int maxPeers, int maxLeafs,
HostCache hostCache, TrustService trustService, MuWireSettings settings) {
super(eventBus, me, hostCache, settings)
this.maxPeers = maxPeers
this.maxLeafs = maxLeafs
super(eventBus, me, hostCache, settings)
this.maxPeers = maxPeers
this.maxLeafs = maxLeafs
this.trustService = trustService
}
@Override
public void drop(Destination d) {
peerConnections.get(d)?.close()
}
@Override
public void drop(Destination d) {
peerConnections.get(d)?.close()
leafConnections.get(d)?.close()
}
}
void onQueryEvent(QueryEvent e) {
forwardQueryToLeafs(e)
@@ -50,57 +50,57 @@ class UltrapeerConnectionManager extends ConnectionManager {
}
}
@Override
public Collection<Connection> getConnections() {
def rv = new ArrayList(peerConnections.size() + leafConnections.size())
rv.addAll(peerConnections.values())
rv.addAll(leafConnections.values())
rv
}
@Override
public Collection<Connection> getConnections() {
def rv = new ArrayList(peerConnections.size() + leafConnections.size())
rv.addAll(peerConnections.values())
rv.addAll(leafConnections.values())
rv
}
boolean hasLeafSlots() {
leafConnections.size() < maxLeafs
}
boolean hasLeafSlots() {
leafConnections.size() < maxLeafs
}
boolean hasPeerSlots() {
peerConnections.size() < maxPeers
}
boolean hasPeerSlots() {
peerConnections.size() < maxPeers
}
@Override
protected int getDesiredConnections() {
return maxPeers / 2;
}
@Override
public boolean isConnected(Destination d) {
peerConnections.containsKey(d) || leafConnections.containsKey(d)
}
@Override
protected int getDesiredConnections() {
return maxPeers / 2;
}
@Override
public boolean isConnected(Destination d) {
peerConnections.containsKey(d) || leafConnections.containsKey(d)
}
@Override
public void onConnectionEvent(ConnectionEvent e) {
if (!e.incoming && e.leaf) {
log.severe("Inconsistent event $e")
return
}
@Override
public void onConnectionEvent(ConnectionEvent e) {
if (!e.incoming && e.leaf) {
log.severe("Inconsistent event $e")
return
}
if (e.status != ConnectionAttemptStatus.SUCCESSFUL)
return
if (e.status != ConnectionAttemptStatus.SUCCESSFUL)
return
Connection c = e.leaf ?
new LeafConnection(eventBus, e.endpoint, hostCache, trustService, settings) :
new PeerConnection(eventBus, e.endpoint, e.incoming, hostCache, trustService, settings)
def map = e.leaf ? leafConnections : peerConnections
map.put(e.endpoint.destination, c)
c.start()
}
Connection c = e.leaf ?
new LeafConnection(eventBus, e.endpoint, hostCache, trustService, settings) :
new PeerConnection(eventBus, e.endpoint, e.incoming, hostCache, trustService, settings)
def map = e.leaf ? leafConnections : peerConnections
map.put(e.endpoint.destination, c)
c.start()
}
@Override
public void onDisconnectionEvent(DisconnectionEvent e) {
def removed = peerConnections.remove(e.destination)
if (removed == null)
removed = leafConnections.remove(e.destination)
if (removed == null)
log.severe("Removed connection not present in either leaf or peer map ${e.destination.toBase32()}")
}
@Override
public void onDisconnectionEvent(DisconnectionEvent e) {
def removed = peerConnections.remove(e.destination)
if (removed == null)
removed = leafConnections.remove(e.destination)
if (removed == null)
log.severe("Removed connection not present in either leaf or peer map ${e.destination.toBase32()}")
}
@Override
void shutdown() {
@@ -110,7 +110,7 @@ class UltrapeerConnectionManager extends ConnectionManager {
leafConnections.clear()
}
void forwardQueryToLeafs(QueryEvent e) {
void forwardQueryToLeafs(QueryEvent e) {
}
}
}

View File

@@ -14,6 +14,7 @@ import static com.muwire.core.util.DataUtil.readTillRN
import groovy.util.logging.Log
import java.nio.ByteBuffer
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
import java.nio.charset.StandardCharsets
import java.nio.file.Files
@@ -25,8 +26,6 @@ import java.util.logging.Level
@Log
class DownloadSession {
private static int SAMPLES = 10
private final EventBus eventBus
private final String meB64
private final Pieces pieces
@@ -38,10 +37,10 @@ class DownloadSession {
private final Set<Integer> available
private final MessageDigest digest
private final LinkedList<Long> timestamps = new LinkedList<>()
private final LinkedList<Integer> reads = new LinkedList<>()
private long lastSpeedRead = System.currentTimeMillis()
private long dataSinceLastRead
private ByteBuffer mapped
private MappedByteBuffer mapped
DownloadSession(EventBus eventBus, String meB64, Pieces pieces, InfoHash infoHash, Endpoint endpoint, File file,
int pieceSize, long fileLength, Set<Integer> available) {
@@ -71,21 +70,23 @@ class DownloadSession {
OutputStream os = endpoint.getOutputStream()
InputStream is = endpoint.getInputStream()
int piece
int[] pieceAndPosition
if (available.isEmpty())
piece = pieces.claim()
pieceAndPosition = pieces.claim()
else
piece = pieces.claim(new HashSet<>(available))
if (piece == -1)
pieceAndPosition = pieces.claim(new HashSet<>(available))
if (pieceAndPosition == null)
return false
int piece = pieceAndPosition[0]
int position = pieceAndPosition[1]
boolean steal = pieceAndPosition[2] == 1
boolean unclaim = true
log.info("will download piece $piece")
long start = piece * pieceSize
long end = Math.min(fileLength, start + pieceSize) - 1
long length = end - start + 1
log.info("will download piece $piece from position $position steal $steal")
long pieceStart = piece * ((long)pieceSize)
long end = Math.min(fileLength, pieceStart + pieceSize) - 1
long start = pieceStart + position
String root = Base64.encode(infoHash.getRoot())
try {
@@ -174,8 +175,9 @@ class DownloadSession {
FileChannel channel
try {
channel = Files.newByteChannel(file.toPath(), EnumSet.of(StandardOpenOption.READ, StandardOpenOption.WRITE,
StandardOpenOption.SPARSE, StandardOpenOption.CREATE)) // TODO: double-check, maybe CREATE_NEW
mapped = channel.map(FileChannel.MapMode.READ_WRITE, start, end - start + 1)
StandardOpenOption.SPARSE, StandardOpenOption.CREATE))
mapped = channel.map(FileChannel.MapMode.READ_WRITE, pieceStart, end - pieceStart + 1)
mapped.position(position)
byte[] tmp = new byte[0x1 << 13]
while(mapped.hasRemaining()) {
@@ -186,31 +188,28 @@ class DownloadSession {
throw new IOException()
synchronized(this) {
mapped.put(tmp, 0, read)
if (timestamps.size() == SAMPLES) {
timestamps.removeFirst()
reads.removeFirst()
}
timestamps.addLast(System.currentTimeMillis())
reads.addLast(read)
dataSinceLastRead += read
pieces.markPartial(piece, mapped.position())
}
}
mapped.clear()
digest.update(mapped)
DataUtil.tryUnmap(mapped)
byte [] hash = digest.digest()
byte [] expected = new byte[32]
System.arraycopy(infoHash.getHashList(), piece * 32, expected, 0, 32)
if (hash != expected)
throw new BadHashException()
if (hash != expected) {
pieces.markPartial(piece, 0)
throw new BadHashException("bad hash on piece $piece")
}
} finally {
try { channel?.close() } catch (IOException ignore) {}
DataUtil.tryUnmap(mapped)
}
pieces.markDownloaded(piece)
unclaim = false
} finally {
if (unclaim)
if (unclaim && !steal)
pieces.unclaim(piece)
}
return true
@@ -223,24 +222,11 @@ class DownloadSession {
}
synchronized int speed() {
if (timestamps.size() < SAMPLES)
return 0
int totalRead = 0
int idx = 0
final long now = System.currentTimeMillis()
while(idx < SAMPLES && timestamps.get(idx) < now - 1000)
idx++
if (idx == SAMPLES)
return 0
if (idx == SAMPLES - 1)
return reads[idx]
long interval = timestamps.last - timestamps[idx]
if (interval == 0)
interval = 1
for (int i = idx; i < SAMPLES; i++)
totalRead += reads[idx]
(int)(totalRead * 1000.0 / interval)
long interval = Math.max(1000, now - lastSpeedRead)
lastSpeedRead = now;
int rv = (int) (dataSinceLastRead * 1000.0 / interval)
dataSinceLastRead = 0
rv
}
}

View File

@@ -7,6 +7,7 @@ import com.muwire.core.connection.Endpoint
import java.nio.file.AtomicMoveNotSupportedException
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.time.Instant
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
@@ -58,6 +59,11 @@ public class Downloader {
private final AtomicBoolean eventFired = new AtomicBoolean()
private boolean piecesFileClosed
private ArrayList speedArr = new ArrayList<Integer>()
private int speedPos = 0
private int speedAvg = 0
private long timestamp = Instant.now().toEpochMilli()
public Downloader(EventBus eventBus, DownloadManager downloadManager,
Persona me, File file, long length, InfoHash infoHash,
int pieceSizePow2, I2PConnector connector, Set<Destination> destinations,
@@ -76,6 +82,10 @@ public class Downloader {
this.pieceSize = 1 << pieceSizePow2
this.pieces = pieces
this.nPieces = pieces.nPieces
// default size suitable for an average of 5 seconds / 5 elements / 5 interval units
// it's easily adjustable by resizing the size of speedArr
this.speedArr = [ 0, 0, 0, 0, 0 ]
}
public synchronized InfoHash getInfoHash() {
@@ -101,8 +111,14 @@ public class Downloader {
if (!piecesFile.exists())
return
piecesFile.eachLine {
int piece = Integer.parseInt(it)
pieces.markDownloaded(piece)
String [] split = it.split(",")
int piece = Integer.parseInt(split[0])
if (split.length == 1)
pieces.markDownloaded(piece)
else {
int position = Integer.parseInt(split[1])
pieces.markPartial(piece, position)
}
}
}
@@ -111,9 +127,7 @@ public class Downloader {
if (piecesFileClosed)
return
piecesFile.withPrintWriter { writer ->
pieces.getDownloaded().each { piece ->
writer.println(piece)
}
pieces.write(writer)
}
}
}
@@ -124,14 +138,35 @@ public class Downloader {
public int speed() {
int total = 0
int currSpeed = 0
if (getCurrentState() == DownloadState.DOWNLOADING) {
activeWorkers.values().each {
if (it.currentState == WorkerState.DOWNLOADING)
total += it.speed()
currSpeed += it.speed()
}
}
total
// normalize to speedArr.size
currSpeed /= speedArr.size()
// compute new speedAvg and update speedArr
if ( speedArr[speedPos] > speedAvg ) {
speedAvg = 0
} else {
speedAvg -= speedArr[speedPos]
}
speedAvg += currSpeed
speedArr[speedPos] = currSpeed
// this might be necessary due to rounding errors
if (speedAvg < 0)
speedAvg = 0
// rolling index over the speedArr
speedPos++
if (speedPos >= speedArr.size())
speedPos=0
speedAvg
}
public DownloadState getCurrentState() {
@@ -272,12 +307,17 @@ public class Downloader {
} catch (Exception bad) {
log.log(Level.WARNING,"Exception while downloading",DataUtil.findRoot(bad))
} finally {
writePieces()
currentState = WorkerState.FINISHED
if (pieces.isComplete() && eventFired.compareAndSet(false, true)) {
synchronized(piecesFile) {
piecesFileClosed = true
piecesFile.delete()
}
activeWorkers.values().each {
if (it.destination != destination)
it.cancel()
}
try {
Files.move(incompleteFile.toPath(), file.toPath(), StandardCopyOption.ATOMIC_MOVE)
} catch (AtomicMoveNotSupportedException e) {

View File

@@ -5,6 +5,7 @@ class Pieces {
private final int nPieces
private final float ratio
private final Random random = new Random()
private final Map<Integer,Integer> partials = new HashMap<>()
Pieces(int nPieces) {
this(nPieces, 1.0f)
@@ -17,16 +18,22 @@ class Pieces {
claimed = new BitSet(nPieces)
}
synchronized int claim() {
synchronized int[] claim() {
int claimedCardinality = claimed.cardinality()
if (claimedCardinality == nPieces)
return -1
if (claimedCardinality == nPieces) {
// steal
int downloadedCardinality = done.cardinality()
if (downloadedCardinality == nPieces)
return null
int rv = done.nextClearBit(0)
return [rv, partials.getOrDefault(rv, 0), 1]
}
// if fuller than ratio just do sequential
if ( (1.0f * claimedCardinality) / nPieces > ratio) {
int rv = claimed.nextClearBit(0)
claimed.set(rv)
return rv
return [rv, partials.getOrDefault(rv, 0), 0]
}
while(true) {
@@ -34,20 +41,28 @@ class Pieces {
if (claimed.get(start))
continue
claimed.set(start)
return start
return [start, partials.getOrDefault(start,0), 0]
}
}
synchronized int claim(Set<Integer> available) {
for (int i = claimed.nextSetBit(0); i >= 0; i = claimed.nextSetBit(i+1))
synchronized int[] claim(Set<Integer> available) {
for (int i = done.nextSetBit(0); i >= 0; i = done.nextSetBit(i+1))
available.remove(i)
if (available.isEmpty())
return -1
List<Integer> toList = available.toList()
return null
Set<Integer> availableCopy = new HashSet<>(available)
for (int i = claimed.nextSetBit(0); i >= 0; i = claimed.nextSetBit(i+1))
availableCopy.remove(i)
if (availableCopy.isEmpty()) {
// steal
int rv = available.first()
return [rv, partials.getOrDefault(rv, 0), 1]
}
List<Integer> toList = availableCopy.toList()
Collections.shuffle(toList)
int rv = toList[0]
claimed.set(rv)
rv
[rv, partials.getOrDefault(rv, 0), 0]
}
synchronized def getDownloaded() {
@@ -61,6 +76,11 @@ class Pieces {
synchronized void markDownloaded(int piece) {
done.set(piece)
claimed.set(piece)
partials.remove(piece)
}
synchronized void markPartial(int piece, int position) {
partials.put(piece, position)
}
synchronized void unclaim(int piece) {
@@ -82,5 +102,15 @@ class Pieces {
synchronized void clearAll() {
done.clear()
claimed.clear()
partials.clear()
}
synchronized void write(PrintWriter writer) {
for (int i = done.nextSetBit(0); i >= 0; i = done.nextSetBit(i+1)) {
writer.println(i)
}
partials.each { piece, position ->
writer.println("$piece,$position")
}
}
}

View File

@@ -8,5 +8,5 @@ import net.i2p.data.Destination
class FileDownloadedEvent extends Event {
Downloader downloader
DownloadedFile downloadedFile
DownloadedFile downloadedFile
}

View File

@@ -5,8 +5,8 @@ import com.muwire.core.SharedFile
class FileHashedEvent extends Event {
SharedFile sharedFile
String error
SharedFile sharedFile
String error
@Override
public String toString() {

View File

@@ -13,67 +13,67 @@ import java.security.NoSuchAlgorithmException
class FileHasher {
/** max size of shared file is 128 GB */
public static final long MAX_SIZE = 0x1L << 37
/** max size of shared file is 128 GB */
public static final long MAX_SIZE = 0x1L << 37
/**
* @param size of the file to be shared
* @return the size of each piece in power of 2
/**
* @param size of the file to be shared
* @return the size of each piece in power of 2
* piece size is minimum 128 KBytees and maximum 16 MBytes in power of 2 steps (2^17 - 2^24)
* there can be up to 8192 pieces maximum per file
*/
static int getPieceSize(long size) {
if (size <= 0x1 << 30)
return 17
*/
static int getPieceSize(long size) {
if (size <= 0x1 << 30)
return 17
for (int i = 31; i <= 37; i++) {
if (size <= 0x1L << i) {
return i-13
}
}
for (int i = 31; i <= 37; i++) {
if (size <= 0x1L << i) {
return i-13
}
}
throw new IllegalArgumentException("File too large $size")
}
throw new IllegalArgumentException("File too large $size")
}
final MessageDigest digest
final MessageDigest digest
FileHasher() {
try {
digest = MessageDigest.getInstance("SHA-256")
} catch (NoSuchAlgorithmException impossible) {
digest = null
System.exit(1)
}
}
FileHasher() {
try {
digest = MessageDigest.getInstance("SHA-256")
} catch (NoSuchAlgorithmException impossible) {
digest = null
System.exit(1)
}
}
InfoHash hashFile(File file) {
final long length = file.length()
final int size = 0x1 << getPieceSize(length)
int numPieces = (int) (length / size)
if (numPieces * size < length)
numPieces++
InfoHash hashFile(File file) {
final long length = file.length()
final int size = 0x1 << getPieceSize(length)
int numPieces = (int) (length / size)
if (numPieces * size < length)
numPieces++
def output = new ByteArrayOutputStream()
RandomAccessFile raf = new RandomAccessFile(file, "r")
try {
MappedByteBuffer buf
for (int i = 0; i < numPieces - 1; i++) {
buf = raf.getChannel().map(MapMode.READ_ONLY, ((long)size) * i, size)
digest.update buf
def output = new ByteArrayOutputStream()
RandomAccessFile raf = new RandomAccessFile(file, "r")
try {
MappedByteBuffer buf
for (int i = 0; i < numPieces - 1; i++) {
buf = raf.getChannel().map(MapMode.READ_ONLY, ((long)size) * i, size)
digest.update buf
DataUtil.tryUnmap(buf)
output.write(digest.digest(), 0, 32)
}
def lastPieceLength = length - (numPieces - 1) * ((long)size)
buf = raf.getChannel().map(MapMode.READ_ONLY, length - lastPieceLength, lastPieceLength)
digest.update buf
output.write(digest.digest(), 0, 32)
} finally {
raf.close()
}
output.write(digest.digest(), 0, 32)
}
def lastPieceLength = length - (numPieces - 1) * ((long)size)
buf = raf.getChannel().map(MapMode.READ_ONLY, length - lastPieceLength, lastPieceLength)
digest.update buf
output.write(digest.digest(), 0, 32)
} finally {
raf.close()
}
byte [] hashList = output.toByteArray()
InfoHash.fromHashList(hashList)
}
byte [] hashList = output.toByteArray()
InfoHash.fromHashList(hashList)
}
public static void main(String[] args) {
if (args.length != 1) {

View File

@@ -0,0 +1,15 @@
package com.muwire.core.files
import com.muwire.core.Event
import com.muwire.core.SharedFile
class FileHashingEvent extends Event {
File hashingFile
@Override
public String toString() {
super.toString() + " hashingFile " + hashingFile.getAbsolutePath()
}
}

View File

@@ -5,5 +5,5 @@ import com.muwire.core.SharedFile
class FileLoadedEvent extends Event {
SharedFile loadedFile
SharedFile loadedFile
}

View File

@@ -15,26 +15,26 @@ import groovy.util.logging.Log
class FileManager {
final EventBus eventBus
final EventBus eventBus
final MuWireSettings settings
final Map<InfoHash, Set<SharedFile>> rootToFiles = Collections.synchronizedMap(new HashMap<>())
final Map<File, SharedFile> fileToSharedFile = Collections.synchronizedMap(new HashMap<>())
final Map<String, Set<File>> nameToFiles = new HashMap<>()
final SearchIndex index = new SearchIndex()
final Map<InfoHash, Set<SharedFile>> rootToFiles = Collections.synchronizedMap(new HashMap<>())
final Map<File, SharedFile> fileToSharedFile = Collections.synchronizedMap(new HashMap<>())
final Map<String, Set<File>> nameToFiles = new HashMap<>()
final SearchIndex index = new SearchIndex()
FileManager(EventBus eventBus, MuWireSettings settings) {
FileManager(EventBus eventBus, MuWireSettings settings) {
this.settings = settings
this.eventBus = eventBus
}
this.eventBus = eventBus
}
void onFileHashedEvent(FileHashedEvent e) {
if (e.sharedFile != null)
addToIndex(e.sharedFile)
}
void onFileLoadedEvent(FileLoadedEvent e) {
addToIndex(e.loadedFile)
}
void onFileLoadedEvent(FileLoadedEvent e) {
addToIndex(e.loadedFile)
}
void onFileDownloadedEvent(FileDownloadedEvent e) {
if (settings.shareDownloadedFiles) {
@@ -42,88 +42,88 @@ class FileManager {
}
}
private void addToIndex(SharedFile sf) {
private void addToIndex(SharedFile sf) {
log.info("Adding shared file " + sf.getFile())
InfoHash infoHash = sf.getInfoHash()
Set<SharedFile> existing = rootToFiles.get(infoHash)
if (existing == null) {
InfoHash infoHash = sf.getInfoHash()
Set<SharedFile> existing = rootToFiles.get(infoHash)
if (existing == null) {
log.info("adding new root")
existing = new HashSet<>()
rootToFiles.put(infoHash, existing);
}
existing.add(sf)
fileToSharedFile.put(sf.file, sf)
existing = new HashSet<>()
rootToFiles.put(infoHash, existing);
}
existing.add(sf)
fileToSharedFile.put(sf.file, sf)
String name = sf.getFile().getName()
Set<File> existingFiles = nameToFiles.get(name)
if (existingFiles == null) {
existingFiles = new HashSet<>()
nameToFiles.put(name, existingFiles)
}
existingFiles.add(sf.getFile())
String name = sf.getFile().getName()
Set<File> existingFiles = nameToFiles.get(name)
if (existingFiles == null) {
existingFiles = new HashSet<>()
nameToFiles.put(name, existingFiles)
}
existingFiles.add(sf.getFile())
index.add(name)
}
index.add(name)
}
void onFileUnsharedEvent(FileUnsharedEvent e) {
SharedFile sf = e.unsharedFile
InfoHash infoHash = sf.getInfoHash()
Set<SharedFile> existing = rootToFiles.get(infoHash)
if (existing != null) {
existing.remove(sf)
if (existing.isEmpty()) {
rootToFiles.remove(infoHash)
}
}
void onFileUnsharedEvent(FileUnsharedEvent e) {
SharedFile sf = e.unsharedFile
InfoHash infoHash = sf.getInfoHash()
Set<SharedFile> existing = rootToFiles.get(infoHash)
if (existing != null) {
existing.remove(sf)
if (existing.isEmpty()) {
rootToFiles.remove(infoHash)
}
}
fileToSharedFile.remove(sf.file)
fileToSharedFile.remove(sf.file)
String name = sf.getFile().getName()
Set<File> existingFiles = nameToFiles.get(name)
if (existingFiles != null) {
existingFiles.remove(sf.file)
if (existingFiles.isEmpty()) {
nameToFiles.remove(name)
}
}
String name = sf.getFile().getName()
Set<File> existingFiles = nameToFiles.get(name)
if (existingFiles != null) {
existingFiles.remove(sf.file)
if (existingFiles.isEmpty()) {
nameToFiles.remove(name)
}
}
index.remove(name)
}
index.remove(name)
}
Map<File, SharedFile> getSharedFiles() {
Map<File, SharedFile> getSharedFiles() {
synchronized(fileToSharedFile) {
return new HashMap<>(fileToSharedFile)
}
}
}
Set<SharedFile> getSharedFiles(byte []root) {
return rootToFiles.get(new InfoHash(root))
}
void onSearchEvent(SearchEvent e) {
// hash takes precedence
ResultsEvent re = null
if (e.searchHash != null) {
void onSearchEvent(SearchEvent e) {
// hash takes precedence
ResultsEvent re = null
if (e.searchHash != null) {
Set<SharedFile> found
found = rootToFiles.get new InfoHash(e.searchHash)
found = filter(found, e.oobInfohash)
if (found != null && !found.isEmpty())
re = new ResultsEvent(results: found.asList(), uuid: e.uuid, searchEvent: e)
} else {
def names = index.search e.searchTerms
Set<File> files = new HashSet<>()
names.each { files.addAll nameToFiles.getOrDefault(it, []) }
Set<SharedFile> sharedFiles = new HashSet<>()
files.each { sharedFiles.add fileToSharedFile[it] }
if (found != null && !found.isEmpty())
re = new ResultsEvent(results: found.asList(), uuid: e.uuid, searchEvent: e)
} else {
def names = index.search e.searchTerms
Set<File> files = new HashSet<>()
names.each { files.addAll nameToFiles.getOrDefault(it, []) }
Set<SharedFile> sharedFiles = new HashSet<>()
files.each { sharedFiles.add fileToSharedFile[it] }
files = filter(sharedFiles, e.oobInfohash)
if (!sharedFiles.isEmpty())
re = new ResultsEvent(results: sharedFiles.asList(), uuid: e.uuid, searchEvent: e)
if (!sharedFiles.isEmpty())
re = new ResultsEvent(results: sharedFiles.asList(), uuid: e.uuid, searchEvent: e)
}
}
if (re != null)
eventBus.publish(re)
}
if (re != null)
eventBus.publish(re)
}
private static Set<SharedFile> filter(Set<SharedFile> files, boolean oob) {
if (!oob)

View File

@@ -4,7 +4,7 @@ import com.muwire.core.Event
class FileSharedEvent extends Event {
File file
File file
@Override
public String toString() {

View File

@@ -4,5 +4,5 @@ import com.muwire.core.Event
import com.muwire.core.SharedFile
class FileUnsharedEvent extends Event {
SharedFile unsharedFile
SharedFile unsharedFile
}

View File

@@ -8,40 +8,41 @@ import com.muwire.core.SharedFile
class HasherService {
final FileHasher hasher
final EventBus eventBus
final FileHasher hasher
final EventBus eventBus
final FileManager fileManager
Executor executor
Executor executor
HasherService(FileHasher hasher, EventBus eventBus, FileManager fileManager) {
this.hasher = hasher
this.eventBus = eventBus
HasherService(FileHasher hasher, EventBus eventBus, FileManager fileManager) {
this.hasher = hasher
this.eventBus = eventBus
this.fileManager = fileManager
}
}
void start() {
executor = Executors.newSingleThreadExecutor()
}
void start() {
executor = Executors.newSingleThreadExecutor()
}
void onFileSharedEvent(FileSharedEvent evt) {
void onFileSharedEvent(FileSharedEvent evt) {
if (fileManager.fileToSharedFile.containsKey(evt.file.getCanonicalFile()))
return
executor.execute( { -> process(evt.file) } as Runnable)
}
executor.execute( { -> process(evt.file) } as Runnable)
}
private void process(File f) {
f = f.getCanonicalFile()
if (f.isDirectory()) {
f.listFiles().each {eventBus.publish new FileSharedEvent(file: it) }
} else {
if (f.length() == 0) {
eventBus.publish new FileHashedEvent(error: "Not sharing empty file $f")
} else if (f.length() > FileHasher.MAX_SIZE) {
eventBus.publish new FileHashedEvent(error: "$f is too large to be shared ${f.length()}")
} else {
def hash = hasher.hashFile f
eventBus.publish new FileHashedEvent(sharedFile: new SharedFile(f, hash, FileHasher.getPieceSize(f.length())))
}
}
}
private void process(File f) {
f = f.getCanonicalFile()
if (f.isDirectory()) {
f.listFiles().each {eventBus.publish new FileSharedEvent(file: it) }
} else {
if (f.length() == 0) {
eventBus.publish new FileHashedEvent(error: "Not sharing empty file $f")
} else if (f.length() > FileHasher.MAX_SIZE) {
eventBus.publish new FileHashedEvent(error: "$f is too large to be shared ${f.length()}")
} else {
eventBus.publish new FileHashingEvent(hashingFile: f)
def hash = hasher.hashFile f
eventBus.publish new FileHashedEvent(sharedFile: new SharedFile(f, hash, FileHasher.getPieceSize(f.length())))
}
}
}
}

View File

@@ -23,135 +23,135 @@ import net.i2p.data.Destination
@Log
class PersisterService extends Service {
final File location
final EventBus listener
final int interval
final Timer timer
final FileManager fileManager
final File location
final EventBus listener
final int interval
final Timer timer
final FileManager fileManager
PersisterService(File location, EventBus listener, int interval, FileManager fileManager) {
this.location = location
this.listener = listener
this.interval = interval
this.fileManager = fileManager
timer = new Timer("file persister", true)
}
PersisterService(File location, EventBus listener, int interval, FileManager fileManager) {
this.location = location
this.listener = listener
this.interval = interval
this.fileManager = fileManager
timer = new Timer("file persister", true)
}
void stop() {
timer.cancel()
}
void stop() {
timer.cancel()
}
void onUILoadedEvent(UILoadedEvent e) {
timer.schedule({load()} as TimerTask, 1)
}
void load() {
if (location.exists() && location.isFile()) {
def slurper = new JsonSlurper()
try {
location.eachLine {
if (it.trim().length() > 0) {
def parsed = slurper.parseText it
def event = fromJson parsed
if (event != null) {
void load() {
if (location.exists() && location.isFile()) {
def slurper = new JsonSlurper()
try {
location.eachLine {
if (it.trim().length() > 0) {
def parsed = slurper.parseText it
def event = fromJson parsed
if (event != null) {
log.fine("loaded file $event.loadedFile.file")
listener.publish event
}
}
}
listener.publish event
}
}
}
listener.publish(new AllFilesLoadedEvent())
} catch (IllegalArgumentException|NumberFormatException e) {
} catch (IllegalArgumentException|NumberFormatException e) {
log.log(Level.WARNING, "couldn't load files",e)
}
} else {
}
} else {
listener.publish(new AllFilesLoadedEvent())
}
timer.schedule({persistFiles()} as TimerTask, 0, interval)
loaded = true
}
timer.schedule({persistFiles()} as TimerTask, 0, interval)
loaded = true
}
private static FileLoadedEvent fromJson(def json) {
if (json.file == null || json.length == null || json.infoHash == null || json.hashList == null)
throw new IllegalArgumentException()
if (!(json.hashList instanceof List))
throw new IllegalArgumentException()
private static FileLoadedEvent fromJson(def json) {
if (json.file == null || json.length == null || json.infoHash == null || json.hashList == null)
throw new IllegalArgumentException()
if (!(json.hashList instanceof List))
throw new IllegalArgumentException()
def file = new File(DataUtil.readi18nString(Base64.decode(json.file)))
file = file.getCanonicalFile()
if (!file.exists() || file.isDirectory())
return null
long length = Long.valueOf(json.length)
if (length != file.length())
return null
def file = new File(DataUtil.readi18nString(Base64.decode(json.file)))
file = file.getCanonicalFile()
if (!file.exists() || file.isDirectory())
return null
long length = Long.valueOf(json.length)
if (length != file.length())
return null
List hashList = (List) json.hashList
ByteArrayOutputStream baos = new ByteArrayOutputStream()
hashList.each {
byte [] hash = Base64.decode it.toString()
if (hash == null)
throw new IllegalArgumentException()
baos.write hash
}
byte[] hashListBytes = baos.toByteArray()
List hashList = (List) json.hashList
ByteArrayOutputStream baos = new ByteArrayOutputStream()
hashList.each {
byte [] hash = Base64.decode it.toString()
if (hash == null)
throw new IllegalArgumentException()
baos.write hash
}
byte[] hashListBytes = baos.toByteArray()
InfoHash ih = InfoHash.fromHashList(hashListBytes)
byte [] root = Base64.decode(json.infoHash.toString())
if (root == null)
throw new IllegalArgumentException()
if (!Arrays.equals(root, ih.getRoot()))
return null
InfoHash ih = InfoHash.fromHashList(hashListBytes)
byte [] root = Base64.decode(json.infoHash.toString())
if (root == null)
throw new IllegalArgumentException()
if (!Arrays.equals(root, ih.getRoot()))
return null
int pieceSize = 0
if (json.pieceSize != null)
pieceSize = json.pieceSize
if (json.sources != null) {
List sources = (List)json.sources
Set<Destination> sourceSet = sources.stream().map({d -> new Destination(d.toString())}).collect Collectors.toSet()
DownloadedFile df = new DownloadedFile(file, ih, pieceSize, sourceSet)
return new FileLoadedEvent(loadedFile : df)
}
List sources = (List)json.sources
Set<Destination> sourceSet = sources.stream().map({d -> new Destination(d.toString())}).collect Collectors.toSet()
DownloadedFile df = new DownloadedFile(file, ih, pieceSize, sourceSet)
return new FileLoadedEvent(loadedFile : df)
}
SharedFile sf = new SharedFile(file, ih, pieceSize)
return new FileLoadedEvent(loadedFile: sf)
SharedFile sf = new SharedFile(file, ih, pieceSize)
return new FileLoadedEvent(loadedFile: sf)
}
}
private void persistFiles() {
def sharedFiles = fileManager.getSharedFiles()
private void persistFiles() {
def sharedFiles = fileManager.getSharedFiles()
File tmp = File.createTempFile("muwire-files", "tmp")
tmp.deleteOnExit()
tmp.withPrintWriter { writer ->
sharedFiles.each { k, v ->
def json = toJson(k,v)
json = JsonOutput.toJson(json)
writer.println json
}
}
tmp.withPrintWriter { writer ->
sharedFiles.each { k, v ->
def json = toJson(k,v)
json = JsonOutput.toJson(json)
writer.println json
}
}
Files.copy(tmp.toPath(), location.toPath(), StandardCopyOption.REPLACE_EXISTING)
tmp.delete()
}
}
private def toJson(File f, SharedFile sf) {
def json = [:]
json.file = Base64.encode DataUtil.encodei18nString(f.getCanonicalFile().toString())
json.length = f.length()
InfoHash ih = sf.getInfoHash()
json.infoHash = Base64.encode ih.getRoot()
private def toJson(File f, SharedFile sf) {
def json = [:]
json.file = Base64.encode DataUtil.encodei18nString(f.getCanonicalFile().toString())
json.length = f.length()
InfoHash ih = sf.getInfoHash()
json.infoHash = Base64.encode ih.getRoot()
json.pieceSize = sf.getPieceSize()
byte [] tmp = new byte [32]
json.hashList = []
for (int i = 0;i < ih.getHashList().length / 32; i++) {
System.arraycopy(ih.getHashList(), i * 32, tmp, 0, 32)
json.hashList.add Base64.encode(tmp)
}
byte [] tmp = new byte [32]
json.hashList = []
for (int i = 0;i < ih.getHashList().length / 32; i++) {
System.arraycopy(ih.getHashList(), i * 32, tmp, 0, 32)
json.hashList.add Base64.encode(tmp)
}
if (sf instanceof DownloadedFile) {
json.sources = sf.sources.stream().map( {d -> d.toBase64()}).collect(Collectors.toList())
}
if (sf instanceof DownloadedFile) {
json.sources = sf.sources.stream().map( {d -> d.toBase64()}).collect(Collectors.toList())
}
json
}
json
}
}

View File

@@ -18,176 +18,176 @@ import net.i2p.data.Destination
@Log
class CacheClient {
private static final int CRAWLER_RETURN = 10
private static final int CRAWLER_RETURN = 10
final EventBus eventBus
final HostCache cache
final ConnectionManager manager
final I2PSession session
final long interval
final MuWireSettings settings
final Timer timer
final EventBus eventBus
final HostCache cache
final ConnectionManager manager
final I2PSession session
final long interval
final MuWireSettings settings
final Timer timer
public CacheClient(EventBus eventBus, HostCache cache,
ConnectionManager manager, I2PSession session,
MuWireSettings settings, long interval) {
this.eventBus = eventBus
this.cache = cache
this.manager = manager
this.session = session
this.settings = settings
this.interval = interval
this.timer = new Timer("hostcache-client",true)
}
public CacheClient(EventBus eventBus, HostCache cache,
ConnectionManager manager, I2PSession session,
MuWireSettings settings, long interval) {
this.eventBus = eventBus
this.cache = cache
this.manager = manager
this.session = session
this.settings = settings
this.interval = interval
this.timer = new Timer("hostcache-client",true)
}
void start() {
session.addMuxedSessionListener(new Listener(), I2PSession.PROTO_DATAGRAM, 0)
timer.schedule({queryIfNeeded()} as TimerTask, 1, interval)
}
void start() {
session.addMuxedSessionListener(new Listener(), I2PSession.PROTO_DATAGRAM, 0)
timer.schedule({queryIfNeeded()} as TimerTask, 1, interval)
}
void stop() {
timer.cancel()
}
void stop() {
timer.cancel()
}
private void queryIfNeeded() {
if (!manager.getConnections().isEmpty())
return
if (!cache.getHosts(1).isEmpty())
return
private void queryIfNeeded() {
if (!manager.getConnections().isEmpty())
return
if (!cache.getHosts(1).isEmpty())
return
log.info "Will query hostcaches"
log.info "Will query hostcaches"
def ping = [type: "Ping", version: 1, leaf: settings.isLeaf()]
ping = JsonOutput.toJson(ping)
def maker = new I2PDatagramMaker(session)
ping = maker.makeI2PDatagram(ping.bytes)
def options = new SendMessageOptions()
options.setSendLeaseSet(true)
CacheServers.getCacheServers().each {
log.info "Querying hostcache ${it.toBase32()}"
session.sendMessage(it, ping, 0, ping.length, I2PSession.PROTO_DATAGRAM, 1, 0, options)
}
}
def ping = [type: "Ping", version: 1, leaf: settings.isLeaf()]
ping = JsonOutput.toJson(ping)
def maker = new I2PDatagramMaker(session)
ping = maker.makeI2PDatagram(ping.bytes)
def options = new SendMessageOptions()
options.setSendLeaseSet(true)
CacheServers.getCacheServers().each {
log.info "Querying hostcache ${it.toBase32()}"
session.sendMessage(it, ping, 0, ping.length, I2PSession.PROTO_DATAGRAM, 1, 0, options)
}
}
class Listener implements I2PSessionMuxedListener {
class Listener implements I2PSessionMuxedListener {
private final JsonSlurper slurper = new JsonSlurper()
private final JsonSlurper slurper = new JsonSlurper()
@Override
public void messageAvailable(I2PSession session, int msgId, long size) {
}
@Override
public void messageAvailable(I2PSession session, int msgId, long size) {
}
@Override
public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromport, int toport) {
@Override
public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromport, int toport) {
if (proto != I2PSession.PROTO_DATAGRAM) {
log.warning "Received unexpected protocol $proto"
return
}
if (proto != I2PSession.PROTO_DATAGRAM) {
log.warning "Received unexpected protocol $proto"
return
}
def payload = session.receiveMessage(msgId)
def dissector = new I2PDatagramDissector()
try {
dissector.loadI2PDatagram(payload)
def sender = dissector.getSender()
log.info("Received something from ${sender.toBase32()}")
def payload = session.receiveMessage(msgId)
def dissector = new I2PDatagramDissector()
try {
dissector.loadI2PDatagram(payload)
def sender = dissector.getSender()
log.info("Received something from ${sender.toBase32()}")
payload = dissector.getPayload()
payload = slurper.parse(payload)
payload = dissector.getPayload()
payload = slurper.parse(payload)
if (payload.type == null) {
log.warning("type missing")
return
}
if (payload.type == null) {
log.warning("type missing")
return
}
switch(payload.type) {
case "Pong" : handlePong(sender, payload); break
case "CrawlerPing": handleCrawlerPing(session, sender, payload); break
default : log.warning("unknown type ${payload.type}")
}
} catch (Exception e) {
log.warning("Invalid datagram $e")
}
}
switch(payload.type) {
case "Pong" : handlePong(sender, payload); break
case "CrawlerPing": handleCrawlerPing(session, sender, payload); break
default : log.warning("unknown type ${payload.type}")
}
} catch (Exception e) {
log.warning("Invalid datagram $e")
}
}
@Override
public void reportAbuse(I2PSession session, int severity) {
}
@Override
public void reportAbuse(I2PSession session, int severity) {
}
@Override
public void disconnected(I2PSession session) {
log.severe "I2P session disconnected"
}
@Override
public void disconnected(I2PSession session) {
log.severe "I2P session disconnected"
}
@Override
public void errorOccurred(I2PSession session, String message, Throwable error) {
log.severe "I2P error occured $message $error"
}
@Override
public void errorOccurred(I2PSession session, String message, Throwable error) {
log.severe "I2P error occured $message $error"
}
}
}
private void handlePong(Destination from, def pong) {
if (!CacheServers.isRegistered(from)) {
log.warning("received pong from non-registered destination")
return
}
private void handlePong(Destination from, def pong) {
if (!CacheServers.isRegistered(from)) {
log.warning("received pong from non-registered destination")
return
}
if (pong.pongs == null) {
log.warning("malformed pong - no pongs")
return
}
if (pong.pongs == null) {
log.warning("malformed pong - no pongs")
return
}
pong.pongs.asList().each {
Destination dest = new Destination(it)
if (!session.getMyDestination().equals(dest))
eventBus.publish(new HostDiscoveredEvent(destination: dest, fromHostcache : true))
}
pong.pongs.asList().each {
Destination dest = new Destination(it)
if (!session.getMyDestination().equals(dest))
eventBus.publish(new HostDiscoveredEvent(destination: dest, fromHostcache : true))
}
}
}
private void handleCrawlerPing(I2PSession session, Destination from, def ping) {
if (settings.isLeaf()) {
log.warning("Received crawler ping but I'm a leaf")
return
}
private void handleCrawlerPing(I2PSession session, Destination from, def ping) {
if (settings.isLeaf()) {
log.warning("Received crawler ping but I'm a leaf")
return
}
switch(settings.getCrawlerResponse()) {
case CrawlerResponse.NONE:
log.info("Responding to crawlers is disabled by user")
break
case CrawlerResponse.ALL:
respondToCrawler(session, from, ping)
break;
case CrawlerResponse.REGISTERED:
if (CacheServers.isRegistered(from))
respondToCrawler(session, from, ping)
else
log.warning("Ignoring crawler ping from non-registered crawler")
break
}
}
switch(settings.getCrawlerResponse()) {
case CrawlerResponse.NONE:
log.info("Responding to crawlers is disabled by user")
break
case CrawlerResponse.ALL:
respondToCrawler(session, from, ping)
break;
case CrawlerResponse.REGISTERED:
if (CacheServers.isRegistered(from))
respondToCrawler(session, from, ping)
else
log.warning("Ignoring crawler ping from non-registered crawler")
break
}
}
private void respondToCrawler(I2PSession session, Destination from, def ping) {
log.info "responding to crawler ping"
private void respondToCrawler(I2PSession session, Destination from, def ping) {
log.info "responding to crawler ping"
def neighbors = manager.getConnections().collect { c -> c.endpoint.destination.toBase64() }
Collections.shuffle(neighbors)
if (neighbors.size() > CRAWLER_RETURN)
neighbors = neighbors[0..CRAWLER_RETURN - 1]
def neighbors = manager.getConnections().collect { c -> c.endpoint.destination.toBase64() }
Collections.shuffle(neighbors)
if (neighbors.size() > CRAWLER_RETURN)
neighbors = neighbors[0..CRAWLER_RETURN - 1]
def upManager = (UltrapeerConnectionManager) manager;
def pong = [:]
pong.peers = neighbors
pong.uuid = ping.uuid
pong.type = "CrawlerPong"
pong.version = 1
pong.leafSlots = upManager.hasLeafSlots()
pong.peerSlots = upManager.hasPeerSlots()
pong = JsonOutput.toJson(pong)
def upManager = (UltrapeerConnectionManager) manager;
def pong = [:]
pong.peers = neighbors
pong.uuid = ping.uuid
pong.type = "CrawlerPong"
pong.version = 1
pong.leafSlots = upManager.hasLeafSlots()
pong.peerSlots = upManager.hasPeerSlots()
pong = JsonOutput.toJson(pong)
def maker = new I2PDatagramMaker(session)
pong = maker.makeI2PDatagram(pong.bytes)
session.sendMessage(from, pong, I2PSession.PROTO_DATAGRAM, 0, 0)
}
def maker = new I2PDatagramMaker(session)
pong = maker.makeI2PDatagram(pong.bytes)
session.sendMessage(from, pong, I2PSession.PROTO_DATAGRAM, 0, 0)
}
}

View File

@@ -4,20 +4,21 @@ import net.i2p.data.Destination
class CacheServers {
private static final int TO_GIVE = 3
private static Set<Destination> CACHES = [
new Destination("Wddh2E6FyyXBF7SvUYHKdN-vjf3~N6uqQWNeBDTM0P33YjiQCOsyedrjmDZmWFrXUJfJLWnCb5bnKezfk4uDaMyj~uvDG~yvLVcFgcPWSUd7BfGgym-zqcG1q1DcM8vfun-US7YamBlmtC6MZ2j-~Igqzmgshita8aLPCfNAA6S6e2UMjjtG7QIXlxpMec75dkHdJlVWbzrk9z8Qgru3YIk0UztYgEwDNBbm9wInsbHhr3HtAfa02QcgRVqRN2PnQXuqUJs7R7~09FZPEviiIcUpkY3FeyLlX1sgQFBeGeA96blaPvZNGd6KnNdgfLgMebx5SSxC-N4KZMSMBz5cgonQF3~m2HHFRSI85zqZNG5X9bJN85t80ltiv1W1es8ZnQW4es11r7MrvJNXz5bmSH641yJIvS6qI8OJJNpFVBIQSXLD-96TayrLQPaYw~uNZ-eXaE6G5dYhiuN8xHsFI1QkdaUaVZnvDGfsRbpS5GtpUbBDbyLkdPurG0i7dN1wAAAA")
]
private static final int TO_GIVE = 3
private static Set<Destination> CACHES = [
new Destination("Wddh2E6FyyXBF7SvUYHKdN-vjf3~N6uqQWNeBDTM0P33YjiQCOsyedrjmDZmWFrXUJfJLWnCb5bnKezfk4uDaMyj~uvDG~yvLVcFgcPWSUd7BfGgym-zqcG1q1DcM8vfun-US7YamBlmtC6MZ2j-~Igqzmgshita8aLPCfNAA6S6e2UMjjtG7QIXlxpMec75dkHdJlVWbzrk9z8Qgru3YIk0UztYgEwDNBbm9wInsbHhr3HtAfa02QcgRVqRN2PnQXuqUJs7R7~09FZPEviiIcUpkY3FeyLlX1sgQFBeGeA96blaPvZNGd6KnNdgfLgMebx5SSxC-N4KZMSMBz5cgonQF3~m2HHFRSI85zqZNG5X9bJN85t80ltiv1W1es8ZnQW4es11r7MrvJNXz5bmSH641yJIvS6qI8OJJNpFVBIQSXLD-96TayrLQPaYw~uNZ-eXaE6G5dYhiuN8xHsFI1QkdaUaVZnvDGfsRbpS5GtpUbBDbyLkdPurG0i7dN1wAAAA"),
new Destination("JC63wJNOqSJmymkj4~UJWywBTvDGikKMoYP0HX2Wz9c5l3otXSkwnxWAFL4cKr~Ygh3BNNi2t93vuLIiI1W8AsE42kR~PwRx~Y-WvIHXR6KUejRmOp-n8WidtjKg9k4aDy428uSOedqXDxys5mpoeQXwDsv1CoPTTwnmb1GWFy~oTGIsCguCl~aJWGnqiKarPO3GJQ~ev-NbvAQzUfC3HeP1e6pdI5CGGjExahTCID5UjpJw8GaDXWlGmYWWH303Xu4x-vAHQy1dJLsOBCn8dZravsn5BKJk~j0POUon45CCx-~NYtaPe0Itt9cMdD2ciC76Rep1D0X0sm1SjlSs8sZ52KmF3oaLZ6OzgI9QLMIyBUrfi41sK5I0qTuUVBAkvW1xr~L-20dYJ9TrbOaOb2-vDIfKaxVi6xQOuhgQDiSBhd3qv2m0xGu-BM9DQYfNA0FdMjnZmqjmji9RMavzQSsVFIbQGLbrLepiEFlb7TseCK5UtRp8TxnG7L4gbYevBQAEAAcAAA==")
]
static List<Destination> getCacheServers() {
List<Destination> allCaches = new ArrayList<>(CACHES)
Collections.shuffle(allCaches)
if (allCaches.size() <= TO_GIVE)
return allCaches
allCaches[0..TO_GIVE-1]
}
static List<Destination> getCacheServers() {
List<Destination> allCaches = new ArrayList<>(CACHES)
Collections.shuffle(allCaches)
if (allCaches.size() <= TO_GIVE)
return allCaches
allCaches[0..TO_GIVE-1]
}
static boolean isRegistered(Destination d) {
return CACHES.contains(d)
}
static boolean isRegistered(Destination d) {
return CACHES.contains(d)
}
}

View File

@@ -4,37 +4,37 @@ import net.i2p.data.Destination
class Host {
private static final int MAX_FAILURES = 3
private static final int MAX_FAILURES = 3
final Destination destination
final Destination destination
private final int clearInterval
int failures,successes
int failures,successes
long lastAttempt
public Host(Destination destination, int clearInterval) {
this.destination = destination
public Host(Destination destination, int clearInterval) {
this.destination = destination
this.clearInterval = clearInterval
}
}
synchronized void onConnect() {
failures = 0
successes++
synchronized void onConnect() {
failures = 0
successes++
lastAttempt = System.currentTimeMillis()
}
}
synchronized void onFailure() {
failures++
successes = 0
synchronized void onFailure() {
failures++
successes = 0
lastAttempt = System.currentTimeMillis()
}
}
synchronized boolean isFailed() {
failures >= MAX_FAILURES
}
synchronized boolean isFailed() {
failures >= MAX_FAILURES
}
synchronized boolean hasSucceeded() {
successes > 0
}
synchronized boolean hasSucceeded() {
successes > 0
}
synchronized void clearFailures() {
failures = 0

View File

@@ -15,141 +15,141 @@ import net.i2p.data.Destination
class HostCache extends Service {
final TrustService trustService
final File storage
final int interval
final Timer timer
final MuWireSettings settings
final Destination myself
final Map<Destination, Host> hosts = new ConcurrentHashMap<>()
final TrustService trustService
final File storage
final int interval
final Timer timer
final MuWireSettings settings
final Destination myself
final Map<Destination, Host> hosts = new ConcurrentHashMap<>()
HostCache(){}
HostCache(){}
public HostCache(TrustService trustService, File storage, int interval,
MuWireSettings settings, Destination myself) {
this.trustService = trustService
this.storage = storage
this.interval = interval
this.settings = settings
this.myself = myself
this.timer = new Timer("host-persister",true)
}
public HostCache(TrustService trustService, File storage, int interval,
MuWireSettings settings, Destination myself) {
this.trustService = trustService
this.storage = storage
this.interval = interval
this.settings = settings
this.myself = myself
this.timer = new Timer("host-persister",true)
}
void start() {
timer.schedule({load()} as TimerTask, 1)
}
void start() {
timer.schedule({load()} as TimerTask, 1)
}
void stop() {
timer.cancel()
}
void stop() {
timer.cancel()
}
void onHostDiscoveredEvent(HostDiscoveredEvent e) {
if (myself == e.destination)
return
if (hosts.containsKey(e.destination)) {
void onHostDiscoveredEvent(HostDiscoveredEvent e) {
if (myself == e.destination)
return
if (hosts.containsKey(e.destination)) {
if (!e.fromHostcache)
return
hosts.get(e.destination).clearFailures()
return
}
Host host = new Host(e.destination, settings.hostClearInterval)
if (allowHost(host)) {
hosts.put(e.destination, host)
}
}
}
Host host = new Host(e.destination, settings.hostClearInterval)
if (allowHost(host)) {
hosts.put(e.destination, host)
}
}
void onConnectionEvent(ConnectionEvent e) {
if (e.leaf)
return
Destination dest = e.endpoint.destination
Host host = hosts.get(dest)
if (host == null) {
host = new Host(dest, settings.hostClearInterval)
hosts.put(dest, host)
}
void onConnectionEvent(ConnectionEvent e) {
if (e.leaf)
return
Destination dest = e.endpoint.destination
Host host = hosts.get(dest)
if (host == null) {
host = new Host(dest, settings.hostClearInterval)
hosts.put(dest, host)
}
switch(e.status) {
case ConnectionAttemptStatus.SUCCESSFUL:
case ConnectionAttemptStatus.REJECTED:
host.onConnect()
break
case ConnectionAttemptStatus.FAILED:
host.onFailure()
break
}
}
switch(e.status) {
case ConnectionAttemptStatus.SUCCESSFUL:
case ConnectionAttemptStatus.REJECTED:
host.onConnect()
break
case ConnectionAttemptStatus.FAILED:
host.onFailure()
break
}
}
List<Destination> getHosts(int n) {
List<Destination> rv = new ArrayList<>(hosts.keySet())
rv.retainAll {allowHost(hosts[it])}
if (rv.size() <= n)
return rv
Collections.shuffle(rv)
rv[0..n-1]
}
List<Destination> getHosts(int n) {
List<Destination> rv = new ArrayList<>(hosts.keySet())
rv.retainAll {allowHost(hosts[it])}
if (rv.size() <= n)
return rv
Collections.shuffle(rv)
rv[0..n-1]
}
List<Destination> getGoodHosts(int n) {
List<Destination> rv = new ArrayList<>(hosts.keySet())
rv.retainAll {
Host host = hosts[it]
allowHost(host) && host.hasSucceeded()
}
if (rv.size() <= n)
return rv
Collections.shuffle(rv)
rv[0..n-1]
}
List<Destination> getGoodHosts(int n) {
List<Destination> rv = new ArrayList<>(hosts.keySet())
rv.retainAll {
Host host = hosts[it]
allowHost(host) && host.hasSucceeded()
}
if (rv.size() <= n)
return rv
Collections.shuffle(rv)
rv[0..n-1]
}
void load() {
if (storage.exists()) {
JsonSlurper slurper = new JsonSlurper()
storage.eachLine {
def entry = slurper.parseText(it)
Destination dest = new Destination(entry.destination)
Host host = new Host(dest, settings.hostClearInterval)
host.failures = Integer.valueOf(String.valueOf(entry.failures))
host.successes = Integer.valueOf(String.valueOf(entry.successes))
void load() {
if (storage.exists()) {
JsonSlurper slurper = new JsonSlurper()
storage.eachLine {
def entry = slurper.parseText(it)
Destination dest = new Destination(entry.destination)
Host host = new Host(dest, settings.hostClearInterval)
host.failures = Integer.valueOf(String.valueOf(entry.failures))
host.successes = Integer.valueOf(String.valueOf(entry.successes))
if (entry.lastAttempt != null)
host.lastAttempt = entry.lastAttempt
if (allowHost(host))
hosts.put(dest, host)
}
}
timer.schedule({save()} as TimerTask, interval, interval)
loaded = true
}
if (allowHost(host))
hosts.put(dest, host)
}
}
timer.schedule({save()} as TimerTask, interval, interval)
loaded = true
}
private boolean allowHost(Host host) {
if (host.isFailed() && !host.canTryAgain())
return false
if (host.destination == myself)
return false
TrustLevel trust = trustService.getLevel(host.destination)
switch(trust) {
case TrustLevel.DISTRUSTED :
return false
case TrustLevel.TRUSTED :
return true
case TrustLevel.NEUTRAL :
return settings.allowUntrusted()
}
false
}
private boolean allowHost(Host host) {
if (host.isFailed() && !host.canTryAgain())
return false
if (host.destination == myself)
return false
TrustLevel trust = trustService.getLevel(host.destination)
switch(trust) {
case TrustLevel.DISTRUSTED :
return false
case TrustLevel.TRUSTED :
return true
case TrustLevel.NEUTRAL :
return settings.allowUntrusted()
}
false
}
private void save() {
storage.delete()
storage.withPrintWriter { writer ->
hosts.each { dest, host ->
if (allowHost(host)) {
def map = [:]
map.destination = dest.toBase64()
map.failures = host.failures
map.successes = host.successes
private void save() {
storage.delete()
storage.withPrintWriter { writer ->
hosts.each { dest, host ->
if (allowHost(host)) {
def map = [:]
map.destination = dest.toBase64()
map.failures = host.failures
map.successes = host.successes
map.lastAttempt = host.lastAttempt
def json = JsonOutput.toJson(map)
writer.println json
}
}
}
}
def json = JsonOutput.toJson(map)
writer.println json
}
}
}
}
}

View File

@@ -6,11 +6,11 @@ import net.i2p.data.Destination
class HostDiscoveredEvent extends Event {
Destination destination
Destination destination
boolean fromHostcache
@Override
public String toString() {
"HostDiscoveredEvent ${super.toString()} destination:${destination.toBase32()} from hostcache $fromHostcache"
}
@Override
public String toString() {
"HostDiscoveredEvent ${super.toString()} destination:${destination.toBase32()} from hostcache $fromHostcache"
}
}

View File

@@ -6,11 +6,11 @@ import net.i2p.data.Base32
import net.i2p.data.Destination
class DeleteEvent extends Event {
byte [] infoHash
Destination leaf
byte [] infoHash
Destination leaf
@Override
public String toString() {
"DeleteEvent ${super.toString()} infoHash:${Base32.encode(infoHash)} leaf:${leaf.toBase32()}"
}
@Override
public String toString() {
"DeleteEvent ${super.toString()} infoHash:${Base32.encode(infoHash)} leaf:${leaf.toBase32()}"
}
}

View File

@@ -7,32 +7,32 @@ import net.i2p.data.Destination
class LeafSearcher {
final UltrapeerConnectionManager connectionManager
final SearchIndex searchIndex = new SearchIndex()
final UltrapeerConnectionManager connectionManager
final SearchIndex searchIndex = new SearchIndex()
final Map<String, Set<byte[]>> fileNameToHashes = new HashMap<>()
final Map<byte[], Set<Destination>> hashToLeafs = new HashMap<>()
final Map<String, Set<byte[]>> fileNameToHashes = new HashMap<>()
final Map<byte[], Set<Destination>> hashToLeafs = new HashMap<>()
final Map<Destination, Map<byte[], Set<String>>> leafToFiles = new HashMap<>()
final Map<Destination, Map<byte[], Set<String>>> leafToFiles = new HashMap<>()
LeafSearcher(UltrapeerConnectionManager connectionManager) {
this.connectionManager = connectionManager
}
LeafSearcher(UltrapeerConnectionManager connectionManager) {
this.connectionManager = connectionManager
}
void onUpsertEvent(UpsertEvent e) {
// TODO: implement
}
void onUpsertEvent(UpsertEvent e) {
// TODO: implement
}
void onDeleteEvent(DeleteEvent e) {
// TODO: implement
}
void onDeleteEvent(DeleteEvent e) {
// TODO: implement
}
void onDisconnectionEvent(DisconnectionEvent e) {
// TODO: implement
}
void onDisconnectionEvent(DisconnectionEvent e) {
// TODO: implement
}
void onQueryEvent(QueryEvent e) {
// TODO: implement
}
void onQueryEvent(QueryEvent e) {
// TODO: implement
}
}

View File

@@ -8,10 +8,10 @@ import net.i2p.data.Destination
class QueryEvent extends Event {
SearchEvent searchEvent
boolean firstHop
Destination replyTo
boolean firstHop
Destination replyTo
Persona originator
Destination receivedOn
Destination receivedOn
String toString() {
"searchEvent: $searchEvent firstHop:$firstHop, replyTo:${replyTo.toBase32()}" +

View File

@@ -6,6 +6,6 @@ import com.muwire.core.SharedFile
class ResultsEvent extends Event {
SearchEvent searchEvent
SharedFile[] results
UUID uuid
SharedFile[] results
UUID uuid
}

View File

@@ -5,9 +5,9 @@ import com.muwire.core.InfoHash
class SearchEvent extends Event {
List<String> searchTerms
byte [] searchHash
UUID uuid
List<String> searchTerms
byte [] searchHash
UUID uuid
boolean oobInfohash
String toString() {

View File

@@ -4,56 +4,56 @@ import com.muwire.core.Constants
class SearchIndex {
final Map<String, Set<String>> keywords = new HashMap<>()
final Map<String, Set<String>> keywords = new HashMap<>()
void add(String string) {
String [] split = split(string)
split.each {
Set<String> existing = keywords.get(it)
if (existing == null) {
existing = new HashSet<>()
keywords.put(it, existing)
}
existing.add(string)
}
}
void add(String string) {
String [] split = split(string)
split.each {
Set<String> existing = keywords.get(it)
if (existing == null) {
existing = new HashSet<>()
keywords.put(it, existing)
}
existing.add(string)
}
}
void remove(String string) {
String [] split = split(string)
split.each {
Set<String> existing = keywords.get it
if (existing != null) {
existing.remove(string)
if (existing.isEmpty()) {
keywords.remove(it)
}
}
}
}
void remove(String string) {
String [] split = split(string)
split.each {
Set<String> existing = keywords.get it
if (existing != null) {
existing.remove(string)
if (existing.isEmpty()) {
keywords.remove(it)
}
}
}
}
private static String[] split(String source) {
source = source.replaceAll(Constants.SPLIT_PATTERN, " ").toLowerCase()
String [] split = source.split(" ")
private static String[] split(String source) {
source = source.replaceAll(Constants.SPLIT_PATTERN, " ").toLowerCase()
String [] split = source.split(" ")
def rv = []
split.each { if (it.length() > 0) rv << it }
rv.toArray(new String[0])
}
}
String[] search(List<String> terms) {
Set<String> rv = null;
String[] search(List<String> terms) {
Set<String> rv = null;
terms.each {
Set<String> forWord = keywords.getOrDefault(it,[])
if (rv == null) {
rv = new HashSet<>(forWord)
} else {
rv.retainAll(forWord)
}
terms.each {
Set<String> forWord = keywords.getOrDefault(it,[])
if (rv == null) {
rv = new HashSet<>(forWord)
} else {
rv.retainAll(forWord)
}
}
}
if (rv != null)
return rv.asList()
[]
}
if (rv != null)
return rv.asList()
[]
}
}

View File

@@ -7,12 +7,12 @@ import net.i2p.data.Destination
class UpsertEvent extends Event {
Set<String> names
byte [] infoHash
Destination leaf
Set<String> names
byte [] infoHash
Destination leaf
@Override
public String toString() {
"UpsertEvent ${super.toString()} names:$names infoHash:${Base32.encode(infoHash)} leaf:${leaf.toBase32()}"
}
@Override
public String toString() {
"UpsertEvent ${super.toString()} names:$names infoHash:${Base32.encode(infoHash)} leaf:${leaf.toBase32()}"
}
}

View File

@@ -7,7 +7,7 @@ import com.muwire.core.Persona
import net.i2p.util.ConcurrentHashSet
class RemoteTrustList {
public enum Status { NEW, UPDATING, UPDATED }
public enum Status { NEW, UPDATING, UPDATED, UPDATE_FAILED }
private final Persona persona
private final Set<Persona> good, bad

View File

@@ -5,6 +5,6 @@ import com.muwire.core.Persona
class TrustEvent extends Event {
Persona persona
TrustLevel level
Persona persona
TrustLevel level
}

View File

@@ -11,87 +11,87 @@ import net.i2p.util.ConcurrentHashSet
class TrustService extends Service {
final File persistGood, persistBad
final long persistInterval
final File persistGood, persistBad
final long persistInterval
final Map<Destination, Persona> good = new ConcurrentHashMap<>()
final Map<Destination, Persona> bad = new ConcurrentHashMap<>()
final Map<Destination, Persona> good = new ConcurrentHashMap<>()
final Map<Destination, Persona> bad = new ConcurrentHashMap<>()
final Timer timer
final Timer timer
TrustService() {}
TrustService() {}
TrustService(File persistGood, File persistBad, long persistInterval) {
this.persistBad = persistBad
this.persistGood = persistGood
this.persistInterval = persistInterval
this.timer = new Timer("trust-persister",true)
}
TrustService(File persistGood, File persistBad, long persistInterval) {
this.persistBad = persistBad
this.persistGood = persistGood
this.persistInterval = persistInterval
this.timer = new Timer("trust-persister",true)
}
void start() {
timer.schedule({load()} as TimerTask, 1)
}
void start() {
timer.schedule({load()} as TimerTask, 1)
}
void stop() {
timer.cancel()
}
void stop() {
timer.cancel()
}
void load() {
if (persistGood.exists()) {
persistGood.eachLine {
void load() {
if (persistGood.exists()) {
persistGood.eachLine {
byte [] decoded = Base64.decode(it)
Persona persona = new Persona(new ByteArrayInputStream(decoded))
good.put(persona.destination, persona)
}
}
if (persistBad.exists()) {
persistBad.eachLine {
good.put(persona.destination, persona)
}
}
if (persistBad.exists()) {
persistBad.eachLine {
byte [] decoded = Base64.decode(it)
Persona persona = new Persona(new ByteArrayInputStream(decoded))
bad.put(persona.destination, persona)
}
}
timer.schedule({persist()} as TimerTask, persistInterval, persistInterval)
loaded = true
}
}
}
timer.schedule({persist()} as TimerTask, persistInterval, persistInterval)
loaded = true
}
private void persist() {
persistGood.delete()
persistGood.withPrintWriter { writer ->
good.each {k,v ->
writer.println v.toBase64()
}
}
persistBad.delete()
persistBad.withPrintWriter { writer ->
bad.each { k,v ->
writer.println v.toBase64()
}
}
}
private void persist() {
persistGood.delete()
persistGood.withPrintWriter { writer ->
good.each {k,v ->
writer.println v.toBase64()
}
}
persistBad.delete()
persistBad.withPrintWriter { writer ->
bad.each { k,v ->
writer.println v.toBase64()
}
}
}
TrustLevel getLevel(Destination dest) {
if (good.containsKey(dest))
return TrustLevel.TRUSTED
else if (bad.containsKey(dest))
return TrustLevel.DISTRUSTED
TrustLevel.NEUTRAL
}
TrustLevel getLevel(Destination dest) {
if (good.containsKey(dest))
return TrustLevel.TRUSTED
else if (bad.containsKey(dest))
return TrustLevel.DISTRUSTED
TrustLevel.NEUTRAL
}
void onTrustEvent(TrustEvent e) {
switch(e.level) {
case TrustLevel.TRUSTED:
bad.remove(e.persona.destination)
good.put(e.persona.destination, e.persona)
break
case TrustLevel.DISTRUSTED:
good.remove(e.persona.destination)
bad.put(e.persona.destination, e.persona)
break
case TrustLevel.NEUTRAL:
good.remove(e.persona.destination)
bad.remove(e.persona.destination)
break
}
}
void onTrustEvent(TrustEvent e) {
switch(e.level) {
case TrustLevel.TRUSTED:
bad.remove(e.persona.destination)
good.put(e.persona.destination, e.persona)
break
case TrustLevel.DISTRUSTED:
good.remove(e.persona.destination)
bad.put(e.persona.destination, e.persona)
break
case TrustLevel.NEUTRAL:
good.remove(e.persona.destination)
bad.remove(e.persona.destination)
break
}
}
}

View File

@@ -94,13 +94,15 @@ class TrustSubscriber {
public void run() {
trustList.status = RemoteTrustList.Status.UPDATING
eventBus.publish(new TrustSubscriptionUpdatedEvent(trustList : trustList))
check(trustList, System.currentTimeMillis())
trustList.status = RemoteTrustList.Status.UPDATED
if (check(trustList, System.currentTimeMillis()))
trustList.status = RemoteTrustList.Status.UPDATED
else
trustList.status = RemoteTrustList.Status.UPDATE_FAILED
eventBus.publish(new TrustSubscriptionUpdatedEvent(trustList : trustList))
}
}
private void check(RemoteTrustList trustList, long now) {
private boolean check(RemoteTrustList trustList, long now) {
log.info("fetching trust list from ${trustList.persona.getHumanReadableName()}")
Endpoint endpoint = null
try {
@@ -118,7 +120,7 @@ class TrustSubscriber {
if (code != 200) {
log.info("couldn't fetch trust list, code $code")
return
return false
}
// swallow any headers
@@ -147,8 +149,10 @@ class TrustSubscriber {
trustList.bad.clear()
trustList.bad.addAll(bad)
return true
} catch (Exception e) {
log.log(Level.WARNING,"exception fetching trust list from ${trustList.persona.getHumanReadableName()}",e)
return false
} finally {
endpoint?.close()
}

View File

@@ -83,7 +83,7 @@ class ContentUploader extends Uploader {
String xHave = DataUtil.encodeXHave(mesh.pieces.getDownloaded(), mesh.pieces.nPieces)
endpoint.getOutputStream().write("X-Have: $xHave\r\n".getBytes(StandardCharsets.US_ASCII))
Set<Persona> sources = mesh.getRandom(3, toExclude)
Set<Persona> sources = mesh.getRandom(9, toExclude)
if (!sources.isEmpty()) {
String xAlts = sources.stream().map({ it.toBase64() }).collect(Collectors.joining(","))
endpoint.getOutputStream().write("X-Alt: $xAlts\r\n".getBytes(StandardCharsets.US_ASCII))
@@ -119,4 +119,8 @@ class ContentUploader extends Uploader {
return mesh.pieces.nPieces;
}
@Override
public long getTotalSize() {
return file.length();
}
}

View File

@@ -61,5 +61,8 @@ class HashListUploader extends Uploader {
return 1;
}
@Override
public long getTotalSize() {
return -1;
}
}

View File

@@ -35,5 +35,7 @@ abstract class Uploader {
abstract int getDonePieces();
abstract int getTotalPieces()
abstract int getTotalPieces();
abstract long getTotalSize();
}

View File

@@ -11,40 +11,40 @@ import net.i2p.data.Base64
class DataUtil {
private final static int MAX_SHORT = (0x1 << 16) - 1
private final static int MAX_SHORT = (0x1 << 16) - 1
static void writeUnsignedShort(int value, OutputStream os) {
if (value > MAX_SHORT || value < 0)
throw new IllegalArgumentException("$value invalid")
static void writeUnsignedShort(int value, OutputStream os) {
if (value > MAX_SHORT || value < 0)
throw new IllegalArgumentException("$value invalid")
byte lsb = (byte) (value & 0xFF)
byte msb = (byte) (value >> 8)
byte lsb = (byte) (value & 0xFF)
byte msb = (byte) (value >> 8)
os.write(msb)
os.write(lsb)
}
os.write(msb)
os.write(lsb)
}
private final static int MAX_HEADER = 0x7FFFFF
private final static int MAX_HEADER = 0x7FFFFF
static void packHeader(int length, byte [] header) {
if (header.length != 3)
throw new IllegalArgumentException("header length $header.length")
if (length < 0 || length > MAX_HEADER)
throw new IllegalArgumentException("length $length")
static void packHeader(int length, byte [] header) {
if (header.length != 3)
throw new IllegalArgumentException("header length $header.length")
if (length < 0 || length > MAX_HEADER)
throw new IllegalArgumentException("length $length")
header[2] = (byte) (length & 0xFF)
header[1] = (byte) ((length >> 8) & 0xFF)
header[0] = (byte) ((length >> 16) & 0x7F)
}
header[2] = (byte) (length & 0xFF)
header[1] = (byte) ((length >> 8) & 0xFF)
header[0] = (byte) ((length >> 16) & 0x7F)
}
static int readLength(byte [] header) {
if (header.length != 3)
throw new IllegalArgumentException("header length $header.length")
static int readLength(byte [] header) {
if (header.length != 3)
throw new IllegalArgumentException("header length $header.length")
return (((int)(header[0] & 0x7F)) << 16) |
(((int)(header[1] & 0xFF) << 8)) |
((int)header[2] & 0xFF)
}
return (((int)(header[0] & 0x7F)) << 16) |
(((int)(header[1] & 0xFF) << 8)) |
((int)header[2] & 0xFF)
}
static String readi18nString(byte [] encoded) {
if (encoded.length < 2)

View File

@@ -8,16 +8,16 @@ import net.i2p.data.Destination;
public class DownloadedFile extends SharedFile {
private final Set<Destination> sources;
private final Set<Destination> sources;
public DownloadedFile(File file, InfoHash infoHash, int pieceSize, Set<Destination> sources)
throws IOException {
super(file, infoHash, pieceSize);
this.sources = sources;
}
public DownloadedFile(File file, InfoHash infoHash, int pieceSize, Set<Destination> sources)
throws IOException {
super(file, infoHash, pieceSize);
this.sources = sources;
}
public Set<Destination> getSources() {
return sources;
}
public Set<Destination> getSources() {
return sources;
}
}

View File

@@ -11,83 +11,83 @@ import net.i2p.data.Base64;
public class InfoHash {
public static final int SIZE = 0x1 << 5;
public static final int SIZE = 0x1 << 5;
private final byte[] root;
private final byte[] hashList;
private final byte[] root;
private final byte[] hashList;
private final int hashCode;
private final int hashCode;
public InfoHash(byte[] root, byte[] hashList) {
if (root.length != SIZE)
throw new IllegalArgumentException("invalid root size "+root.length);
if (hashList != null && hashList.length % SIZE != 0)
throw new IllegalArgumentException("invalid hashList size " + hashList.length);
this.root = root;
this.hashList = hashList;
hashCode = root[0] << 24 |
root[1] << 16 |
root[2] << 8 |
root[3];
}
public InfoHash(byte[] root, byte[] hashList) {
if (root.length != SIZE)
throw new IllegalArgumentException("invalid root size "+root.length);
if (hashList != null && hashList.length % SIZE != 0)
throw new IllegalArgumentException("invalid hashList size " + hashList.length);
this.root = root;
this.hashList = hashList;
hashCode = root[0] << 24 |
root[1] << 16 |
root[2] << 8 |
root[3];
}
public InfoHash(byte[] root) {
this(root, null);
}
public InfoHash(byte[] root) {
this(root, null);
}
public InfoHash(String base32) {
this(Base32.decode(base32));
}
public InfoHash(String base32) {
this(Base32.decode(base32));
}
public static InfoHash fromHashList(byte []hashList) {
try {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] root = sha256.digest(hashList);
return new InfoHash(root, hashList);
} catch (NoSuchAlgorithmException impossible) {
impossible.printStackTrace();
System.exit(1);
}
return null;
}
public static InfoHash fromHashList(byte []hashList) {
try {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] root = sha256.digest(hashList);
return new InfoHash(root, hashList);
} catch (NoSuchAlgorithmException impossible) {
impossible.printStackTrace();
System.exit(1);
}
return null;
}
public byte[] getRoot() {
return root;
}
public byte[] getRoot() {
return root;
}
public byte[] getHashList() {
return hashList;
}
public byte[] getHashList() {
return hashList;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof InfoHash)) {
return false;
}
InfoHash other = (InfoHash) o;
return Arrays.equals(root, other.root);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof InfoHash)) {
return false;
}
InfoHash other = (InfoHash) o;
return Arrays.equals(root, other.root);
}
public String toString() {
String rv = "InfoHash[root:"+Base64.encode(root) + " hashList:";
List<String> b64HashList = new ArrayList<>();
if (hashList != null) {
byte [] tmp = new byte[SIZE];
for (int i = 0; i < hashList.length / SIZE; i++) {
System.arraycopy(hashList, SIZE * i, tmp, 0, SIZE);
b64HashList.add(Base64.encode(tmp));
}
}
rv += b64HashList.toString();
rv += "]";
return rv;
}
public String toString() {
String rv = "InfoHash[root:"+Base64.encode(root) + " hashList:";
List<String> b64HashList = new ArrayList<>();
if (hashList != null) {
byte [] tmp = new byte[SIZE];
for (int i = 0; i < hashList.length / SIZE; i++) {
System.arraycopy(hashList, SIZE * i, tmp, 0, SIZE);
b64HashList.add(Base64.encode(tmp));
}
}
rv += b64HashList.toString();
rv += "]";
return rv;
}
}

View File

@@ -5,60 +5,60 @@ import java.io.IOException;
public class SharedFile {
private final File file;
private final InfoHash infoHash;
private final int pieceSize;
private final File file;
private final InfoHash infoHash;
private final int pieceSize;
private final String cachedPath;
private final long cachedLength;
private final String cachedPath;
private final long cachedLength;
public SharedFile(File file, InfoHash infoHash, int pieceSize) throws IOException {
this.file = file;
this.infoHash = infoHash;
this.pieceSize = pieceSize;
this.cachedPath = file.getAbsolutePath();
this.cachedLength = file.length();
}
public SharedFile(File file, InfoHash infoHash, int pieceSize) throws IOException {
this.file = file;
this.infoHash = infoHash;
this.pieceSize = pieceSize;
this.cachedPath = file.getAbsolutePath();
this.cachedLength = file.length();
}
public File getFile() {
return file;
}
public File getFile() {
return file;
}
public InfoHash getInfoHash() {
return infoHash;
}
public InfoHash getInfoHash() {
return infoHash;
}
public int getPieceSize() {
return pieceSize;
}
public int getPieceSize() {
return pieceSize;
}
public int getNPieces() {
long length = file.length();
int rawPieceSize = 0x1 << pieceSize;
int rv = (int) (length / rawPieceSize);
if (length % rawPieceSize != 0)
rv++;
return rv;
}
public int getNPieces() {
long length = file.length();
int rawPieceSize = 0x1 << pieceSize;
int rv = (int) (length / rawPieceSize);
if (length % rawPieceSize != 0)
rv++;
return rv;
}
public String getCachedPath() {
return cachedPath;
}
public String getCachedPath() {
return cachedPath;
}
public long getCachedLength() {
return cachedLength;
}
public long getCachedLength() {
return cachedLength;
}
@Override
public int hashCode() {
return file.hashCode() ^ infoHash.hashCode();
}
@Override
public int hashCode() {
return file.hashCode() ^ infoHash.hashCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof SharedFile))
return false;
SharedFile other = (SharedFile)o;
return file.equals(other.file) && infoHash.equals(other.infoHash);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof SharedFile))
return false;
SharedFile other = (SharedFile)o;
return file.equals(other.file) && infoHash.equals(other.infoHash);
}
}

View File

@@ -1,5 +1,5 @@
package com.muwire.core.connection;
public enum ConnectionAttemptStatus {
SUCCESSFUL, REJECTED, FAILED
SUCCESSFUL, REJECTED, FAILED
}

View File

@@ -6,5 +6,5 @@ package com.muwire.core.hostcache;
*
*/
public enum CrawlerResponse {
ALL, REGISTERED, NONE
ALL, REGISTERED, NONE
}

View File

@@ -1,5 +1,5 @@
package com.muwire.core.trust;
public enum TrustLevel {
TRUSTED, NEUTRAL, DISTRUSTED
TRUSTED, NEUTRAL, DISTRUSTED
}

View File

@@ -4,6 +4,6 @@ import net.i2p.data.Destination
class Destinations {
Destination dest1 = new Destination("KvwWPKMSAtzf7Yruj8TQaHi2jaQpSNsXJskbpmSBTxkcYlDB2GllH~QBu-cs4FSYdaRmKDUUx7793jjnYJgTMbrjqeIL5-BTORZ09n6PUfhSejDpJjdkUxaV1OHRatfYs70RNBv7rvdj1-nXUow5tMfOJtoWVocUoKefUGFQFbJLDDkBqjm1kFyKFZv6m6S6YqXxBgVB1qYicooy67cNQF5HLUFtP15pk5fMDNGz5eNCjPfC~2Gp8FF~OpSy92HT0XN7uAMJykPcbdnWfcvVwqD7eS0K4XEnsqnMPLEiMAhqsugEFiFqtB3Wmm7UHVc03lcAfRhr1e2uZBNFTtM2Uol4MD5sCCKRZVHGcH-WGPSEz0BM5YO~Xi~dQ~N3NVud32PVzhh8xoGcAlhTqMqAbRJndCv-H6NflX90pYmbirCTIDOaR9758mThrqX0d4CwCn4jFXer52l8Qe8CErGoLuB-4LL~Gwrn7R1k7ZQc2PthkqeW8MfigyiN7hZVkul9AAAA")
Destination dest2 = new Destination("KvwWPKMSAtzf7Yruj8TQaHi2jaQpSNsXJskbpmSBTxkcYlDB2GllH~QBu-cs4FSYdaRmKDUUx7793jjnYJgTMbrjqeIL5-BTORZ09n6PUfhSejDpJjdkUxaV1OHRatfYs70RNBv7rvdj1-nXUow5tMfOJtoWVocUoKefUGFQFbJLDDkBqjm1kFyKFZv6m6S6YqXxBgVB1qYicooy67cNQF5HLUFtP15pk5fMDNGz5eNCjPfC~2Gp8FF~OpSy92HT0XN7uAMJykPcbdnWfcvVwqD7eS0K4XEnsqnMPLEiMAhqsugEFiFqtB3Wmm7UHVc03lcAfRhr1e2uZBNFTtM2Uol4MD5sCCKRZVHGcH-WGPSEz0BM5YO~Xi~dQ~N3NVud32PVzhh8xoGcAlhTqMqAbRJndCv-H6NflX90pYmbirCTIDOaR9758mThrqX0d4CwCn4jFXer52l8Qe8CErGoLuB-4LL~Gwrn7R1k7ZQc2PthkqeW8MfigyiN7hZVkul8AAAA")
Destination dest1 = new Destination("KvwWPKMSAtzf7Yruj8TQaHi2jaQpSNsXJskbpmSBTxkcYlDB2GllH~QBu-cs4FSYdaRmKDUUx7793jjnYJgTMbrjqeIL5-BTORZ09n6PUfhSejDpJjdkUxaV1OHRatfYs70RNBv7rvdj1-nXUow5tMfOJtoWVocUoKefUGFQFbJLDDkBqjm1kFyKFZv6m6S6YqXxBgVB1qYicooy67cNQF5HLUFtP15pk5fMDNGz5eNCjPfC~2Gp8FF~OpSy92HT0XN7uAMJykPcbdnWfcvVwqD7eS0K4XEnsqnMPLEiMAhqsugEFiFqtB3Wmm7UHVc03lcAfRhr1e2uZBNFTtM2Uol4MD5sCCKRZVHGcH-WGPSEz0BM5YO~Xi~dQ~N3NVud32PVzhh8xoGcAlhTqMqAbRJndCv-H6NflX90pYmbirCTIDOaR9758mThrqX0d4CwCn4jFXer52l8Qe8CErGoLuB-4LL~Gwrn7R1k7ZQc2PthkqeW8MfigyiN7hZVkul9AAAA")
Destination dest2 = new Destination("KvwWPKMSAtzf7Yruj8TQaHi2jaQpSNsXJskbpmSBTxkcYlDB2GllH~QBu-cs4FSYdaRmKDUUx7793jjnYJgTMbrjqeIL5-BTORZ09n6PUfhSejDpJjdkUxaV1OHRatfYs70RNBv7rvdj1-nXUow5tMfOJtoWVocUoKefUGFQFbJLDDkBqjm1kFyKFZv6m6S6YqXxBgVB1qYicooy67cNQF5HLUFtP15pk5fMDNGz5eNCjPfC~2Gp8FF~OpSy92HT0XN7uAMJykPcbdnWfcvVwqD7eS0K4XEnsqnMPLEiMAhqsugEFiFqtB3Wmm7UHVc03lcAfRhr1e2uZBNFTtM2Uol4MD5sCCKRZVHGcH-WGPSEz0BM5YO~Xi~dQ~N3NVud32PVzhh8xoGcAlhTqMqAbRJndCv-H6NflX90pYmbirCTIDOaR9758mThrqX0d4CwCn4jFXer52l8Qe8CErGoLuB-4LL~Gwrn7R1k7ZQc2PthkqeW8MfigyiN7hZVkul8AAAA")
}

View File

@@ -4,23 +4,23 @@ import org.junit.Test
class EventBusTest {
class FakeEvent extends Event {}
class FakeEvent extends Event {}
class FakeEventHandler {
def onFakeEvent(FakeEvent e) {
assert e == fakeEvent
}
}
class FakeEventHandler {
def onFakeEvent(FakeEvent e) {
assert e == fakeEvent
}
}
FakeEvent fakeEvent = new FakeEvent()
FakeEvent fakeEvent = new FakeEvent()
EventBus bus = new EventBus()
def handler = new FakeEventHandler()
EventBus bus = new EventBus()
def handler = new FakeEventHandler()
@Test
void testDynamicEvent() {
bus.register(FakeEvent.class, handler)
bus.publish(fakeEvent)
}
@Test
void testDynamicEvent() {
bus.register(FakeEvent.class, handler)
bus.publish(fakeEvent)
}
}

View File

@@ -6,11 +6,11 @@ import org.junit.Test
class InfoHashTest {
@Test
void testEmpty() {
byte [] empty = new byte[0x1 << 6];
def ih = InfoHash.fromHashList(empty)
def ih2 = new InfoHash("6ws72qwrniqdaj4y55xngcmxtnbqapjdedm7b2hktay2sj2z7nfq");
assertEquals(ih, ih2);
}
@Test
void testEmpty() {
byte [] empty = new byte[0x1 << 6];
def ih = InfoHash.fromHashList(empty)
def ih2 = new InfoHash("6ws72qwrniqdaj4y55xngcmxtnbqapjdedm7b2hktay2sj2z7nfq");
assertEquals(ih, ih2);
}
}

View File

@@ -22,21 +22,21 @@ import groovy.mock.interceptor.MockFor
class ConnectionAcceptorTest {
EventBus eventBus
final Destinations destinations = new Destinations()
def settings
EventBus eventBus
final Destinations destinations = new Destinations()
def settings
def connectionManagerMock
UltrapeerConnectionManager connectionManager
def connectionManagerMock
UltrapeerConnectionManager connectionManager
def i2pAcceptorMock
I2PAcceptor i2pAcceptor
def i2pAcceptorMock
I2PAcceptor i2pAcceptor
def hostCacheMock
HostCache hostCache
def hostCacheMock
HostCache hostCache
def trustServiceMock
TrustService trustService
def trustServiceMock
TrustService trustService
def searchManagerMock
SearchManager searchManager
@@ -47,361 +47,361 @@ class ConnectionAcceptorTest {
def connectionEstablisherMock
ConnectionEstablisher connectionEstablisher
ConnectionAcceptor acceptor
List<ConnectionEvent> connectionEvents
InputStream inputStream
OutputStream outputStream
ConnectionAcceptor acceptor
List<ConnectionEvent> connectionEvents
InputStream inputStream
OutputStream outputStream
@Before
void before() {
connectionManagerMock = new MockFor(UltrapeerConnectionManager.class)
i2pAcceptorMock = new MockFor(I2PAcceptor.class)
hostCacheMock = new MockFor(HostCache.class)
trustServiceMock = new MockFor(TrustService.class)
@Before
void before() {
connectionManagerMock = new MockFor(UltrapeerConnectionManager.class)
i2pAcceptorMock = new MockFor(I2PAcceptor.class)
hostCacheMock = new MockFor(HostCache.class)
trustServiceMock = new MockFor(TrustService.class)
searchManagerMock = new MockFor(SearchManager.class)
uploadManagerMock = new MockFor(UploadManager.class)
connectionEstablisherMock = new MockFor(ConnectionEstablisher.class)
}
}
@After
void after() {
acceptor?.stop()
connectionManagerMock.verify connectionManager
i2pAcceptorMock.verify i2pAcceptor
hostCacheMock.verify hostCache
trustServiceMock.verify trustService
@After
void after() {
acceptor?.stop()
connectionManagerMock.verify connectionManager
i2pAcceptorMock.verify i2pAcceptor
hostCacheMock.verify hostCache
trustServiceMock.verify trustService
searchManagerMock.verify searchManager
uploadManagerMock.verify uploadManager
connectionEstablisherMock.verify connectionEstablisher
Thread.sleep(100)
}
Thread.sleep(100)
}
private void initMocks() {
connectionEvents = new CopyOnWriteArrayList()
eventBus = new EventBus()
def listener = new Object() {
void onConnectionEvent(ConnectionEvent e) {
connectionEvents.add e
}
}
eventBus.register(ConnectionEvent.class, listener)
private void initMocks() {
connectionEvents = new CopyOnWriteArrayList()
eventBus = new EventBus()
def listener = new Object() {
void onConnectionEvent(ConnectionEvent e) {
connectionEvents.add e
}
}
eventBus.register(ConnectionEvent.class, listener)
connectionManager = connectionManagerMock.proxyInstance()
i2pAcceptor = i2pAcceptorMock.proxyInstance()
hostCache = hostCacheMock.proxyInstance()
trustService = trustServiceMock.proxyInstance()
connectionManager = connectionManagerMock.proxyInstance()
i2pAcceptor = i2pAcceptorMock.proxyInstance()
hostCache = hostCacheMock.proxyInstance()
trustService = trustServiceMock.proxyInstance()
searchManager = searchManagerMock.proxyInstance()
uploadManager = uploadManagerMock.proxyInstance()
connectionEstablisher = connectionEstablisherMock.proxyInstance()
acceptor = new ConnectionAcceptor(eventBus, connectionManager, settings, i2pAcceptor,
acceptor = new ConnectionAcceptor(eventBus, connectionManager, settings, i2pAcceptor,
hostCache, trustService, searchManager, uploadManager, connectionEstablisher)
acceptor.start()
Thread.sleep(100)
}
acceptor.start()
Thread.sleep(100)
}
@Test
void testSuccessfulLeaf() {
settings = new MuWireSettings() {
boolean isLeaf() {
false
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
@Test
void testSuccessfulLeaf() {
settings = new MuWireSettings() {
boolean isLeaf() {
false
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
connectionEstablisherMock.demand.isInProgress(destinations.dest1) { false }
connectionManagerMock.demand.isConnected { dest ->
assert dest == destinations.dest1
false
}
connectionManagerMock.demand.hasLeafSlots() { true }
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
initMocks()
outputStream.write("MuWire leaf".bytes)
byte [] OK = new byte[2]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "OK".bytes
outputStream.write("MuWire leaf".bytes)
byte [] OK = new byte[2]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "OK".bytes
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.SUCCESSFUL
assert event.incoming == true
assert event.leaf == true
}
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.SUCCESSFUL
assert event.incoming == true
assert event.leaf == true
}
@Test
void testSuccessfulPeer() {
settings = new MuWireSettings() {
boolean isLeaf() {
false
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
@Test
void testSuccessfulPeer() {
settings = new MuWireSettings() {
boolean isLeaf() {
false
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
connectionEstablisherMock.demand.isInProgress(destinations.dest1) { false }
connectionManagerMock.demand.isConnected { dest ->
assert dest == destinations.dest1
false
}
connectionManagerMock.demand.hasPeerSlots() { true }
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
initMocks()
outputStream.write("MuWire peer".bytes)
byte [] OK = new byte[2]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "OK".bytes
outputStream.write("MuWire peer".bytes)
byte [] OK = new byte[2]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "OK".bytes
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.SUCCESSFUL
assert event.incoming == true
assert event.leaf == false
}
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.SUCCESSFUL
assert event.incoming == true
assert event.leaf == false
}
@Test
void testLeafRejectsLeaf() {
settings = new MuWireSettings() {
boolean isLeaf() {
true
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
@Test
void testLeafRejectsLeaf() {
settings = new MuWireSettings() {
boolean isLeaf() {
true
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
initMocks()
outputStream.write("MuWire leaf".bytes)
outputStream.flush()
Thread.sleep(50)
assert inputStream.read() == -1
outputStream.write("MuWire leaf".bytes)
outputStream.flush()
Thread.sleep(50)
assert inputStream.read() == -1
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.FAILED
assert event.incoming == true
assert event.leaf == null
}
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.FAILED
assert event.incoming == true
assert event.leaf == null
}
@Test
void testLeafRejectsPeer() {
settings = new MuWireSettings() {
boolean isLeaf() {
true
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
@Test
void testLeafRejectsPeer() {
settings = new MuWireSettings() {
boolean isLeaf() {
true
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
initMocks()
outputStream.write("MuWire peer".bytes)
outputStream.flush()
Thread.sleep(50)
assert inputStream.read() == -1
outputStream.write("MuWire peer".bytes)
outputStream.flush()
Thread.sleep(50)
assert inputStream.read() == -1
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.FAILED
assert event.incoming == true
assert event.leaf == null
}
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.FAILED
assert event.incoming == true
assert event.leaf == null
}
@Test
void testPeerRejectsPeerSlots() {
settings = new MuWireSettings() {
boolean isLeaf() {
false
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
@Test
void testPeerRejectsPeerSlots() {
settings = new MuWireSettings() {
boolean isLeaf() {
false
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
connectionEstablisherMock.demand.isInProgress(destinations.dest1) { false }
connectionManagerMock.demand.isConnected { dest ->
assert dest == destinations.dest1
false
}
connectionManagerMock.demand.hasPeerSlots() { false }
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
hostCacheMock.ignore.getGoodHosts { n -> [] }
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
hostCacheMock.ignore.getGoodHosts { n -> [] }
initMocks()
initMocks()
outputStream.write("MuWire peer".bytes)
byte [] OK = new byte[6]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "REJECT".bytes
outputStream.write("MuWire peer".bytes)
byte [] OK = new byte[6]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "REJECT".bytes
Thread.sleep(50)
assert dis.read() == -1
Thread.sleep(50)
assert dis.read() == -1
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.REJECTED
assert event.incoming == true
assert event.leaf == false
}
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.REJECTED
assert event.incoming == true
assert event.leaf == false
}
@Test
void testPeerRejectsLeafSlots() {
settings = new MuWireSettings() {
boolean isLeaf() {
false
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
@Test
void testPeerRejectsLeafSlots() {
settings = new MuWireSettings() {
boolean isLeaf() {
false
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
connectionEstablisherMock.demand.isInProgress(destinations.dest1) { false }
connectionManagerMock.demand.isConnected { dest ->
assert dest == destinations.dest1
false
}
connectionManagerMock.demand.hasLeafSlots() { false }
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
hostCacheMock.ignore.getGoodHosts { n -> [] }
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
hostCacheMock.ignore.getGoodHosts { n -> [] }
initMocks()
initMocks()
outputStream.write("MuWire leaf".bytes)
byte [] OK = new byte[6]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "REJECT".bytes
outputStream.write("MuWire leaf".bytes)
byte [] OK = new byte[6]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "REJECT".bytes
Thread.sleep(50)
assert dis.read() == -1
Thread.sleep(50)
assert dis.read() == -1
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.REJECTED
assert event.incoming == true
assert event.leaf == true
}
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.REJECTED
assert event.incoming == true
assert event.leaf == true
}
@Test
void testPeerRejectsPeerSuggests() {
settings = new MuWireSettings() {
boolean isLeaf() {
false
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
@Test
void testPeerRejectsPeerSuggests() {
settings = new MuWireSettings() {
boolean isLeaf() {
false
}
}
i2pAcceptorMock.demand.accept {
def is = new PipedInputStream()
outputStream = new PipedOutputStream(is)
def os = new PipedOutputStream()
inputStream = new PipedInputStream(os)
new Endpoint(destinations.dest1, is, os, null)
}
i2pAcceptorMock.demand.accept { Thread.sleep(Integer.MAX_VALUE) }
connectionEstablisherMock.demand.isInProgress(destinations.dest1) { false }
connectionManagerMock.demand.isConnected { dest ->
assert dest == destinations.dest1
false
}
connectionManagerMock.demand.hasPeerSlots() { false }
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
hostCacheMock.ignore.getGoodHosts { n -> [destinations.dest2] }
trustServiceMock.demand.getLevel { dest ->
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
hostCacheMock.ignore.getGoodHosts { n -> [destinations.dest2] }
initMocks()
initMocks()
outputStream.write("MuWire peer".bytes)
byte [] OK = new byte[6]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "REJECT".bytes
outputStream.write("MuWire peer".bytes)
byte [] OK = new byte[6]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "REJECT".bytes
short payloadSize = dis.readUnsignedShort()
byte[] payload = new byte[payloadSize]
dis.readFully(payload)
assert dis.read() == -1
short payloadSize = dis.readUnsignedShort()
byte[] payload = new byte[payloadSize]
dis.readFully(payload)
assert dis.read() == -1
def json = new JsonSlurper()
json = json.parse(payload)
assert json.tryHosts != null
assert json.tryHosts.size() == 1
assert json.tryHosts.contains(destinations.dest2.toBase64())
def json = new JsonSlurper()
json = json.parse(payload)
assert json.tryHosts != null
assert json.tryHosts.size() == 1
assert json.tryHosts.contains(destinations.dest2.toBase64())
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.REJECTED
}
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.status == ConnectionAttemptStatus.REJECTED
}
}

View File

@@ -17,271 +17,271 @@ import groovy.mock.interceptor.MockFor
class ConnectionEstablisherTest {
EventBus eventBus
final Destinations destinations = new Destinations()
List<ConnectionEvent> connectionEvents
List<HostDiscoveredEvent> discoveredEvents
DataInputStream inputStream
DataOutputStream outputStream
EventBus eventBus
final Destinations destinations = new Destinations()
List<ConnectionEvent> connectionEvents
List<HostDiscoveredEvent> discoveredEvents
DataInputStream inputStream
DataOutputStream outputStream
def i2pConnectorMock
I2PConnector i2pConnector
def i2pConnectorMock
I2PConnector i2pConnector
MuWireSettings settings
MuWireSettings settings
def connectionManagerMock
ConnectionManager connectionManager
def connectionManagerMock
ConnectionManager connectionManager
def hostCacheMock
HostCache hostCache
def hostCacheMock
HostCache hostCache
ConnectionEstablisher establisher
ConnectionEstablisher establisher
@Before
void before() {
connectionEvents = new CopyOnWriteArrayList()
discoveredEvents = new CopyOnWriteArrayList()
def listener = new Object() {
void onConnectionEvent(ConnectionEvent e) {
connectionEvents.add(e)
}
void onHostDiscoveredEvent(HostDiscoveredEvent e) {
discoveredEvents.add e
}
}
eventBus = new EventBus()
eventBus.register(ConnectionEvent.class, listener)
eventBus.register(HostDiscoveredEvent.class, listener)
i2pConnectorMock = new MockFor(I2PConnector.class)
connectionManagerMock = new MockFor(ConnectionManager.class)
hostCacheMock = new MockFor(HostCache.class)
}
@Before
void before() {
connectionEvents = new CopyOnWriteArrayList()
discoveredEvents = new CopyOnWriteArrayList()
def listener = new Object() {
void onConnectionEvent(ConnectionEvent e) {
connectionEvents.add(e)
}
void onHostDiscoveredEvent(HostDiscoveredEvent e) {
discoveredEvents.add e
}
}
eventBus = new EventBus()
eventBus.register(ConnectionEvent.class, listener)
eventBus.register(HostDiscoveredEvent.class, listener)
i2pConnectorMock = new MockFor(I2PConnector.class)
connectionManagerMock = new MockFor(ConnectionManager.class)
hostCacheMock = new MockFor(HostCache.class)
}
@After
void after() {
establisher?.stop()
i2pConnectorMock.verify i2pConnector
connectionManagerMock.verify connectionManager
hostCacheMock.verify hostCache
Thread.sleep(100)
}
@After
void after() {
establisher?.stop()
i2pConnectorMock.verify i2pConnector
connectionManagerMock.verify connectionManager
hostCacheMock.verify hostCache
Thread.sleep(100)
}
private void initMocks() {
i2pConnector = i2pConnectorMock.proxyInstance()
connectionManager = connectionManagerMock.proxyInstance()
hostCache = hostCacheMock.proxyInstance()
establisher = new ConnectionEstablisher(eventBus, i2pConnector, settings, connectionManager, hostCache)
establisher.start()
Thread.sleep(250)
}
private void initMocks() {
i2pConnector = i2pConnectorMock.proxyInstance()
connectionManager = connectionManagerMock.proxyInstance()
hostCache = hostCacheMock.proxyInstance()
establisher = new ConnectionEstablisher(eventBus, i2pConnector, settings, connectionManager, hostCache)
establisher.start()
Thread.sleep(250)
}
@Test
void testConnectFails() {
settings = new MuWireSettings()
connectionManagerMock.ignore.needsConnections {
true
}
hostCacheMock.ignore.getHosts { num ->
assert num == 1
[destinations.dest1]
}
connectionManagerMock.ignore.isConnected { dest ->
assert dest == destinations.dest1
false
}
i2pConnectorMock.demand.connect { dest ->
assert dest == destinations.dest1
throw new IOException()
}
@Test
void testConnectFails() {
settings = new MuWireSettings()
connectionManagerMock.ignore.needsConnections {
true
}
hostCacheMock.ignore.getHosts { num ->
assert num == 1
[destinations.dest1]
}
connectionManagerMock.ignore.isConnected { dest ->
assert dest == destinations.dest1
false
}
i2pConnectorMock.demand.connect { dest ->
assert dest == destinations.dest1
throw new IOException()
}
initMocks()
initMocks()
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.FAILED
}
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.FAILED
}
@Test
void testConnectionSucceedsPeer() {
settings = new MuWireSettings() {
boolean isLeaf() {false}
}
connectionManagerMock.ignore.needsConnections {
true
}
hostCacheMock.ignore.getHosts { num ->
assert num == 1
[destinations.dest1]
}
connectionManagerMock.ignore.isConnected { dest ->
assert dest == destinations.dest1
false
}
i2pConnectorMock.demand.connect { dest ->
PipedOutputStream os = new PipedOutputStream()
inputStream = new DataInputStream(new PipedInputStream(os))
PipedInputStream is = new PipedInputStream()
outputStream = new DataOutputStream(new PipedOutputStream(is))
new Endpoint(dest, is, os, null)
}
@Test
void testConnectionSucceedsPeer() {
settings = new MuWireSettings() {
boolean isLeaf() {false}
}
connectionManagerMock.ignore.needsConnections {
true
}
hostCacheMock.ignore.getHosts { num ->
assert num == 1
[destinations.dest1]
}
connectionManagerMock.ignore.isConnected { dest ->
assert dest == destinations.dest1
false
}
i2pConnectorMock.demand.connect { dest ->
PipedOutputStream os = new PipedOutputStream()
inputStream = new DataInputStream(new PipedInputStream(os))
PipedInputStream is = new PipedInputStream()
outputStream = new DataOutputStream(new PipedOutputStream(is))
new Endpoint(dest, is, os, null)
}
initMocks()
initMocks()
byte [] header = new byte[11]
inputStream.readFully(header)
assert header == "MuWire peer".bytes
byte [] header = new byte[11]
inputStream.readFully(header)
assert header == "MuWire peer".bytes
outputStream.write("OK".bytes)
outputStream.flush()
outputStream.write("OK".bytes)
outputStream.flush()
Thread.sleep(100)
Thread.sleep(100)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.SUCCESSFUL
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.SUCCESSFUL
}
}
@Test
void testConnectionSucceedsLeaf() {
settings = new MuWireSettings() {
boolean isLeaf() {true}
}
connectionManagerMock.ignore.needsConnections {
true
}
hostCacheMock.ignore.getHosts { num ->
assert num == 1
[destinations.dest1]
}
connectionManagerMock.ignore.isConnected { dest ->
assert dest == destinations.dest1
false
}
i2pConnectorMock.demand.connect { dest ->
PipedOutputStream os = new PipedOutputStream()
inputStream = new DataInputStream(new PipedInputStream(os))
PipedInputStream is = new PipedInputStream()
outputStream = new DataOutputStream(new PipedOutputStream(is))
new Endpoint(dest, is, os, null)
}
@Test
void testConnectionSucceedsLeaf() {
settings = new MuWireSettings() {
boolean isLeaf() {true}
}
connectionManagerMock.ignore.needsConnections {
true
}
hostCacheMock.ignore.getHosts { num ->
assert num == 1
[destinations.dest1]
}
connectionManagerMock.ignore.isConnected { dest ->
assert dest == destinations.dest1
false
}
i2pConnectorMock.demand.connect { dest ->
PipedOutputStream os = new PipedOutputStream()
inputStream = new DataInputStream(new PipedInputStream(os))
PipedInputStream is = new PipedInputStream()
outputStream = new DataOutputStream(new PipedOutputStream(is))
new Endpoint(dest, is, os, null)
}
initMocks()
initMocks()
byte [] header = new byte[11]
inputStream.readFully(header)
assert header == "MuWire leaf".bytes
byte [] header = new byte[11]
inputStream.readFully(header)
assert header == "MuWire leaf".bytes
outputStream.write("OK".bytes)
outputStream.flush()
outputStream.write("OK".bytes)
outputStream.flush()
Thread.sleep(100)
Thread.sleep(100)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.SUCCESSFUL
}
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.SUCCESSFUL
}
@Test
void testConnectionRejected() {
settings = new MuWireSettings() {
boolean isLeaf() {false}
}
connectionManagerMock.ignore.needsConnections {
true
}
hostCacheMock.ignore.getHosts { num ->
assert num == 1
[destinations.dest1]
}
connectionManagerMock.ignore.isConnected { dest ->
assert dest == destinations.dest1
false
}
i2pConnectorMock.demand.connect { dest ->
PipedOutputStream os = new PipedOutputStream()
inputStream = new DataInputStream(new PipedInputStream(os))
PipedInputStream is = new PipedInputStream()
outputStream = new DataOutputStream(new PipedOutputStream(is))
new Endpoint(dest, is, os, null)
}
@Test
void testConnectionRejected() {
settings = new MuWireSettings() {
boolean isLeaf() {false}
}
connectionManagerMock.ignore.needsConnections {
true
}
hostCacheMock.ignore.getHosts { num ->
assert num == 1
[destinations.dest1]
}
connectionManagerMock.ignore.isConnected { dest ->
assert dest == destinations.dest1
false
}
i2pConnectorMock.demand.connect { dest ->
PipedOutputStream os = new PipedOutputStream()
inputStream = new DataInputStream(new PipedInputStream(os))
PipedInputStream is = new PipedInputStream()
outputStream = new DataOutputStream(new PipedOutputStream(is))
new Endpoint(dest, is, os, null)
}
initMocks()
initMocks()
byte [] header = new byte[11]
inputStream.readFully(header)
assert header == "MuWire peer".bytes
byte [] header = new byte[11]
inputStream.readFully(header)
assert header == "MuWire peer".bytes
outputStream.write("REJECT".bytes)
outputStream.flush()
outputStream.write("REJECT".bytes)
outputStream.flush()
Thread.sleep(100)
Thread.sleep(100)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.REJECTED
assert discoveredEvents.isEmpty()
}
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.REJECTED
assert discoveredEvents.isEmpty()
}
@Test
void testConnectionRejectedSuggestions() {
settings = new MuWireSettings() {
boolean isLeaf() {false}
}
connectionManagerMock.ignore.needsConnections {
true
}
hostCacheMock.ignore.getHosts { num ->
assert num == 1
[destinations.dest1]
}
connectionManagerMock.ignore.isConnected { dest ->
assert dest == destinations.dest1
false
}
i2pConnectorMock.demand.connect { dest ->
PipedOutputStream os = new PipedOutputStream()
inputStream = new DataInputStream(new PipedInputStream(os))
PipedInputStream is = new PipedInputStream()
outputStream = new DataOutputStream(new PipedOutputStream(is))
new Endpoint(dest, is, os, null)
}
@Test
void testConnectionRejectedSuggestions() {
settings = new MuWireSettings() {
boolean isLeaf() {false}
}
connectionManagerMock.ignore.needsConnections {
true
}
hostCacheMock.ignore.getHosts { num ->
assert num == 1
[destinations.dest1]
}
connectionManagerMock.ignore.isConnected { dest ->
assert dest == destinations.dest1
false
}
i2pConnectorMock.demand.connect { dest ->
PipedOutputStream os = new PipedOutputStream()
inputStream = new DataInputStream(new PipedInputStream(os))
PipedInputStream is = new PipedInputStream()
outputStream = new DataOutputStream(new PipedOutputStream(is))
new Endpoint(dest, is, os, null)
}
initMocks()
initMocks()
byte [] header = new byte[11]
inputStream.readFully(header)
assert header == "MuWire peer".bytes
byte [] header = new byte[11]
inputStream.readFully(header)
assert header == "MuWire peer".bytes
outputStream.write("REJECT".bytes)
outputStream.flush()
outputStream.write("REJECT".bytes)
outputStream.flush()
def json = [:]
json.tryHosts = [destinations.dest2.toBase64()]
json = JsonOutput.toJson(json)
outputStream.writeShort(json.bytes.length)
outputStream.write(json.bytes)
outputStream.flush()
Thread.sleep(100)
def json = [:]
json.tryHosts = [destinations.dest2.toBase64()]
json = JsonOutput.toJson(json)
outputStream.writeShort(json.bytes.length)
outputStream.write(json.bytes)
outputStream.flush()
Thread.sleep(100)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.REJECTED
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.REJECTED
assert discoveredEvents.size() == 1
event = discoveredEvents[0]
assert event.destination == destinations.dest2
}
assert discoveredEvents.size() == 1
event = discoveredEvents[0]
assert event.destination == destinations.dest2
}
}

View File

@@ -8,76 +8,76 @@ import org.junit.Test
class FileHasherTest extends GroovyTestCase {
def hasher = new FileHasher()
File tmp
def hasher = new FileHasher()
File tmp
@Before
void setUp() {
tmp = File.createTempFile("testFile", "test")
tmp.deleteOnExit()
}
@Before
void setUp() {
tmp = File.createTempFile("testFile", "test")
tmp.deleteOnExit()
}
@After
void tearDown() {
tmp?.delete()
}
@After
void tearDown() {
tmp?.delete()
}
@Test
void testPieceSize() {
assert 17 == FileHasher.getPieceSize(1000000)
assert 17 == FileHasher.getPieceSize(100000000)
assert 24 == FileHasher.getPieceSize(FileHasher.MAX_SIZE)
shouldFail IllegalArgumentException, {
FileHasher.getPieceSize(Long.MAX_VALUE)
}
}
@Test
void testPieceSize() {
assert 17 == FileHasher.getPieceSize(1000000)
assert 17 == FileHasher.getPieceSize(100000000)
assert 24 == FileHasher.getPieceSize(FileHasher.MAX_SIZE)
shouldFail IllegalArgumentException, {
FileHasher.getPieceSize(Long.MAX_VALUE)
}
}
@Test
void testHash1Byte() {
def fos = new FileOutputStream(tmp)
fos.write(0)
fos.close()
def ih = hasher.hashFile(tmp)
assert ih.getHashList().length == 32
}
@Test
void testHash1Byte() {
def fos = new FileOutputStream(tmp)
fos.write(0)
fos.close()
def ih = hasher.hashFile(tmp)
assert ih.getHashList().length == 32
}
@Test
void testHash1PieceExact() {
def fos = new FileOutputStream(tmp)
byte [] b = new byte[ 0x1 << 18]
fos.write b
fos.close()
def ih = hasher.hashFile tmp
assert ih.getHashList().length == 64
}
@Test
void testHash1PieceExact() {
def fos = new FileOutputStream(tmp)
byte [] b = new byte[ 0x1 << 18]
fos.write b
fos.close()
def ih = hasher.hashFile tmp
assert ih.getHashList().length == 64
}
@Test
void testHash1Piece1Byte() {
def fos = new FileOutputStream(tmp)
byte [] b = new byte[ (0x1 << 18) + 1]
fos.write b
fos.close()
def ih = hasher.hashFile tmp
assert ih.getHashList().length == 96
}
@Test
void testHash1Piece1Byte() {
def fos = new FileOutputStream(tmp)
byte [] b = new byte[ (0x1 << 18) + 1]
fos.write b
fos.close()
def ih = hasher.hashFile tmp
assert ih.getHashList().length == 96
}
@Test
void testHash2Pieces() {
def fos = new FileOutputStream(tmp)
byte [] b = new byte[ (0x1 << 19)]
fos.write b
fos.close()
def ih = hasher.hashFile tmp
assert ih.getHashList().length == 128
}
@Test
void testHash2Pieces() {
def fos = new FileOutputStream(tmp)
byte [] b = new byte[ (0x1 << 19)]
fos.write b
fos.close()
def ih = hasher.hashFile tmp
assert ih.getHashList().length == 128
}
@Test
void testHash2Pieces2Bytes() {
def fos = new FileOutputStream(tmp)
byte [] b = new byte[ (0x1 << 19) + 2]
fos.write b
fos.close()
def ih = hasher.hashFile tmp
assert ih.getHashList().length == 160
}
@Test
void testHash2Pieces2Bytes() {
def fos = new FileOutputStream(tmp)
byte [] b = new byte[ (0x1 << 19) + 2]
fos.write b
fos.close()
def ih = hasher.hashFile tmp
assert ih.getHashList().length == 160
}
}

View File

@@ -12,177 +12,177 @@ import com.muwire.core.search.SearchEvent
class FileManagerTest {
EventBus eventBus
EventBus eventBus
FileManager manager
volatile ResultsEvent results
FileManager manager
volatile ResultsEvent results
def listener = new Object() {
void onResultsEvent(ResultsEvent e) {
results = e
}
}
def listener = new Object() {
void onResultsEvent(ResultsEvent e) {
results = e
}
}
@Before
void before() {
eventBus = new EventBus()
eventBus.register(ResultsEvent.class, listener)
manager = new FileManager(eventBus, new MuWireSettings())
results = null
}
@Before
void before() {
eventBus = new EventBus()
eventBus.register(ResultsEvent.class, listener)
manager = new FileManager(eventBus, new MuWireSettings())
results = null
}
@Test
void testHash1Result() {
File f = new File("a b.c")
InfoHash ih = InfoHash.fromHashList(new byte[32])
SharedFile sf = new SharedFile(f,ih, 0)
FileHashedEvent fhe = new FileHashedEvent(sharedFile: sf)
manager.onFileHashedEvent(fhe)
@Test
void testHash1Result() {
File f = new File("a b.c")
InfoHash ih = InfoHash.fromHashList(new byte[32])
SharedFile sf = new SharedFile(f,ih, 0)
FileHashedEvent fhe = new FileHashedEvent(sharedFile: sf)
manager.onFileHashedEvent(fhe)
UUID uuid = UUID.randomUUID()
SearchEvent se = new SearchEvent(searchHash: ih.getRoot(), uuid: uuid)
UUID uuid = UUID.randomUUID()
SearchEvent se = new SearchEvent(searchHash: ih.getRoot(), uuid: uuid)
manager.onSearchEvent(se)
Thread.sleep(20)
manager.onSearchEvent(se)
Thread.sleep(20)
assert results != null
assert results.results.size() == 1
assert results.results.contains(sf)
assert results.uuid == uuid
}
assert results != null
assert results.results.size() == 1
assert results.results.contains(sf)
assert results.uuid == uuid
}
@Test
void testHash2Results() {
InfoHash ih = InfoHash.fromHashList(new byte[32])
SharedFile sf1 = new SharedFile(new File("a b.c"), ih, 0)
SharedFile sf2 = new SharedFile(new File("d e.f"), ih, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile : sf1)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile : sf2)
@Test
void testHash2Results() {
InfoHash ih = InfoHash.fromHashList(new byte[32])
SharedFile sf1 = new SharedFile(new File("a b.c"), ih, 0)
SharedFile sf2 = new SharedFile(new File("d e.f"), ih, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile : sf1)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile : sf2)
UUID uuid = UUID.randomUUID()
SearchEvent se = new SearchEvent(searchHash: ih.getRoot(), uuid: uuid)
UUID uuid = UUID.randomUUID()
SearchEvent se = new SearchEvent(searchHash: ih.getRoot(), uuid: uuid)
manager.onSearchEvent(se)
Thread.sleep(20)
manager.onSearchEvent(se)
Thread.sleep(20)
assert results != null
assert results.results.size() == 2
assert results.results.contains(sf1)
assert results.results.contains(sf2)
assert results.uuid == uuid
}
assert results != null
assert results.results.size() == 2
assert results.results.contains(sf1)
assert results.results.contains(sf2)
assert results.uuid == uuid
}
@Test
void testHash0Results() {
File f = new File("a b.c")
InfoHash ih = InfoHash.fromHashList(new byte[32])
SharedFile sf = new SharedFile(f,ih, 0)
FileHashedEvent fhe = new FileHashedEvent(sharedFile: sf)
manager.onFileHashedEvent(fhe)
@Test
void testHash0Results() {
File f = new File("a b.c")
InfoHash ih = InfoHash.fromHashList(new byte[32])
SharedFile sf = new SharedFile(f,ih, 0)
FileHashedEvent fhe = new FileHashedEvent(sharedFile: sf)
manager.onFileHashedEvent(fhe)
manager.onSearchEvent new SearchEvent(searchHash: new byte[32], uuid: UUID.randomUUID())
Thread.sleep(20)
manager.onSearchEvent new SearchEvent(searchHash: new byte[32], uuid: UUID.randomUUID())
Thread.sleep(20)
assert results == null
}
assert results == null
}
@Test
void testKeyword1Result() {
File f = new File("a b.c")
InfoHash ih = InfoHash.fromHashList(new byte[32])
SharedFile sf = new SharedFile(f,ih,0)
FileHashedEvent fhe = new FileHashedEvent(sharedFile: sf)
manager.onFileHashedEvent(fhe)
@Test
void testKeyword1Result() {
File f = new File("a b.c")
InfoHash ih = InfoHash.fromHashList(new byte[32])
SharedFile sf = new SharedFile(f,ih,0)
FileHashedEvent fhe = new FileHashedEvent(sharedFile: sf)
manager.onFileHashedEvent(fhe)
UUID uuid = UUID.randomUUID()
manager.onSearchEvent new SearchEvent(searchTerms: ["a"], uuid:uuid)
Thread.sleep(20)
UUID uuid = UUID.randomUUID()
manager.onSearchEvent new SearchEvent(searchTerms: ["a"], uuid:uuid)
Thread.sleep(20)
assert results != null
assert results.results.size() == 1
assert results.results.contains(sf)
assert results.uuid == uuid
}
assert results != null
assert results.results.size() == 1
assert results.results.contains(sf)
assert results.uuid == uuid
}
@Test
void testKeyword2Results() {
File f1 = new File("a b.c")
InfoHash ih1 = InfoHash.fromHashList(new byte[32])
SharedFile sf1 = new SharedFile(f1, ih1, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile: sf1)
@Test
void testKeyword2Results() {
File f1 = new File("a b.c")
InfoHash ih1 = InfoHash.fromHashList(new byte[32])
SharedFile sf1 = new SharedFile(f1, ih1, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile: sf1)
File f2 = new File("c d.e")
InfoHash ih2 = InfoHash.fromHashList(new byte[64])
SharedFile sf2 = new SharedFile(f2, ih2, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile: sf2)
File f2 = new File("c d.e")
InfoHash ih2 = InfoHash.fromHashList(new byte[64])
SharedFile sf2 = new SharedFile(f2, ih2, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile: sf2)
UUID uuid = UUID.randomUUID()
manager.onSearchEvent new SearchEvent(searchTerms: ["c"], uuid:uuid)
Thread.sleep(20)
UUID uuid = UUID.randomUUID()
manager.onSearchEvent new SearchEvent(searchTerms: ["c"], uuid:uuid)
Thread.sleep(20)
assert results != null
assert results.results.size() == 2
assert results.results.contains(sf1)
assert results.results.contains(sf2)
assert results.uuid == uuid
}
assert results != null
assert results.results.size() == 2
assert results.results.contains(sf1)
assert results.results.contains(sf2)
assert results.uuid == uuid
}
@Test
void testKeyword0Results() {
File f = new File("a b.c")
InfoHash ih = InfoHash.fromHashList(new byte[32])
SharedFile sf = new SharedFile(f,ih,0)
FileHashedEvent fhe = new FileHashedEvent(sharedFile: sf)
manager.onFileHashedEvent(fhe)
@Test
void testKeyword0Results() {
File f = new File("a b.c")
InfoHash ih = InfoHash.fromHashList(new byte[32])
SharedFile sf = new SharedFile(f,ih,0)
FileHashedEvent fhe = new FileHashedEvent(sharedFile: sf)
manager.onFileHashedEvent(fhe)
manager.onSearchEvent new SearchEvent(searchTerms: ["d"], uuid: UUID.randomUUID())
Thread.sleep(20)
manager.onSearchEvent new SearchEvent(searchTerms: ["d"], uuid: UUID.randomUUID())
Thread.sleep(20)
assert results == null
}
assert results == null
}
@Test
void testRemoveFileExistingHash() {
InfoHash ih = InfoHash.fromHashList(new byte[32])
SharedFile sf1 = new SharedFile(new File("a b.c"), ih, 0)
SharedFile sf2 = new SharedFile(new File("d e.f"), ih, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile : sf1)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile : sf2)
@Test
void testRemoveFileExistingHash() {
InfoHash ih = InfoHash.fromHashList(new byte[32])
SharedFile sf1 = new SharedFile(new File("a b.c"), ih, 0)
SharedFile sf2 = new SharedFile(new File("d e.f"), ih, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile : sf1)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile : sf2)
manager.onFileUnsharedEvent new FileUnsharedEvent(unsharedFile: sf2)
manager.onFileUnsharedEvent new FileUnsharedEvent(unsharedFile: sf2)
manager.onSearchEvent new SearchEvent(searchHash : ih.getRoot())
Thread.sleep(20)
assert results != null
assert results.results.size() == 1
assert results.results.contains(sf1)
}
manager.onSearchEvent new SearchEvent(searchHash : ih.getRoot())
Thread.sleep(20)
assert results != null
assert results.results.size() == 1
assert results.results.contains(sf1)
}
@Test
void testRemoveFile() {
File f1 = new File("a b.c")
InfoHash ih1 = InfoHash.fromHashList(new byte[32])
SharedFile sf1 = new SharedFile(f1, ih1, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile: sf1)
@Test
void testRemoveFile() {
File f1 = new File("a b.c")
InfoHash ih1 = InfoHash.fromHashList(new byte[32])
SharedFile sf1 = new SharedFile(f1, ih1, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile: sf1)
File f2 = new File("c d.e")
InfoHash ih2 = InfoHash.fromHashList(new byte[64])
SharedFile sf2 = new SharedFile(f2, ih2, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile: sf2)
File f2 = new File("c d.e")
InfoHash ih2 = InfoHash.fromHashList(new byte[64])
SharedFile sf2 = new SharedFile(f2, ih2, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile: sf2)
manager.onFileUnsharedEvent new FileUnsharedEvent(unsharedFile: sf2)
manager.onFileUnsharedEvent new FileUnsharedEvent(unsharedFile: sf2)
// 1 match left
manager.onSearchEvent new SearchEvent(searchTerms: ["c"])
Thread.sleep(20)
assert results != null
assert results.results.size() == 1
assert results.results.contains(sf1)
// 1 match left
manager.onSearchEvent new SearchEvent(searchTerms: ["c"])
Thread.sleep(20)
assert results != null
assert results.results.size() == 1
assert results.results.contains(sf1)
// no match
results = null
manager.onSearchEvent new SearchEvent(searchTerms: ["d"])
assert results == null
// no match
results = null
manager.onSearchEvent new SearchEvent(searchTerms: ["d"])
assert results == null
}
}
}

View File

@@ -12,54 +12,54 @@ import com.muwire.core.MuWireSettings
class HasherServiceTest {
HasherService service
FileHasher hasher
HasherService service
FileHasher hasher
EventBus eventBus
def listener = new ArrayBlockingQueue(100) {
void onFileHashedEvent(FileHashedEvent evt) {
offer evt
}
}
def listener = new ArrayBlockingQueue(100) {
void onFileHashedEvent(FileHashedEvent evt) {
offer evt
}
}
@Before
void before() {
@Before
void before() {
eventBus = new EventBus()
hasher = new FileHasher()
service = new HasherService(hasher, eventBus, new FileManager(eventBus, new MuWireSettings()))
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()
}
service.start()
}
@After
void after() {
listener.clear()
}
@After
void after() {
listener.clear()
}
@Test
void testSingleFile() {
File f = new File("build.gradle")
service.onFileSharedEvent new FileSharedEvent(file: f)
Thread.sleep(100)
def hashed = listener.poll()
assert hashed instanceof FileHashedEvent
assert hashed.sharedFile.file == f.getCanonicalFile()
assert hashed.sharedFile.infoHash != null
assert listener.isEmpty()
}
@Test
void testSingleFile() {
File f = new File("build.gradle")
service.onFileSharedEvent new FileSharedEvent(file: f)
Thread.sleep(100)
def hashed = listener.poll()
assert hashed instanceof FileHashedEvent
assert hashed.sharedFile.file == f.getCanonicalFile()
assert hashed.sharedFile.infoHash != null
assert listener.isEmpty()
}
@Test
void testDirectory() {
File f = new File(".")
service.onFileSharedEvent new FileSharedEvent(file: f)
Set<String> fileNames = new HashSet<>()
while (true) {
def hashed = listener.poll(1000, TimeUnit.MILLISECONDS)
if (hashed == null)
break
fileNames.add(hashed.sharedFile?.file?.getName())
}
assert fileNames.contains("build.gradle")
assert fileNames.contains("HasherServiceTest.groovy")
}
@Test
void testDirectory() {
File f = new File(".")
service.onFileSharedEvent new FileSharedEvent(file: f)
Set<String> fileNames = new HashSet<>()
while (true) {
def hashed = listener.poll(1000, TimeUnit.MILLISECONDS)
if (hashed == null)
break
fileNames.add(hashed.sharedFile?.file?.getName())
}
assert fileNames.contains("build.gradle")
assert fileNames.contains("HasherServiceTest.groovy")
}
}

View File

@@ -17,193 +17,193 @@ import net.i2p.data.Base64
class PersisterServiceLoadingTest {
class Listener {
def publishedFiles = []
def onFileLoadedEvent(FileLoadedEvent e) {
publishedFiles.add(e.loadedFile)
}
}
class Listener {
def publishedFiles = []
def onFileLoadedEvent(FileLoadedEvent e) {
publishedFiles.add(e.loadedFile)
}
}
EventBus eventBus
Listener listener
File sharedDir
File sharedFile1, sharedFile2
EventBus eventBus
Listener listener
File sharedDir
File sharedFile1, sharedFile2
@Before
void setup() {
eventBus = new EventBus()
listener = new Listener()
eventBus.register(FileLoadedEvent.class, listener)
@Before
void setup() {
eventBus = new EventBus()
listener = new Listener()
eventBus.register(FileLoadedEvent.class, listener)
sharedDir = new File("sharedDir")
sharedDir.mkdir()
sharedDir.deleteOnExit()
sharedDir = new File("sharedDir")
sharedDir.mkdir()
sharedDir.deleteOnExit()
sharedFile1 = new File(sharedDir,"file1")
sharedFile1.deleteOnExit()
sharedFile1 = new File(sharedDir,"file1")
sharedFile1.deleteOnExit()
sharedFile2 = new File(sharedDir,"file2")
sharedFile2.deleteOnExit()
}
sharedFile2 = new File(sharedDir,"file2")
sharedFile2.deleteOnExit()
}
private void writeToSharedFile(File file, int size) {
FileOutputStream fos = new FileOutputStream(file);
fos.write new byte[size]
fos.close()
}
private void writeToSharedFile(File file, int size) {
FileOutputStream fos = new FileOutputStream(file);
fos.write new byte[size]
fos.close()
}
private File initPersisted() {
File persisted = new File("persisted")
if (persisted.exists())
persisted.delete()
persisted.deleteOnExit()
persisted
}
private File initPersisted() {
File persisted = new File("persisted")
if (persisted.exists())
persisted.delete()
persisted.deleteOnExit()
persisted
}
@Test
void test1SharedFile1Piece() {
writeToSharedFile(sharedFile1, 1)
FileHasher fh = new FileHasher()
InfoHash ih1 = fh.hashFile(sharedFile1)
@Test
void test1SharedFile1Piece() {
writeToSharedFile(sharedFile1, 1)
FileHasher fh = new FileHasher()
InfoHash ih1 = fh.hashFile(sharedFile1)
def json = [:]
json.file = getSharedFileJsonName(sharedFile1)
json.length = 1
json.infoHash = Base64.encode(ih1.getRoot())
json.hashList = [Base64.encode(ih1.getHashList())]
def json = [:]
json.file = getSharedFileJsonName(sharedFile1)
json.length = 1
json.infoHash = Base64.encode(ih1.getRoot())
json.hashList = [Base64.encode(ih1.getHashList())]
json = JsonOutput.toJson(json)
json = JsonOutput.toJson(json)
File persisted = initPersisted()
persisted.write json
File persisted = initPersisted()
persisted.write json
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
ps.onUILoadedEvent(null)
Thread.sleep(2000)
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
ps.onUILoadedEvent(null)
Thread.sleep(2000)
assert listener.publishedFiles.size() == 1
def loadedFile = listener.publishedFiles[0]
assert loadedFile != null
assert loadedFile.file == sharedFile1.getCanonicalFile()
assert loadedFile.infoHash == ih1
}
assert listener.publishedFiles.size() == 1
def loadedFile = listener.publishedFiles[0]
assert loadedFile != null
assert loadedFile.file == sharedFile1.getCanonicalFile()
assert loadedFile.infoHash == ih1
}
private static String getSharedFileJsonName(File sharedFile) {
def encoded = DataUtil.encodei18nString(sharedFile.getCanonicalFile().toString())
Base64.encode(encoded)
}
@Test
public void test1SharedFile2Pieces() {
writeToSharedFile(sharedFile1, (0x1 << 18) + 1)
FileHasher fh = new FileHasher()
InfoHash ih1 = fh.hashFile(sharedFile1)
@Test
public void test1SharedFile2Pieces() {
writeToSharedFile(sharedFile1, (0x1 << 18) + 1)
FileHasher fh = new FileHasher()
InfoHash ih1 = fh.hashFile(sharedFile1)
assert ih1.getHashList().length == 96
assert ih1.getHashList().length == 96
def json = [:]
json.file = getSharedFileJsonName(sharedFile1)
json.length = sharedFile1.length()
json.infoHash = Base64.encode ih1.getRoot()
def json = [:]
json.file = getSharedFileJsonName(sharedFile1)
json.length = sharedFile1.length()
json.infoHash = Base64.encode ih1.getRoot()
byte [] tmp = new byte[32]
System.arraycopy(ih1.getHashList(), 0, tmp, 0, 32)
String hash1 = Base64.encode(tmp)
System.arraycopy(ih1.getHashList(), 32, tmp, 0, 32)
String hash2 = Base64.encode(tmp)
byte [] tmp = new byte[32]
System.arraycopy(ih1.getHashList(), 0, tmp, 0, 32)
String hash1 = Base64.encode(tmp)
System.arraycopy(ih1.getHashList(), 32, tmp, 0, 32)
String hash2 = Base64.encode(tmp)
System.arraycopy(ih1.getHashList(), 64, tmp, 0, 32)
String hash3 = Base64.encode(tmp)
json.hashList = [hash1, hash2, hash3]
json.hashList = [hash1, hash2, hash3]
json = JsonOutput.toJson(json)
json = JsonOutput.toJson(json)
File persisted = initPersisted()
persisted.write json
File persisted = initPersisted()
persisted.write json
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
ps.onUILoadedEvent(null)
Thread.sleep(2000)
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
ps.onUILoadedEvent(null)
Thread.sleep(2000)
assert listener.publishedFiles.size() == 1
def loadedFile = listener.publishedFiles[0]
assert loadedFile != null
assert loadedFile.file == sharedFile1.getCanonicalFile()
assert loadedFile.infoHash == ih1
}
assert listener.publishedFiles.size() == 1
def loadedFile = listener.publishedFiles[0]
assert loadedFile != null
assert loadedFile.file == sharedFile1.getCanonicalFile()
assert loadedFile.infoHash == ih1
}
@Test
void test2SharedFiles() {
writeToSharedFile(sharedFile1, 1)
writeToSharedFile(sharedFile2, 2)
FileHasher fh = new FileHasher()
InfoHash ih1 = fh.hashFile(sharedFile1)
InfoHash ih2 = fh.hashFile(sharedFile2)
@Test
void test2SharedFiles() {
writeToSharedFile(sharedFile1, 1)
writeToSharedFile(sharedFile2, 2)
FileHasher fh = new FileHasher()
InfoHash ih1 = fh.hashFile(sharedFile1)
InfoHash ih2 = fh.hashFile(sharedFile2)
assert ih1 != ih2
assert ih1 != ih2
File persisted = initPersisted()
File persisted = initPersisted()
def json1 = [:]
json1.file = getSharedFileJsonName(sharedFile1)
json1.length = 1
json1.infoHash = Base64.encode(ih1.getRoot())
json1.hashList = [Base64.encode(ih1.getHashList())]
def json1 = [:]
json1.file = getSharedFileJsonName(sharedFile1)
json1.length = 1
json1.infoHash = Base64.encode(ih1.getRoot())
json1.hashList = [Base64.encode(ih1.getHashList())]
json1 = JsonOutput.toJson(json1)
json1 = JsonOutput.toJson(json1)
def json2 = [:]
json2.file = getSharedFileJsonName(sharedFile2)
json2.length = 2
json2.infoHash = Base64.encode(ih2.getRoot())
json2.hashList = [Base64.encode(ih2.getHashList())]
def json2 = [:]
json2.file = getSharedFileJsonName(sharedFile2)
json2.length = 2
json2.infoHash = Base64.encode(ih2.getRoot())
json2.hashList = [Base64.encode(ih2.getHashList())]
json2 = JsonOutput.toJson(json2)
json2 = JsonOutput.toJson(json2)
persisted.append "$json1\n"
persisted.append "$json2\n"
persisted.append "$json1\n"
persisted.append "$json2\n"
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
ps.onUILoadedEvent(null)
Thread.sleep(2000)
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
ps.onUILoadedEvent(null)
Thread.sleep(2000)
assert listener.publishedFiles.size() == 2
def loadedFile1 = listener.publishedFiles[0]
assert loadedFile1.file == sharedFile1.getCanonicalFile()
assert loadedFile1.infoHash == ih1
def loadedFile2 = listener.publishedFiles[1]
assert loadedFile2.file == sharedFile2.getCanonicalFile()
assert loadedFile2.infoHash == ih2
}
assert listener.publishedFiles.size() == 2
def loadedFile1 = listener.publishedFiles[0]
assert loadedFile1.file == sharedFile1.getCanonicalFile()
assert loadedFile1.infoHash == ih1
def loadedFile2 = listener.publishedFiles[1]
assert loadedFile2.file == sharedFile2.getCanonicalFile()
assert loadedFile2.infoHash == ih2
}
@Test
void testDownloadedFile() {
writeToSharedFile(sharedFile1, 1)
FileHasher fh = new FileHasher()
InfoHash ih1 = fh.hashFile(sharedFile1)
@Test
void testDownloadedFile() {
writeToSharedFile(sharedFile1, 1)
FileHasher fh = new FileHasher()
InfoHash ih1 = fh.hashFile(sharedFile1)
File persisted = initPersisted()
File persisted = initPersisted()
Destinations dests = new Destinations()
def json1 = [:]
json1.file = getSharedFileJsonName(sharedFile1)
json1.length = 1
json1.infoHash = Base64.encode(ih1.getRoot())
json1.hashList = [Base64.encode(ih1.getHashList())]
json1.sources = [ dests.dest1.toBase64(), dests.dest2.toBase64()]
Destinations dests = new Destinations()
def json1 = [:]
json1.file = getSharedFileJsonName(sharedFile1)
json1.length = 1
json1.infoHash = Base64.encode(ih1.getRoot())
json1.hashList = [Base64.encode(ih1.getHashList())]
json1.sources = [ dests.dest1.toBase64(), dests.dest2.toBase64()]
json1 = JsonOutput.toJson(json1)
persisted.write json1
json1 = JsonOutput.toJson(json1)
persisted.write json1
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
ps.onUILoadedEvent(null)
Thread.sleep(2000)
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
ps.onUILoadedEvent(null)
Thread.sleep(2000)
assert listener.publishedFiles.size() == 1
def loadedFile1 = listener.publishedFiles[0]
assert loadedFile1 instanceof DownloadedFile
assert loadedFile1.sources.size() == 2
assert loadedFile1.sources.contains(dests.dest1)
assert loadedFile1.sources.contains(dests.dest2)
assert listener.publishedFiles.size() == 1
def loadedFile1 = listener.publishedFiles[0]
assert loadedFile1 instanceof DownloadedFile
assert loadedFile1.sources.size() == 2
assert loadedFile1.sources.contains(dests.dest1)
assert loadedFile1.sources.contains(dests.dest2)
}
}
}

View File

@@ -18,79 +18,79 @@ import net.i2p.data.Base64
class PersisterServiceSavingTest {
File f
FileHasher fh = new FileHasher()
InfoHash ih
SharedFile sf
def fileSource
EventBus eventBus = new EventBus()
File persisted
PersisterService ps
File f
FileHasher fh = new FileHasher()
InfoHash ih
SharedFile sf
def fileSource
EventBus eventBus = new EventBus()
File persisted
PersisterService ps
@Before
void before() {
f = new File("build.gradle")
f = f.getCanonicalFile()
ih = fh.hashFile(f)
fileSource = new FileManager(eventBus, new MuWireSettings()) {
Map<File, SharedFile> getSharedFiles() {
Map<File, SharedFile> rv = new HashMap<>()
rv.put(f, sf)
rv
}
}
persisted = new File("persisted")
persisted.delete()
persisted.deleteOnExit()
}
@Before
void before() {
f = new File("build.gradle")
f = f.getCanonicalFile()
ih = fh.hashFile(f)
fileSource = new FileManager(eventBus, new MuWireSettings()) {
Map<File, SharedFile> getSharedFiles() {
Map<File, SharedFile> rv = new HashMap<>()
rv.put(f, sf)
rv
}
}
persisted = new File("persisted")
persisted.delete()
persisted.deleteOnExit()
}
@After
void after() {
ps?.stop()
}
@After
void after() {
ps?.stop()
}
private static String fromB64(String text) {
DataUtil.readi18nString(Base64.decode(text))
}
@Test
void testSavingSharedFile() {
sf = new SharedFile(f, ih, 0)
@Test
void testSavingSharedFile() {
sf = new SharedFile(f, ih, 0)
ps = new PersisterService(persisted, eventBus, 100, fileSource)
ps.onUILoadedEvent(null)
Thread.sleep(1500)
ps = new PersisterService(persisted, eventBus, 100, fileSource)
ps.onUILoadedEvent(null)
Thread.sleep(1500)
JsonSlurper jsonSlurper = new JsonSlurper()
persisted.eachLine {
def json = jsonSlurper.parseText(it)
assert fromB64(json.file) == f.toString()
assert json.length == f.length()
assert json.infoHash == Base64.encode(ih.getRoot())
assert json.hashList == [Base64.encode(ih.getHashList())]
}
}
JsonSlurper jsonSlurper = new JsonSlurper()
persisted.eachLine {
def json = jsonSlurper.parseText(it)
assert fromB64(json.file) == f.toString()
assert json.length == f.length()
assert json.infoHash == Base64.encode(ih.getRoot())
assert json.hashList == [Base64.encode(ih.getHashList())]
}
}
@Test
void testSavingDownloadedFile() {
Destinations dests = new Destinations()
sf = new DownloadedFile(f, ih, 0, new HashSet([dests.dest1, dests.dest2]))
@Test
void testSavingDownloadedFile() {
Destinations dests = new Destinations()
sf = new DownloadedFile(f, ih, 0, new HashSet([dests.dest1, dests.dest2]))
ps = new PersisterService(persisted, eventBus, 100, fileSource)
ps.onUILoadedEvent(null)
Thread.sleep(1500)
ps = new PersisterService(persisted, eventBus, 100, fileSource)
ps.onUILoadedEvent(null)
Thread.sleep(1500)
JsonSlurper jsonSlurper = new JsonSlurper()
persisted.eachLine {
def json = jsonSlurper.parseText(it)
assert fromB64(json.file) == f.toString()
assert json.length == f.length()
assert json.infoHash == Base64.encode(ih.getRoot())
assert json.hashList == [Base64.encode(ih.getHashList())]
assert json.sources.size() == 2
assert json.sources.contains(dests.dest1.toBase64())
assert json.sources.contains(dests.dest2.toBase64())
}
}
JsonSlurper jsonSlurper = new JsonSlurper()
persisted.eachLine {
def json = jsonSlurper.parseText(it)
assert fromB64(json.file) == f.toString()
assert json.length == f.length()
assert json.infoHash == Base64.encode(ih.getRoot())
assert json.hashList == [Base64.encode(ih.getHashList())]
assert json.sources.size() == 2
assert json.sources.contains(dests.dest1.toBase64())
assert json.sources.contains(dests.dest2.toBase64())
}
}
}

View File

@@ -21,250 +21,250 @@ import net.i2p.data.Destination
class HostCacheTest {
File persist
HostCache cache
File persist
HostCache cache
def trustMock
TrustService trust
def trustMock
TrustService trust
def settingsMock
MuWireSettings settings
def settingsMock
MuWireSettings settings
Destinations destinations = new Destinations()
Destinations destinations = new Destinations()
@Before
void before() {
persist = new File("hostPersist")
persist.delete()
persist.deleteOnExit()
@Before
void before() {
persist = new File("hostPersist")
persist.delete()
persist.deleteOnExit()
trustMock = new MockFor(TrustService.class)
settingsMock = new MockFor(MuWireSettings.class)
}
trustMock = new MockFor(TrustService.class)
settingsMock = new MockFor(MuWireSettings.class)
}
@After
void after() {
cache?.stop()
trustMock.verify trust
settingsMock.verify settings
Thread.sleep(150)
}
@After
void after() {
cache?.stop()
trustMock.verify trust
settingsMock.verify settings
Thread.sleep(150)
}
private void initMocks() {
trust = trustMock.proxyInstance()
settings = settingsMock.proxyInstance()
cache = new HostCache(trust, persist, 100, settings, new Destination())
cache.start()
Thread.sleep(150)
}
private void initMocks() {
trust = trustMock.proxyInstance()
settings = settingsMock.proxyInstance()
cache = new HostCache(trust, persist, 100, settings, new Destination())
cache.start()
Thread.sleep(150)
}
@Test
void testEmpty() {
initMocks()
assert cache.getHosts(5).size() == 0
assert cache.getGoodHosts(5).size() == 0
}
@Test
void testEmpty() {
initMocks()
assert cache.getHosts(5).size() == 0
assert cache.getGoodHosts(5).size() == 0
}
@Test
void testOnDiscoveredEvent() {
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.NEUTRAL
}
settingsMock.ignore.allowUntrusted { true }
@Test
void testOnDiscoveredEvent() {
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.NEUTRAL
}
settingsMock.ignore.allowUntrusted { true }
initMocks()
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
def rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
def rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
assert cache.getGoodHosts(5).size() == 0
}
assert cache.getGoodHosts(5).size() == 0
}
@Test
void testOnDiscoveredUntrustedHost() {
trustMock.demand.getLevel { d ->
assert d == destinations.dest1
TrustLevel.DISTRUSTED
}
@Test
void testOnDiscoveredUntrustedHost() {
trustMock.demand.getLevel { d ->
assert d == destinations.dest1
TrustLevel.DISTRUSTED
}
initMocks()
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
assert cache.getHosts(5).size() == 0
}
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
assert cache.getHosts(5).size() == 0
}
@Test
void testOnDiscoverNeutralHostsProhibited() {
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.NEUTRAL
}
settingsMock.ignore.allowUntrusted { false }
@Test
void testOnDiscoverNeutralHostsProhibited() {
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.NEUTRAL
}
settingsMock.ignore.allowUntrusted { false }
initMocks()
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
assert cache.getHosts(5).size() == 0
}
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
assert cache.getHosts(5).size() == 0
}
@Test
void test2DiscoveredGoodHosts() {
trustMock.demand.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
trustMock.demand.getLevel { d ->
assert d == destinations.dest2
TrustLevel.TRUSTED
}
trustMock.demand.getLevel{ d -> TrustLevel.TRUSTED }
trustMock.demand.getLevel{ d -> TrustLevel.TRUSTED }
@Test
void test2DiscoveredGoodHosts() {
trustMock.demand.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
trustMock.demand.getLevel { d ->
assert d == destinations.dest2
TrustLevel.TRUSTED
}
trustMock.demand.getLevel{ d -> TrustLevel.TRUSTED }
trustMock.demand.getLevel{ d -> TrustLevel.TRUSTED }
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest2))
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest2))
def rv = cache.getHosts(1)
assert rv.size() == 1
assert rv.contains(destinations.dest1) || rv.contains(destinations.dest2)
}
def rv = cache.getHosts(1)
assert rv.size() == 1
assert rv.contains(destinations.dest1) || rv.contains(destinations.dest2)
}
@Test
void testHostFailed() {
trustMock.demand.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
@Test
void testHostFailed() {
trustMock.demand.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
def endpoint = new Endpoint(destinations.dest1, null, null, null)
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
def endpoint = new Endpoint(destinations.dest1, null, null, null)
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
assert cache.getHosts(5).size() == 0
}
assert cache.getHosts(5).size() == 0
}
@Test
void testFailedHostSuceeds() {
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
@Test
void testFailedHostSuceeds() {
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
def endpoint = new Endpoint(destinations.dest1, null, null, null)
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.SUCCESSFUL))
def endpoint = new Endpoint(destinations.dest1, null, null, null)
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.SUCCESSFUL))
def rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
def rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
rv = cache.getGoodHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
}
rv = cache.getGoodHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
}
@Test
void testFailedOnceNoLongerGood() {
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
@Test
void testFailedOnceNoLongerGood() {
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
def endpoint = new Endpoint(destinations.dest1, null, null, null)
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.SUCCESSFUL))
def endpoint = new Endpoint(destinations.dest1, null, null, null)
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.SUCCESSFUL))
def rv = cache.getHosts(5)
def rv2 = cache.getGoodHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
assert rv == rv2
def rv = cache.getHosts(5)
def rv2 = cache.getGoodHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
assert rv == rv2
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
assert cache.getGoodHosts(5).size() == 0
}
rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
assert cache.getGoodHosts(5).size() == 0
}
@Test
void testDuplicateHostDiscovered() {
trustMock.demand.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
trustMock.demand.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
@Test
void testDuplicateHostDiscovered() {
trustMock.demand.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
trustMock.demand.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
def rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
}
def rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
}
@Test
void testSaving() {
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
Thread.sleep(150)
@Test
void testSaving() {
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
Thread.sleep(150)
assert persist.exists()
int lines = 0
persist.eachLine {
lines++
JsonSlurper slurper = new JsonSlurper()
def json = slurper.parseText(it)
assert json.destination == destinations.dest1.toBase64()
assert json.failures == 0
assert json.successes == 0
}
assert lines == 1
}
assert persist.exists()
int lines = 0
persist.eachLine {
lines++
JsonSlurper slurper = new JsonSlurper()
def json = slurper.parseText(it)
assert json.destination == destinations.dest1.toBase64()
assert json.failures == 0
assert json.successes == 0
}
assert lines == 1
}
@Test
void testLoading() {
def json = [:]
json.destination = destinations.dest1.toBase64()
json.failures = 0
json.successes = 1
json = JsonOutput.toJson(json)
persist.append("${json}\n")
@Test
void testLoading() {
def json = [:]
json.destination = destinations.dest1.toBase64()
json.failures = 0
json.successes = 1
json = JsonOutput.toJson(json)
persist.append("${json}\n")
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
def rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
initMocks()
def rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
assert cache.getGoodHosts(5) == rv
}
assert cache.getGoodHosts(5) == rv
}
}

View File

@@ -4,34 +4,34 @@ import org.junit.Test
class SearchIndexTest {
SearchIndex index
SearchIndex index
private void initIndex(List<String> entries) {
index = new SearchIndex()
entries.each {
index.add(it)
}
}
private void initIndex(List<String> entries) {
index = new SearchIndex()
entries.each {
index.add(it)
}
}
@Test
void testSingleTerm() {
initIndex(["a b.c", "d e.f"])
@Test
void testSingleTerm() {
initIndex(["a b.c", "d e.f"])
def found = index.search(["a"])
assert found.size() == 1
assert found.contains("a b.c")
}
def found = index.search(["a"])
assert found.size() == 1
assert found.contains("a b.c")
}
@Test
void testSingleTermOverlap() {
initIndex(["a b.c", "c d.e"])
@Test
void testSingleTermOverlap() {
initIndex(["a b.c", "c d.e"])
def found = index.search(["c"])
assert found.size() == 2
assert found.contains("a b.c")
assert found.contains("c d.e")
def found = index.search(["c"])
assert found.size() == 2
assert found.contains("a b.c")
assert found.contains("c d.e")
}
}
@Test
public void testDrillDownDoesNotModifyIndex() {
@@ -43,46 +43,46 @@ class SearchIndexTest {
assert found.contains("c d.e")
}
@Test
void testDrillDown() {
initIndex(["a b.c", "c d.e"])
@Test
void testDrillDown() {
initIndex(["a b.c", "c d.e"])
def found = index.search(["c", "e"])
assert found.size() == 1
assert found.contains("c d.e")
}
def found = index.search(["c", "e"])
assert found.size() == 1
assert found.contains("c d.e")
}
@Test
void testNotFound() {
initIndex(["a b.c"])
def found = index.search(["d"])
assert found.size() == 0
}
@Test
void testNotFound() {
initIndex(["a b.c"])
def found = index.search(["d"])
assert found.size() == 0
}
@Test
void testSomeNotFound() {
initIndex(["a b.c"])
def found = index.search(["a","d"])
assert found.size() == 0
@Test
void testSomeNotFound() {
initIndex(["a b.c"])
def found = index.search(["a","d"])
assert found.size() == 0
}
}
@Test
void testRemove() {
initIndex(["a b.c"])
index.remove("a b.c")
def found = index.search(["a"])
assert found.size() == 0
}
@Test
void testRemove() {
initIndex(["a b.c"])
index.remove("a b.c")
def found = index.search(["a"])
assert found.size() == 0
}
@Test
void testRemoveOverlap() {
initIndex(["a b.c", "b c.d"])
index.remove("a b.c")
def found = index.search(["b"])
assert found.size() == 1
assert found.contains("b c.d")
}
@Test
void testRemoveOverlap() {
initIndex(["a b.c", "b c.d"])
index.remove("a b.c")
def found = index.search(["b"])
assert found.size() == 1
assert found.contains("b c.d")
}
@Test
void testDuplicateTerm() {

View File

@@ -13,73 +13,73 @@ import net.i2p.data.Destination
class TrustServiceTest {
TrustService service
File persistGood, persistBad
Personas personas = new Personas()
TrustService service
File persistGood, persistBad
Personas personas = new Personas()
@Before
void before() {
persistGood = new File("good.trust")
persistBad = new File("bad.trust")
persistGood.delete()
persistBad.delete()
persistGood.deleteOnExit()
persistBad.deleteOnExit()
service = new TrustService(persistGood, persistBad, 100)
service.start()
}
@Before
void before() {
persistGood = new File("good.trust")
persistBad = new File("bad.trust")
persistGood.delete()
persistBad.delete()
persistGood.deleteOnExit()
persistBad.deleteOnExit()
service = new TrustService(persistGood, persistBad, 100)
service.start()
}
@After
void after() {
service.stop()
}
@After
void after() {
service.stop()
}
@Test
void testEmpty() {
assert TrustLevel.NEUTRAL == service.getLevel(personas.persona1.destination)
assert TrustLevel.NEUTRAL == service.getLevel(personas.persona2.destination)
}
@Test
void testEmpty() {
assert TrustLevel.NEUTRAL == service.getLevel(personas.persona1.destination)
assert TrustLevel.NEUTRAL == service.getLevel(personas.persona2.destination)
}
@Test
void testOnEvent() {
service.onTrustEvent new TrustEvent(level: TrustLevel.TRUSTED, persona: personas.persona1)
service.onTrustEvent new TrustEvent(level: TrustLevel.DISTRUSTED, persona: personas.persona2)
@Test
void testOnEvent() {
service.onTrustEvent new TrustEvent(level: TrustLevel.TRUSTED, persona: personas.persona1)
service.onTrustEvent new TrustEvent(level: TrustLevel.DISTRUSTED, persona: personas.persona2)
assert TrustLevel.TRUSTED == service.getLevel(personas.persona1.destination)
assert TrustLevel.DISTRUSTED == service.getLevel(personas.persona2.destination)
}
assert TrustLevel.TRUSTED == service.getLevel(personas.persona1.destination)
assert TrustLevel.DISTRUSTED == service.getLevel(personas.persona2.destination)
}
@Test
void testPersist() {
service.onTrustEvent new TrustEvent(level: TrustLevel.TRUSTED, persona: personas.persona1)
service.onTrustEvent new TrustEvent(level: TrustLevel.DISTRUSTED, persona: personas.persona2)
@Test
void testPersist() {
service.onTrustEvent new TrustEvent(level: TrustLevel.TRUSTED, persona: personas.persona1)
service.onTrustEvent new TrustEvent(level: TrustLevel.DISTRUSTED, persona: personas.persona2)
Thread.sleep(250)
def trusted = new HashSet<>()
persistGood.eachLine {
trusted.add(new Persona(new ByteArrayInputStream(Base64.decode(it))))
}
def distrusted = new HashSet<>()
persistBad.eachLine {
distrusted.add(new Persona(new ByteArrayInputStream(Base64.decode(it))))
}
Thread.sleep(250)
def trusted = new HashSet<>()
persistGood.eachLine {
trusted.add(new Persona(new ByteArrayInputStream(Base64.decode(it))))
}
def distrusted = new HashSet<>()
persistBad.eachLine {
distrusted.add(new Persona(new ByteArrayInputStream(Base64.decode(it))))
}
assert trusted.size() == 1
assert trusted.contains(personas.persona1)
assert distrusted.size() == 1
assert distrusted.contains(personas.persona2)
}
assert trusted.size() == 1
assert trusted.contains(personas.persona1)
assert distrusted.size() == 1
assert distrusted.contains(personas.persona2)
}
@Test
void testLoad() {
service.stop()
persistGood.append("${personas.persona1.toBase64()}\n")
persistBad.append("${personas.persona2.toBase64()}\n")
service = new TrustService(persistGood, persistBad, 100)
service.start()
Thread.sleep(50)
@Test
void testLoad() {
service.stop()
persistGood.append("${personas.persona1.toBase64()}\n")
persistBad.append("${personas.persona2.toBase64()}\n")
service = new TrustService(persistGood, persistBad, 100)
service.start()
Thread.sleep(50)
assert TrustLevel.TRUSTED == service.getLevel(personas.persona1.destination)
assert TrustLevel.DISTRUSTED == service.getLevel(personas.persona2.destination)
}
assert TrustLevel.TRUSTED == service.getLevel(personas.persona1.destination)
assert TrustLevel.DISTRUSTED == service.getLevel(personas.persona2.destination)
}
}

View File

@@ -7,41 +7,41 @@ import org.junit.Test
class DataUtilTest {
private static void usVal(int value) {
ByteArrayOutputStream baos = new ByteArrayOutputStream()
DataUtil.writeUnsignedShort(value, baos)
def is = new DataInputStream(new ByteArrayInputStream(baos.toByteArray()))
assert is.readUnsignedShort() == value
}
@Test
void testUnsignedShort() {
usVal(0)
usVal(20)
usVal(Short.MAX_VALUE)
usVal(Short.MAX_VALUE + 1)
usVal(0xFFFF)
private static void usVal(int value) {
ByteArrayOutputStream baos = new ByteArrayOutputStream()
DataUtil.writeUnsignedShort(value, baos)
def is = new DataInputStream(new ByteArrayInputStream(baos.toByteArray()))
assert is.readUnsignedShort() == value
}
@Test
void testUnsignedShort() {
usVal(0)
usVal(20)
usVal(Short.MAX_VALUE)
usVal(Short.MAX_VALUE + 1)
usVal(0xFFFF)
try {
usVal(0xFFFF + 1)
fail()
} catch (IllegalArgumentException expected) {}
}
try {
usVal(0xFFFF + 1)
fail()
} catch (IllegalArgumentException expected) {}
}
private static header(int value) {
byte [] header = new byte[3]
DataUtil.packHeader(value, header)
assert value == DataUtil.readLength(header)
}
private static header(int value) {
byte [] header = new byte[3]
DataUtil.packHeader(value, header)
assert value == DataUtil.readLength(header)
}
@Test
void testHeader() {
header(0)
header(1)
header(556)
header(8 * 1024 * 1024 - 1)
try {
header(8 * 1024 * 1024)
fail()
} catch (IllegalArgumentException expected) {}
}
@Test
void testHeader() {
header(0)
header(1)
header(556)
header(8 * 1024 * 1024 - 1)
try {
header(8 * 1024 * 1024)
fail()
} catch (IllegalArgumentException expected) {}
}
}

View File

@@ -1,5 +1,5 @@
group = com.muwire
version = 0.4.4
version = 0.4.8
groovyVersion = 2.4.15
slf4jVersion = 1.7.25
spockVersion = 1.1-groovy-2.4

34
gradlew vendored
View File

@@ -11,21 +11,21 @@
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
ls=$(ls -ld "$PRG")
link=$(expr "$ls" : '.*-> \(.*\)$')
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
PRG=$(dirname "$PRG")"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
SAVED="$(pwd)"
cd "$(dirname "$PRG")/" >/dev/null
APP_HOME="$(pwd -P)"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=$(basename "$0")
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
@@ -49,7 +49,7 @@ cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
case "$(uname)" in
CYGWIN* )
cygwin=true
;;
@@ -90,7 +90,7 @@ fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
MAX_FD_LIMIT=$(ulimit -H -n)
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
@@ -111,12 +111,12 @@ fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
APP_HOME=$(cygpath --path --mixed "$APP_HOME")
CLASSPATH=$(cygpath --path --mixed "$CLASSPATH")
JAVACMD=$(cygpath --unix "$JAVACMD")
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null)
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
@@ -130,13 +130,13 @@ if $cygwin ; then
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
CHECK=$(echo "$arg"|egrep -c "$OURCYGPATTERN" -)
CHECK2=$(echo "$arg"|egrep -c "^-") ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg")
else
eval `echo args$i`="\"$arg\""
eval $(echo args$i)="\"$arg\""
fi
i=$((i+1))
done

View File

@@ -165,6 +165,24 @@ class MainFrameController {
core.eventBus.publish( new TrustEvent(persona : result.sender, level : TrustLevel.DISTRUSTED))
}
@ControllerAction
void trustPersonaFromSearch() {
int selected = builder.getVariable("searches-table").getSelectedRow()
if (selected < 0)
return
Persona p = model.searches[selected].originator
core.eventBus.publish( new TrustEvent(persona : p, level : TrustLevel.TRUSTED) )
}
@ControllerAction
void distrustPersonaFromSearch() {
int selected = builder.getVariable("searches-table").getSelectedRow()
if (selected < 0)
return
Persona p = model.searches[selected].originator
core.eventBus.publish( new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED) )
}
@ControllerAction
void cancel() {
def downloader = model.downloads[selectedDownload()].downloader
@@ -191,27 +209,38 @@ class MainFrameController {
int row = view.getSelectedTrustTablesRow(tableName)
if (row < 0)
return
builder.getVariable(tableName).model.fireTableDataChanged()
core.eventBus.publish(new TrustEvent(persona : list[row], level : level))
}
@ControllerAction
void markTrusted() {
markTrust("distrusted-table", TrustLevel.TRUSTED, model.distrusted)
model.markTrustedButtonEnabled = false
model.markNeutralFromDistrustedButtonEnabled = false
}
@ControllerAction
void markNeutralFromDistrusted() {
markTrust("distrusted-table", TrustLevel.NEUTRAL, model.distrusted)
model.markTrustedButtonEnabled = false
model.markNeutralFromDistrustedButtonEnabled = false
}
@ControllerAction
void markDistrusted() {
markTrust("trusted-table", TrustLevel.DISTRUSTED, model.trusted)
model.subscribeButtonEnabled = false
model.markDistrustedButtonEnabled = false
model.markNeutralFromTrustedButtonEnabled = false
}
@ControllerAction
void markNeutralFromTrusted() {
markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted)
model.subscribeButtonEnabled = false
model.markDistrustedButtonEnabled = false
model.markNeutralFromTrustedButtonEnabled = false
}
@ControllerAction
@@ -223,6 +252,9 @@ class MainFrameController {
core.muOptions.trustSubscriptions.add(p)
saveMuWireSettings()
core.eventBus.publish(new TrustSubscriptionEvent(persona : p, subscribe : true))
model.subscribeButtonEnabled = false
model.markDistrustedButtonEnabled = false
model.markNeutralFromTrustedButtonEnabled = false
}
@ControllerAction

View File

@@ -27,6 +27,7 @@ class TrustListController {
return
Persona p = model.trusted[selectedRow]
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED))
view.fireUpdate("trusted-table")
}
@ControllerAction
@@ -36,6 +37,7 @@ class TrustListController {
return
Persona p = model.distrusted[selectedRow]
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED))
view.fireUpdate("distrusted-table")
}
@ControllerAction
@@ -45,6 +47,7 @@ class TrustListController {
return
Persona p = model.trusted[selectedRow]
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED))
view.fireUpdate("trusted-table")
}
@ControllerAction
@@ -54,5 +57,6 @@ class TrustListController {
return
Persona p = model.distrusted[selectedRow]
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED))
view.fireUpdate("distrusted-table")
}
}

View File

@@ -49,7 +49,7 @@ class Ready extends AbstractLifecycleHandler {
log.info("creating new properties")
props = new MuWireSettings()
props.embeddedRouter = Boolean.parseBoolean(System.getProperties().getProperty("embeddedRouter"))
props.updateType = System.getProperty("updateType")
props.updateType = System.getProperty("updateType","jar")
def nickname
while (true) {
nickname = JOptionPane.showInputDialog(null,

View File

@@ -1,6 +1,8 @@
package com.muwire.gui
import java.util.concurrent.ConcurrentHashMap
import java.util.Calendar
import java.util.UUID
import javax.annotation.Nonnull
import javax.inject.Inject
@@ -20,6 +22,7 @@ import com.muwire.core.download.Downloader
import com.muwire.core.files.AllFilesLoadedEvent
import com.muwire.core.files.FileDownloadedEvent
import com.muwire.core.files.FileHashedEvent
import com.muwire.core.files.FileHashingEvent
import com.muwire.core.files.FileLoadedEvent
import com.muwire.core.files.FileSharedEvent
import com.muwire.core.files.FileUnsharedEvent
@@ -70,12 +73,19 @@ class MainFrameModel {
@Observable int connections
@Observable String me
@Observable int loadedFiles
@Observable File hashingFile
@Observable boolean downloadActionEnabled
@Observable boolean trustButtonsEnabled
@Observable boolean cancelButtonEnabled
@Observable boolean retryButtonEnabled
@Observable boolean pauseButtonEnabled
@Observable String resumeButtonText
@Observable boolean subscribeButtonEnabled
@Observable boolean markNeutralFromTrustedButtonEnabled
@Observable boolean markDistrustedButtonEnabled
@Observable boolean markNeutralFromDistrustedButtonEnabled
@Observable boolean markTrustedButtonEnabled
@Observable boolean reviewButtonEnabled
@Observable boolean updateButtonEnabled
@Observable boolean unsubscribeButtonEnabled
@@ -140,6 +150,7 @@ class MainFrameModel {
core.eventBus.register(ConnectionEvent.class, this)
core.eventBus.register(DisconnectionEvent.class, this)
core.eventBus.register(FileHashedEvent.class, this)
core.eventBus.register(FileHashingEvent.class, this)
core.eventBus.register(FileLoadedEvent.class, this)
core.eventBus.register(UploadEvent.class, this)
core.eventBus.register(UploadFinishedEvent.class, this)
@@ -256,7 +267,17 @@ class MainFrameModel {
}
}
void onFileHashingEvent(FileHashingEvent e) {
runInsideUIAsync {
loadedFiles = shared.size()
hashingFile = e.hashingFile
}
}
void onFileHashedEvent(FileHashedEvent e) {
runInsideUIAsync {
hashingFile = null
}
if (e.error != null)
return // TODO do something
if (infoHashes.contains(e.sharedFile.infoHash))
@@ -264,6 +285,7 @@ class MainFrameModel {
infoHashes.add(e.sharedFile.infoHash)
runInsideUIAsync {
shared << e.sharedFile
loadedFiles = shared.size()
JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged()
}
@@ -275,6 +297,7 @@ class MainFrameModel {
infoHashes.add(e.loadedFile.infoHash)
runInsideUIAsync {
shared << e.loadedFile
loadedFiles = shared.size()
JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged()
}
@@ -286,6 +309,7 @@ class MainFrameModel {
return
runInsideUIAsync {
shared.remove(e.unsharedFile)
loadedFiles = shared.size()
JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged()
}
@@ -355,10 +379,32 @@ class MainFrameModel {
return
}
runInsideUIAsync {
searches.addFirst(new IncomingSearch(search : search, replyTo : e.replyTo, originator : e.originator))
JTable table = builder.getVariable("searches-table")
Boolean searchFound = false
Iterator searchIter = searches.iterator()
while ( searchIter.hasNext() ) {
IncomingSearch searchEle = searchIter.next()
if ( searchEle.search == search
&& searchEle.originator == e.originator
&& searchEle.uuid == e.searchEvent.getUuid() ) {
searchIter.remove()
table.model.fireTableDataChanged()
searchFound = true
searchEle.count++
searchEle.timestamp = Calendar.getInstance()
searches.addFirst(searchEle)
break
}
}
if (!searchFound) {
searches.addFirst(new IncomingSearch(search, e.replyTo, e.originator, e.searchEvent.getUuid()))
}
while(searches.size() > 200)
searches.removeLast()
JTable table = builder.getVariable("searches-table")
table.model.fireTableDataChanged()
}
}
@@ -367,6 +413,18 @@ class MainFrameModel {
String search
Destination replyTo
Persona originator
long count
UUID uuid
Calendar timestamp
IncomingSearch( String search, Destination replyTo, Persona originator, UUID uuid ) {
this.search = search
this.replyTo = replyTo
this.originator = originator
this.uuid = uuid
this.count = 1
this.timestamp = Calendar.getInstance()
}
}
void onUpdateAvailableEvent(UpdateAvailableEvent e) {

View File

@@ -44,7 +44,7 @@ class I2PStatusView {
label(text : "Participating Tunnels", constraints : gbc(gridx:0, gridy:4))
label(text : bind {model.participatingTunnels}, constraints : gbc(gridx: 1, gridy:4))
label(text : "Participating Bandwidth", constraints : gbc(gridx:0, gridy:5))
label(text : bind {model.participatingBW}, constraints : gbc(gridx: 1, gridy:6))
label(text : bind {model.participatingBW}, constraints : gbc(gridx: 1, gridy:5))
label(text : "Active Peers", constraints : gbc(gridx:0, gridy:6))
label(text : bind {model.activePeers}, constraints : gbc(gridx: 1, gridy:6))
label(text : "Receive Bps (15 seconds)", constraints : gbc(gridx:0, gridy:7))

View File

@@ -26,7 +26,6 @@ import com.muwire.core.MuWireSettings
import com.muwire.core.download.Downloader
import com.muwire.core.files.FileSharedEvent
import com.muwire.core.trust.RemoteTrustList
import java.awt.BorderLayout
import java.awt.CardLayout
import java.awt.FlowLayout
@@ -129,13 +128,9 @@ class MainFrameView {
scrollPane (constraints : BorderLayout.CENTER) {
downloadsTable = table(id : "downloads-table", autoCreateRowSorter : true) {
tableModel(list: model.downloads) {
closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.downloader.file.getName()})
closureColumn(header: "Name", preferredWidth: 300, type: String, read : {row -> row.downloader.file.getName()})
closureColumn(header: "Status", preferredWidth: 50, type: String, read : {row -> row.downloader.getCurrentState().toString()})
closureColumn(header: "Progress", preferredWidth: 20, type: String, read: { row ->
int pieces = row.downloader.nPieces
int done = row.downloader.donePieces()
"$done/$pieces pieces".toString()
})
closureColumn(header: "Progress", preferredWidth: 70, type: Downloader, read: { row -> row.downloader })
closureColumn(header: "Sources", preferredWidth : 10, type: Integer, read : {row -> row.downloader.activeWorkers()})
closureColumn(header: "Speed", preferredWidth: 50, type:String, read :{row ->
DataHelper.formatSize2Decimal(row.downloader.speed(), false) + "B/sec"
@@ -154,36 +149,53 @@ class MainFrameView {
panel (constraints: "uploads window"){
gridLayout(cols : 1, rows : 2)
panel {
gridLayout(cols : 2, rows : 1)
panel {
borderLayout()
panel (constraints : BorderLayout.NORTH) {
button(text : "Add directories to watch", actionPerformed : watchDirectories)
borderLayout()
panel (constraints : BorderLayout.NORTH) {
label(text : bind {
if (model.hashingFile == null) {
""
} else {
"hashing: " + model.hashingFile.getAbsolutePath() + " (" + DataHelper.formatSize2Decimal(model.hashingFile.length(), false).toString() + "B)"
}
})
}
panel (border : etchedBorder(), constraints : BorderLayout.CENTER) {
gridLayout(cols : 2, rows : 1)
panel {
borderLayout()
scrollPane (constraints : BorderLayout.CENTER) {
table(id : "watched-directories-table", autoCreateRowSorter: true) {
tableModel(list : model.watched) {
closureColumn(header: "Watched Directories", type : String, read : { it })
}
}
}
}
scrollPane (constraints : BorderLayout.CENTER) {
table(id : "watched-directories-table", autoCreateRowSorter: true) {
tableModel(list : model.watched) {
closureColumn(header: "Watched Directories", type : String, read : { it })
panel {
borderLayout()
scrollPane(constraints : BorderLayout.CENTER) {
table(id : "shared-files-table", autoCreateRowSorter: true) {
tableModel(list : model.shared) {
closureColumn(header : "Name", preferredWidth : 500, type : String, read : {row -> row.getCachedPath()})
closureColumn(header : "Size", preferredWidth : 100, type : Long, read : {row -> row.getCachedLength() })
}
}
}
}
}
panel {
borderLayout()
panel (constraints : BorderLayout.NORTH) {
panel (constraints : BorderLayout.SOUTH) {
gridLayout(rows:1, cols:2)
panel {
button(text : "Add directories to watch", actionPerformed : watchDirectories)
button(text : "Share files", actionPerformed : shareFiles)
}
scrollPane(constraints : BorderLayout.CENTER) {
table(id : "shared-files-table", autoCreateRowSorter: true) {
tableModel(list : model.shared) {
closureColumn(header : "Name", preferredWidth : 500, type : String, read : {row -> row.getCachedPath()})
closureColumn(header : "Size", preferredWidth : 100, type : Long, read : {row -> row.getCachedLength() })
}
}
panel {
label("Shared:")
label(text : bind {model.loadedFiles.toString()})
}
}
}
panel {
panel (border : etchedBorder()) {
borderLayout()
panel (constraints : BorderLayout.NORTH){
label("Uploads")
@@ -200,7 +212,18 @@ class MainFrameView {
row.getDownloader()
})
closureColumn(header : "Remote Pieces", type : String, read : { row ->
"${row.getDonePieces()}/${row.getTotalPieces()}".toString()
int pieces = row.getTotalPieces()
int done = row.getDonePieces()
int percent = -1
if ( pieces != 0 ) {
percent = (done * 100) / pieces
}
long size = row.getTotalSize()
String totalSize = ""
if (size >= 0 ) {
totalSize = " of " + DataHelper.formatSize2Decimal(size, false) + "B"
}
String.format("%02d", percent) + "% ${totalSize} ($done/$pieces pcs)".toString()
})
}
}
@@ -247,6 +270,14 @@ class MainFrameView {
return it.replyTo.toBase32()
}
})
closureColumn(header : "Count", type : String, read : {
it.count.toString()
})
closureColumn(header : "Timestamp", type : String, read : {
String.format("%02d", it.timestamp.get(Calendar.HOUR_OF_DAY)) + ":" +
String.format("%02d", it.timestamp.get(Calendar.MINUTE)) + ":" +
String.format("%02d", it.timestamp.get(Calendar.SECOND))
})
}
}
}
@@ -265,11 +296,11 @@ class MainFrameView {
}
}
}
panel (constraints : BorderLayout.EAST) {
panel (constraints : BorderLayout.SOUTH) {
gridBagLayout()
button(text : "Mark Neutral", constraints : gbc(gridx: 0, gridy: 0), markNeutralFromTrustedAction)
button(text : "Mark Distrusted", constraints : gbc(gridx: 0, gridy:1), markDistrustedAction)
button(text : "Subscribe", constraints : gbc(gridx: 0, gridy : 2), subscribeAction)
button(text : "Subscribe", enabled : bind {model.subscribeButtonEnabled}, constraints : gbc(gridx: 0, gridy : 0), subscribeAction)
button(text : "Mark Neutral", enabled : bind {model.markNeutralFromTrustedButtonEnabled}, constraints : gbc(gridx: 1, gridy: 0), markNeutralFromTrustedAction)
button(text : "Mark Distrusted", enabled : bind {model.markDistrustedButtonEnabled}, constraints : gbc(gridx: 2, gridy:0), markDistrustedAction)
}
}
panel (border : etchedBorder()){
@@ -281,10 +312,10 @@ class MainFrameView {
}
}
}
panel(constraints : BorderLayout.WEST) {
panel(constraints : BorderLayout.SOUTH) {
gridBagLayout()
button(text: "Mark Neutral", constraints: gbc(gridx: 0, gridy: 0), markNeutralFromDistrustedAction)
button(text: "Mark Trusted", constraints : gbc(gridx: 0, gridy : 1), markTrustedAction)
button(text: "Mark Neutral", enabled : bind {model.markNeutralFromDistrustedButtonEnabled}, constraints: gbc(gridx: 0, gridy: 0), markNeutralFromDistrustedAction)
button(text: "Mark Trusted", enabled : bind {model.markTrustedButtonEnabled}, constraints : gbc(gridx: 1, gridy : 0), markTrustedAction)
}
}
}
@@ -296,11 +327,11 @@ class MainFrameView {
scrollPane(constraints : BorderLayout.CENTER) {
table(id : "subscription-table", autoCreateRowSorter : true) {
tableModel(list : model.subscriptions) {
closureColumn(header : "Name", type: String, read : {it.persona.getHumanReadableName()})
closureColumn(header : "Trusted", type: Integer, read : {it.good.size()})
closureColumn(header : "Distrusted", type: Integer, read : {it.bad.size()})
closureColumn(header : "Status", type: String, read : {it.status.toString()})
closureColumn(header : "Last Updated", type : String, read : {
closureColumn(header : "Name", preferredWidth: 200, type: String, read : {it.persona.getHumanReadableName()})
closureColumn(header : "Trusted", preferredWidth : 20, type: Integer, read : {it.good.size()})
closureColumn(header : "Distrusted", preferredWidth: 20, type: Integer, read : {it.bad.size()})
closureColumn(header : "Status", preferredWidth: 30, type: String, read : {it.status.toString()})
closureColumn(header : "Last Updated", preferredWidth: 200, type : String, read : {
if (it.timestamp == 0)
return "Never"
else
@@ -375,9 +406,11 @@ class MainFrameView {
def centerRenderer = new DefaultTableCellRenderer()
centerRenderer.setHorizontalAlignment(JLabel.CENTER)
downloadsTable.setDefaultRenderer(Integer.class, centerRenderer)
downloadsTable.setDefaultRenderer(Downloader.class, new DownloadProgressRenderer())
downloadsTable.rowSorter.addRowSorterListener({evt -> lastDownloadSortEvent = evt})
downloadsTable.rowSorter.setSortsOnUpdates(true)
downloadsTable.rowSorter.setComparator(2, new DownloaderComparator())
downloadsTable.addMouseListener(new MouseAdapter() {
@Override
@@ -422,9 +455,18 @@ class MainFrameView {
// searches table
def searchesTable = builder.getVariable("searches-table")
JPopupMenu searchTableMenu = new JPopupMenu()
JMenuItem copySearchToClipboard = new JMenuItem("Copy search to clipboard")
copySearchToClipboard.addActionListener({mvcGroup.view.copySearchToClipboard(searchesTable)})
JMenuItem trustSearcher = new JMenuItem("Trust searcher")
trustSearcher.addActionListener({mvcGroup.controller.trustPersonaFromSearch()})
JMenuItem distrustSearcher = new JMenuItem("Distrust searcher")
distrustSearcher.addActionListener({mvcGroup.controller.distrustPersonaFromSearch()})
searchTableMenu.add(copySearchToClipboard)
searchTableMenu.add(trustSearcher)
searchTableMenu.add(distrustSearcher)
searchesTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
@@ -461,6 +503,7 @@ class MainFrameView {
// subscription table
def subscriptionTable = builder.getVariable("subscription-table")
subscriptionTable.setDefaultRenderer(Integer.class, centerRenderer)
subscriptionTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["subscription-table"] = evt})
subscriptionTable.rowSorter.setSortsOnUpdates(true)
selectionModel = subscriptionTable.getSelectionModel()
@@ -488,6 +531,11 @@ class MainFrameView {
model.updateButtonEnabled = true
model.unsubscribeButtonEnabled = true
break
case RemoteTrustList.Status.UPDATE_FAILED:
model.reviewButtonEnabled = false
model.updateButtonEnabled = true
model.unsubscribeButtonEnabled = true
break
}
})
@@ -495,11 +543,37 @@ class MainFrameView {
def trustedTable = builder.getVariable("trusted-table")
trustedTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["trusted-table"] = evt})
trustedTable.rowSorter.setSortsOnUpdates(true)
selectionModel = trustedTable.getSelectionModel()
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
selectionModel.addListSelectionListener({
int selectedRow = getSelectedTrustTablesRow("trusted-table")
if (selectedRow < 0) {
model.subscribeButtonEnabled = false
model.markDistrustedButtonEnabled = false
model.markNeutralFromTrustedButtonEnabled = false
} else {
model.subscribeButtonEnabled = true
model.markDistrustedButtonEnabled = true
model.markNeutralFromTrustedButtonEnabled = true
}
})
// distrusted table
def distrustedTable = builder.getVariable("distrusted-table")
distrustedTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["distrusted-table"] = evt})
distrustedTable.rowSorter.setSortsOnUpdates(true)
selectionModel = distrustedTable.getSelectionModel()
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
selectionModel.addListSelectionListener({
int selectedRow = getSelectedTrustTablesRow("distrusted-table")
if (selectedRow < 0) {
model.markTrustedButtonEnabled = false
model.markNeutralFromDistrustedButtonEnabled = false
} else {
model.markTrustedButtonEnabled = true
model.markNeutralFromDistrustedButtonEnabled = true
}
})
}
private static void showPopupMenu(JPopupMenu menu, MouseEvent event) {

View File

@@ -83,10 +83,12 @@ class TrustListView {
def trustedTable = builder.getVariable("trusted-table")
trustedTable.rowSorter.addRowSorterListener({evt -> sortEvents["trusted-table"] = evt})
trustedTable.rowSorter.setSortsOnUpdates(true)
trustedTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
def distrustedTable = builder.getVariable("distrusted-table")
distrustedTable.rowSorter.addRowSorterListener({evt -> sortEvents["distrusted-table"] = evt})
distrustedTable.rowSorter.setSortsOnUpdates(true)
distrustedTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
dialog.getContentPane().add(mainPanel)
@@ -110,4 +112,9 @@ class TrustListView {
selectedRow = table.rowSorter.convertRowIndexToModel(selectedRow)
selectedRow
}
void fireUpdate(String tableName) {
def table = builder.getVariable(tableName)
table.model.fireTableDataChanged()
}
}

View File

@@ -0,0 +1,39 @@
package com.muwire.gui
import javax.swing.JComponent
import javax.swing.JLabel
import javax.swing.JTable
import javax.swing.table.DefaultTableCellRenderer
import com.muwire.core.download.Downloader
import net.i2p.data.DataHelper
class DownloadProgressRenderer extends DefaultTableCellRenderer {
DownloadProgressRenderer() {
setHorizontalAlignment(JLabel.CENTER)
}
@Override
JComponent getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
Downloader d = (Downloader) value
int pieces = d.nPieces
int done = d.donePieces()
int percent = -1
if (pieces != 0)
percent = (done * 100 / pieces)
String totalSize = DataHelper.formatSize2Decimal(d.length, false) + "B"
setText(String.format("%2d", percent) + "% of ${totalSize} ($done/$pieces pcs)".toString())
if (isSelected) {
setForeground(table.getSelectionForeground())
setBackground(table.getSelectionBackground())
} else {
setForeground(table.getForeground())
setBackground(table.getBackground())
}
this
}
}

View File

@@ -0,0 +1,13 @@
package com.muwire.gui
import com.muwire.core.download.Downloader
class DownloaderComparator implements Comparator<Downloader>{
@Override
public int compare(Downloader o1, Downloader o2) {
double d1 = o1.donePieces() * 1.0 / o1.nPieces
double d2 = o2.donePieces() * 1.0 / o2.nPieces
return Double.compare(d1, d2);
}
}

View File

@@ -11,7 +11,7 @@ class Crawler {
final def pinger
final def hostPool
final int parallel
final int parallel
final Map<Destination, Host> inFlight = new HashMap<>()
@@ -20,7 +20,7 @@ class Crawler {
Crawler(pinger, hostPool, int parallel) {
this.pinger = pinger
this.hostPool = hostPool
this.parallel = parallel
this.parallel = parallel
}
synchronized def handleCrawlerPong(pong, Destination source) {
@@ -74,15 +74,15 @@ class Crawler {
return Boolean.parseBoolean(value.toString())
}
synchronized def startCrawl() {
if (!inFlight.isEmpty()) {
inFlight.values().each { hostPool.fail(it) }
inFlight.clear()
}
currentUUID = UUID.randomUUID()
hostPool.getUnverified(parallel).each {
inFlight.put(it.destination, it)
pinger.ping(it, currentUUID)
}
}
synchronized def startCrawl() {
if (!inFlight.isEmpty()) {
inFlight.values().each { hostPool.fail(it) }
inFlight.clear()
}
currentUUID = UUID.randomUUID()
hostPool.getUnverified(parallel).each {
inFlight.put(it.destination, it)
pinger.ping(it, currentUUID)
}
}
}

View File

@@ -11,6 +11,7 @@ import net.i2p.client.I2PSession
import net.i2p.client.I2PSessionMuxedListener
import net.i2p.client.datagram.I2PDatagramDissector
import net.i2p.client.datagram.I2PDatagramMaker
import net.i2p.crypto.SigType
import net.i2p.util.SystemVersion
import net.i2p.data.*
@@ -43,7 +44,7 @@ public class HostCache {
def session
if (!keyfile.exists()) {
def os = new FileOutputStream(keyfile);
myDest = i2pClient.createDestination(os)
myDest = i2pClient.createDestination(os, SigType.EdDSA_SHA512_Ed25519)
os.close()
println "No key.dat file was found, so creating a new destination."
println "This is the destination you want to give out for your new HostCache"
@@ -55,14 +56,17 @@ public class HostCache {
session = i2pClient.createSession(new FileInputStream(keyfile), props)
myDest = session.getMyDestination()
// initialize hostpool and crawler
HostPool hostPool = new HostPool(3, 60 * 60 * 1000)
Pinger pinger = new Pinger(session)
Crawler crawler = new Crawler(pinger, hostPool, 5)
// initialize hostpool and crawler
HostPool hostPool = new HostPool(3, 60 * 60 * 1000)
Pinger pinger = new Pinger(session)
Crawler crawler = new Crawler(pinger, hostPool, 5)
Timer timer = new Timer("timer", true)
timer.schedule({hostPool.age()} as TimerTask, 1000,1000)
timer.schedule({crawler.startCrawl()} as TimerTask, 10000, 10000)
Timer timer = new Timer("timer", true)
timer.schedule({hostPool.age()} as TimerTask, 1000,1000)
timer.schedule({crawler.startCrawl()} as TimerTask, 10000, 10000)
File verified = new File("verified.json")
File unverified = new File("unverified.json")
timer.schedule({hostPool.serialize(verified, unverified)} as TimerTask, 10000, 60 * 60 * 1000)
session.addMuxedSessionListener(new Listener(hostPool: hostPool, toReturn: 2, crawler: crawler),
I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY)
@@ -74,9 +78,9 @@ public class HostCache {
static class Listener implements I2PSessionMuxedListener {
final def json = new JsonSlurper()
def hostPool
int toReturn
def crawler
def hostPool
int toReturn
def crawler
void reportAbuse(I2PSession sesison, int severity) {}
void disconnected(I2PSession session) {
@@ -109,18 +113,18 @@ public class HostCache {
switch(payload.type) {
case "Ping" :
log.info("ping from $b32")
if (payload.leaf == null) {
log.warning("ping didn't specify if leaf from $b32")
return
}
payload.leaf = Boolean.parseBoolean(payload.leaf.toString())
if (!payload.leaf)
hostPool.addUnverified(new Host(destination: sender))
respond(session, sender, payload)
if (payload.leaf == null) {
log.warning("ping didn't specify if leaf from $b32")
return
}
payload.leaf = Boolean.parseBoolean(payload.leaf.toString())
if (!payload.leaf)
hostPool.addUnverified(new Host(destination: sender))
respond(session, sender, payload)
break
case "CrawlerPong":
log.info("CrawlerPong from $b32")
crawler.handleCrawlerPong(payload, sender)
crawler.handleCrawlerPong(payload, sender)
break
default:
log.warning("Unexpected message type ${payload.type}, dropping from $b32")
@@ -132,16 +136,16 @@ public class HostCache {
void messageAvailable(I2PSession session, int msgId, long size) {
}
def respond(session, destination, ping) {
def respond(session, destination, ping) {
def pongs = hostPool.getVerified(toReturn, ping.leaf)
pongs = pongs.stream().map({ x -> x.destination.toBase64() }).collect(Collectors.toList())
def pongs = hostPool.getVerified(toReturn, ping.leaf)
pongs = pongs.stream().map({ x -> x.destination.toBase64() }).collect(Collectors.toList())
def pong = [type:"Pong", version: 1, pongs: pongs]
pong = JsonOutput.toJson(pong)
def maker = new I2PDatagramMaker(session)
pong = maker.makeI2PDatagram(pong.bytes)
session.sendMessage(destination, pong, I2PSession.PROTO_DATAGRAM, 0, 0)
}
def pong = [type:"Pong", version: 1, pongs: pongs]
pong = JsonOutput.toJson(pong)
def maker = new I2PDatagramMaker(session)
pong = maker.makeI2PDatagram(pong.bytes)
session.sendMessage(destination, pong, I2PSession.PROTO_DATAGRAM, 0, 0)
}
}
}

View File

@@ -2,6 +2,8 @@ package com.muwire.hostcache
import java.util.stream.Collectors
import groovy.json.JsonOutput
class HostPool {
final def maxFailures
@@ -10,7 +12,7 @@ class HostPool {
def verified = new HashMap()
def unverified = new HashMap()
HostPool() {}
HostPool() {}
HostPool(maxFailures, maxAge) {
this.maxAge = maxAge
this.maxFailures = maxFailures
@@ -51,7 +53,7 @@ class HostPool {
synchronized def fail(host) {
if (!unverified.containsKey(host.destination))
return
return
host.verificationFailures++
}
@@ -74,4 +76,25 @@ class HostPool {
}
}
}
synchronized void serialize(File verifiedFile, File unverifiedFile) {
write(verifiedFile, verified.values())
write(unverifiedFile, unverified.values())
}
private void write(File target, Collection hosts) {
JsonOutput jsonOutput = new JsonOutput()
target.withPrintWriter { writer ->
hosts.each {
def json = [:]
json.destination = it.destination.toBase64()
json.verifyTime = it.verifyTime
json.leafSlots = it.leafSlots
json.peerSlots = it.peerSlots
json.verificationFailures = it.verificationFailures
def str = jsonOutput.toJson(json)
writer.println(str)
}
}
}
}

View File

@@ -12,7 +12,7 @@ class Pinger {
}
def ping(host, uuid) {
def maker = new I2PDatagramMaker(session)
def maker = new I2PDatagramMaker(session)
def payload = new HashMap()
payload.type = "CrawlerPing"
payload.version = 1

View File

@@ -10,129 +10,129 @@ import net.i2p.data.Destination
class CrawlerTest {
def pingerMock
def pinger
def pingerMock
def pinger
def hostPoolMock
def hostPool
def hostPoolMock
def hostPool
def crawler
def crawler
Destinations destinations = new Destinations()
final Host host = new Host(destination: new Destination())
final Host host1 = new Host(destination: destinations.dest1)
final Host host2 = new Host(destination: destinations.dest2)
Destinations destinations = new Destinations()
final Host host = new Host(destination: new Destination())
final Host host1 = new Host(destination: destinations.dest1)
final Host host2 = new Host(destination: destinations.dest2)
final int parallel = 5
final int parallel = 5
@Before
void before() {
pingerMock = new MockFor(Pinger)
hostPoolMock = new MockFor(HostPool)
}
@Before
void before() {
pingerMock = new MockFor(Pinger)
hostPoolMock = new MockFor(HostPool)
}
@After
void after() {
hostPoolMock.verify hostPool
pingerMock.verify pinger
}
@After
void after() {
hostPoolMock.verify hostPool
pingerMock.verify pinger
}
private def initCrawler() {
pinger = pingerMock.proxyInstance()
hostPool = hostPoolMock.proxyInstance()
crawler = new Crawler(pinger, hostPool, parallel)
private def initCrawler() {
pinger = pingerMock.proxyInstance()
hostPool = hostPoolMock.proxyInstance()
crawler = new Crawler(pinger, hostPool, parallel)
}
}
@Test
void testBadJson() {
initCrawler()
def unpingedHost = new Host(destination : new Destination())
crawler.handleCrawlerPong(null, new Destination())
}
@Test
void testBadJson() {
initCrawler()
def unpingedHost = new Host(destination : new Destination())
crawler.handleCrawlerPong(null, new Destination())
}
@Test
void testStartCrawl() {
hostPoolMock.demand.getUnverified { n ->
assert n == parallel
[host]
}
pingerMock.demand.ping { h,uuid -> assert h == host }
@Test
void testStartCrawl() {
hostPoolMock.demand.getUnverified { n ->
assert n == parallel
[host]
}
pingerMock.demand.ping { h,uuid -> assert h == host }
initCrawler()
crawler.startCrawl()
initCrawler()
crawler.startCrawl()
}
}
@Test
void testFailsUnanswered() {
hostPoolMock.demand.getUnverified {n -> [host]}
hostPoolMock.demand.fail { h -> assert h == host }
hostPoolMock.demand.getUnverified {n -> [:]}
pingerMock.demand.ping {h,uuid -> }
initCrawler()
@Test
void testFailsUnanswered() {
hostPoolMock.demand.getUnverified {n -> [host]}
hostPoolMock.demand.fail { h -> assert h == host }
hostPoolMock.demand.getUnverified {n -> [:]}
pingerMock.demand.ping {h,uuid -> }
initCrawler()
crawler.startCrawl()
crawler.startCrawl()
}
crawler.startCrawl()
crawler.startCrawl()
}
@Test
void testVerifiesAnswered() {
def currentUUID
hostPoolMock.demand.getUnverified { n -> [host1] }
hostPoolMock.demand.verify { h -> assert h == host1 }
pingerMock.demand.ping { h, uuid -> currentUUID = uuid }
@Test
void testVerifiesAnswered() {
def currentUUID
hostPoolMock.demand.getUnverified { n -> [host1] }
hostPoolMock.demand.verify { h -> assert h == host1 }
pingerMock.demand.ping { h, uuid -> currentUUID = uuid }
initCrawler()
initCrawler()
crawler.startCrawl()
crawler.startCrawl()
def pong = [uuid : currentUUID.toString(), leafSlots : "false", peerSlots: "false", peers: [] ]
crawler.handleCrawlerPong(pong, host1.destination)
}
def pong = [uuid : currentUUID.toString(), leafSlots : "false", peerSlots: "false", peers: [] ]
crawler.handleCrawlerPong(pong, host1.destination)
}
@Test
void testWrongSourceIgnored() {
def currentUUID
hostPoolMock.demand.getUnverified { n -> [host1] }
pingerMock.demand.ping { h, uuid -> currentUUID = uuid }
@Test
void testWrongSourceIgnored() {
def currentUUID
hostPoolMock.demand.getUnverified { n -> [host1] }
pingerMock.demand.ping { h, uuid -> currentUUID = uuid }
initCrawler()
initCrawler()
crawler.startCrawl()
crawler.startCrawl()
def pong = [uuid : currentUUID.toString(), leafSlots : "false", peerSlots: "false", peers: [] ]
crawler.handleCrawlerPong(pong, host2.destination)
}
def pong = [uuid : currentUUID.toString(), leafSlots : "false", peerSlots: "false", peers: [] ]
crawler.handleCrawlerPong(pong, host2.destination)
}
@Test
void testHost1CarriesHost2() {
def currentUUID
hostPoolMock.demand.getUnverified { n -> [host1] }
hostPoolMock.demand.addUnverified { h -> assert h == host2 }
hostPoolMock.demand.verify { h -> assert h == host1 }
pingerMock.demand.ping { h, uuid -> currentUUID = uuid }
@Test
void testHost1CarriesHost2() {
def currentUUID
hostPoolMock.demand.getUnverified { n -> [host1] }
hostPoolMock.demand.addUnverified { h -> assert h == host2 }
hostPoolMock.demand.verify { h -> assert h == host1 }
pingerMock.demand.ping { h, uuid -> currentUUID = uuid }
initCrawler()
initCrawler()
crawler.startCrawl()
crawler.startCrawl()
def pong = [uuid : currentUUID.toString(), leafSlots : "false", peerSlots: "false", peers: [destinations.dest2.toBase64()] ]
crawler.handleCrawlerPong(pong, host1.destination)
}
def pong = [uuid : currentUUID.toString(), leafSlots : "false", peerSlots: "false", peers: [destinations.dest2.toBase64()] ]
crawler.handleCrawlerPong(pong, host1.destination)
}
@Test
void testWrongUUID() {
def currentUUID
hostPoolMock.demand.getUnverified { n -> [host1] }
hostPoolMock.demand.fail { h -> assert h == host1 }
pingerMock.demand.ping { h, uuid -> currentUUID = uuid }
@Test
void testWrongUUID() {
def currentUUID
hostPoolMock.demand.getUnverified { n -> [host1] }
hostPoolMock.demand.fail { h -> assert h == host1 }
pingerMock.demand.ping { h, uuid -> currentUUID = uuid }
initCrawler()
initCrawler()
crawler.startCrawl()
crawler.startCrawl()
def pong = [uuid : UUID.randomUUID().toString(), leafSlots : "false", peerSlots: "false", peers: [] ]
crawler.handleCrawlerPong(pong, host1.destination)
}
def pong = [uuid : UUID.randomUUID().toString(), leafSlots : "false", peerSlots: "false", peers: [] ]
crawler.handleCrawlerPong(pong, host1.destination)
}
}

View File

@@ -4,6 +4,6 @@ import net.i2p.data.Destination
class Destinations {
Destination dest1 = new Destination("KvwWPKMSAtzf7Yruj8TQaHi2jaQpSNsXJskbpmSBTxkcYlDB2GllH~QBu-cs4FSYdaRmKDUUx7793jjnYJgTMbrjqeIL5-BTORZ09n6PUfhSejDpJjdkUxaV1OHRatfYs70RNBv7rvdj1-nXUow5tMfOJtoWVocUoKefUGFQFbJLDDkBqjm1kFyKFZv6m6S6YqXxBgVB1qYicooy67cNQF5HLUFtP15pk5fMDNGz5eNCjPfC~2Gp8FF~OpSy92HT0XN7uAMJykPcbdnWfcvVwqD7eS0K4XEnsqnMPLEiMAhqsugEFiFqtB3Wmm7UHVc03lcAfRhr1e2uZBNFTtM2Uol4MD5sCCKRZVHGcH-WGPSEz0BM5YO~Xi~dQ~N3NVud32PVzhh8xoGcAlhTqMqAbRJndCv-H6NflX90pYmbirCTIDOaR9758mThrqX0d4CwCn4jFXer52l8Qe8CErGoLuB-4LL~Gwrn7R1k7ZQc2PthkqeW8MfigyiN7hZVkul9AAAA")
Destination dest2 = new Destination("KvwWPKMSAtzf7Yruj8TQaHi2jaQpSNsXJskbpmSBTxkcYlDB2GllH~QBu-cs4FSYdaRmKDUUx7793jjnYJgTMbrjqeIL5-BTORZ09n6PUfhSejDpJjdkUxaV1OHRatfYs70RNBv7rvdj1-nXUow5tMfOJtoWVocUoKefUGFQFbJLDDkBqjm1kFyKFZv6m6S6YqXxBgVB1qYicooy67cNQF5HLUFtP15pk5fMDNGz5eNCjPfC~2Gp8FF~OpSy92HT0XN7uAMJykPcbdnWfcvVwqD7eS0K4XEnsqnMPLEiMAhqsugEFiFqtB3Wmm7UHVc03lcAfRhr1e2uZBNFTtM2Uol4MD5sCCKRZVHGcH-WGPSEz0BM5YO~Xi~dQ~N3NVud32PVzhh8xoGcAlhTqMqAbRJndCv-H6NflX90pYmbirCTIDOaR9758mThrqX0d4CwCn4jFXer52l8Qe8CErGoLuB-4LL~Gwrn7R1k7ZQc2PthkqeW8MfigyiN7hZVkul8AAAA")
Destination dest1 = new Destination("KvwWPKMSAtzf7Yruj8TQaHi2jaQpSNsXJskbpmSBTxkcYlDB2GllH~QBu-cs4FSYdaRmKDUUx7793jjnYJgTMbrjqeIL5-BTORZ09n6PUfhSejDpJjdkUxaV1OHRatfYs70RNBv7rvdj1-nXUow5tMfOJtoWVocUoKefUGFQFbJLDDkBqjm1kFyKFZv6m6S6YqXxBgVB1qYicooy67cNQF5HLUFtP15pk5fMDNGz5eNCjPfC~2Gp8FF~OpSy92HT0XN7uAMJykPcbdnWfcvVwqD7eS0K4XEnsqnMPLEiMAhqsugEFiFqtB3Wmm7UHVc03lcAfRhr1e2uZBNFTtM2Uol4MD5sCCKRZVHGcH-WGPSEz0BM5YO~Xi~dQ~N3NVud32PVzhh8xoGcAlhTqMqAbRJndCv-H6NflX90pYmbirCTIDOaR9758mThrqX0d4CwCn4jFXer52l8Qe8CErGoLuB-4LL~Gwrn7R1k7ZQc2PthkqeW8MfigyiN7hZVkul9AAAA")
Destination dest2 = new Destination("KvwWPKMSAtzf7Yruj8TQaHi2jaQpSNsXJskbpmSBTxkcYlDB2GllH~QBu-cs4FSYdaRmKDUUx7793jjnYJgTMbrjqeIL5-BTORZ09n6PUfhSejDpJjdkUxaV1OHRatfYs70RNBv7rvdj1-nXUow5tMfOJtoWVocUoKefUGFQFbJLDDkBqjm1kFyKFZv6m6S6YqXxBgVB1qYicooy67cNQF5HLUFtP15pk5fMDNGz5eNCjPfC~2Gp8FF~OpSy92HT0XN7uAMJykPcbdnWfcvVwqD7eS0K4XEnsqnMPLEiMAhqsugEFiFqtB3Wmm7UHVc03lcAfRhr1e2uZBNFTtM2Uol4MD5sCCKRZVHGcH-WGPSEz0BM5YO~Xi~dQ~N3NVud32PVzhh8xoGcAlhTqMqAbRJndCv-H6NflX90pYmbirCTIDOaR9758mThrqX0d4CwCn4jFXer52l8Qe8CErGoLuB-4LL~Gwrn7R1k7ZQc2PthkqeW8MfigyiN7hZVkul8AAAA")
}

View File

@@ -34,52 +34,52 @@ public class Pinger {
def session = i2pClient.createSession(bais, props)
session.addMuxedSessionListener(new Listener(),
I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY)
session.addMuxedSessionListener(new Listener(),
I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY)
session.connect()
def maker = new I2PDatagramMaker(session)
payload = maker.makeI2PDatagram(payload)
session.sendMessage(target, payload, I2PSession.PROTO_DATAGRAM, 0, 0)
println "Sent message, going to sleep"
Thread.sleep(Integer.MAX_VALUE)
println "Sent message, going to sleep"
Thread.sleep(Integer.MAX_VALUE)
}
static class Listener implements I2PSessionMuxedListener {
static class Listener implements I2PSessionMuxedListener {
@Override
public void messageAvailable(I2PSession session, int msgId, long size) {
@Override
public void messageAvailable(I2PSession session, int msgId, long size) {
}
}
@Override
public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromport, int toport) {
def payload = session.receiveMessage(msgId)
def dissector = new I2PDatagramDissector()
try {
dissector.loadI2PDatagram(payload)
def sender = dissector.getSender().toBase32()
payload = new String(dissector.getPayload())
println "From $sender received $payload"
} catch (Exception e) {
e.printStackTrace()
}
}
@Override
public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromport, int toport) {
def payload = session.receiveMessage(msgId)
def dissector = new I2PDatagramDissector()
try {
dissector.loadI2PDatagram(payload)
def sender = dissector.getSender().toBase32()
payload = new String(dissector.getPayload())
println "From $sender received $payload"
} catch (Exception e) {
e.printStackTrace()
}
}
@Override
public void reportAbuse(I2PSession session, int severity) {
println "abuse $severity"
}
@Override
public void reportAbuse(I2PSession session, int severity) {
println "abuse $severity"
}
@Override
public void disconnected(I2PSession session) {
println "disconnected"
}
@Override
public void disconnected(I2PSession session) {
println "disconnected"
}
@Override
public void errorOccurred(I2PSession session, String message, Throwable error) {
println "Error $message $error"
}
@Override
public void errorOccurred(I2PSession session, String message, Throwable error) {
println "Error $message $error"
}
}
}
}