diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PeerHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/PeerHelper.java index e5e4aa0fe..d3e59e08c 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PeerHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PeerHelper.java @@ -1,12 +1,38 @@ package net.i2p.router.web; import java.io.IOException; +import java.io.Serializable; +import java.io.Writer; +import java.util.Comparator; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.TreeSet; + +import net.i2p.data.DataHelper; +import net.i2p.data.router.RouterAddress; +import net.i2p.router.transport.Transport; +import net.i2p.router.transport.ntcp.NTCPConnection; +import net.i2p.router.transport.ntcp.NTCPTransport; +import net.i2p.router.transport.udp.PeerState; +import net.i2p.router.transport.udp.UDPTransport; +import static net.i2p.router.web.UDPSorters.*; +import net.i2p.util.SystemVersion; + public class PeerHelper extends HelperBase { private int _sortFlags; private String _urlBase; + // Opera doesn't have the char, TODO check UA + //private static final String THINSP = " / "; + private static final String THINSP = " / "; + public PeerHelper() {} public void setSort(String flags) { @@ -24,7 +50,7 @@ public class PeerHelper extends HelperBase { public String getPeerSummary() { try { - _context.commSystem().renderStatusHTML(_out, _urlBase, _sortFlags); + renderStatusHTML(_out, _urlBase, _sortFlags); // boring and not worth translating //_context.bandwidthLimiter().renderStatusHTML(_out); } catch (IOException ioe) { @@ -32,4 +58,578 @@ public class PeerHelper extends HelperBase { } return ""; } + + /** + * Warning - blocking, very slow, queries the active UPnP router, + * will take many seconds if it has vanished. + * + * @since 0.9.31 moved from TransportManager + */ + private void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException { + if (isAdvanced()) { + out.write("

"); + out.write(_t("Status")); + out.write(": "); + out.write(_t(_context.commSystem().getStatus().toStatusString())); + out.write("

"); + } + SortedMap transports = _context.commSystem().getTransports(); + for (Map.Entry e : transports.entrySet()) { + String style = e.getKey(); + Transport t = e.getValue(); + if (style.equals("NTCP")) { + NTCPTransport nt = (NTCPTransport) t; + render(nt, out, urlBase, sortFlags); + } else if (style.equals("SSU")) { + UDPTransport ut = (UDPTransport) t; + render(ut, out, urlBase, sortFlags); + } else { + // pluggable (none yet_ + t.renderStatusHTML(out, urlBase, sortFlags); + } + } + + if (!transports.isEmpty()) { + out.write(getTransportsLegend()); + } + + StringBuilder buf = new StringBuilder(4*1024); + buf.append("

").append(_t("Router Transport Addresses")).append("

\n");
+        for (Transport t : transports.values()) {
+            if (t.hasCurrentAddress()) {
+                for (RouterAddress ra : t.getCurrentAddresses()) {
+                    buf.append(ra.toString());
+                    buf.append("\n\n");
+                }
+            } else {
+                buf.append(_t("{0} is used for outbound connections only", t.getStyle()));
+                buf.append("\n\n");
+            }
+        }
+        buf.append("
\n"); + out.write(buf.toString()); + // UPnP Status + _context.commSystem().renderStatusHTML(_out, _urlBase, _sortFlags); + out.write("

\n"); + out.flush(); + } + + /** + * @since 0.9.31 moved from TransportManager + */ + private final String getTransportsLegend() { + StringBuilder buf = new StringBuilder(1024); + buf.append("

") + .append(_t("Your transport connection limits are automatically set based on your configured bandwidth.")) + .append('\n') + .append(_t("To override these limits, add the settings i2np.ntcp.maxConnections=nnn and i2np.udp.maxConnections=nnn on the advanced configuration page.")) + .append("

\n"); + buf.append("

").append(_t("Definitions")).append("

") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + //.append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("\n") + .append("
").append(_t("Peer")).append("").append(_t("The remote peer, identified by router hash")).append("
").append(_t("Dir")).append("\"Inbound\" ").append(_t("Inbound connection")).append("
\"Outbound\" ").append(_t("Outbound connection")).append("
\"V\" ").append(_t("They offered to introduce us (help other peers traverse our firewall)")).append("
\"^\" ").append(_t("We offered to introduce them (help other peers traverse their firewall)")).append("
").append(_t("Idle")).append("").append(_t("How long since a packet has been received / sent")).append("
").append(_t("In/Out")).append("").append(_t("The smoothed inbound / outbound transfer rate (KBytes per second)")).append("
").append(_t("Up")).append("").append(_t("How long ago this connection was established")).append("
").append(_t("Skew")).append("").append(_t("The difference between the peer's clock and your own")).append("
CWND").append(_t("The congestion window, which is how many bytes can be sent without an acknowledgement")).append(" /
").append(_t("The number of sent messages awaiting acknowledgement")).append(" /
").append(_t("The maximum number of concurrent messages to send")).append(" /
").append(_t("The number of pending sends which exceed congestion window")).append("
SST").append(_t("The slow start threshold")).append("
RTT").append(_t("The round trip time in milliseconds")).append("
").append(_t("Dev")).append("").append(_t("The standard deviation of the round trip time in milliseconds")).append("
RTO").append(_t("The retransmit timeout in milliseconds")).append("
MTU").append(_t("Current maximum send packet size / estimated maximum receive packet size (bytes)")).append("
").append(_t("TX")).append("").append(_t("The total number of messages sent to the peer")).append("
").append(_t("RX")).append("").append(_t("The total number of messages received from the peer")).append("
").append(_t("Dup TX")).append("").append(_t("The total number of packets retransmitted to the peer")).append("
").append(_t("Dup RX")).append("").append(_t("The total number of duplicate packets received from the peer")).append("
"); + return buf.toString(); + } + + /// begin SSU + + /** + * @since 0.9.31 moved from NTCPTransport + */ + private void render(NTCPTransport nt, Writer out, String urlBase, int sortFlags) throws IOException { + TreeSet peers = new TreeSet(getNTCPComparator(sortFlags)); + peers.addAll(nt.getPeers()); + + long offsetTotal = 0; + float bpsSend = 0; + float bpsRecv = 0; + long totalUptime = 0; + long totalSend = 0; + long totalRecv = 0; + + if (!isAdvanced()) { + for (Iterator iter = peers.iterator(); iter.hasNext(); ) { + // outbound conns get put in the map before they are established + if (!iter.next().isEstablished()) + iter.remove(); + } + } + + StringBuilder buf = new StringBuilder(512); + buf.append("

").append(_t("NTCP connections")).append(": ").append(peers.size()); + buf.append(". ").append(_t("Limit")).append(": ").append(nt.getMaxConnections()); + //buf.append(". ").append(_t("Timeout")).append(": ").append(DataHelper.formatDuration2(_pumper.getIdleTimeout())); + if (_context.getBooleanProperty(PROP_ADVANCED)) { + buf.append(". ").append(_t("Status")).append(": ").append(_t(nt.getReachabilityStatus().toStatusString())); + } + buf.append(".

\n" + + "
\n" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + //"" + + " \n"); + out.write(buf.toString()); + buf.setLength(0); + for (NTCPConnection con : peers) { + buf.append("\n"); + out.write(buf.toString()); + buf.setLength(0); + } + + if (!peers.isEmpty()) { +// buf.append("\n"); + buf.append("\n"); + } + + buf.append("
").append(_t("Peer")).append("").append(_t("Dir")).append("").append(_t("IPv6")).append("").append(_t("Idle")).append("").append(_t("In/Out")).append("").append(_t("Up")).append("").append(_t("Skew")).append("").append(_t("TX")).append("").append(_t("RX")).append("").append(_t("Out Queue")).append("").append(_t("Backlogged?")).append("").append(_t("Reading?")).append("
"); + buf.append(_context.commSystem().renderPeerHTML(con.getRemotePeer().calculateHash())); + //byte[] ip = getIP(con.getRemotePeer().calculateHash()); + //if (ip != null) + // buf.append(' ').append(_context.blocklist().toStr(ip)); + buf.append(""); + if (con.isInbound()) + buf.append("\"Inbound\""); + else + buf.append("\"Outbound\""); + buf.append(""); + if (con.isIPv6()) + buf.append(""); + else + buf.append(" "); + buf.append(""); + buf.append(DataHelper.formatDuration2(con.getTimeSinceReceive())); + buf.append(THINSP).append(DataHelper.formatDuration2(con.getTimeSinceSend())); + buf.append(""); + if (con.getTimeSinceReceive() < 2*60*1000) { + float r = con.getRecvRate(); + buf.append(formatRate(r / 1024)); + bpsRecv += r; + } else { + buf.append(formatRate(0)); + } + buf.append(THINSP); + if (con.getTimeSinceSend() < 2*60*1000) { + float r = con.getSendRate(); + buf.append(formatRate(r / 1024)); + bpsSend += r; + } else { + buf.append(formatRate(0)); + } + //buf.append(" K/s"); + buf.append("").append(DataHelper.formatDuration2(con.getUptime())); + totalUptime += con.getUptime(); + offsetTotal = offsetTotal + con.getClockSkew(); + buf.append("").append(DataHelper.formatDuration2(1000 * con.getClockSkew())); + buf.append("").append(con.getMessagesSent()); + totalSend += con.getMessagesSent(); + buf.append("").append(con.getMessagesReceived()); + totalRecv += con.getMessagesReceived(); + long outQueue = con.getOutboundQueueSize(); + buf.append("").append(outQueue); + buf.append(""); + if (con.isBacklogged()) + buf.append(""); + else + buf.append(" "); + //long readTime = con.getReadTime(); + //if (readTime <= 0) { + // buf.append(" 0"); + //} else { + // buf.append(" ").append(DataHelper.formatDuration(readTime)); + //} + buf.append("

") + .append(ngettext("{0} peer", "{0} peers", peers.size())); + buf.append("").append(formatRate(bpsRecv/1024)).append(THINSP).append(formatRate(bpsSend/1024)).append(""); + buf.append("").append(DataHelper.formatDuration2(totalUptime/peers.size())); + buf.append("").append(DataHelper.formatDuration2(offsetTotal*1000/peers.size())); + buf.append("").append(totalSend).append("").append(totalRecv); + buf.append("  
\n"); + out.write(buf.toString()); + buf.setLength(0); + } + + private static final NumberFormat _rateFmt = new DecimalFormat("#,##0.00"); + + private static String formatRate(float rate) { + synchronized (_rateFmt) { return _rateFmt.format(rate); } + } + + private Comparator getNTCPComparator(int sortFlags) { + Comparator rv = null; + switch (Math.abs(sortFlags)) { + default: + rv = AlphaComparator.instance(); + } + if (sortFlags < 0) + rv = Collections.reverseOrder(rv); + return rv; + } + + private static class AlphaComparator extends PeerComparator { + private static final AlphaComparator _instance = new AlphaComparator(); + public static final AlphaComparator instance() { return _instance; } + } + + private static class PeerComparator implements Comparator, Serializable { + public int compare(NTCPConnection l, NTCPConnection r) { + if (l == null || r == null) + throw new IllegalArgumentException(); + // base64 retains binary ordering + // UM, no it doesn't, but close enough + return l.getRemotePeer().calculateHash().toBase64().compareTo(r.getRemotePeer().calculateHash().toBase64()); + } + } + + /// end NTCP + /// begin SSU + + /** + * @since 0.9.31 moved from UDPTransport + */ + private void render(UDPTransport ut, Writer out, String urlBase, int sortFlags) throws IOException { + TreeSet peers = new TreeSet(getComparator(sortFlags)); + peers.addAll(ut.getPeers()); + long offsetTotal = 0; + + int bpsIn = 0; + int bpsOut = 0; + long uptimeMsTotal = 0; + long cwinTotal = 0; + long rttTotal = 0; + long rtoTotal = 0; + long sendTotal = 0; + long recvTotal = 0; + long resentTotal = 0; + long dupRecvTotal = 0; + int numPeers = 0; + + StringBuilder buf = new StringBuilder(512); + buf.append("

").append(_t("UDP connections")).append(": ").append(peers.size()); + buf.append(". ").append(_t("Limit")).append(": ").append(ut.getMaxConnections()); + //buf.append(". ").append(_t("Timeout")).append(": ").append(DataHelper.formatDuration2(_expireTimeout)); + if (isAdvanced()) { + buf.append(". ").append(_t("Status")).append(": ").append(_t(ut.getReachabilityStatus().toStatusString())); + } + buf.append(".

\n"); + buf.append("
\n"); + buf.append(""); + buf.append("\n"); + buf.append("\n"); + buf.append("\n"); + buf.append("\n"); + buf.append("\n"); + buf.append("\n"); + out.write(buf.toString()); + buf.setLength(0); + long now = _context.clock().now(); + for (PeerState peer : peers) { + if (now-peer.getLastReceiveTime() > 60*60*1000) + continue; // don't include old peers + + buf.append(""); + + buf.append(""); + + long idleIn = Math.max(now-peer.getLastReceiveTime(), 0); + long idleOut = Math.max(now-peer.getLastSendTime(), 0); + + buf.append(""); + + int recvBps = (idleIn > 15*1000 ? 0 : peer.getReceiveBps()); + int sendBps = (idleOut > 15*1000 ? 0 : peer.getSendBps()); + + buf.append(""); + + long uptime = now - peer.getKeyEstablishedTime(); + + buf.append(""); + + buf.append(""); + offsetTotal = offsetTotal + skew; + + long sendWindow = peer.getSendWindowBytes(); + + buf.append(""); + + buf.append(""); + + int rtt = peer.getRTT(); + int rto = peer.getRTO(); + + buf.append(""); + + //buf.append(""); + + buf.append(""); + + buf.append(""); + + long sent = peer.getMessagesSent(); + long recv = peer.getMessagesReceived(); + + buf.append(""); + + buf.append(""); + + //double sent = (double)peer.getPacketsPeriodTransmitted(); + //double sendLostPct = 0; + //if (sent > 0) + // sendLostPct = (double)peer.getPacketsRetransmitted()/(sent); + + long resent = peer.getPacketsRetransmitted(); + long dupRecv = peer.getPacketsReceivedDuplicate(); + + buf.append(""); + + buf.append(""); + + buf.append("\n"); + out.write(buf.toString()); + buf.setLength(0); + + bpsIn += recvBps; + bpsOut += sendBps; + + uptimeMsTotal += uptime; + cwinTotal += sendWindow; + rttTotal += rtt; + rtoTotal += rto; + + sendTotal += sent; + recvTotal += recv; + resentTotal += resent; + dupRecvTotal += dupRecv; + + numPeers++; + } + + if (numPeers > 0) { +// buf.append("\n"); + buf.append("" + + "" + + "\n" + + "\n" + + "\n" + + "\n"); +/**** + if (sortFlags == FLAG_DEBUG) { + buf.append(""); + } +****/ + } // numPeers > 0 + buf.append("
").append(_t("Peer")).append("
"); + if (sortFlags != FLAG_ALPHA) + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by peer hash"), FLAG_ALPHA); + buf.append("
").append(_t("Dir")) + .append("").append(_t("IPv6")) + .append("").append(_t("Idle")).append("
"); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by idle inbound"), FLAG_IDLE_IN); + buf.append(" / "); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by idle outbound"), FLAG_IDLE_OUT); + buf.append("
").append(_t("In/Out")).append("
"); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by inbound rate"), FLAG_RATE_IN); + buf.append(" / "); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by outbound rate"), FLAG_RATE_OUT); + buf.append("
").append(_t("Up")).append("
"); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by connection uptime"), FLAG_UPTIME); + buf.append("
").append(_t("Skew")).append("
"); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by clock skew"), FLAG_SKEW); + buf.append("
CWND
"); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by congestion window"), FLAG_CWND); + buf.append("
SST
"); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by slow start threshold"), FLAG_SSTHRESH); + buf.append("
RTT
"); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by round trip time"), FLAG_RTT); + //buf.append("
").append(_t("Dev")).append("
"); + //appendSortLinks(buf, urlBase, sortFlags, _t("Sort by round trip time deviation"), FLAG_DEV); + buf.append("
RTO
"); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by retransmission timeout"), FLAG_RTO); + buf.append("
MTU
"); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by outbound maximum transmit unit"), FLAG_MTU); + buf.append("
").append(_t("TX")).append("
"); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets sent"), FLAG_SEND); + buf.append("
").append(_t("RX")).append("
"); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets received"), FLAG_RECV); + buf.append("
").append(_t("Dup TX")).append("
"); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets retransmitted"), FLAG_RESEND); + buf.append("
").append(_t("Dup RX")).append("
"); + appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets received more than once"), FLAG_DUP); + buf.append("
"); + buf.append(_context.commSystem().renderPeerHTML(peer.getRemotePeer())); + //byte ip[] = peer.getRemoteIP(); + //if (ip != null) + // buf.append(' ').append(_context.blocklist().toStr(ip)); + buf.append(""); + if (peer.isInbound()) + buf.append("\"Inbound\""); + else + buf.append("\"Outbound\""); + if (peer.getWeRelayToThemAs() > 0) + buf.append("  \"^\""); + if (peer.getTheyRelayToUsAs() > 0) + buf.append("  \"V\""); + + boolean appended = false; + //if (_activeThrottle.isChoked(peer.getRemotePeer())) { + // buf.append("
").append(_t("Choked")).append(""); + // appended = true; + //} + int cfs = peer.getConsecutiveFailedSends(); + if (cfs > 0) { + if (!appended) buf.append("
"); + buf.append(" "); + if (cfs == 1) + buf.append(_t("1 fail")); + else + buf.append(_t("{0} fails", cfs)); + buf.append(""); + appended = true; + } + if (_context.banlist().isBanlisted(peer.getRemotePeer(), "SSU")) { + if (!appended) buf.append("
"); + buf.append(" ").append(_t("Banned")).append(""); + appended = true; + } + //byte[] ip = getIP(peer.getRemotePeer()); + //if (ip != null) + // buf.append(' ').append(_context.blocklist().toStr(ip)); + buf.append("
"); + if (peer.isIPv6()) + buf.append("✓"); + else + buf.append(" "); + buf.append(""); + buf.append(DataHelper.formatDuration2(idleIn)); + buf.append(THINSP); + buf.append(DataHelper.formatDuration2(idleOut)); + buf.append(""); + buf.append(formatKBps(recvBps)); + buf.append(THINSP); + buf.append(formatKBps(sendBps)); + //buf.append(" K/s"); + //buf.append(formatKBps(peer.getReceiveACKBps())); + //buf.append("K/s/"); + //buf.append(formatKBps(peer.getSendACKBps())); + //buf.append("K/s "); + buf.append(""); + buf.append(DataHelper.formatDuration2(uptime)); + buf.append(""); + long skew = peer.getClockSkew(); + buf.append(DataHelper.formatDuration2(skew)); + buf.append(""); + buf.append(sendWindow/1024); + buf.append("K"); + buf.append(THINSP).append(peer.getConcurrentSends()); + buf.append(THINSP).append(peer.getConcurrentSendWindow()); + buf.append(THINSP).append(peer.getConsecutiveSendRejections()); + if (peer.isBacklogged()) + buf.append(' ').append(_t("backlogged")); + buf.append(""); + buf.append(peer.getSlowStartThreshold()/1024); + buf.append("K"); + buf.append(DataHelper.formatDuration2(rtt)); + buf.append(""); + //buf.append(DataHelper.formatDuration2(peer.getRTTDeviation())); + //buf.append(""); + buf.append(DataHelper.formatDuration2(rto)); + buf.append(""); + buf.append(peer.getMTU()).append(THINSP).append(peer.getReceiveMTU()); + + //.append('/'); + //buf.append(peer.getMTUIncreases()).append('/'); + //buf.append(peer.getMTUDecreases()); + buf.append(""); + buf.append(sent); + buf.append(""); + buf.append(recv); + buf.append(""); + //buf.append(formatPct(sendLostPct)); + buf.append(resent); // + "/" + peer.getPacketsPeriodRetransmitted() + "/" + sent); + //buf.append(peer.getPacketRetransmissionRate()); + buf.append(""); + buf.append(dupRecv); //formatPct(recvDupPct)); + buf.append("

