2009-10-17 23:49:59 +00:00
package net.i2p.router.web ;
import java.io.IOException ;
2014-06-15 16:14:13 +00:00
import java.io.Serializable ;
2009-10-17 23:49:59 +00:00
import java.io.Writer ;
import java.util.ArrayList ;
import java.util.Collections ;
import java.util.Comparator ;
import java.util.List ;
import java.util.Map ;
import net.i2p.data.DataHelper ;
import net.i2p.data.Hash ;
2014-08-21 17:36:06 +00:00
import net.i2p.data.router.RouterInfo ;
2009-10-17 23:49:59 +00:00
import net.i2p.data.TunnelId ;
import net.i2p.router.Router ;
import net.i2p.router.RouterContext ;
import net.i2p.router.TunnelInfo ;
import net.i2p.router.tunnel.HopConfig ;
import net.i2p.router.tunnel.pool.TunnelPool ;
import net.i2p.stat.RateStat ;
/ * *
2011-11-14 15:02:57 +00:00
* tunnels . jsp
2009-10-17 23:49:59 +00:00
* /
public class TunnelRenderer {
private RouterContext _context ;
2011-09-07 01:36:52 +00:00
private static final int DISPLAY_LIMIT = 200 ;
2009-10-17 23:49:59 +00:00
public TunnelRenderer ( RouterContext ctx ) {
_context = ctx ;
}
public void renderStatusHTML ( Writer out ) throws IOException {
2010-11-17 22:26:31 +00:00
out . write ( " <div class= \" wideload \" ><h2><a name= \" exploratory \" ></a> " + _ ( " Exploratory tunnels " ) + " (<a href= \" /configtunnels#exploratory \" > " + _ ( " configure " ) + " </a>)</h2> \ n " ) ;
2009-10-17 23:49:59 +00:00
renderPool ( out , _context . tunnelManager ( ) . getInboundExploratoryPool ( ) , _context . tunnelManager ( ) . getOutboundExploratoryPool ( ) ) ;
List < Hash > destinations = null ;
Map < Hash , TunnelPool > clientInboundPools = _context . tunnelManager ( ) . getInboundClientPools ( ) ;
Map < Hash , TunnelPool > clientOutboundPools = _context . tunnelManager ( ) . getOutboundClientPools ( ) ;
2013-11-21 11:31:50 +00:00
destinations = new ArrayList < Hash > ( clientInboundPools . keySet ( ) ) ;
2013-10-04 19:06:39 +00:00
boolean debug = _context . getBooleanProperty ( HelperBase . PROP_ADVANCED ) ;
2009-10-17 23:49:59 +00:00
for ( int i = 0 ; i < destinations . size ( ) ; i + + ) {
Hash client = destinations . get ( i ) ;
2013-07-03 19:14:21 +00:00
boolean isLocal = _context . clientManager ( ) . isLocal ( client ) ;
if ( ( ! isLocal ) & & ( ! debug ) )
continue ;
2009-10-17 23:49:59 +00:00
TunnelPool in = null ;
TunnelPool outPool = null ;
in = clientInboundPools . get ( client ) ;
outPool = clientOutboundPools . get ( client ) ;
2009-10-28 18:26:50 +00:00
// TODO the following code is duplicated in SummaryHelper
2009-10-17 23:49:59 +00:00
String name = ( in ! = null ? in . getSettings ( ) . getDestinationNickname ( ) : null ) ;
if ( ( name = = null ) & & ( outPool ! = null ) )
name = outPool . getSettings ( ) . getDestinationNickname ( ) ;
if ( name = = null )
name = client . toBase64 ( ) . substring ( 0 , 4 ) ;
out . write ( " <h2><a name= \" " + client . toBase64 ( ) . substring ( 0 , 4 )
2014-08-23 13:44:56 +00:00
+ " \" ></a> " + _ ( " Client tunnels for " ) + ' ' + DataHelper . escapeHTML ( _ ( name ) ) ) ;
2013-07-03 19:14:21 +00:00
if ( isLocal )
2010-11-17 22:26:31 +00:00
out . write ( " (<a href= \" /configtunnels# " + client . toBase64 ( ) . substring ( 0 , 4 ) + " \" > " + _ ( " configure " ) + " </a>)</h2> \ n " ) ;
2009-10-17 23:49:59 +00:00
else
2009-11-11 20:28:13 +00:00
out . write ( " ( " + _ ( " dead " ) + " )</h2> \ n " ) ;
2009-10-17 23:49:59 +00:00
renderPool ( out , in , outPool ) ;
}
2013-11-21 11:31:50 +00:00
List < HopConfig > participating = _context . tunnelDispatcher ( ) . listParticipatingTunnels ( ) ;
2009-10-17 23:49:59 +00:00
Collections . sort ( participating , new TunnelComparator ( ) ) ;
2009-11-11 20:28:13 +00:00
out . write ( " <h2><a name= \" participating \" ></a> " + _ ( " Participating tunnels " ) + " </h2><table> \ n " ) ;
2009-10-26 10:53:53 +00:00
out . write ( " <tr><th> " + _ ( " Receive on " ) + " </th><th> " + _ ( " From " ) + " </th><th> "
2009-10-28 18:26:50 +00:00
+ _ ( " Send on " ) + " </th><th> " + _ ( " To " ) + " </th><th> " + _ ( " Expiration " ) + " </th> "
2009-10-26 10:53:53 +00:00
+ " <th> " + _ ( " Usage " ) + " </th><th> " + _ ( " Rate " ) + " </th><th> " + _ ( " Role " ) + " </th></tr> \ n " ) ;
2009-10-17 23:49:59 +00:00
long processed = 0 ;
RateStat rs = _context . statManager ( ) . getRate ( " tunnel.participatingMessageCount " ) ;
if ( rs ! = null )
processed = ( long ) rs . getRate ( 10 * 60 * 1000 ) . getLifetimeTotalValue ( ) ;
int inactive = 0 ;
2011-09-07 01:36:52 +00:00
int displayed = 0 ;
2009-10-17 23:49:59 +00:00
for ( int i = 0 ; i < participating . size ( ) ; i + + ) {
2014-06-26 15:26:58 +00:00
HopConfig cfg = participating . get ( i ) ;
2011-09-07 01:36:52 +00:00
long count = cfg . getProcessedMessagesCount ( ) ;
if ( count < = 0 ) {
2009-10-17 23:49:59 +00:00
inactive + + ;
continue ;
}
2011-09-07 01:36:52 +00:00
processed + = count ;
if ( + + displayed > DISPLAY_LIMIT )
continue ;
2009-10-17 23:49:59 +00:00
out . write ( " <tr> " ) ;
if ( cfg . getReceiveTunnel ( ) ! = null )
2010-01-02 02:39:24 +00:00
out . write ( " <td class= \" cells \" align= \" center \" > " + cfg . getReceiveTunnel ( ) . getTunnelId ( ) + " </td> " ) ;
2009-10-17 23:49:59 +00:00
else
2010-01-02 02:39:24 +00:00
out . write ( " <td class= \" cells \" align= \" center \" >n/a</td> " ) ;
2009-10-17 23:49:59 +00:00
if ( cfg . getReceiveFrom ( ) ! = null )
2010-01-02 02:39:24 +00:00
out . write ( " <td class= \" cells \" align= \" center \" > " + netDbLink ( cfg . getReceiveFrom ( ) ) + " </td> " ) ;
2009-10-17 23:49:59 +00:00
else
2010-01-02 02:39:24 +00:00
out . write ( " <td class= \" cells \" > </td> " ) ;
2009-10-17 23:49:59 +00:00
if ( cfg . getSendTunnel ( ) ! = null )
2010-01-02 02:39:24 +00:00
out . write ( " <td class= \" cells \" align= \" center \" > " + cfg . getSendTunnel ( ) . getTunnelId ( ) + " </td> " ) ;
2009-10-17 23:49:59 +00:00
else
2010-01-02 02:39:24 +00:00
out . write ( " <td class= \" cells \" > </td> " ) ;
2009-10-17 23:49:59 +00:00
if ( cfg . getSendTo ( ) ! = null )
2010-01-02 02:39:24 +00:00
out . write ( " <td class= \" cells \" align= \" center \" > " + netDbLink ( cfg . getSendTo ( ) ) + " </td> " ) ;
2009-10-17 23:49:59 +00:00
else
2010-01-02 02:39:24 +00:00
out . write ( " <td class= \" cells \" > </td> " ) ;
2009-10-17 23:49:59 +00:00
long timeLeft = cfg . getExpiration ( ) - _context . clock ( ) . now ( ) ;
if ( timeLeft > 0 )
2010-11-06 12:28:38 +00:00
out . write ( " <td class= \" cells \" align= \" center \" > " + DataHelper . formatDuration2 ( timeLeft ) + " </td> " ) ;
2009-10-17 23:49:59 +00:00
else
2010-01-02 02:39:24 +00:00
out . write ( " <td class= \" cells \" align= \" center \" >( " + _ ( " grace period " ) + " )</td> " ) ;
2010-06-29 02:32:08 +00:00
out . write ( " <td class= \" cells \" align= \" center \" > " + cfg . getProcessedMessagesCount ( ) + " KB</td> " ) ;
2009-10-17 23:49:59 +00:00
int lifetime = ( int ) ( ( _context . clock ( ) . now ( ) - cfg . getCreation ( ) ) / 1000 ) ;
if ( lifetime < = 0 )
lifetime = 1 ;
if ( lifetime > 10 * 60 )
lifetime = 10 * 60 ;
2012-03-25 20:42:41 +00:00
int bps = 1024 * cfg . getProcessedMessagesCount ( ) / lifetime ;
2010-10-26 23:38:01 +00:00
out . write ( " <td class= \" cells \" align= \" center \" > " + bps + " Bps</td> " ) ;
2009-10-17 23:49:59 +00:00
if ( cfg . getSendTo ( ) = = null )
2010-01-02 02:39:24 +00:00
out . write ( " <td class= \" cells \" align= \" center \" > " + _ ( " Outbound Endpoint " ) + " </td> " ) ;
2009-10-17 23:49:59 +00:00
else if ( cfg . getReceiveFrom ( ) = = null )
2010-01-02 02:39:24 +00:00
out . write ( " <td class= \" cells \" align= \" center \" > " + _ ( " Inbound Gateway " ) + " </td> " ) ;
2009-10-17 23:49:59 +00:00
else
2010-01-02 02:39:24 +00:00
out . write ( " <td class= \" cells \" align= \" center \" > " + _ ( " Participant " ) + " </td> " ) ;
2009-10-17 23:49:59 +00:00
out . write ( " </tr> \ n " ) ;
}
out . write ( " </table> \ n " ) ;
2011-09-07 01:36:52 +00:00
if ( displayed > DISPLAY_LIMIT )
out . write ( " <div class= \" statusnotes \" ><b> " + _ ( " Limited display to the {0} tunnels with the highest usage " , DISPLAY_LIMIT ) + " </b></div> \ n " ) ;
2009-10-26 10:53:53 +00:00
out . write ( " <div class= \" statusnotes \" ><b> " + _ ( " Inactive participating tunnels " ) + " : " + inactive + " </b></div> \ n " ) ;
2010-06-29 02:32:08 +00:00
out . write ( " <div class= \" statusnotes \" ><b> " + _ ( " Lifetime bandwidth usage " ) + " : " + DataHelper . formatSize2 ( processed * 1024 ) + " B</b></div> \ n " ) ;
2011-11-14 15:02:57 +00:00
//renderPeers(out);
2012-03-02 18:46:34 +00:00
out . write ( " </div> " ) ;
2009-10-17 23:49:59 +00:00
}
2014-06-15 16:14:13 +00:00
private static class TunnelComparator implements Comparator < HopConfig > , Serializable {
2011-11-14 15:02:57 +00:00
public int compare ( HopConfig l , HopConfig r ) {
2012-03-25 20:42:41 +00:00
return ( r . getProcessedMessagesCount ( ) - l . getProcessedMessagesCount ( ) ) ;
2009-10-17 23:49:59 +00:00
}
}
private void renderPool ( Writer out , TunnelPool in , TunnelPool outPool ) throws IOException {
List < TunnelInfo > tunnels = null ;
if ( in = = null )
2013-11-21 11:31:50 +00:00
tunnels = new ArrayList < TunnelInfo > ( ) ;
2009-10-17 23:49:59 +00:00
else
tunnels = in . listTunnels ( ) ;
if ( outPool ! = null )
tunnels . addAll ( outPool . listTunnels ( ) ) ;
long processedIn = ( in ! = null ? in . getLifetimeProcessed ( ) : 0 ) ;
long processedOut = ( outPool ! = null ? outPool . getLifetimeProcessed ( ) : 0 ) ;
int live = 0 ;
int maxLength = 1 ;
for ( int i = 0 ; i < tunnels . size ( ) ; i + + ) {
TunnelInfo info = tunnels . get ( i ) ;
if ( info . getLength ( ) > maxLength )
maxLength = info . getLength ( ) ;
}
2009-10-26 10:53:53 +00:00
out . write ( " <table><tr><th> " + _ ( " In/Out " ) + " </th><th> " + _ ( " Expiry " ) + " </th><th> " + _ ( " Usage " ) + " </th><th> " + _ ( " Gateway " ) + " </th> " ) ;
2009-10-17 23:49:59 +00:00
if ( maxLength > 3 ) {
out . write ( " <th align= \" center \" colspan= \" " + ( maxLength - 2 ) ) ;
2009-10-26 10:53:53 +00:00
out . write ( " \" > " + _ ( " Participants " ) + " </th> " ) ;
2009-10-17 23:49:59 +00:00
}
else if ( maxLength = = 3 ) {
2009-10-26 10:53:53 +00:00
out . write ( " <th> " + _ ( " Participant " ) + " </th> " ) ;
2009-10-17 23:49:59 +00:00
}
if ( maxLength > 1 ) {
2009-10-26 10:53:53 +00:00
out . write ( " <th> " + _ ( " Endpoint " ) + " </th> " ) ;
2009-10-17 23:49:59 +00:00
}
out . write ( " </tr> \ n " ) ;
for ( int i = 0 ; i < tunnels . size ( ) ; i + + ) {
TunnelInfo info = tunnels . get ( i ) ;
long timeLeft = info . getExpiration ( ) - _context . clock ( ) . now ( ) ;
if ( timeLeft < = 0 )
continue ; // don't display tunnels in their grace period
live + + ;
if ( info . isInbound ( ) )
out . write ( " <tr> <td class= \" cells \" align= \" center \" ><img src= \" /themes/console/images/inbound.png \" alt= \" Inbound \" title= \" Inbound \" ></td> " ) ;
else
out . write ( " <tr> <td class= \" cells \" align= \" center \" ><img src= \" /themes/console/images/outbound.png \" alt= \" Outbound \" title= \" Outbound \" ></td> " ) ;
2010-11-06 12:28:38 +00:00
out . write ( " <td class= \" cells \" align= \" center \" > " + DataHelper . formatDuration2 ( timeLeft ) + " </td> \ n " ) ;
2010-06-29 02:32:08 +00:00
out . write ( " <td class= \" cells \" align= \" center \" > " + info . getProcessedMessagesCount ( ) + " KB</td> \ n " ) ;
2009-10-17 23:49:59 +00:00
for ( int j = 0 ; j < info . getLength ( ) ; j + + ) {
Hash peer = info . getPeer ( j ) ;
TunnelId id = ( info . isInbound ( ) ? info . getReceiveTunnelId ( j ) : info . getSendTunnelId ( j ) ) ;
if ( _context . routerHash ( ) . equals ( peer ) ) {
out . write ( " <td class= \" cells \" align= \" center \" > " + ( id = = null ? " " : " " + id ) + " </td> " ) ;
} else {
String cap = getCapacity ( peer ) ;
out . write ( " <td class= \" cells \" align= \" center \" > " + netDbLink ( peer ) + ( id = = null ? " " : " " + id ) + cap + " </td> " ) ;
}
if ( info . getLength ( ) < maxLength & & ( info . getLength ( ) = = 1 | | j = = info . getLength ( ) - 2 ) ) {
for ( int k = info . getLength ( ) ; k < maxLength ; k + + )
out . write ( " <td class= \" cells \" align= \" center \" > </td> " ) ;
}
}
out . write ( " </tr> \ n " ) ;
if ( info . isInbound ( ) )
processedIn + = info . getProcessedMessagesCount ( ) ;
else
processedOut + = info . getProcessedMessagesCount ( ) ;
}
out . write ( " </table> \ n " ) ;
if ( in ! = null ) {
List pending = in . listPending ( ) ;
2010-05-05 16:51:54 +00:00
if ( ! pending . isEmpty ( ) ) {
2009-10-26 10:53:53 +00:00
out . write ( " <div class= \" statusnotes \" ><center><b> " + _ ( " Build in progress " ) + " : " + pending . size ( ) + " " + _ ( " inbound " ) + " </b></center></div> \ n " ) ;
2010-05-05 16:51:54 +00:00
live + = pending . size ( ) ;
}
2009-10-17 23:49:59 +00:00
}
if ( outPool ! = null ) {
List pending = outPool . listPending ( ) ;
2010-05-05 16:51:54 +00:00
if ( ! pending . isEmpty ( ) ) {
2009-10-26 10:53:53 +00:00
out . write ( " <div class= \" statusnotes \" ><center><b> " + _ ( " Build in progress " ) + " : " + pending . size ( ) + " " + _ ( " outbound " ) + " </b></center></div> \ n " ) ;
2010-05-05 16:51:54 +00:00
live + = pending . size ( ) ;
}
2009-10-17 23:49:59 +00:00
}
if ( live < = 0 )
2012-03-02 18:46:34 +00:00
out . write ( " <div class= \" statusnotes \" ><center><b> " + _ ( " No tunnels; waiting for the grace period to end. " ) + " </b></center></div> \ n " ) ;
2010-06-29 02:32:08 +00:00
out . write ( " <div class= \" statusnotes \" ><center><b> " + _ ( " Lifetime bandwidth usage " ) + " : " +
DataHelper . formatSize2 ( processedIn * 1024 ) + " B " + _ ( " in " ) + " , " +
DataHelper . formatSize2 ( processedOut * 1024 ) + " B " + _ ( " out " ) + " </b></center></div> " ) ;
2009-10-17 23:49:59 +00:00
}
2011-11-14 15:02:57 +00:00
/ * * * *
2009-10-17 23:49:59 +00:00
private void renderPeers ( Writer out ) throws IOException {
// count up the peers in the local pools
ObjectCounter < Hash > lc = new ObjectCounter ( ) ;
int tunnelCount = countTunnelsPerPeer ( lc ) ;
// count up the peers in the participating tunnels
ObjectCounter < Hash > pc = new ObjectCounter ( ) ;
int partCount = countParticipatingPerPeer ( pc ) ;
Set < Hash > peers = new HashSet ( lc . objects ( ) ) ;
peers . addAll ( pc . objects ( ) ) ;
List < Hash > peerList = new ArrayList ( peers ) ;
2009-12-12 17:49:54 +00:00
Collections . sort ( peerList , new CountryComparator ( this . _context . commSystem ( ) ) ) ;
2009-10-17 23:49:59 +00:00
2009-11-11 20:28:13 +00:00
out . write ( " <h2><a name= \" peers \" ></a> " + _ ( " Tunnel Counts By Peer " ) + " </h2> \ n " ) ;
2009-12-19 16:40:57 +00:00
out . write ( " <table><tr><th> " + _ ( " Peer " ) + " </th><th> " + _ ( " Our Tunnels " ) + " </th><th> " + _ ( " % of total " ) + " </th><th> " + _ ( " Participating Tunnels " ) + " </th><th> " + _ ( " % of total " ) + " </th></tr> \ n " ) ;
2009-10-17 23:49:59 +00:00
for ( Hash h : peerList ) {
out . write ( " <tr> <td class= \" cells \" align= \" center \" > " ) ;
out . write ( netDbLink ( h ) ) ;
out . write ( " <td class= \" cells \" align= \" center \" > " + lc . count ( h ) ) ;
out . write ( " <td class= \" cells \" align= \" center \" > " ) ;
if ( tunnelCount > 0 )
out . write ( " " + ( lc . count ( h ) * 100 / tunnelCount ) ) ;
else
out . write ( '0' ) ;
out . write ( " <td class= \" cells \" align= \" center \" > " + pc . count ( h ) ) ;
out . write ( " <td class= \" cells \" align= \" center \" > " ) ;
if ( partCount > 0 )
out . write ( " " + ( pc . count ( h ) * 100 / partCount ) ) ;
else
out . write ( '0' ) ;
out . write ( '\n' ) ;
}
2009-12-19 16:40:57 +00:00
out . write ( " <tr class= \" tablefooter \" > <td align= \" center \" ><b> " + _ ( " Totals " ) + " </b> <td align= \" center \" ><b> " + tunnelCount ) ;
2009-10-17 23:49:59 +00:00
out . write ( " </b> <td> </td> <td align= \" center \" ><b> " + partCount ) ;
out . write ( " </b> <td> </td></tr></table></div> \ n " ) ;
}
2011-11-14 15:02:57 +00:00
* * * * /
2009-10-17 23:49:59 +00:00
/* duplicate of that in tunnelPoolManager for now */
/** @return total number of non-fallback expl. + client tunnels */
2011-11-14 15:02:57 +00:00
/ * * * *
2009-10-17 23:49:59 +00:00
private int countTunnelsPerPeer ( ObjectCounter < Hash > lc ) {
List < TunnelPool > pools = new ArrayList ( ) ;
_context . tunnelManager ( ) . listPools ( pools ) ;
int tunnelCount = 0 ;
for ( TunnelPool tp : pools ) {
for ( TunnelInfo info : tp . listTunnels ( ) ) {
if ( info . getLength ( ) > 1 ) {
tunnelCount + + ;
for ( int j = 0 ; j < info . getLength ( ) ; j + + ) {
Hash peer = info . getPeer ( j ) ;
if ( ! _context . routerHash ( ) . equals ( peer ) )
lc . increment ( peer ) ;
}
}
}
}
return tunnelCount ;
}
2011-11-14 15:02:57 +00:00
* * * * /
2009-10-17 23:49:59 +00:00
/** @return total number of part. tunnels */
2011-11-14 15:02:57 +00:00
/ * * * *
2009-10-17 23:49:59 +00:00
private int countParticipatingPerPeer ( ObjectCounter < Hash > pc ) {
List < HopConfig > participating = _context . tunnelDispatcher ( ) . listParticipatingTunnels ( ) ;
for ( HopConfig cfg : participating ) {
Hash from = cfg . getReceiveFrom ( ) ;
if ( from ! = null )
pc . increment ( from ) ;
Hash to = cfg . getSendTo ( ) ;
if ( to ! = null )
pc . increment ( to ) ;
}
return participating . size ( ) ;
}
2009-12-12 17:49:54 +00:00
private static class CountryComparator implements Comparator < Hash > {
public CountryComparator ( CommSystemFacade comm ) {
this . comm = comm ;
}
public int compare ( Hash l , Hash r ) {
// get both countries
String lc = this . comm . getCountry ( l ) ;
String rc = this . comm . getCountry ( r ) ;
2009-12-13 09:24:25 +00:00
// make them non-null
lc = ( lc = = null ) ? " zzzz " : lc ;
rc = ( rc = = null ) ? " zzzz " : rc ;
2009-12-12 17:49:54 +00:00
// let String handle the rest
return lc . compareTo ( rc ) ;
}
private CommSystemFacade comm ;
}
2011-11-14 15:02:57 +00:00
* * * * /
2009-10-17 23:49:59 +00:00
2011-11-14 15:02:57 +00:00
/** cap string */
2009-10-17 23:49:59 +00:00
private String getCapacity ( Hash peer ) {
RouterInfo info = _context . netDb ( ) . lookupRouterInfoLocally ( peer ) ;
if ( info ! = null ) {
String caps = info . getCapabilities ( ) ;
for ( char c = Router . CAPABILITY_BW12 ; c < = Router . CAPABILITY_BW256 ; c + + ) {
if ( caps . indexOf ( c ) > = 0 )
return " " + c ;
}
}
return " " ;
}
private String netDbLink ( Hash peer ) {
return _context . commSystem ( ) . renderPeerHTML ( peer ) ;
}
2009-10-18 14:06:07 +00:00
/** translate a string */
private String _ ( String s ) {
return Messages . getString ( s , _context ) ;
}
2011-09-07 01:36:52 +00:00
/** translate a string */
public String _ ( String s , Object o ) {
return Messages . getString ( s , o , _context ) ;
}
2009-10-17 23:49:59 +00:00
}