Try to use Content-Type when loading a URL

This is intended to fix #67. Unfortunately we don't have test coverage
for URL loading, so not feeling super confident.
This commit is contained in:
Havoc Pennington 2013-09-27 13:26:40 -04:00
parent 54ec27ec2a
commit 868a50a53d
2 changed files with 66 additions and 3 deletions

View File

@ -1111,6 +1111,10 @@ The details:
"2" then the resulting array would have indices "0" and "1", "2" then the resulting array would have indices "0" and "1",
i.e. missing indices in the object are eliminated. i.e. missing indices in the object are eliminated.
## MIME Type
Use "application/hocon" for Content-Type.
## API Recommendations ## API Recommendations
Implementations of HOCON ideally follow certain conventions and Implementations of HOCON ideally follow certain conventions and

View File

@ -18,6 +18,7 @@ import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
@ -104,6 +105,10 @@ public abstract class Parseable implements ConfigParseable {
return null; return null;
} }
ConfigSyntax contentType() {
return null;
}
ConfigParseable relativeTo(String filename) { ConfigParseable relativeTo(String filename) {
// fall back to classpath; we treat the "filename" as absolute // fall back to classpath; we treat the "filename" as absolute
// (don't add a package name in front), // (don't add a package name in front),
@ -186,8 +191,23 @@ public abstract class Parseable implements ConfigParseable {
protected AbstractConfigValue rawParseValue(ConfigOrigin origin, ConfigParseOptions finalOptions) protected AbstractConfigValue rawParseValue(ConfigOrigin origin, ConfigParseOptions finalOptions)
throws IOException { throws IOException {
Reader reader = reader(); Reader reader = reader();
// after reader() we will have loaded the Content-Type.
ConfigSyntax contentType = contentType();
ConfigParseOptions optionsWithContentType;
if (contentType != null) {
if (ConfigImpl.traceLoadsEnabled() && finalOptions.getSyntax() != null)
trace("Overriding syntax " + finalOptions.getSyntax()
+ " with Content-Type which specified " + contentType);
optionsWithContentType = finalOptions.setSyntax(contentType);
} else {
optionsWithContentType = finalOptions;
}
try { try {
return rawParseValue(reader, origin, finalOptions); return rawParseValue(reader, origin, optionsWithContentType);
} finally { } finally {
reader.close(); reader.close();
} }
@ -240,12 +260,16 @@ public abstract class Parseable implements ConfigParseable {
} }
private static Reader readerFromStream(InputStream input) { private static Reader readerFromStream(InputStream input) {
return readerFromStream(input, "UTF-8");
}
private static Reader readerFromStream(InputStream input, String encoding) {
try { try {
// well, this is messed up. If we aren't going to close // well, this is messed up. If we aren't going to close
// the passed-in InputStream then we have no way to // the passed-in InputStream then we have no way to
// close these readers. So maybe we should not have an // close these readers. So maybe we should not have an
// InputStream version, only a Reader version. // InputStream version, only a Reader version.
Reader reader = new InputStreamReader(input, "UTF-8"); Reader reader = new InputStreamReader(input, encoding);
return new BufferedReader(reader); return new BufferedReader(reader);
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new ConfigException.BugOrBroken("Java runtime does not support UTF-8", e); throw new ConfigException.BugOrBroken("Java runtime does not support UTF-8", e);
@ -390,6 +414,7 @@ public abstract class Parseable implements ConfigParseable {
private final static class ParseableURL extends Parseable { private final static class ParseableURL extends Parseable {
final private URL input; final private URL input;
private String contentType = null;
ParseableURL(URL input, ConfigParseOptions options) { ParseableURL(URL input, ConfigParseOptions options) {
this.input = input; this.input = input;
@ -400,7 +425,22 @@ public abstract class Parseable implements ConfigParseable {
protected Reader reader() throws IOException { protected Reader reader() throws IOException {
if (ConfigImpl.traceLoadsEnabled()) if (ConfigImpl.traceLoadsEnabled())
trace("Loading config from a URL: " + input.toExternalForm()); trace("Loading config from a URL: " + input.toExternalForm());
InputStream stream = input.openStream(); URLConnection connection = input.openConnection();
connection.connect();
// save content type for later
contentType = connection.getContentType();
if (contentType != null) {
if (ConfigImpl.traceLoadsEnabled())
trace("URL sets Content-Type: '" + contentType + "'");
contentType = contentType.trim();
int semi = contentType.indexOf(';');
if (semi >= 0)
contentType = contentType.substring(0, semi);
}
InputStream stream = connection.getInputStream();
return readerFromStream(stream); return readerFromStream(stream);
} }
@ -409,6 +449,25 @@ public abstract class Parseable implements ConfigParseable {
return syntaxFromExtension(input.getPath()); return syntaxFromExtension(input.getPath());
} }
@Override
ConfigSyntax contentType() {
if (contentType != null) {
if (contentType.equals("application/json"))
return ConfigSyntax.JSON;
else if (contentType.equals("text/x-java-properties"))
return ConfigSyntax.PROPERTIES;
else if (contentType.equals("application/hocon"))
return ConfigSyntax.CONF;
else {
if (ConfigImpl.traceLoadsEnabled())
trace("'" + contentType + "' isn't a known content type");
return null;
}
} else {
return null;
}
}
@Override @Override
ConfigParseable relativeTo(String filename) { ConfigParseable relativeTo(String filename) {
URL url = relativeTo(input, filename); URL url = relativeTo(input, filename);