") + .append(ngettext("{0} peer", "{0} peers", peers.size())) + .append(""); + buf.append(formatKBps(bpsIn)).append(THINSP).append(formatKBps(bpsOut)); + long x = uptimeMsTotal/numPeers; + buf.append("").append(DataHelper.formatDuration2(x)); + x = offsetTotal/numPeers; + buf.append("").append(DataHelper.formatDuration2(x)).append(""); + buf.append(cwinTotal/(numPeers*1024) + "K"); + buf.append(" "); + buf.append(DataHelper.formatDuration2(rttTotal/numPeers)); + //buf.append(" "); + buf.append(""); + buf.append(DataHelper.formatDuration2(rtoTotal/numPeers)); + buf.append("").append(ut.getMTU(false)).append(""); + buf.append(sendTotal).append("").append(recvTotal).append("").append(resentTotal); + buf.append("").append(dupRecvTotal).append("
"); + buf.append("peersByIdent: ").append(_peersByIdent.size()); + buf.append(" peersByRemoteHost: ").append(_peersByRemoteHost.size()); + int dir = 0; + int indir = 0; + for (RemoteHostId rhi : _peersByRemoteHost.keySet()) { + if (rhi.getIP() != null) + dir++; + else + indir++; + } + buf.append(" pBRH direct: ").append(dir).append(" indirect: ").append(indir); + buf.append("
\n"); + + /***** + long bytesTransmitted = _context.bandwidthLimiter().getTotalAllocatedOutboundBytes(); + // NPE here early + double averagePacketSize = _context.statManager().getRate("udp.sendPacketSize").getLifetimeAverageValue(); + // lifetime value, not just the retransmitted packets of current connections + resentTotal = (long)_context.statManager().getRate("udp.packetsRetransmitted").getLifetimeEventCount(); + double nondupSent = ((double)bytesTransmitted - ((double)resentTotal)*averagePacketSize); + double bwResent = (nondupSent <= 0 ? 0d : ((((double)resentTotal)*averagePacketSize) / nondupSent)); + buf.append("

