diff --git a/pom.xml b/pom.xml
index a9d5b629..9aeba1c1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -160,6 +160,16 @@
byteunits
0.9.1
+
+ io.netty
+ netty-all
+ 4.1.8.Final
+
+
+ io.netty
+ netty-resolver-dns
+ 4.1.8.Final
+
us.physion
osx-keychain
@@ -274,7 +284,6 @@
src/assembly/bundle.xml
- oksocial-${project.version}
com.baulsupp.oksocial.Main
diff --git a/src/main/java/com/baulsupp/oksocial/Main.java b/src/main/java/com/baulsupp/oksocial/Main.java
index 40ce9db9..9702fa50 100644
--- a/src/main/java/com/baulsupp/oksocial/Main.java
+++ b/src/main/java/com/baulsupp/oksocial/Main.java
@@ -21,8 +21,8 @@
import com.baulsupp.oksocial.location.BestLocation;
import com.baulsupp.oksocial.location.LocationSource;
import com.baulsupp.oksocial.network.DnsOverride;
-import com.baulsupp.oksocial.network.DnsSelector;
import com.baulsupp.oksocial.network.InterfaceSocketFactory;
+import com.baulsupp.oksocial.network.NettyDns;
import com.baulsupp.oksocial.okhttp.OkHttpResponseFuture;
import com.baulsupp.oksocial.output.ConsoleHandler;
import com.baulsupp.oksocial.output.DownloadHandler;
@@ -51,6 +51,7 @@
import io.airlift.airline.HelpOption;
import io.airlift.airline.Option;
import io.airlift.airline.SingleCommand;
+import io.netty.channel.nio.NioEventLoopGroup;
import java.io.File;
import java.io.IOException;
import java.net.Proxy;
@@ -62,6 +63,7 @@
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.SocketFactory;
@@ -245,6 +247,8 @@ public static void main(String... args) {
public LocationSource locationSource = new BestLocation();
+ private NioEventLoopGroup eventLoopGroup;
+
private String versionString() {
return Util.versionString("/oksocial-version.properties");
}
@@ -457,6 +461,10 @@ private void closeClients() {
client.dispatcher().executorService().shutdown();
client.connectionPool().evictAll();
}
+
+ if (eventLoopGroup != null) {
+ eventLoopGroup.shutdownGracefully(0, 0, TimeUnit.SECONDS);
+ }
}
private OutputHandler buildHandler() {
@@ -578,7 +586,7 @@ public OkHttpClient.Builder createClientBuilder() throws Exception {
builder.readTimeout(readTimeout, SECONDS);
}
- Dns dns = DnsSelector.byName(ipmode);
+ Dns dns = NettyDns.byName(ipmode, getEventLoopGroup());
if (resolve != null) {
dns = DnsOverride.build(dns, resolve);
}
@@ -619,6 +627,14 @@ public OkHttpClient.Builder createClientBuilder() throws Exception {
return builder;
}
+ private NioEventLoopGroup getEventLoopGroup() {
+ if (eventLoopGroup == null) {
+ eventLoopGroup = new NioEventLoopGroup(1);
+ }
+
+ return eventLoopGroup;
+ }
+
private SocketFactory getSocketFactory() throws SocketException {
Optional socketFactory = InterfaceSocketFactory.byName(networkInterface);
diff --git a/src/main/java/com/baulsupp/oksocial/network/DnsSelector.java b/src/main/java/com/baulsupp/oksocial/network/DnsSelector.java
index f504122f..b44612dd 100644
--- a/src/main/java/com/baulsupp/oksocial/network/DnsSelector.java
+++ b/src/main/java/com/baulsupp/oksocial/network/DnsSelector.java
@@ -1,14 +1,11 @@
package com.baulsupp.oksocial.network;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Comparator;
import java.util.List;
-import java.util.Map;
import java.util.logging.Logger;
import okhttp3.Dns;
@@ -26,8 +23,6 @@ public enum Mode {
IPV4_ONLY
}
- private Map> overrides = Maps.newHashMap();
-
private Mode mode;
public DnsSelector(Mode mode) {
@@ -58,13 +53,7 @@ public static Dns byName(String ipMode) {
}
@Override public List lookup(String hostname) throws UnknownHostException {
- List addresses = overrides.get(hostname.toLowerCase());
-
- if (addresses != null) {
- return addresses;
- }
-
- addresses = Dns.SYSTEM.lookup(hostname);
+ List addresses = Dns.SYSTEM.lookup(hostname);
switch (mode) {
case IPV6_FIRST:
@@ -87,8 +76,4 @@ public static Dns byName(String ipMode) {
return addresses;
}
-
- public void addOverride(String hostname, InetAddress address) {
- overrides.put(hostname.toLowerCase(), Lists.newArrayList(address));
- }
}
diff --git a/src/main/java/com/baulsupp/oksocial/network/NettyDns.java b/src/main/java/com/baulsupp/oksocial/network/NettyDns.java
new file mode 100644
index 00000000..23523ba0
--- /dev/null
+++ b/src/main/java/com/baulsupp/oksocial/network/NettyDns.java
@@ -0,0 +1,89 @@
+package com.baulsupp.oksocial.network;
+
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.socket.InternetProtocolFamily;
+import io.netty.channel.socket.nio.NioDatagramChannel;
+import io.netty.resolver.dns.DnsNameResolver;
+import io.netty.resolver.dns.DnsNameResolverBuilder;
+import io.netty.util.concurrent.Future;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import okhttp3.Dns;
+
+import static io.netty.channel.socket.InternetProtocolFamily.IPv4;
+import static io.netty.channel.socket.InternetProtocolFamily.IPv6;
+import static java.util.stream.Collectors.joining;
+
+public class NettyDns implements Dns {
+ private static Logger logger = Logger.getLogger(NettyDns.class.getName());
+
+ private final DnsNameResolver r;
+ private final EventLoopGroup group;
+
+ public NettyDns(EventLoopGroup group, Iterable addressTypes) {
+ this.group = group;
+ DnsNameResolverBuilder builder = new DnsNameResolverBuilder(this.group.next())
+ .channelType(NioDatagramChannel.class)
+ .optResourceEnabled(false)
+ .maxQueriesPerResolve(3)
+ .recursionDesired(true);
+
+ if (logger.isLoggable(Level.FINEST)) {
+ builder.traceEnabled(true);
+ }
+
+ if (addressTypes != null) {
+ builder.resolvedAddressTypes(addressTypes);
+ }
+
+ r = builder.build();
+ }
+
+ @Override public List lookup(String hostname) throws UnknownHostException {
+ Future> f = r.resolveAll(hostname);
+
+ try {
+ List addresses = f.get();
+
+ logger.fine("Dns (" + hostname + "): " + addresses.stream()
+ .map(Object::toString)
+ .collect(joining(", ")));
+
+ return addresses;
+ } catch (InterruptedException e) {
+ throw new UnknownHostException(e.toString());
+ } catch (ExecutionException e) {
+ throw ((UnknownHostException) new UnknownHostException(e.getCause().getMessage()).initCause(
+ e.getCause()));
+ }
+ }
+
+ public static Dns byName(String ipMode, EventLoopGroup eventLoopGroup) {
+ List types;
+
+ switch (ipMode) {
+ case "ipv6":
+ types = Arrays.asList(IPv6, IPv4);
+ break;
+ case "ipv4":
+ types = Arrays.asList(IPv4, IPv6);
+ break;
+ case "ipv6only":
+ types = Arrays.asList(IPv6);
+ break;
+ case "ipv4only":
+ types = Arrays.asList(IPv4);
+ break;
+ default:
+ types = null;
+ break;
+ }
+
+ return new NettyDns(eventLoopGroup, types);
+ }
+}
diff --git a/src/main/java/com/baulsupp/oksocial/util/LoggingUtil.java b/src/main/java/com/baulsupp/oksocial/util/LoggingUtil.java
index d47ece7d..fb6e0618 100644
--- a/src/main/java/com/baulsupp/oksocial/util/LoggingUtil.java
+++ b/src/main/java/com/baulsupp/oksocial/util/LoggingUtil.java
@@ -1,6 +1,8 @@
package com.baulsupp.oksocial.util;
import com.google.common.collect.Lists;
+import io.netty.util.internal.logging.InternalLoggerFactory;
+import io.netty.util.internal.logging.JdkLoggerFactory;
import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
@@ -14,6 +16,8 @@ public class LoggingUtil {
private static List activeLoggers = Lists.newArrayList();
public static void configureLogging(boolean debug, boolean showHttp2Frames) {
+ InternalLoggerFactory.setDefaultFactory(JdkLoggerFactory.INSTANCE);
+
if (debug || showHttp2Frames) {
LogManager.getLogManager().reset();
ConsoleHandler handler = new ConsoleHandler();
@@ -25,8 +29,9 @@ public static void configureLogging(boolean debug, boolean showHttp2Frames) {
activeLogger.addHandler(handler);
activeLogger.setLevel(Level.ALL);
- Logger x = getLogger("org.zeroturnaround.exec.stream");
- x.setLevel(Level.INFO);
+ getLogger("org.zeroturnaround.exec").setLevel(Level.INFO);
+ getLogger("io.netty").setLevel(Level.INFO);
+ getLogger("io.netty.resolver.dns").setLevel(Level.FINE);
} else if (showHttp2Frames) {
Logger activeLogger = getLogger(Http2.class.getName());
activeLogger.setLevel(Level.FINE);
@@ -37,7 +42,10 @@ public static void configureLogging(boolean debug, boolean showHttp2Frames) {
}
});
activeLogger.addHandler(handler);
+ getLogger("io.netty.resolver.dns.DnsServerAddresses").setLevel(Level.SEVERE);
}
+ } else {
+ getLogger("io.netty.resolver.dns.DnsServerAddresses").setLevel(Level.SEVERE);
}
}
diff --git a/src/test/java/com/baulsupp/oksocial/TestMain.java b/src/test/java/com/baulsupp/oksocial/TestMain.java
new file mode 100644
index 00000000..48af2e4e
--- /dev/null
+++ b/src/test/java/com/baulsupp/oksocial/TestMain.java
@@ -0,0 +1,7 @@
+package com.baulsupp.oksocial;
+
+public class TestMain {
+ public static void main(String[] args) throws Exception {
+ Main.main("--debug", "https://api.twitter.com/robots.txt");
+ }
+}
diff --git a/src/test/java/com/baulsupp/oksocial/services/google/DiscoveryApiDocPresenterTest.java b/src/test/java/com/baulsupp/oksocial/services/google/DiscoveryApiDocPresenterTest.java
index 4ff46ba6..759d3233 100644
--- a/src/test/java/com/baulsupp/oksocial/services/google/DiscoveryApiDocPresenterTest.java
+++ b/src/test/java/com/baulsupp/oksocial/services/google/DiscoveryApiDocPresenterTest.java
@@ -36,15 +36,13 @@ public void loadPresenter() throws IOException {
"url: https://people.googleapis.com/v1/{+resourceName}",
"scopes: https://www.googleapis.com/auth/contacts, https://www.googleapis.com/auth/contacts.readonly, https://www.googleapis.com/auth/plus.login, https://www.googleapis.com/auth/user.addresses.read, https://www.googleapis.com/auth/user.birthday.read, https://www.googleapis.com/auth/user.emails.read, https://www.googleapis.com/auth/user.phonenumbers.read, https://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile",
"",
- "Provides information about a person resource for a resource name. Use `people/me` to indicate the authenticated user.",
- "",
- "parameter: resourceName (string)",
- "The resource name of the person to provide information about. - To get information about the authenticated user, specify `people/me`. - To get information about any user, specify the resource name that identifies the user, such as the resource names returned by [`people.connections.list`](/people/api/rest/v1/people.connections/list).",
- "parameter: requestMask.includeField (string)",
- "Comma-separated list of fields to be included in the response. Omitting this field will include all fields. Each path should start with `person.`: for example, `person.names` or `person.photos`."
+ "Provides information about a person resource for a resource name. Use\n"
+ + "`people/me` to indicate the authenticated user."
);
- assertEquals(es, outputHandler.stdout);
+ for (String l: es) {
+ assertTrue(outputHandler.stdout.contains(l), l);
+ }
}
@Test public void testExplainsExpandedUrl() throws IOException {