Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added Classes to represent a server's publicly avaliable details (eg.

exit policy), its privately tracked performance stats and to decide
which server to use to connect to a given host and port.
  • Loading branch information...
commit 628262ef4b510e48eb87eaa6a0f6d88aea06538d 1 parent 87ecf98
@DaemonF DaemonF authored
View
53 oneswarm_f2f/src/edu/washington/cs/oneswarm/f2f/servicesharing/ServerExtendedInfo.java
@@ -0,0 +1,53 @@
+package edu.washington.cs.oneswarm.f2f.servicesharing;
+
+import java.util.Date;
+import java.util.Queue;
+
+public class ServerExtendedInfo extends ServerPublicInfo {
+ // Measured in kilobytes (rounded)
+ private Queue<Integer> bandwidthHistory; // Last 10 reported bandwidths
+ private int avgBandwidth;
+ private Queue<Integer> latencyHistory; // Last 10 reported latencies
+ private int avgLatency;
+
+ private static final int HISTORY_LENGTH = 10;
+
+ public ServerExtendedInfo(String nickname, long id, int advertBandwidth, String[] exitPolicy,
+ Date lastOutage, String version) {
+ super(nickname, id, advertBandwidth, exitPolicy, lastOutage, version);
+ }
+
+ public int compareTo(ServerPublicInfo other) {
+ if (other instanceof ServerExtendedInfo) {
+ return this.avgBandwidth - ((ServerExtendedInfo) other).avgBandwidth;
+ }
+ return super.compareTo(other);
+ }
+
+ public void recordBandwidth(int kbps) {
+ bandwidthHistory.add(kbps);
+ avgBandwidth = averageIntQueue(bandwidthHistory);
+ }
+
+ public void recordLatency(int ms) {
+ latencyHistory.add(ms);
+ avgLatency = averageIntQueue(latencyHistory);
+ }
+
+ public int getAvgBandwidth() {
+ return avgBandwidth;
+ }
+
+ public int getAvgLatency() {
+ return avgLatency;
+ }
+
+ private int averageIntQueue(Queue<Integer> q) {
+ while (q.size() > HISTORY_LENGTH)
+ q.remove();
+ int sum = 0;
+ for (int i = 0; i < q.size(); i++)
+ sum += q.remove();
+ return sum / q.size();
+ }
+}
View
213 oneswarm_f2f/src/edu/washington/cs/oneswarm/f2f/servicesharing/ServerPublicInfo.java
@@ -0,0 +1,213 @@
+package edu.washington.cs.oneswarm.f2f.servicesharing;
+
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ServerPublicInfo implements Comparable<ServerPublicInfo> {
+ private String nickname;
+ private long id;
+ private int advertizedBandwith;
+ private PolicyTree exitPolicy;
+ private Date onlineSince;
+ private String version;
+
+ public ServerPublicInfo(String nickname, long id, int advertBandwidth, String[] exitPolicy,
+ Date lastOutage, String version) {
+ this.nickname = nickname;
+ this.id = id;
+ this.advertizedBandwith = advertBandwidth;
+ this.exitPolicy = new PolicyTree(exitPolicy);
+ this.onlineSince = lastOutage;
+ this.version = version;
+ }
+
+ /**
+ * Sets the exit policy of the server using Tor's notation.
+ *
+ * The format is: (reject|accept) (domain|ip)[:port] with one policy per
+ * line of the string.
+ *
+ * EX: reject 66.146.193.31:* accept *:80
+ *
+ * @param policy
+ * Tor style exit policy array
+ */
+ public void setExitPolicy(String[] policy) {
+ exitPolicy = new PolicyTree(policy);
+ }
+
+ public boolean allowsConnectionTo(String url, int port) {
+ return exitPolicy.getPolicy(url, port);
+ }
+
+ public int compareTo(ServerPublicInfo other) {
+ return this.advertizedBandwith - other.advertizedBandwith;
+ }
+
+ public String getNickname() {
+ return nickname;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public int getAdvertizedBandwith() {
+ return advertizedBandwith;
+ }
+
+ public Date getOnlineSinceDate() {
+ return onlineSince;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ private enum PolicyValue {
+ ACCEPT, REJECT
+ }
+
+ private enum NodeType {
+ DOMAIN, PORT
+ }
+
+ private class PolicyTree {
+ private PolicyNode root;
+
+ public PolicyTree(String[] policy) {
+ root = new PolicyNode("");
+ addPolicies(policy);
+ }
+
+ public void addPolicies(String[] policyStrings) {
+ for (int i = 0; i < policyStrings.length; i++)
+ addPolicy(policyStrings[i]);
+ }
+
+ public void addPolicy(String policyString) {
+ policyString = policyString.toLowerCase();
+ PolicyValue policy;
+ int port;
+
+ String[] policyParts = policyString.split("[ :]");
+
+ switch (policyParts.length) {
+ case 2:
+ port = -1;
+ break;
+ case 3:
+ port = policyParts[2].equals("*") ? -1 : Integer.parseInt(policyParts[2]);
+ if (port < -1 || port > 65535)
+ throw new IllegalArgumentException("Improper Format - Port out of range.");
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Improper Format - Should be (reject|accept) (domain|ip)[:port]");
+ }
+
+ if (policyParts[0].equalsIgnoreCase("accept"))
+ policy = PolicyValue.ACCEPT;
+ else if (policyParts[0].equalsIgnoreCase("reject"))
+ policy = PolicyValue.REJECT;
+ else
+ throw new IllegalArgumentException(
+ "Improper Format - First word is not (accept|reject)");
+
+ String[] urlParts = policyParts[1].split("\\.");
+ root = addPolicy(urlParts, urlParts.length - 1, port, policy, root);
+ }
+
+ private PolicyNode addPolicy(String[] url, int index, int port, PolicyValue policy,
+ PolicyNode root) {
+ if (index < 0) {
+ root.children.add(new PolicyNode(port, policy));
+ } else {
+ PolicyNode child = root.lastInstanceOfUrlPart(url[index]);
+ if (child == null)
+ child = root.add(new PolicyNode(url[index]));
+ child = addPolicy(url, index - 1, port, policy, child);
+ }
+ return root;
+ }
+
+ // Must be a specific url or ip, and a specific port
+ public boolean getPolicy(String url, int port) {
+ url = url.toLowerCase();
+ String[] urlParts = url.split("\\.");
+ PolicyValue policy = getPolicy(urlParts, urlParts.length - 1, port, root);
+ if (PolicyValue.ACCEPT == policy)
+ return true;
+ return false;
+ }
+
+ private PolicyValue getPolicy(String[] domain, int index, int port, PolicyNode root) {
+ for (PolicyNode child : root.children) {
+ if (index >= 0 && (child.domain.equals("*") || domain[index].equals(child.domain))) {
+ PolicyValue temp = getPolicy(domain, index - 1, port, child);
+ if (temp != null)
+ return temp;
+ } else if (port == child.port || child.port == -1)
+ return child.policy;
+ }
+ return null;
+ }
+ }
+
+ class PolicyNode {
+ /*
+ * Either domainPart or port will be filled for each node. "*" is wild
+ * card for domainPart, "" is unused field -1 means wild card for port,
+ * -2 is unused field
+ */
+ String domain;
+ int port;
+ List<PolicyNode> children;
+ PolicyValue policy;
+ NodeType type;
+
+ // Constructs a domainPart node
+ public PolicyNode(String domainPart) {
+ this(domainPart, -2, null, NodeType.DOMAIN);
+ }
+
+ // Constructs a port node
+ public PolicyNode(int port, PolicyValue policy) {
+ this("", port, policy, NodeType.PORT);
+ }
+
+ private PolicyNode(String domainPart, int port, PolicyValue policy, NodeType type) {
+ this.domain = domainPart;
+ this.port = port;
+ this.policy = policy;
+ this.type = type;
+ this.children = new LinkedList<PolicyNode>();
+ }
+
+ public PolicyNode lastInstanceOfUrlPart(String urlPart) {
+ if (!children.isEmpty() && children.get(children.size() - 1).domain.equals(urlPart))
+ return children.get(children.size() - 1);
+ return null;
+ }
+
+ public PolicyNode lastInstanceOfPort(int port) {
+ if (!children.isEmpty() && children.get(children.size() - 1).port == port)
+ return children.get(children.size() - 1);
+ return null;
+ }
+
+ public PolicyNode add(PolicyNode node) {
+ children.add(node);
+ return node;
+ }
+
+ public String toString() {
+ String temp = "";
+ temp += domain.isEmpty() ? "--" : domain;
+ temp += ":";
+ temp += port == -1 ? "*" : port == -2 ? "--" : port;
+ return temp;
+ }
+ }
+}
View
25 oneswarm_f2f/src/edu/washington/cs/oneswarm/f2f/servicesharing/UrlToServer.java
@@ -0,0 +1,25 @@
+package edu.washington.cs.oneswarm.f2f.servicesharing;
+
+import java.util.Collections;
+import java.util.List;
+
+public class UrlToServer {
+ List<ServerPublicInfo> servers;
+
+ public UrlToServer(ServerPublicInfo[] servers) {
+ for (ServerPublicInfo server : servers)
+ addServer(server);
+ }
+
+ public void addServer(ServerPublicInfo server) {
+ servers.add(server);
+ Collections.sort(servers);
+ }
+
+ public ServerPublicInfo pickServer(String url, int port) {
+ for (ServerPublicInfo server : servers)
+ if (server.allowsConnectionTo(url, port))
+ return server;
+ return null;
+ }
+}
View
84 oneswarm_f2f/test/edu/washington/cs/oneswarm/f2f/servicesharing/ProxyPolicyTest.java
@@ -0,0 +1,84 @@
+package edu.washington.cs.oneswarm.f2f.servicesharing;
+
+import static org.junit.Assert.*;
+
+import java.util.Date;
+import java.util.logging.Logger;
+
+import org.junit.*;
+
+import edu.washington.cs.oneswarm.test.integration.ServiceSharingClientTest;
+import edu.washington.cs.oneswarm.test.util.OneSwarmTestBase;
+import edu.washington.cs.oneswarm.test.util.TestUtils;
+
+/**
+ * Tests ServerPublicInfo, verifying that it correctly represents the policy of
+ * the server it represents.
+ *
+ * @author Nick
+ *
+ */
+public class ProxyPolicyTest extends OneSwarmTestBase {
+
+ private static Logger logger = Logger.getLogger(ServiceSharingClientTest.class.getName());
+
+ @Test
+ public void testServerPublicInfo() throws Exception {
+ /*
+ * Verify that the exit policy passed to the ServerPublicInfo is
+ * correctly represented in subsequent calls to 'allowsConnection(String
+ * url, int port)'
+ *
+ * Test plan: -Create a ServerPublicInfo with a complex exit policy
+ * -Verify that the results match the set of manually predetermined
+ * results for each case
+ */
+
+ try {
+ String[] policy = new String[] { "reject yahoo.com", "accept *:80",
+ "reject *.google.com:40", "accept google.com:40", "reject *.2.*.*:40",
+ "accept 4.*.2.2:40", "reject *:*" };
+
+ ServerPublicInfo server = new ServerPublicInfo("Servo The Magnificent", 123456789, 275,
+ policy, new Date(), "Version string 2.0");
+
+ // Sample Url's to test
+ String[] urls = new String[] { "google.com", "yahoo.com", "maps.google.com", "4.2.2.2",
+ "4.5.2.2" };
+
+ // Sample ports to test
+ int[] ports = new int[] { 80, 40 };
+
+ // First index is url, second index is port
+ boolean[][] expected = new boolean[][] { { true, true }, { false, false },
+ { true, false }, { true, false }, { true, true } };
+
+ if (expected.length != urls.length || expected[0].length != ports.length)
+ throw new Exception(
+ "Invalid test: Dimmensions of 'expected' does not match the number of urls or ports.");
+
+ for (int x = 0; x < urls.length; x++) {
+ for (int y = 0; y < ports.length; y++) {
+ if (expected[x][y] != server.allowsConnectionTo(urls[x], ports[y])) {
+ throw new Exception("Reported result (" + expected[x][y]
+ + ") differs from expeceted result (" + !expected[x][y] + ") for "
+ + urls[x] + ":" + ports[y]);
+ }
+ }
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ logger.severe(e.toString());
+ fail();
+ } finally {
+ logger.info("End testServerPublicInfo()");
+ }
+ }
+
+ /** Boilerplate code for running as executable. */
+ public static void main(String[] args) throws Exception {
+ TestUtils.swtCompatibleTestRunner(ProxyPolicyTest.class);
+ }
+
+}
Please sign in to comment.
Something went wrong with that request. Please try again.