bytes=1000-1999
) and extract values from
+ * it.
+ */
+ private static final Pattern RANGE_PATTERN = Pattern.compile("bytes=(\\d+)-(\\d+)");
+
+ /**
+ * A pattern to validate a Content-Range response header (for example, bytes 1000-1999/12345
) and
+ * extract values from it.
+ */
+ private static final Pattern CONTENT_RANGE_PATTERN = Pattern.compile("bytes (\\d+)-(\\d+)/(\\d+)");
+
+ /**
+ * The values passed in a Range request header value.
+ */
+ static class RangeHeaderData
+ {
+ /** The start position of the requested part. **/
+ public final long startPos;
+
+ /** The end position (inclusive) of the requested part. **/
+ public final long endPos;
+
+ public RangeHeaderData(final long startPos, final long endPos)
+ {
+ this.startPos = startPos;
+ this.endPos = endPos;
+ }
+ }
+
+ /**
+ * The values passed in a Content-Range response header value.
+ */
+ static class ContentRangeHeaderData
+ {
+ /** The start position of the returned part. */
+ public final long startPos;
+
+ /** The end position (inclusive) of the returned part. */
+ public final long endPos;
+
+ /** The total size of the resource. */
+ public final long totalBytes;
+
+ public ContentRangeHeaderData(final long startPos, final long endPos, final long totalBytes)
+ {
+ this.startPos = startPos;
+ this.endPos = endPos;
+ this.totalBytes = totalBytes;
+ }
+ }
+
+ /**
+ * Formats the given values as a valid Range request header value.
+ *
+ * @param startPos
+ * the start position of the requested part
+ * @param endPos
+ * the end position (inclusive) of the requested part
+ * @return the formatted header value
+ */
+ static String formatRangeHeader(final long startPos, final long endPos)
+ {
+ return "bytes=" + startPos + "-" + endPos;
+ }
+
+ /**
+ * Parses the given header value as a Range header and returns the extracted data.
+ *
+ * @param rangeHeaderValue
+ * the header value to parse
+ * @return the extracted data if the header could be parsed successfully, null
otherwise
+ */
+ static RangeHeaderData parseRangeHeader(final String rangeHeaderValue)
+ {
+ if (rangeHeaderValue != null)
+ {
+ final Matcher matcher = RANGE_PATTERN.matcher(rangeHeaderValue);
+ if (matcher.matches())
+ {
+ final long startPos = ParseNumbers.parseLong(matcher.group(1));
+ final long endPos = ParseNumbers.parseLong(matcher.group(2));
+
+ return new RangeHeaderData(startPos, endPos);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Formats the given values as a valid Content-Range response header value.
+ *
+ * @param startPos
+ * the start position of the returned part
+ * @param endPos
+ * the end position (inclusive) of the returned part
+ * @param totalBytes
+ * the total size of the resource
+ * @return the formatted header value
+ */
+ static String formatContentRangeHeader(final long startPos, final long endPos, final long totalBytes)
+ {
+ return "bytes " + startPos + "-" + endPos + "/" + totalBytes;
+ }
+
+ /**
+ * Parses the given header value as a Content-Range header and returns the extracted data.
+ *
+ * @param contentRangeHeaderValue
+ * the header value to parse
+ * @return the extracted data if the header could be parsed successfully, null
otherwise
+ */
+ static ContentRangeHeaderData parseContentRangeHeader(final String contentRangeHeaderValue)
+ {
+ if (contentRangeHeaderValue != null)
+ {
+ final Matcher matcher = CONTENT_RANGE_PATTERN.matcher(contentRangeHeaderValue);
+ if (matcher.matches())
+ {
+ final long startPos = ParseNumbers.parseLong(matcher.group(1));
+ final long endPos = ParseNumbers.parseLong(matcher.group(2));
+ final long totalBytes = ParseNumbers.parseLong(matcher.group(3));
+
+ return new ContentRangeHeaderData(startPos, endPos, totalBytes);
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/com/xceptance/xlt/mastercontroller/MasterControllerConfiguration.java b/src/main/java/com/xceptance/xlt/mastercontroller/MasterControllerConfiguration.java
index 63405fae2..a1213490a 100644
--- a/src/main/java/com/xceptance/xlt/mastercontroller/MasterControllerConfiguration.java
+++ b/src/main/java/com/xceptance/xlt/mastercontroller/MasterControllerConfiguration.java
@@ -28,13 +28,14 @@
import org.apache.commons.io.FileUtils;
import com.xceptance.common.util.AbstractConfiguration;
+import com.xceptance.xlt.agentcontroller.AgentControllerProxy;
import com.xceptance.xlt.common.XltConstants;
import com.xceptance.xlt.engine.XltExecutionContext;
/**
* The MasterControllerConfiguration is the central place where all configuration information of the master controller
* can be retrieved from.
- *
+ *
* @author Jörg Werner (Xceptance Software Technologies GmbH)
*/
public class MasterControllerConfiguration extends AbstractConfiguration
@@ -102,7 +103,11 @@ public class MasterControllerConfiguration extends AbstractConfiguration
private static final String PROP_PASSWORD = PROP_PREFIX + "password";
private static final String PROP_COMPRESSED_TIMER_FILES = PROP_PREFIX + "compressedTimerFiles";
-
+
+ private static final String PROP_DOWNLOAD_CHUNK_SIZE = PROP_PREFIX + "download.chunkSize";
+
+ private static final String PROP_DOWNLOAD_MAX_RETRIES = PROP_PREFIX + "download.maxRetries";
+
private final Listtrue
if the agent controller connection is relaxed; false
otherwise
*/
public boolean isAgentControllerConnectionRelaxed()
@@ -454,7 +468,7 @@ public boolean isAgentControllerConnectionRelaxed()
/**
* Tells to use a proxy or not.
- *
+ *
* @return true
if using a proxy is enabled explicitly; false
otherwise
*/
public boolean isHttpsProxyEnabled()
@@ -464,7 +478,7 @@ public boolean isHttpsProxyEnabled()
/**
* Returns the https proxy host.
- *
+ *
* @return https proxy host
*/
public String getHttpsProxyHost()
@@ -474,7 +488,7 @@ public String getHttpsProxyHost()
/**
* Returns the https proxy port.
- *
+ *
* @return https proxy port
*/
public String getHttpsProxyPort()
@@ -484,7 +498,7 @@ public String getHttpsProxyPort()
/**
* Returns the hosts to bypass the proxy connection.
- *
+ *
* @return hosts to bypass the proxy connection
*/
public String getHttpsProxyBypassHosts()
@@ -494,7 +508,7 @@ public String getHttpsProxyBypassHosts()
/**
* Returns the default agent count.
- *
+ *
* @return default agent count
*/
public int getDefaultAgentCount()
@@ -504,7 +518,7 @@ public int getDefaultAgentCount()
/**
* Returns the default agent controller weight.
- *
+ *
* @return default agent controller weight
*/
public int getDefaultWeight()
@@ -514,7 +528,7 @@ public int getDefaultWeight()
/**
* Returns the number of maximum parallel agent controller communication limit
- *
+ *
* @return the number of maximum parallel agent controller communication limit
*/
public int getParallelCommunicationLimit()
@@ -524,7 +538,7 @@ public int getParallelCommunicationLimit()
/**
* Returns the number of maximum parallel uploads
- *
+ *
* @return the number of maximum parallel uploads
*/
public int getParallelUploadLimit()
@@ -534,7 +548,7 @@ public int getParallelUploadLimit()
/**
* Returns the number of maximum parallel downloads
- *
+ *
* @return the number of maximum parallel downloads
*/
public int getParallelDownloadLimit()
@@ -544,7 +558,7 @@ public int getParallelDownloadLimit()
/**
* Returns the configured agent-controller connection timeout.
- *
+ *
* @return agent-controller connection timeout
*/
public int getAgentControllerConnectTimeout()
@@ -554,7 +568,7 @@ public int getAgentControllerConnectTimeout()
/**
* Returns the configured agent-controller read timeout.
- *
+ *
* @return agent-controller read timeout
*/
public int getAgentControllerReadTimeout()
@@ -564,7 +578,7 @@ public int getAgentControllerReadTimeout()
/**
* Returns the configured agent controller initial response timeout.
- *
+ *
* @return agent controller initial response timeout
*/
public int getAgentControllerInitialResponseTimeout()
@@ -574,7 +588,7 @@ public int getAgentControllerInitialResponseTimeout()
/**
* Returns the configured user name.
- *
+ *
* @return the user name
*/
public String getUserName()
@@ -584,7 +598,7 @@ public String getUserName()
/**
* Returns the configured password.
- *
+ *
* @return the password
*/
public String getPassword()
@@ -594,7 +608,7 @@ public String getPassword()
/**
* Returns the result output directory override as specified on command line.
- *
+ *
* @return result output directory override
*/
public File getResultOutputDirectory()
@@ -605,7 +619,7 @@ public File getResultOutputDirectory()
/**
* Sets the result output directory override. If the given directory name denotes a relative file then it will be
* rooted at the test results root directory.
- *
+ *
* @param outputDirectory
* the result output directory name to use as override
*/
@@ -638,14 +652,34 @@ public boolean isEmbedded()
{
return isEmbedded;
}
-
+
/**
* How shall we handle timer files after the download
- *
+ *
* @return true, keep them compressed, false, classic expanded storage
*/
public boolean isCompressedTimerFiles()
{
return compressedTimerFiles;
}
+
+ /**
+ * Returns the size of a file chunk when downloading a result archive from an agent controller.
+ *
+ * @return the chunk size (in bytes)
+ */
+ public long getDownloadChunkSize()
+ {
+ return downloadChunkSize;
+ }
+
+ /**
+ * Returns the maximum number of retries in case downloading a result file (chunk) failed because of an I/O error.
+ *
+ * @return the maximum number of retries
+ */
+ public int getDownloadMaxRetries()
+ {
+ return downloadMaxRetries;
+ }
}
diff --git a/src/main/java/com/xceptance/xlt/mastercontroller/MasterControllerMain.java b/src/main/java/com/xceptance/xlt/mastercontroller/MasterControllerMain.java
index 6c6ee50e7..0b44cf0df 100644
--- a/src/main/java/com/xceptance/xlt/mastercontroller/MasterControllerMain.java
+++ b/src/main/java/com/xceptance/xlt/mastercontroller/MasterControllerMain.java
@@ -274,7 +274,9 @@ private void startAgentControllerRemote(final Map