Percentage of bytes retransmitted (lifetime): ").append(formatPct(bwResent)); + buf.append("

(Includes retransmission required by packet loss)\n"); + *****/ + + out.write(buf.toString()); + buf.setLength(0); + } + + private static final DecimalFormat _fmt = new DecimalFormat("#,##0.00"); + + private static final String formatKBps(int bps) { + synchronized (_fmt) { + return _fmt.format((float)bps/1024); + } + } } diff --git a/router/java/src/net/i2p/router/transport/udp/Sorters.java b/apps/routerconsole/java/src/net/i2p/router/web/UDPSorters.java similarity index 98% rename from router/java/src/net/i2p/router/transport/udp/Sorters.java rename to apps/routerconsole/java/src/net/i2p/router/web/UDPSorters.java index ba9d3497f..0a287282d 100644 --- a/router/java/src/net/i2p/router/transport/udp/Sorters.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/UDPSorters.java @@ -1,17 +1,18 @@ -package net.i2p.router.transport.udp; +package net.i2p.router.web; import java.io.Serializable; import java.util.Collections; import java.util.Comparator; import net.i2p.data.DataHelper; +import net.i2p.router.transport.udp.PeerState; /** * Comparators for various columns * - * @since 0.9.18 moved from UDPTransport + * @since 0.9.31 moved from udp; 0.9.18 moved from UDPTransport */ -class Sorters { +class UDPSorters { static final int FLAG_ALPHA = 0; static final int FLAG_IDLE_IN = 1; diff --git a/history.txt b/history.txt index 791fd0d58..9f693f398 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,6 @@ +2017-05-19 zzz + * Console: Move /peers page rendering from router to console (ticket #1879) + 2017-05-18 str4d Prop from i2p.i2p.str4d.ui: * Backend HTML changes to routerconsole and apps: @@ -57,6 +60,7 @@ Prop from i2p.i2p.str4d.ui: - Consolidation of Hostname/host name/name referenced: now all "Hostname" 2017-05-14 zzz + * Crypto: Fix AES NPE on 4-core RPi (ticket #1989) * i2psnark: Fix HTML double-escape (ticket #1992) * Router: New method to get bandwidth class diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 4e28a2c8d..c10128fe6 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 5; + public final static long BUILD = 6; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java index 7c1370222..d0360b497 100644 --- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java +++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.SortedMap; import java.util.Vector; import net.i2p.data.Hash; @@ -210,11 +211,25 @@ public class CommSystemFacadeImpl extends CommSystemFacade { @Deprecated public void recheckReachability() { _manager.recheckReachability(); } + /** + * As of 0.9.31, only outputs UPnP status + * + * Warning - blocking, very slow, queries the active UPnP router, + * will take many seconds if it has vanished. + */ @Override public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException { _manager.renderStatusHTML(out, urlBase, sortFlags); } + /** + * @return SortedMap of style to Transport (a copy) + * @since 0.9.31 + */ + public SortedMap getTransports() { + return _manager.getTransports(); + } + /** @return non-null, possibly empty */ @Override public List createAddresses() { diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java index 54553657b..385f9b491 100644 --- a/router/java/src/net/i2p/router/transport/TransportManager.java +++ b/router/java/src/net/i2p/router/transport/TransportManager.java @@ -19,6 +19,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.SortedMap; import java.util.TreeMap; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; @@ -342,6 +343,18 @@ public class TransportManager implements TransportEventListener { int getTransportCount() { return _transports.size(); } + /** + * @return SortedMap of style to Transport (a copy) + * @since 0.9.31 + */ + public SortedMap getTransports() { + TreeMap rv = new TreeMap(); + rv.putAll(_transports); + // TODO (also synch) + //rv.putAll(_pluggableTransports); + return rv; + } + /** * How many peers are we currently connected to, that we have * sent a message to or received a message from in the last five minutes. @@ -716,44 +729,12 @@ public class TransportManager implements TransportEventListener { } /** + * As of 0.9.31, only outputs UPnP status + * * Warning - blocking, very slow, queries the active UPnP router, * will take many seconds if it has vanished. */ public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException { - if (_context.getBooleanProperty(PROP_ADVANCED)) { - out.write("

