Merge branch 'master' into noEngineAvailable
This commit is contained in:
+8
-2
@@ -1,12 +1,18 @@
|
||||
# Getdown Releases
|
||||
|
||||
## 1.8.7 - Unreleased
|
||||
|
||||
* Reinstated env var support in `appbase` property.
|
||||
|
||||
* Fixed issue with `myIpAddress()` in PAC proxy support.
|
||||
|
||||
## 1.8.6 - June 4, 2019
|
||||
|
||||
* Fixed issues with PAC proxy support: added `myIpAddress()`, fixed `dnsResolve()`, fixed crash
|
||||
when detecting PAC proxy.
|
||||
|
||||
* Reverted env var support in `appbase` and `latest` properties. It's causing problems that need to
|
||||
be investigated.
|
||||
* Reverted env var support in `appbase` property. It's causing problems that need to be
|
||||
investigated.
|
||||
|
||||
## 1.8.5 - May 29, 2019
|
||||
|
||||
|
||||
@@ -605,7 +605,7 @@ public class Application
|
||||
}
|
||||
|
||||
// check if we're overriding the domain in the appbase, and sub envvars
|
||||
_appbase = SysProps.overrideAppbase(_appbase);
|
||||
_appbase = resolveEnvVars(SysProps.overrideAppbase(_appbase));
|
||||
|
||||
// make sure there's a trailing slash
|
||||
if (!_appbase.endsWith("/")) {
|
||||
@@ -1130,27 +1130,31 @@ public class Application
|
||||
}
|
||||
}
|
||||
|
||||
/** Replaces the application directory and version in any argument. */
|
||||
/** Replaces the application directory, version and env vars in any argument. */
|
||||
protected String processArg (String arg)
|
||||
{
|
||||
arg = arg.replace("%APPDIR%", getAppDir().getAbsolutePath());
|
||||
arg = arg.replace("%VERSION%", String.valueOf(_version));
|
||||
arg = resolveEnvVars(arg);
|
||||
return arg;
|
||||
}
|
||||
|
||||
// if this argument contains %ENV.FOO% replace those with the associated values looked up
|
||||
// from the environment
|
||||
if (arg.contains(ENV_VAR_PREFIX)) {
|
||||
/** Resolves env var substitutions in {@code text}. */
|
||||
protected String resolveEnvVars (String text) {
|
||||
// if the text contains %ENV.FOO% replace it with FOO looked up in the environment
|
||||
if (text.contains(ENV_VAR_PREFIX)) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
Matcher matcher = ENV_VAR_PATTERN.matcher(arg);
|
||||
Matcher matcher = ENV_VAR_PATTERN.matcher(text);
|
||||
while (matcher.find()) {
|
||||
String varName = matcher.group(1), varValue = System.getenv(varName);
|
||||
String repValue = varValue == null ? "MISSING:"+varName : varValue;
|
||||
matcher.appendReplacement(sb, Matcher.quoteReplacement(repValue));
|
||||
}
|
||||
matcher.appendTail(sb);
|
||||
arg = sb.toString();
|
||||
return sb.toString();
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -143,15 +143,23 @@ public class Connector {
|
||||
* If the connection failed for proxy related reasons, this changes the state of this connector
|
||||
* to reflect the needed proxy information.
|
||||
*/
|
||||
public void checkConnectOK (URLConnection conn, String errpre) throws IOException
|
||||
{
|
||||
// if it's not an HTTP connection, there's nothing to check
|
||||
if (!(conn instanceof HttpURLConnection)) return;
|
||||
public void checkConnectOK (URLConnection conn, String errpre) throws IOException {
|
||||
int code = checkConnectStatus(conn);
|
||||
if (code != HttpURLConnection.HTTP_OK) {
|
||||
throw new IOException(errpre + " [code=" + code + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection status of {@code conn}. If the connection failed for proxy related
|
||||
* reasons, this changes the state of this connector to reflect the needed proxy information.
|
||||
*/
|
||||
public int checkConnectStatus (URLConnection conn) throws IOException {
|
||||
// if it's not an HTTP connection, we assume it's OK
|
||||
if (!(conn instanceof HttpURLConnection)) return HttpURLConnection.HTTP_OK;
|
||||
|
||||
int code = ((HttpURLConnection)conn).getResponseCode();
|
||||
switch (code) {
|
||||
case HttpURLConnection.HTTP_OK:
|
||||
return;
|
||||
case HttpURLConnection.HTTP_FORBIDDEN:
|
||||
case HttpURLConnection.HTTP_USE_PROXY:
|
||||
state = State.NEED_PROXY;
|
||||
@@ -160,7 +168,7 @@ public class Connector {
|
||||
state = State.NEED_PROXY_AUTH;
|
||||
break;
|
||||
}
|
||||
throw new IOException(errpre + " [code=" + code + "]");
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -140,6 +140,11 @@ public class Downloader
|
||||
*/
|
||||
protected void downloadFailed (Resource rsrc, Exception cause) {}
|
||||
|
||||
/**
|
||||
* Called when a to-be-downloaded resource returns a 404 not found.
|
||||
*/
|
||||
protected void resourceMissing (Resource rsrc) {}
|
||||
|
||||
/**
|
||||
* Performs the protocol-specific portion of checking download size.
|
||||
*/
|
||||
@@ -150,9 +155,10 @@ public class Downloader
|
||||
if (conn instanceof HttpURLConnection) {
|
||||
((HttpURLConnection)conn).setRequestMethod("HEAD");
|
||||
}
|
||||
// make sure we got a satisfactory response code
|
||||
_conn.checkConnectOK(conn, "Unable to check up-to-date for " + rsrc.getRemote());
|
||||
return conn.getContentLength();
|
||||
// if we get a satisfactory response code, return a size; ignore errors as we'll report
|
||||
// those when we actually attempt to download the resource
|
||||
int code = _conn.checkConnectStatus(conn);
|
||||
return code == HttpURLConnection.HTTP_OK ? conn.getContentLength() : 0;
|
||||
|
||||
} finally {
|
||||
// let it be known that we're done with this connection
|
||||
@@ -232,7 +238,13 @@ public class Downloader
|
||||
protected void download (Resource rsrc) throws IOException {
|
||||
URLConnection conn = _conn.open(rsrc.getRemote(), 0, 0);
|
||||
// make sure we got a satisfactory response code
|
||||
_conn.checkConnectOK(conn, "Unable to download resource " + rsrc.getRemote());
|
||||
int code = _conn.checkConnectStatus(conn);
|
||||
if (code == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||
resourceMissing(rsrc);
|
||||
} else if (code != HttpURLConnection.HTTP_OK) {
|
||||
throw new IOException(
|
||||
"Resource returned HTTP error " + rsrc.getRemote() + " [code=" + code + "]");
|
||||
}
|
||||
|
||||
// TODO: make FileChannel download impl (below) robust and allow apps to opt-into it via a
|
||||
// system property
|
||||
|
||||
@@ -57,27 +57,11 @@ public class Config
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a configuration file containing key/value pairs. The file must be in the UTF-8
|
||||
* encoding.
|
||||
*
|
||||
* Parses configuration text containing key/value pairs.
|
||||
* @param opts options that influence the parsing. See {@link #createOpts}.
|
||||
*
|
||||
* @return a list of {@code String[]} instances containing the key/value pairs in the
|
||||
* order they were parsed from the file.
|
||||
*/
|
||||
public static List<String[]> parsePairs (File source, ParseOpts opts)
|
||||
throws IOException
|
||||
{
|
||||
// annoyingly FileReader does not allow encoding to be specified (uses platform default)
|
||||
try (FileInputStream fis = new FileInputStream(source);
|
||||
InputStreamReader input = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
|
||||
return parsePairs(input, opts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #parsePairs(File,ParseOpts)}.
|
||||
*/
|
||||
public static List<String[]> parsePairs (Reader source, ParseOpts opts) throws IOException
|
||||
{
|
||||
List<String[]> pairs = new ArrayList<>();
|
||||
@@ -131,6 +115,21 @@ public class Config
|
||||
return pairs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses configuration file containing key/value pairs.
|
||||
* @param source the file containing the config text. Must be in the UTF-8 encoding.
|
||||
* @param opts options that influence the parsing. See {@link #createOpts}.
|
||||
*/
|
||||
public static List<String[]> parsePairs (File source, ParseOpts opts)
|
||||
throws IOException
|
||||
{
|
||||
// annoyingly FileReader does not allow encoding to be specified (uses platform default)
|
||||
try (FileInputStream fis = new FileInputStream(source);
|
||||
InputStreamReader input = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
|
||||
return parsePairs(input, opts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a comma-separated String of four integers and returns a rectangle using those ints as
|
||||
* the its x, y, width, and height.
|
||||
@@ -166,13 +165,10 @@ public class Config
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a configuration file containing key/value pairs. The file must be in the UTF-8
|
||||
* encoding.
|
||||
*
|
||||
* @return a map from keys to values, where a value will be an array of strings if more than
|
||||
* one key/value pair in the config file was associated with the same key.
|
||||
* Parses the data for a config instance from the supplied {@code source} reader.
|
||||
* @return a map that can be used to create a {@link #Config}.
|
||||
*/
|
||||
public static Config parseConfig (File source, ParseOpts opts)
|
||||
public static Map<String, Object> parseData (Reader source, ParseOpts opts)
|
||||
throws IOException
|
||||
{
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
@@ -195,15 +191,34 @@ public class Config
|
||||
}
|
||||
}
|
||||
|
||||
// special magic for the getdown.txt config: if the parsed data contains 'strict_comments =
|
||||
// true' then we reparse the file with strict comments (i.e. # is only assumed to start a
|
||||
// comment in column 0)
|
||||
if (!opts.strictComments && Boolean.parseBoolean((String)data.get("strict_comments"))) {
|
||||
opts.strictComments = true;
|
||||
return parseConfig(source, opts);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
return new Config(data);
|
||||
/**
|
||||
* Parses a configuration file containing key/value pairs. The file must be in the UTF-8
|
||||
* encoding.
|
||||
*
|
||||
* @return a map from keys to values, where a value will be an array of strings if more than
|
||||
* one key/value pair in the config file was associated with the same key.
|
||||
*/
|
||||
public static Config parseConfig (File source, ParseOpts opts)
|
||||
throws IOException
|
||||
{
|
||||
// annoyingly FileReader does not allow encoding to be specified (uses platform default)
|
||||
try (FileInputStream fis = new FileInputStream(source);
|
||||
InputStreamReader input = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
|
||||
Map<String, Object> data = parseData(input, opts);
|
||||
|
||||
// special magic for the getdown.txt config: if the parsed data contains
|
||||
// 'strict_comments = true' then we reparse the file with strict comments (i.e. # is
|
||||
// only assumed to start a comment in column 0)
|
||||
if (!opts.strictComments && Boolean.parseBoolean((String)data.get("strict_comments"))) {
|
||||
opts.strictComments = true;
|
||||
return parseConfig(source, opts);
|
||||
}
|
||||
|
||||
return new Config(data);
|
||||
}
|
||||
}
|
||||
|
||||
public Config (Map<String, Object> data) {
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2018 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.data;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import com.threerings.getdown.util.Config;
|
||||
|
||||
public class ApplicationTest {
|
||||
|
||||
Application createApp () {
|
||||
List<EnvConfig.Note> notes = new ArrayList<>();
|
||||
EnvConfig env = EnvConfig.create(new String[0], notes);
|
||||
EnvConfigTest.checkNoNotes(notes);
|
||||
return new Application(env);
|
||||
}
|
||||
|
||||
@Test public void testBaseConfig () throws Exception {
|
||||
Application app = createApp();
|
||||
URL appbase = new URL("https://test.com/foo/bar/");
|
||||
Config config = new Config(Config.parseData(toReader(
|
||||
"appbase", appbase.toString()
|
||||
), Config.createOpts(true)));
|
||||
app.initBase(config);
|
||||
|
||||
assertEquals(appbase, app.getRemoteURL(""));
|
||||
}
|
||||
|
||||
@Test public void testVersionedBase () throws Exception {
|
||||
Application app = createApp();
|
||||
String rootAppbase = "https://test.com/foo/bar/";
|
||||
Config config = new Config(Config.parseData(toReader(
|
||||
"appbase", rootAppbase + "%VERSION%",
|
||||
"version", "42"
|
||||
), Config.createOpts(true)));
|
||||
app.initBase(config);
|
||||
|
||||
assertEquals(new URL(rootAppbase + "42/"), app.getRemoteURL(""));
|
||||
}
|
||||
|
||||
@Test public void testEnvVarBase () throws Exception {
|
||||
// fiddling to make test work on Windows or Unix
|
||||
String evar = System.getenv("USER") == null ? "USERNAME" : "USER";
|
||||
Application app = createApp();
|
||||
String rootAppbase = "https://test.com/foo/%ENV." + evar + "%/";
|
||||
Config config = new Config(Config.parseData(toReader(
|
||||
"appbase", rootAppbase + "%VERSION%",
|
||||
"version", "42"
|
||||
), Config.createOpts(true)));
|
||||
app.initBase(config);
|
||||
|
||||
String expectAppbase = "https://test.com/foo/" + System.getenv(evar) + "/42/";
|
||||
assertEquals(new URL(expectAppbase), app.getRemoteURL(""));
|
||||
}
|
||||
|
||||
protected static StringReader toReader (String... pairs)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int ii = 0; ii < pairs.length; ii += 2) {
|
||||
builder.append(pairs[ii]).append("=").append(pairs[ii+1]).append("\n");
|
||||
}
|
||||
return new StringReader(builder.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,13 +19,13 @@ public class EnvConfigTest {
|
||||
static String TESTID = "testid";
|
||||
static String TESTBASE = "https://test.com/test";
|
||||
|
||||
private void debugNotes(List<EnvConfig.Note> notes) {
|
||||
private void debugNotes (List<EnvConfig.Note> notes) {
|
||||
for (EnvConfig.Note note : notes) {
|
||||
System.out.println(note.message);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkNoNotes (List<EnvConfig.Note> notes) {
|
||||
static void checkNoNotes (List<EnvConfig.Note> notes) {
|
||||
StringBuilder msg = new StringBuilder();
|
||||
for (EnvConfig.Note note : notes) {
|
||||
if (note.level != EnvConfig.Note.Level.INFO) {
|
||||
|
||||
@@ -601,6 +601,8 @@ public abstract class Getdown
|
||||
int ii = 0; for (Resource prsrc : list) {
|
||||
ProgressObserver pobs = pragg.startElement(ii++);
|
||||
try {
|
||||
// if this patch file failed to download, skip it
|
||||
if (!prsrc.getLocalNew().exists()) continue;
|
||||
// install the patch file (renaming them from _new)
|
||||
prsrc.install(false);
|
||||
// now apply the patch
|
||||
@@ -663,6 +665,10 @@ public abstract class Getdown
|
||||
log.warning("Download failed", "rsrc", rsrc, e);
|
||||
}
|
||||
|
||||
@Override protected void resourceMissing (Resource rsrc) {
|
||||
log.warning("Resource missing (got 404)", "rsrc", rsrc);
|
||||
}
|
||||
|
||||
/** The last percentage at which we checked for another getdown running, or -1 for not
|
||||
* having checked at all. */
|
||||
protected int _lastCheck = -1;
|
||||
|
||||
@@ -23,6 +23,10 @@ function dnsResolve (host) {
|
||||
return resolver.dnsResolve(host)
|
||||
}
|
||||
|
||||
function myIpAddress () {
|
||||
return resolver.myIpAddress()
|
||||
}
|
||||
|
||||
function isInNet (addrOrHost, pattern, maskstr) {
|
||||
var testRE = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
|
||||
var test = testRE.exec(addrOrHost);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package com.threerings.getdown.launcher;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -133,5 +134,12 @@ public class ProxyUtilTest {
|
||||
// return 'DIRECT';
|
||||
// }
|
||||
// }
|
||||
|
||||
String MYIP =
|
||||
"function FindProxyForURL(url, host) {\n" +
|
||||
" return 'PROXY ' + myIpAddress() + ':8080';\n" +
|
||||
"}";
|
||||
String myIp = InetAddress.getLocalHost().getHostAddress();
|
||||
testPAC(MYIP, "http://testurl.com/", "PROXY " + myIp + ":8080");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user