package freenet.clients.http.wizardsteps;
import java.text.DecimalFormat;
import freenet.clients.http.FirstTimeWizardToadlet;
import freenet.config.Config;
import freenet.config.InvalidConfigValueException;
import freenet.l10n.NodeL10n;
import freenet.node.NodeClientCore;
import freenet.support.HTMLNode;
import freenet.support.Logger;
import freenet.support.SizeUtil;
import freenet.support.URLEncoder;
import freenet.support.api.HTTPRequest;
/**
* Allows the user to set bandwidth limits with an emphasis on limiting to certain download and upload rates.
*/
public class BANDWIDTH_RATE extends BandwidthManipulator implements Step {
private final BandwidthLimit[] limits;
public BANDWIDTH_RATE(NodeClientCore core, Config config) {
super(core, config);
final int KiB = 1024;
limits = new BandwidthLimit[] {
// FIXME feedback on typical real world ratios on slow connections would be helpful.
// // Dial-up
// // 57.6/33.6; call it 4KB/sec each way
// new BandwidthLimit(4*KiB, 4*KiB, "bandwidthConnectionDialUp"),
// // 128kbps symmetrical = 16KB/sec each way, take half so 8KB/sec each way
// new BandwidthLimit(8*KiB, 8*KiB, "bandwidthConnectionISDN"),
// // 256kbps/64kbps developing world broadband
// new BandwidthLimit(16*KiB, 4*KiB, "bandwidthConnectionSlow256"),
// // 512kbps/128kbps very slow broadband
// new BandwidthLimit(32*KiB, 8*KiB, "bandwidthConnectionSlow512"),
// // 1Mbps/128kbps
// new BandwidthLimit(64*KiB, 8*KiB, "bandwidthConnection1M"),
// // 2Mbps/128kbps (slow often => poor ratios)
// new BandwidthLimit(128*KiB, 8*KiB, "bandwidthConnection2M"),
// 4Mbps/256kbps
new BandwidthLimit(256*KiB, 16*KiB, "bandwidthConnection4M", false),
// 6Mbps/256kbps - 6Mbps is common in parts of china, as well as being the real value in lots of DSL areas
new BandwidthLimit(384*KiB, 16*KiB, "bandwidthConnection6M", true),
// 8Mbps/512kbps - UK DSL1 is either 448k up or 832k up
new BandwidthLimit(512*KiB, 32*KiB, "bandwidthConnection8M", false),
// 12Mbps/1Mbps - typical DSL2
new BandwidthLimit(768*KiB, 64*KiB, "bandwidthConnection12M", false),
// 20Mbps/1Mbps - fast DSL2
new BandwidthLimit(1280*KiB, 64*KiB, "bandwidthConnection20M", false),
// 20Mbps/5Mbps - Slow end of VDSL
new BandwidthLimit(1280*KiB, 320*KiB, "bandwidthConnectionVDSL", false),
// 100Mbps fibre etc
new BandwidthLimit(2048*KiB, 2048*KiB, "bandwidthConnection100M", false)
};
}
@Override
public void getStep(HTTPRequest request, PageHelper helper) {
HTMLNode contentNode = helper.getPageContent(WizardL10n.l10n("bandwidthLimit"));
HTMLNode formNode = helper.addFormChild(contentNode, ".", "limit");
if (request.isParameterSet("parseError")) {
parseErrorBox(contentNode, helper, request.getParam("parseTarget"));
}
HTMLNode infoBox = helper.getInfobox("infobox-normal", WizardL10n.l10n("bandwidthLimitRateTitle"),
formNode, null, false);
NodeL10n.getBase().addL10nSubstitution(infoBox, "FirstTimeWizardToadlet.bandwidthLimitRate",
new String[] { "bold", "coreSettings" }, new HTMLNode[] { HTMLNode.STRONG,
new HTMLNode("#", NodeL10n.getBase().getString("ConfigToadlet.node"))});
//Table header
HTMLNode table = infoBox.addChild("table");
HTMLNode headerRow = table.addChild("tr");
headerRow.addChild("th", WizardL10n.l10n("bandwidthConnectionHeader"));
headerRow.addChild("th", WizardL10n.l10n("bandwidthDownloadHeader"));
headerRow.addChild("th", WizardL10n.l10n("bandwidthUploadHeader"));
headerRow.addChild("th", WizardL10n.l10n("bandwidthSelect"));
boolean addedDefault = false;
BandwidthLimit detected = detectBandwidthLimits();
if (detected.downBytes > 0 && detected.upBytes > 0) {
//Detected limits reasonable; add half of both as recommended option.
BandwidthLimit usable = new BandwidthLimit(detected.downBytes/2, detected.upBytes/2, "bandwidthDetected", true);
addLimitRow(table, helper, usable, true, true);
addedDefault = true;
}
BandwidthLimit current = getCurrentBandwidthLimitsOrNull();
if(current != null) {
addLimitRow(table, helper, current, false, !addedDefault);
addedDefault = true;
}
for (BandwidthLimit limit : limits) {
addLimitRow(table, helper, limit, false, !addedDefault);
}
//Add custom option.
HTMLNode customForm = table.addChild("tr");
customForm.addChild("td", WizardL10n.l10n("bandwidthCustom"));
customForm.addChild("td").addChild("input",
new String[] { "type", "name" },
new String[] { "text", "customDown" });
customForm.addChild("td").addChild("input",
new String[] { "type", "name" },
new String[] { "text", "customUp" });
// This is valid if it's filled in. So don't show the selector.
// FIXME javascript to auto-select it?
// customForm.addChild("td").addChild("input",
// new String[] { "type", "name", "value" },
// new String[] { "radio", "bandwidth", "custom" });
infoBox.addChild("input",
new String[] { "type", "name", "value" },
new String[] { "submit", "back", NodeL10n.getBase().getString("Toadlet.back")});
infoBox.addChild("input",
new String[] { "type", "name", "value" },
new String[] { "submit", "next", NodeL10n.getBase().getString("Toadlet.next")});
}
@Override
public String postStep(HTTPRequest request) {
String limitSelected = request.getPartAsStringFailsafe("bandwidth", 100);
String down = request.getPartAsStringFailsafe("customDown", 20);
String up = request.getPartAsStringFailsafe("customUp", 20);
// Try to parse custom limit first.
if(!down.equals("") && !up.equals("")) {
String failedLimits = attemptSet(up, down);
if (!failedLimits.isEmpty()) {
//Some at least one limit failed to parse.
return "BANDWIDTH_RATE&parseError=true&parseTarget="+
URLEncoder.encode(failedLimits, true);
}
//Success
setWizardComplete();
return FirstTimeWizardToadlet.WIZARD_STEP.COMPLETE.name();
}
if(!limitSelected.isEmpty()) {
int x = limitSelected.indexOf('/');
if(x != -1) {
String downString = limitSelected.substring(0, x);
String upString = limitSelected.substring(x+1);
//Pre-defined limit selected.
String preset = attemptSet(upString, downString);
if(!preset.isEmpty()) {
//Error parsing predefined limit.
//This should not happen, as there are no units to confound the parser.
Logger.error(this, "Failed to parse pre-defined limit! Please report.");
return FirstTimeWizardToadlet.WIZARD_STEP.BANDWIDTH_RATE+"&parseError=true&parseTarget="+
URLEncoder.encode(preset, true);
}
}
} else {
Logger.error(this, "No bandwidth limit set!");
return FirstTimeWizardToadlet.WIZARD_STEP.BANDWIDTH_RATE.name();
}
setWizardComplete();
return FirstTimeWizardToadlet.WIZARD_STEP.COMPLETE.name();
}
/**
* Attempts to set bandwidth limits.
* @param up output limit
* @param down input limit
* @return a space-separated string of the messages from any exceptions thrown when setting limits. If both are successful, an empty string.
*/
private String attemptSet(String up, String down) {
String failedLimits = "";
try {
setBandwidthLimit(down, false);
} catch (InvalidConfigValueException e) {
failedLimits = e.getMessage();
}
try {
setBandwidthLimit(up, true);
} catch (InvalidConfigValueException e) {
if (!failedLimits.isEmpty()) failedLimits += ' ';
failedLimits += e.getMessage();
}
return failedLimits;
}
/**
* Adds a row to the table for the given limit. Adds download limit, upload limit, and selection button.
* @param table Table to add a row to.
* @param helper To make a form for the button and hidden fields.
* @param limit Limit to display.
* @param recommended Whether to mark the limit with (Recommended) next to the select button.
*/
private void addLimitRow(HTMLNode table, PageHelper helper, BandwidthLimit limit, boolean recommended, boolean useMaybeDefault) {
HTMLNode row = table.addChild("tr");
row.addChild("td", WizardL10n.l10n(limit.descriptionKey));
String downColumn = SizeUtil.formatSize(limit.downBytes)+WizardL10n.l10n("bandwidthPerSecond");
if(limit.downBytes >= 32*1024) {
downColumn += " (= ";
if(limit.downBytes < 256*1024)
downColumn += new DecimalFormat("0.0").format(((double)((limit.downBytes*8)))/(1024*1024));
else
downColumn += ((limit.downBytes*8)/(1024*1024));
downColumn+="Mbps)";
}
row.addChild("td", downColumn);
row.addChild("td", SizeUtil.formatSize(limit.upBytes)+WizardL10n.l10n("bandwidthPerSecond"));
HTMLNode buttonCell = row.addChild("td");
HTMLNode radio =
buttonCell.addChild("input",
new String[] { "type", "name", "value" },
new String[] { "radio", "bandwidth", limit.downBytes+"/"+limit.upBytes });
if(recommended || (useMaybeDefault && limit.maybeDefault))
radio.addAttribute("checked", "checked");
if (recommended) {
buttonCell.addChild("#", WizardL10n.l10n("autodetectedSuggestedLimit"));
}
}
}