When creating desktop applications it is often taken for granted that you can open socket connections and call out to the internet. That is, until you have a corporate client with a locked down computer and an Internet proxy.
This article shows how, using Java's proxy-vole library, you can create middleware for Butter that automatically set proxies from your user's system settings.
Reliably detecting and setting proxy configuration settings in Java is notoriously difficult. Questions such as this and this on Stackoverflow prove that many people struggle with it. Sure, you can invent your own config files and make your user edit the settings in yet another place, but what of Proxy Auto-Config (PAC) files that are becoming increasingly prevalent?
Proxy-Vole
Thankfully someone else (Bernd Rosstauscher) sought a solution to the problem and created the proxy-vole library. Proxy-vole does all the hard work for you and system proxy settings can be discovered in just a few lines of Java code:
// proxy-vole codeProxySearch proxySearch = ProxySearch.getDefaultProxySearch(); ProxySelector myProxySelector = proxySearch.getProxySelector();// standard java proxy codeList<Proxy> proxies = proxySelector.select(new URI("http://foo/bar")); if (proxies != null) for (Proxy proxy : proxies) { InetSocketAddress addr = (InetSocketAddress) proxy.address(); if (addr != null) { System.out.println("proxy host : " + addr.getHostName()); System.out.println("proxy port : " + addr.getPort()); } }
To use proxy-vole in Fantom, you need to download the jar and place it in the %FAN_HOME%\lib\java\etc\
directory.
Butter Middleware
Butter, as of v1.1, lets you specify a proxy. The proxy Uri
just needs to be set in the request stash. It is then picked up and used by the default HttpTerminator
.
Converting the above Java code to Fantom and wrapping it up as Butter middleware is a trivial task:
using afButter using [java] java.lang::System using [java] java.net::InetSocketAddress using [java] java.net::Proxy using [java] java.net::ProxySelector using [java] java.net::URI using [java] com.btr.proxy.search::ProxySearch class ProxyVoleMiddleware : ButterMiddleware { ProxySelector? proxySelector new make() {// proxySelector is null if the system doesn't use a proxyproxySelector = ProxySearch.getDefaultProxySearch.getProxySelector } override ButterResponse sendRequest(Butter butter, ButterRequest req) { if (proxySelector == null) return butter.sendRequest(req) iter := proxySelector.select(URI(req.url.encode)).iterator while (iter.hasNext) { proxy := (Proxy) iter.next addr := (InetSocketAddress?) proxy.address if (addr != null) { proxyUrl := `${req.url.scheme}://${addr.getHostName}:${addr.getPort}` req.stash["afButter.proxy"] = proxyUrl break } } return butter.sendRequest(req) } }
To use the ProxyVoleMiddleware
just ensure it is included in the middleware stack when you create your Butter
/ ButterDish
instance:
middleware := Butter.defaultStack.insert(0, ProxyVoleMiddleware()) butterdish := ButterDish(Butter.churnOut(middleware)) response := butterdish.get(`http://www.example.org/`)
And that's it!
When you now make any Butter calls, the proxy settings are automatically detected for the URL being called and placed in the request stash for the HttpTerminator
to use.
Logging
Sometimes you may wish to debug what proxy-vole is doing. In those cases you can turn on debugging; create a proxy-vole logger class:
using [java] java.lang::Class using [java] com.btr.proxy.util::Logger using [java] com.btr.proxy.util::Logger$LogBackEnd as LogBackEnd using [java] com.btr.proxy.util::Logger$LogLevel as LogLevel class MyLogger : LogBackEnd { static Void enable() { Logger.setBackend(MyLogger()) } override Bool isLogginEnabled(LogLevel? logLevel) { true } override Void log(Class? clas, LogLevel? loglevel, Str? msg, Obj?[]? params) { echo("$msg + $params") } }
To enable and use, just call the static method and any subsequent calls to proxy-vole should be echoed to the console.
MyLogger.enable()