package freenet.clients.http.wizardsteps; import freenet.clients.http.FirstTimeWizardToadlet; import freenet.config.Config; import freenet.config.InvalidConfigValueException; import freenet.l10n.NodeL10n; import freenet.node.Node; import freenet.node.NodeClientCore; import freenet.support.*; import freenet.support.api.HTTPRequest; /** * Allows the user to set bandwidth limits with an emphasis on capping to a monthly total. */ public class BANDWIDTH_MONTHLY extends BandwidthManipulator implements Step { /** * 1 gigabyte in bytes. */ private static final long GB = 1000000000; /** * Seconds in 30 days. Used for limit calculations. */ private static final double secondsPerMonth = 2592000d; /* * Bandwidth used if both the upload and download limit are at the minimum. In GB. Assumes 24/7 uptime. */ private static final Double minCap = 2*Node.getMinimumBandwidth()*secondsPerMonth/GB; private static final long[] caps = { (long)Math.ceil(minCap), 100, 150, 250, 500 }; public BANDWIDTH_MONTHLY(NodeClientCore core, Config config) { super(core, config); } @Override public void getStep(HTTPRequest request, PageHelper helper) { HTMLNode contentNode = helper.getPageContent(WizardL10n.l10n("bandwidthLimit")); // Check for and display any errors. final String parseTarget = request.getParam("parseTarget"); if (request.isParameterSet("parseError")) { parseErrorBox(contentNode, helper, WizardL10n.l10n("bandwidthCouldNotParse", "limit", parseTarget)); } else if (request.isParameterSet("tooLow")) { HTMLNode errorBox = parseErrorBox(contentNode, helper, WizardL10n.l10n("bandwidthMonthlyLow", new String[] { "requested", "minimum", "useMinimum" }, new String[] { parseTarget, String.valueOf(Math.round(minCap)), WizardL10n.l10n("bandwidthMonthlyUseMinimum")})); HTMLNode minimumForm = helper.addFormChild(errorBox, ".", "use-minimum"); minimumForm.addChild("input", new String[]{"type", "name", "value"}, new String[]{"hidden", "capTo", String.valueOf(minCap)}); minimumForm.addChild("input", new String[]{"type", "value"}, new String[]{"submit", WizardL10n.l10n("bandwidthMonthlyUseMinimum")}); } // Explain this step's operation. HTMLNode infoBox = helper.getInfobox("infobox-normal", WizardL10n.l10n("bandwidthLimitMonthlyTitle"), contentNode, null, false); NodeL10n.getBase().addL10nSubstitution(infoBox, "FirstTimeWizardToadlet.bandwidthLimitMonthly", new String[] { "bold", "coreSettings" }, new HTMLNode[] { HTMLNode.STRONG, new HTMLNode("#", NodeL10n.getBase().getString("ConfigToadlet.node"))}); //TODO: Might want to detect bandwidth limit and hide those too high to reach. //TODO: The user can always set a custom limit. At least one limit should be displayed in order to //TODO: demonstrate how to specify the limit, though. // Table header HTMLNode table = infoBox.addChild("table"); HTMLNode headerRow = table.addChild("tr"); headerRow.addChild("th", WizardL10n.l10n("bandwidthLimitMonthlyTitle")); headerRow.addChild("th", WizardL10n.l10n("bandwidthSelect")); // Row for each cap for (long cap : caps) { HTMLNode row = table.addChild("tr"); //ISPs are likely to list limits in GB instead of GiB, so display GB here. row.addChild("td", String.valueOf(cap) +" GB"); HTMLNode selectForm = helper.addFormChild(row.addChild("td"), ".", "limit"); selectForm.addChild("input", new String[] { "type", "name", "value" }, new String[] { "hidden", "capTo", String.valueOf(cap)}); selectForm.addChild("input", new String[] { "type", "value" }, new String[] { "submit", WizardL10n.l10n("bandwidthSelect")}); } // Row for custom entry HTMLNode customForm = helper.addFormChild(table.addChild("tr"), ".", "custom-form"); HTMLNode capInput = customForm.addChild("td"); capInput.addChild("input", new String[]{"type", "name"}, new String[]{"text", "capTo"}); capInput.addChild("#", " GB"); customForm.addChild("td").addChild("input", new String[]{"type", "value"}, new String[]{"submit", WizardL10n.l10n("bandwidthSelect")}); // Back / next buttons HTMLNode backForm = helper.addFormChild(infoBox, ".", "backForm"); backForm.addChild("input", new String[] { "type", "name", "value" }, new String[] { "submit", "back", NodeL10n.getBase().getString("Toadlet.back")}); } @Override public String postStep(HTTPRequest request) { double GBPerMonth; long bytesPerMonth; // capTo is specified as floating point GB. String capTo = request.getPartAsStringFailsafe("capTo", 4096); // Target for an error page. StringBuilder target = new StringBuilder(FirstTimeWizardToadlet.WIZARD_STEP.BANDWIDTH_MONTHLY.name()).append("&parseTarget="); try { GBPerMonth = Double.valueOf(capTo); bytesPerMonth = Math.round(GBPerMonth * GB); } catch (NumberFormatException e) { target.append(URLEncoder.encode(capTo, true)); target.append("&parseError=true"); return target.toString(); } /* * Fraction of total limit used for download. Asymptotically from 0.5 at the minimum cap to 0.8. * * FIXME: Why do we do this? It does not actually work, since * download cannot be larger than upload for any long amount * of time. * * This 50/50 split is consistent with the assumption in the definition of minCap that the upload and * download limits are equal. */ double bytesPerSecond = bytesPerMonth/secondsPerMonth; double minBytesPerSecond = Node.getMinimumBandwidth(); double bwinc = bytesPerSecond - 2*minBytesPerSecond; // min for up and min for down double asymptoticDlFraction = 4. / 5.; double dllimit = minBytesPerSecond + (bwinc * asymptoticDlFraction); double ullimit = minBytesPerSecond + (bwinc * (1 - asymptoticDlFraction)); String downloadLimit = String.valueOf(Math.ceil(dllimit)); String uploadLimit = String.valueOf(Math.ceil(ullimit)); try { setBandwidthLimit(downloadLimit, false); setBandwidthLimit(uploadLimit, true); } catch (InvalidConfigValueException e) { target.append(URLEncoder.encode(String.valueOf(GBPerMonth), true)); target.append("&tooLow=true"); return target.toString(); } setWizardComplete(); return FirstTimeWizardToadlet.WIZARD_STEP.COMPLETE.name(); } }