"); - out.write(_t("Status")); - out.write(": "); - out.write(_t(getReachabilityStatus().toStatusString())); - out.write("

"); - } - TreeMap transports = new TreeMap(); - for (Transport t : _transports.values()) { - transports.put(t.getStyle(), t); - } - for (Transport t : transports.values()) { - t.renderStatusHTML(out, urlBase, sortFlags); - } - - if (!_transports.isEmpty()) { - out.write(getTransportsLegend()); - } - - StringBuilder buf = new StringBuilder(4*1024); - buf.append("

").append(_t("Router Transport Addresses")).append("

\n");
-        for (Transport t : _transports.values()) {
-            if (t.hasCurrentAddress()) {
-                for (RouterAddress ra : t.getCurrentAddresses()) {
-                    buf.append(ra.toString());
-                    buf.append("\n\n");
-                }
-            } else {
-                buf.append(_t("{0} is used for outbound connections only", t.getStyle()));
-                buf.append("\n\n");
-            }
-        }
-        buf.append("
\n"); - out.write(buf.toString()); if (SystemVersion.isAndroid()) { // newer androids crash w/ network on IO thread } else if (_upnpManager != null) { @@ -761,46 +742,7 @@ public class TransportManager implements TransportEventListener { } else { out.write("

" + _t("UPnP is not enabled") + "

\n"); } - out.write("

\n"); - out.flush(); } - - - private final String getTransportsLegend() { - StringBuilder buf = new StringBuilder(1024); - buf.append("

") - .append(_t("Your transport connection limits are automatically set based on your configured bandwidth.")) - .append('\n') - .append(_t("To override these limits, add the settings i2np.ntcp.maxConnections=nnn and i2np.udp.maxConnections=nnn on the advanced configuration page.")) - .append("

\n"); - buf.append("

").append(_t("Definitions")).append("

") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - //.append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("\n") - .append("
").append(_t("Peer")).append("").append(_t("The remote peer, identified by router hash")).append("
").append(_t("Dir")).append("\"Inbound\" ").append(_t("Inbound connection")).append("
\"Outbound\" ").append(_t("Outbound connection")).append("
\"V\" ").append(_t("They offered to introduce us (help other peers traverse our firewall)")).append("
\"^\" ").append(_t("We offered to introduce them (help other peers traverse their firewall)")).append("
").append(_t("Idle")).append("").append(_t("How long since a packet has been received / sent")).append("
").append(_t("In/Out")).append("").append(_t("The smoothed inbound / outbound transfer rate (KBytes per second)")).append("
").append(_t("Up")).append("").append(_t("How long ago this connection was established")).append("
").append(_t("Skew")).append("").append(_t("The difference between the peer's clock and your own")).append("
CWND").append(_t("The congestion window, which is how many bytes can be sent without an acknowledgement")).append(" /
").append(_t("The number of sent messages awaiting acknowledgement")).append(" /
").append(_t("The maximum number of concurrent messages to send")).append(" /
").append(_t("The number of pending sends which exceed congestion window")).append("
SST").append(_t("The slow start threshold")).append("
RTT").append(_t("The round trip time in milliseconds")).append("
").append(_t("Dev")).append("").append(_t("The standard deviation of the round trip time in milliseconds")).append("
RTO").append(_t("The retransmit timeout in milliseconds")).append("
MTU").append(_t("Current maximum send packet size / estimated maximum receive packet size (bytes)")).append("
").append(_t("TX")).append("").append(_t("The total number of messages sent to the peer")).append("
").append(_t("RX")).append("").append(_t("The total number of messages received from the peer")).append("
").append(_t("Dup TX")).append("").append(_t("The total number of packets retransmitted to the peer")).append("
").append(_t("Dup RX")).append("").append(_t("The total number of duplicate packets received from the peer")).append("
"); - return buf.toString(); - } - /** * Mark a string for extraction by xgettext and translation. @@ -812,7 +754,6 @@ public class TransportManager implements TransportEventListener { return s; } - private static final String BUNDLE_NAME = "net.i2p.router.web.messages"; /** @@ -821,11 +762,4 @@ public class TransportManager implements TransportEventListener { private final String _t(String s) { return Translate.getString(s, _context, BUNDLE_NAME); } - - /** - * Translate - */ - private final String _t(String s, Object o) { - return Translate.getString(s, o, _context, BUNDLE_NAME); - } } diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java index 5e7bfcb0e..99e70c8c5 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPConnection.java @@ -44,6 +44,8 @@ import net.i2p.util.VersionComparator; /** * Coordinate the connection to a single peer. * + * Public only for UI peers page. Not a public API, not for external use. + * * The NTCP transport sends individual I2NP messages AES/256/CBC encrypted with * a simple checksum. The unencrypted message is encoded as follows: *
@@ -66,7 +68,7 @@ import net.i2p.util.VersionComparator;
  *
* */ -class NTCPConnection implements Closeable { +public class NTCPConnection implements Closeable { private final RouterContext _context; private final Log _log; private SocketChannel _chan; diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java index 29a6abc10..e80c81a9e 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java @@ -11,6 +11,7 @@ import java.nio.channels.SocketChannel; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; @@ -523,6 +524,16 @@ public class NTCPTransport extends TransportImpl { public int countPeers() { return _conByIdent.size(); } + + /** + * For /peers UI only. Not a public API, not for external use. + * + * @return not a copy, do not modify + * @since 0.9.31 + */ + public Collection getPeers() { + return _conByIdent.values(); + } /** * How many peers have we talked to in the last 5 minutes? @@ -1340,158 +1351,13 @@ public class NTCPTransport extends TransportImpl { public void renderStatusHTML(java.io.Writer out, int sortFlags) throws IOException {} + /** + * Does nothing + * @deprecated as of 0.9.31 + */ @Override + @Deprecated public void renderStatusHTML(java.io.Writer out, String urlBase, int sortFlags) throws IOException { - TreeSet peers = new TreeSet(getComparator(sortFlags)); - peers.addAll(_conByIdent.values()); - - long offsetTotal = 0; - float bpsSend = 0; - float bpsRecv = 0; - long totalUptime = 0; - long totalSend = 0; - long totalRecv = 0; - - if (!_context.getBooleanProperty(PROP_ADVANCED)) { - for (Iterator iter = peers.iterator(); iter.hasNext(); ) { - // outbound conns get put in the map before they are established - if (!iter.next().isEstablished()) - iter.remove(); - } - } - - StringBuilder buf = new StringBuilder(512); - buf.append("

").append(_t("NTCP connections")).append(": ").append(peers.size()); - buf.append(". ").append(_t("Limit")).append(": ").append(getMaxConnections()); - buf.append(". ").append(_t("Timeout")).append(": ").append(DataHelper.formatDuration2(_pumper.getIdleTimeout())); - if (_context.getBooleanProperty(PROP_ADVANCED)) { - buf.append(". ").append(_t("Status")).append(": ").append(_t(getReachabilityStatus().toStatusString())); - } - buf.append(".

\n" + - "
\n" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - //"" + - " \n"); - out.write(buf.toString()); - buf.setLength(0); - for (NTCPConnection con : peers) { - buf.append("\n"); - out.write(buf.toString()); - buf.setLength(0); - } - - if (!peers.isEmpty()) { -// buf.append("\n"); - buf.append("\n"); - } - - buf.append("
").append(_t("Peer")).append("").append(_t("Dir")).append("").append(_t("IPv6")).append("").append(_t("Idle")).append("").append(_t("In/Out")).append("").append(_t("Up")).append("").append(_t("Skew")).append("").append(_t("TX")).append("").append(_t("RX")).append("").append(_t("Out Queue")).append("").append(_t("Backlogged?")).append("").append(_t("Reading?")).append("
"); - buf.append(_context.commSystem().renderPeerHTML(con.getRemotePeer().calculateHash())); - //byte[] ip = getIP(con.getRemotePeer().calculateHash()); - //if (ip != null) - // buf.append(' ').append(_context.blocklist().toStr(ip)); - buf.append(""); - if (con.isInbound()) - buf.append("\"Inbound\""); - else - buf.append("\"Outbound\""); - buf.append(""); - if (con.isIPv6()) - buf.append(""); - else - buf.append(" "); - buf.append(""); - buf.append(DataHelper.formatDuration2(con.getTimeSinceReceive())); - buf.append(THINSP).append(DataHelper.formatDuration2(con.getTimeSinceSend())); - buf.append(""); - if (con.getTimeSinceReceive() < 2*60*1000) { - float r = con.getRecvRate(); - buf.append(formatRate(r / 1024)); - bpsRecv += r; - } else { - buf.append(formatRate(0)); - } - buf.append(THINSP); - if (con.getTimeSinceSend() < 2*60*1000) { - float r = con.getSendRate(); - buf.append(formatRate(r / 1024)); - bpsSend += r; - } else { - buf.append(formatRate(0)); - } - //buf.append(" K/s"); - buf.append("").append(DataHelper.formatDuration2(con.getUptime())); - totalUptime += con.getUptime(); - offsetTotal = offsetTotal + con.getClockSkew(); - buf.append("").append(DataHelper.formatDuration2(1000 * con.getClockSkew())); - buf.append("").append(con.getMessagesSent()); - totalSend += con.getMessagesSent(); - buf.append("").append(con.getMessagesReceived()); - totalRecv += con.getMessagesReceived(); - long outQueue = con.getOutboundQueueSize(); - buf.append("").append(outQueue); - buf.append(""); - if (con.isBacklogged()) - buf.append(""); - else - buf.append(" "); - //long readTime = con.getReadTime(); - //if (readTime <= 0) { - // buf.append(" 0"); - //} else { - // buf.append(" ").append(DataHelper.formatDuration(readTime)); - //} - buf.append("

") - .append(ngettext("{0} peer", "{0} peers", peers.size())); - buf.append("").append(formatRate(bpsRecv/1024)).append(THINSP).append(formatRate(bpsSend/1024)).append(""); - buf.append("").append(DataHelper.formatDuration2(totalUptime/peers.size())); - buf.append("").append(DataHelper.formatDuration2(offsetTotal*1000/peers.size())); - buf.append("").append(totalSend).append("").append(totalRecv); - buf.append("  
\n"); - out.write(buf.toString()); - buf.setLength(0); - } - - private static final NumberFormat _rateFmt = new DecimalFormat("#,##0.00"); - - private static String formatRate(float rate) { - synchronized (_rateFmt) { return _rateFmt.format(rate); } - } - - private Comparator getComparator(int sortFlags) { - Comparator rv = null; - switch (Math.abs(sortFlags)) { - default: - rv = AlphaComparator.instance(); - } - if (sortFlags < 0) - rv = Collections.reverseOrder(rv); - return rv; - } - - private static class AlphaComparator extends PeerComparator { - private static final AlphaComparator _instance = new AlphaComparator(); - public static final AlphaComparator instance() { return _instance; } - } - - private static class PeerComparator implements Comparator, Serializable { - public int compare(NTCPConnection l, NTCPConnection r) { - if (l == null || r == null) - throw new IllegalArgumentException(); - // base64 retains binary ordering - // UM, no it doesn't, but close enough - return l.getRemotePeer().calculateHash().toBase64().compareTo(r.getRemotePeer().calculateHash().toBase64()); - } } /** diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState.java b/router/java/src/net/i2p/router/transport/udp/PeerState.java index ca5eef438..afeffa89f 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerState.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerState.java @@ -26,8 +26,11 @@ import net.i2p.util.ConcurrentHashSet; /** * Contain all of the state about a UDP connection to a peer. * This is instantiated only after a connection is fully established. + * + * Public only for UI peers page. Not a public API, not for external use. + * */ -class PeerState { +public class PeerState { private final RouterContext _context; private final Log _log; /** diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index d90ddf29f..f47476d6a 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -9,6 +9,7 @@ import java.net.UnknownHostException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -774,9 +775,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority * The MTU for the socket interface. * To be used as the "large" MTU. * @return limited to range PeerState.MIN_MTU to PeerState.LARGE_MTU. - * @since 0.9.2 + * @since 0.9.2, public since 0.9.31 */ - int getMTU(boolean ipv6) { + public int getMTU(boolean ipv6) { // TODO multiple interfaces of each type return ipv6 ? _mtu_ipv6 : _mtu; } @@ -1166,6 +1167,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority return _peersByIdent.get(remotePeer); } + /** + * For /peers UI only. Not a public API, not for external use. + * + * @return not a copy, do not modify + * @since 0.9.31 + */ + public Collection getPeers() { + return _peersByIdent.values(); + } + /** * Remove and add to peersByRemoteHost map * @since 0.9.3 @@ -2646,322 +2657,15 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority return _dhFactory; } + /** + * Does nothing + * @deprecated as of 0.9.31 + */ @Override + @Deprecated public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException { - TreeSet peers = new TreeSet(getComparator(sortFlags)); - peers.addAll(_peersByIdent.values()); - long offsetTotal = 0; - - int bpsIn = 0; - int bpsOut = 0; - long uptimeMsTotal = 0; - long cwinTotal = 0; - long rttTotal = 0; - long rtoTotal = 0; - long sendTotal = 0; - long recvTotal = 0; - long resentTotal = 0; - long dupRecvTotal = 0; - int numPeers = 0; - - StringBuilder buf = new StringBuilder(512); - buf.append("

").append(_t("UDP connections")).append(": ").append(peers.size()); - buf.append(". ").append(_t("Limit")).append(": ").append(getMaxConnections()); - buf.append(". ").append(_t("Timeout")).append(": ").append(DataHelper.formatDuration2(_expireTimeout)); - if (_context.getBooleanProperty(PROP_ADVANCED)) { - buf.append(". ").append(_t("Status")).append(": ").append(_t(_reachabilityStatus.toStatusString())); - } - buf.append(".

\n"); - buf.append("
\n"); - buf.append(""); - buf.append("\n"); - buf.append("\n"); - buf.append("\n"); - buf.append("\n"); - buf.append("\n"); - buf.append("\n"); - out.write(buf.toString()); - buf.setLength(0); - long now = _context.clock().now(); - for (PeerState peer : peers) { - if (now-peer.getLastReceiveTime() > 60*60*1000) - continue; // don't include old peers - - buf.append(""); - - buf.append(""); - - long idleIn = Math.max(now-peer.getLastReceiveTime(), 0); - long idleOut = Math.max(now-peer.getLastSendTime(), 0); - - buf.append(""); - - int recvBps = (idleIn > 15*1000 ? 0 : peer.getReceiveBps()); - int sendBps = (idleOut > 15*1000 ? 0 : peer.getSendBps()); - - buf.append(""); - - long uptime = now - peer.getKeyEstablishedTime(); - - buf.append(""); - - buf.append(""); - offsetTotal = offsetTotal + skew; - - long sendWindow = peer.getSendWindowBytes(); - - buf.append(""); - - buf.append(""); - - int rtt = peer.getRTT(); - int rto = peer.getRTO(); - - buf.append(""); - - //buf.append(""); - - buf.append(""); - - buf.append(""); - - long sent = peer.getMessagesSent(); - long recv = peer.getMessagesReceived(); - - buf.append(""); - - buf.append(""); - - //double sent = (double)peer.getPacketsPeriodTransmitted(); - //double sendLostPct = 0; - //if (sent > 0) - // sendLostPct = (double)peer.getPacketsRetransmitted()/(sent); - - long resent = peer.getPacketsRetransmitted(); - long dupRecv = peer.getPacketsReceivedDuplicate(); - - buf.append(""); - - buf.append(""); - - buf.append("\n"); - out.write(buf.toString()); - buf.setLength(0); - - bpsIn += recvBps; - bpsOut += sendBps; - - uptimeMsTotal += uptime; - cwinTotal += sendWindow; - rttTotal += rtt; - rtoTotal += rto; - - sendTotal += sent; - recvTotal += recv; - resentTotal += resent; - dupRecvTotal += dupRecv; - - numPeers++; - } - - if (numPeers > 0) { -// buf.append("\n"); - buf.append("" + - "" + - "\n" + - "\n" + - "\n" + - "\n"); - if (sortFlags == FLAG_DEBUG) { - buf.append(""); - } - } // numPeers > 0 - buf.append("
").append(_t("Peer")).append("
"); - if (sortFlags != FLAG_ALPHA) - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by peer hash"), FLAG_ALPHA); - buf.append("
").append(_t("Dir")) - .append("").append(_t("IPv6")) - .append("").append(_t("Idle")).append("
"); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by idle inbound"), FLAG_IDLE_IN); - buf.append(" / "); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by idle outbound"), FLAG_IDLE_OUT); - buf.append("
").append(_t("In/Out")).append("
"); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by inbound rate"), FLAG_RATE_IN); - buf.append(" / "); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by outbound rate"), FLAG_RATE_OUT); - buf.append("
").append(_t("Up")).append("
"); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by connection uptime"), FLAG_UPTIME); - buf.append("
").append(_t("Skew")).append("
"); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by clock skew"), FLAG_SKEW); - buf.append("
CWND
"); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by congestion window"), FLAG_CWND); - buf.append("
SST
"); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by slow start threshold"), FLAG_SSTHRESH); - buf.append("
RTT
"); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by round trip time"), FLAG_RTT); - //buf.append("
").append(_t("Dev")).append("
"); - //appendSortLinks(buf, urlBase, sortFlags, _t("Sort by round trip time deviation"), FLAG_DEV); - buf.append("
RTO
"); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by retransmission timeout"), FLAG_RTO); - buf.append("
MTU
"); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by outbound maximum transmit unit"), FLAG_MTU); - buf.append("
").append(_t("TX")).append("
"); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets sent"), FLAG_SEND); - buf.append("
").append(_t("RX")).append("
"); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets received"), FLAG_RECV); - buf.append("
").append(_t("Dup TX")).append("
"); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets retransmitted"), FLAG_RESEND); - buf.append("
").append(_t("Dup RX")).append("
"); - appendSortLinks(buf, urlBase, sortFlags, _t("Sort by packets received more than once"), FLAG_DUP); - buf.append("
"); - buf.append(_context.commSystem().renderPeerHTML(peer.getRemotePeer())); - //byte ip[] = peer.getRemoteIP(); - //if (ip != null) - // buf.append(' ').append(_context.blocklist().toStr(ip)); - buf.append(""); - if (peer.isInbound()) - buf.append("\"Inbound\""); - else - buf.append("\"Outbound\""); - if (peer.getWeRelayToThemAs() > 0) - buf.append("  \"^\""); - if (peer.getTheyRelayToUsAs() > 0) - buf.append("  \"V\""); - - boolean appended = false; - if (_activeThrottle.isChoked(peer.getRemotePeer())) { - buf.append("
").append(_t("Choked")).append(""); - appended = true; - } - int cfs = peer.getConsecutiveFailedSends(); - if (cfs > 0) { - if (!appended) buf.append("
"); - buf.append(" "); - if (cfs == 1) - buf.append(_t("1 fail")); - else - buf.append(_t("{0} fails", cfs)); - buf.append(""); - appended = true; - } - if (_context.banlist().isBanlisted(peer.getRemotePeer(), STYLE)) { - if (!appended) buf.append("
"); - buf.append(" ").append(_t("Banned")).append(""); - appended = true; - } - //byte[] ip = getIP(peer.getRemotePeer()); - //if (ip != null) - // buf.append(' ').append(_context.blocklist().toStr(ip)); - buf.append("
"); - if (peer.isIPv6()) - buf.append("✓"); - else - buf.append(" "); - buf.append(""); - buf.append(DataHelper.formatDuration2(idleIn)); - buf.append(THINSP); - buf.append(DataHelper.formatDuration2(idleOut)); - buf.append(""); - buf.append(formatKBps(recvBps)); - buf.append(THINSP); - buf.append(formatKBps(sendBps)); - //buf.append(" K/s"); - //buf.append(formatKBps(peer.getReceiveACKBps())); - //buf.append("K/s/"); - //buf.append(formatKBps(peer.getSendACKBps())); - //buf.append("K/s "); - buf.append(""); - buf.append(DataHelper.formatDuration2(uptime)); - buf.append(""); - long skew = peer.getClockSkew(); - buf.append(DataHelper.formatDuration2(skew)); - buf.append(""); - buf.append(sendWindow/1024); - buf.append("K"); - buf.append(THINSP).append(peer.getConcurrentSends()); - buf.append(THINSP).append(peer.getConcurrentSendWindow()); - buf.append(THINSP).append(peer.getConsecutiveSendRejections()); - if (peer.isBacklogged()) - buf.append(' ').append(_t("backlogged")); - buf.append(""); - buf.append(peer.getSlowStartThreshold()/1024); - buf.append("K"); - buf.append(DataHelper.formatDuration2(rtt)); - buf.append(""); - //buf.append(DataHelper.formatDuration2(peer.getRTTDeviation())); - //buf.append(""); - buf.append(DataHelper.formatDuration2(rto)); - buf.append(""); - buf.append(peer.getMTU()).append(THINSP).append(peer.getReceiveMTU()); - - //.append('/'); - //buf.append(peer.getMTUIncreases()).append('/'); - //buf.append(peer.getMTUDecreases()); - buf.append(""); - buf.append(sent); - buf.append(""); - buf.append(recv); - buf.append(""); - //buf.append(formatPct(sendLostPct)); - buf.append(resent); // + "/" + peer.getPacketsPeriodRetransmitted() + "/" + sent); - //buf.append(peer.getPacketRetransmissionRate()); - buf.append(""); - buf.append(dupRecv); //formatPct(recvDupPct)); - buf.append("

") - .append(ngettext("{0} peer", "{0} peers", peers.size())) - .append(""); - buf.append(formatKBps(bpsIn)).append(THINSP).append(formatKBps(bpsOut)); - long x = uptimeMsTotal/numPeers; - buf.append("").append(DataHelper.formatDuration2(x)); - x = offsetTotal/numPeers; - buf.append("").append(DataHelper.formatDuration2(x)).append(""); - buf.append(cwinTotal/(numPeers*1024) + "K"); - buf.append(" "); - buf.append(DataHelper.formatDuration2(rttTotal/numPeers)); - //buf.append(" "); - buf.append(""); - buf.append(DataHelper.formatDuration2(rtoTotal/numPeers)); - buf.append("").append(_mtu).append(""); - buf.append(sendTotal).append("").append(recvTotal).append("").append(resentTotal); - buf.append("").append(dupRecvTotal).append("
"); - buf.append("peersByIdent: ").append(_peersByIdent.size()); - buf.append(" peersByRemoteHost: ").append(_peersByRemoteHost.size()); - int dir = 0; - int indir = 0; - for (RemoteHostId rhi : _peersByRemoteHost.keySet()) { - if (rhi.getIP() != null) - dir++; - else - indir++; - } - buf.append(" pBRH direct: ").append(dir).append(" indirect: ").append(indir); - buf.append("
\n"); - - /***** - long bytesTransmitted = _context.bandwidthLimiter().getTotalAllocatedOutboundBytes(); - // NPE here early - double averagePacketSize = _context.statManager().getRate("udp.sendPacketSize").getLifetimeAverageValue(); - // lifetime value, not just the retransmitted packets of current connections - resentTotal = (long)_context.statManager().getRate("udp.packetsRetransmitted").getLifetimeEventCount(); - double nondupSent = ((double)bytesTransmitted - ((double)resentTotal)*averagePacketSize); - double bwResent = (nondupSent <= 0 ? 0d : ((((double)resentTotal)*averagePacketSize) / nondupSent)); - buf.append("

Percentage of bytes retransmitted (lifetime): ").append(formatPct(bwResent)); - buf.append("

(Includes retransmission required by packet loss)\n"); - *****/ - - out.write(buf.toString()); - buf.setLength(0); } - private static final DecimalFormat _fmt = new DecimalFormat("#,##0.00"); - private static final String formatKBps(int bps) { - synchronized (_fmt) { - return _fmt.format((float)bps/1024); - } - } - private static final DecimalFormat _pctFmt = new DecimalFormat("#0.0%"); - /* * Cache the bid to reduce object churn */