Skip to content

Commit

Permalink
Allow to parse hosts file which is stored in a different encoding the…
Browse files Browse the repository at this point in the history
…n the default system encoding. (netty#8211)

Motivation:

We should support to parse and read a hosts file which is stored in a different encoding then the system default. Beside this when we are on windows we should just try to parse it with multiple different charset before giving up as there is no real standard what charset to use.

Modifications:

- Add more method overloads to HostsFileParser that take a Charset.
- Try to parse with multiple Charsets in DefaultHostsFileEntriesResolver when windows is used.
- Add unit test

Result:

Fixes netty#8208.
  • Loading branch information
normanmaurer authored Aug 24, 2018
1 parent a580dc7 commit 6888af6
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
*/
package io.netty.resolver;

import io.netty.util.CharsetUtil;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.UnstableApi;

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.charset.Charset;
import java.util.Locale;
import java.util.Map;

Expand All @@ -33,7 +36,7 @@ public final class DefaultHostsFileEntriesResolver implements HostsFileEntriesRe
private final Map<String, Inet6Address> inet6Entries;

public DefaultHostsFileEntriesResolver() {
this(HostsFileParser.parseSilently());
this(parseEntries());
}

// for testing purpose only
Expand Down Expand Up @@ -65,4 +68,14 @@ public InetAddress address(String inetHost, ResolvedAddressTypes resolvedAddress
String normalize(String inetHost) {
return inetHost.toLowerCase(Locale.ENGLISH);
}

private static HostsFileEntries parseEntries() {
if (PlatformDependent.isWindows()) {
// Ony windows there seems to be no standard for the encoding used for the hosts file, so let us
// try multiple until we either were able to parse it or there is none left and so we return an
// empty intstance.
return HostsFileParser.parseSilently(Charset.defaultCharset(), CharsetUtil.UTF_16, CharsetUtil.UTF_8);
}
return HostsFileParser.parseSilently();
}
}
47 changes: 39 additions & 8 deletions resolver/src/main/java/io/netty/resolver/HostsFileParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
Expand Down Expand Up @@ -66,14 +68,25 @@ private static File locateHostsFile() {
}

/**
* Parse hosts file at standard OS location.
* Parse hosts file at standard OS location using the systems default {@link Charset} for decoding.
*
* @return a {@link HostsFileEntries}
*/
public static HostsFileEntries parseSilently() {
return parseSilently(Charset.defaultCharset());
}

/**
* Parse hosts file at standard OS location using the given {@link Charset}s one after each other until
* we were able to parse something or none is left.
*
* @param charsets the {@link Charset}s to try as file encodings when parsing.
* @return a {@link HostsFileEntries}
*/
public static HostsFileEntries parseSilently(Charset... charsets) {
File hostsFile = locateHostsFile();
try {
return parse(hostsFile);
return parse(hostsFile, charsets);
} catch (IOException e) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to load and parse hosts file at " + hostsFile.getPath(), e);
Expand All @@ -83,7 +96,7 @@ public static HostsFileEntries parseSilently() {
}

/**
* Parse hosts file at standard OS location.
* Parse hosts file at standard OS location using the system default {@link Charset} for decoding.
*
* @return a {@link HostsFileEntries}
* @throws IOException file could not be read
Expand All @@ -93,19 +106,37 @@ public static HostsFileEntries parse() throws IOException {
}

/**
* Parse a hosts file.
* Parse a hosts file using the system default {@link Charset} for decoding.
*
* @param file the file to be parsed
* @return a {@link HostsFileEntries}
* @throws IOException file could not be read
*/
public static HostsFileEntries parse(File file) throws IOException {
return parse(file, Charset.defaultCharset());
}

/**
* Parse a hosts file.
*
* @param file the file to be parsed
* @param charsets the {@link Charset}s to try as file encodings when parsing.
* @return a {@link HostsFileEntries}
* @throws IOException file could not be read
*/
public static HostsFileEntries parse(File file, Charset... charsets) throws IOException {
checkNotNull(file, "file");
checkNotNull(charsets, "charsets");
if (file.exists() && file.isFile()) {
return parse(new BufferedReader(new FileReader(file)));
} else {
return HostsFileEntries.EMPTY;
for (Charset charset: charsets) {
HostsFileEntries entries = parse(new BufferedReader(new InputStreamReader(
new FileInputStream(file), charset)));
if (entries != HostsFileEntries.EMPTY) {
return entries;
}
}
}
return HostsFileEntries.EMPTY;
}

/**
Expand Down
42 changes: 42 additions & 0 deletions resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@
*/
package io.netty.resolver;

import io.netty.util.CharsetUtil;
import org.junit.Assume;
import org.junit.Test;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Map;

import static org.junit.Assert.*;
Expand Down Expand Up @@ -60,4 +65,41 @@ public void testParse() throws IOException {
assertEquals("192.168.0.5", inet4Entries.get("host7").getHostAddress());
assertEquals("0:0:0:0:0:0:0:1", inet6Entries.get("host1").getHostAddress());
}

@Test
public void testParseUnicode() throws IOException {
final Charset unicodeCharset;
try {
unicodeCharset = Charset.forName("unicode");
} catch (UnsupportedCharsetException e) {
Assume.assumeNoException(e);
return;
}
testParseFile(HostsFileParser.parse(
new File(getClass().getResource("hosts-unicode").getFile()), unicodeCharset));
}

@Test
public void testParseMultipleCharsets() throws IOException {
final Charset unicodeCharset;
try {
unicodeCharset = Charset.forName("unicode");
} catch (UnsupportedCharsetException e) {
Assume.assumeNoException(e);
return;
}
testParseFile(HostsFileParser.parse(new File(getClass().getResource("hosts-unicode").getFile()),
CharsetUtil.UTF_8, CharsetUtil.ISO_8859_1, unicodeCharset));
}

private static void testParseFile(HostsFileEntries entries) throws IOException {
Map<String, Inet4Address> inet4Entries = entries.inet4Entries();
Map<String, Inet6Address> inet6Entries = entries.inet6Entries();

assertEquals("Expected 2 IPv4 entries", 2, inet4Entries.size());
assertEquals("Expected 1 IPv6 entries", 1, inet6Entries.size());
assertEquals("127.0.0.1", inet4Entries.get("localhost").getHostAddress());
assertEquals("255.255.255.255", inet4Entries.get("broadcasthost").getHostAddress());
assertEquals("0:0:0:0:0:0:0:1", inet6Entries.get("localhost").getHostAddress());
}
}
Binary file not shown.

0 comments on commit 6888af6

Please sign in to comment.