diff --git a/src/com/machinepublishers/jbrowserdriver/Context.java b/src/com/machinepublishers/jbrowserdriver/Context.java index dc034f32a37..d55aabf97ca 100644 --- a/src/com/machinepublishers/jbrowserdriver/Context.java +++ b/src/com/machinepublishers/jbrowserdriver/Context.java @@ -92,7 +92,7 @@ void init(final JBrowserDriverServer driver) { capabilities.set(new CapabilitiesServer()); navigation.set(new NavigationServer( new AtomicReference(driver), this, statusCode)); - options.set(new OptionsServer(this, SettingsManager.settings().cookieStore(), timeouts)); + options.set(new OptionsServer(this, timeouts)); } catch (RemoteException e) { LogsServer.instance().exception(e); } diff --git a/src/com/machinepublishers/jbrowserdriver/CookieStore.java b/src/com/machinepublishers/jbrowserdriver/CookieStore.java new file mode 100644 index 00000000000..ed20f32bcbe --- /dev/null +++ b/src/com/machinepublishers/jbrowserdriver/CookieStore.java @@ -0,0 +1,157 @@ +/* + * jBrowserDriver (TM) + * Copyright (C) 2014-2016 Machine Publishers, LLC + * + * Sales and support: ops@machinepublishers.com + * Updates: https://github.com/MachinePublishers/jBrowserDriver + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.machinepublishers.jbrowserdriver; + +import java.io.IOException; +import java.io.Serializable; +import java.net.CookieHandler; +import java.net.URI; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.http.cookie.Cookie; +import org.apache.http.cookie.CookieOrigin; +import org.apache.http.cookie.CookieSpec; +import org.apache.http.cookie.MalformedCookieException; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.cookie.LaxCookieSpecProvider; +import org.apache.http.message.BasicHeader; + +public class CookieStore extends CookieHandler implements org.apache.http.client.CookieStore, Serializable { + + private static final CookieSpec spec = new LaxCookieSpecProvider().create(null); + private final org.apache.http.client.CookieStore store = new BasicCookieStore(); + + @Override + public Map> get(URI uri, Map> requestHeaders) throws IOException { + final String reqHost = canonicalHost(uri.getHost()); + final String reqPath = canonicalPath(uri.getPath()); + final boolean reqSecure = isSecure(uri.getScheme()); + final boolean reqJavascript = isJavascript(uri.getScheme()); + StringBuilder builder = new StringBuilder(); + if (reqJavascript) { + List list; + synchronized (store) { + store.clearExpired(new Date()); + list = store.getCookies(); + } + for (Cookie cookie : list) { + if ((!cookie.isSecure() || reqSecure) + && reqHost.endsWith(canonicalHost(cookie.getDomain())) + && reqPath.startsWith(canonicalPath(cookie.getPath()))) { + if (builder.length() > 0) { + builder.append(';'); + } + builder.append(cookie.getName()); + builder.append('='); + builder.append(cookie.getValue()); + } + } + } + String cookies = builder.length() == 0 ? null : builder.toString(); + Map> map; + if (cookies != null) { + map = new HashMap>(); + map.put("Cookie", Arrays.asList(cookies)); + } else { + map = Collections.emptyMap(); + } + return map; + } + + @Override + public void put(URI uri, Map> responseHeaders) throws IOException { + if (isJavascript(uri.getScheme())) { + for (Map.Entry> entry : responseHeaders.entrySet()) { + for (String value : entry.getValue()) { + try { + List cookies = spec.parse(new BasicHeader(entry.getKey(), value), + new CookieOrigin(uri.getHost(), uri.getPort(), uri.getPath(), isSecure(uri.getScheme()))); + for (Cookie cookie : cookies) { + synchronized (store) { + store.addCookie(cookie); + } + } + } catch (MalformedCookieException e) { + LogsServer.instance().warn( + "Malformed cookie for cookie named " + entry.getKey() + ". " + e.getMessage()); + } + } + } + } + } + + private static boolean isSecure(String scheme) { + return "https".equalsIgnoreCase(scheme) || "javascripts".equalsIgnoreCase(scheme); + } + + private static boolean isJavascript(String scheme) { + return "javascripts".equalsIgnoreCase(scheme) || "javascript".equalsIgnoreCase(scheme); + } + + private static String canonicalHost(String host) { + if (host == null || host.isEmpty()) { + return ""; + } + return host.startsWith(".") ? host.toLowerCase() : "." + host.toLowerCase(); + } + + private static String canonicalPath(String path) { + if (path == null || path.isEmpty()) { + return ""; + } + String canonical = path; + canonical = canonical.startsWith("/") ? canonical : "/" + canonical; + canonical = canonical.endsWith("/") ? canonical : canonical + "/"; + return canonical.toLowerCase(); + } + + @Override + public void addCookie(Cookie cookie) { + synchronized (store) { + store.addCookie(cookie); + } + } + + @Override + public List getCookies() { + synchronized (store) { + return store.getCookies(); + } + } + + @Override + public boolean clearExpired(Date date) { + synchronized (store) { + return store.clearExpired(date); + } + } + + @Override + public void clear() { + synchronized (store) { + store.clear(); + } + } +} diff --git a/src/com/machinepublishers/jbrowserdriver/HttpListener.java b/src/com/machinepublishers/jbrowserdriver/HttpListener.java index c35fc00606f..1a0c9582147 100644 --- a/src/com/machinepublishers/jbrowserdriver/HttpListener.java +++ b/src/com/machinepublishers/jbrowserdriver/HttpListener.java @@ -29,14 +29,8 @@ import java.util.concurrent.atomic.AtomicLong; import com.sun.webkit.LoadListenerClient; -import com.sun.webkit.network.CookieManager; class HttpListener implements LoadListenerClient { - static { - //TODO set a cookie manager that extends Java's CookieHandler and implements Apache's CookieStore - CookieManager.setDefault(null); - } - private static final Map states; private static final Map errors; diff --git a/src/com/machinepublishers/jbrowserdriver/JBrowserDriverServer.java b/src/com/machinepublishers/jbrowserdriver/JBrowserDriverServer.java index 3dc48f59399..0cd5c7ac449 100644 --- a/src/com/machinepublishers/jbrowserdriver/JBrowserDriverServer.java +++ b/src/com/machinepublishers/jbrowserdriver/JBrowserDriverServer.java @@ -54,6 +54,7 @@ import com.machinepublishers.jbrowserdriver.Util.Sync; import com.sun.javafx.webkit.Accessor; import com.sun.webkit.WebPage; +import com.sun.webkit.network.CookieManager; import javafx.embed.swing.JFXPanel; @@ -68,6 +69,7 @@ class JBrowserDriverServer extends UnicastRemoteObject implements JBrowserDriver * RMI entry point. */ public static void main(String[] args) { + CookieManager.setDefault(new CookieStore()); if (Settings.headless()) { System.setProperty("glass.platform", "Monocle"); System.setProperty("monocle.platform", "Headless"); @@ -165,7 +167,7 @@ public Object perform() { } }); Accessor.getPageFor(context.get().item().engine.get()).stop(); - SettingsManager.settings().cookieStore().clear(); + ((CookieStore) CookieManager.getDefault()).clear(); StatusMonitor.instance().clearStatusMonitor(); LogsServer.instance().clear(); SettingsManager.register(settings); diff --git a/src/com/machinepublishers/jbrowserdriver/OptionsServer.java b/src/com/machinepublishers/jbrowserdriver/OptionsServer.java index 5fbaed0d399..c0d966b72a4 100644 --- a/src/com/machinepublishers/jbrowserdriver/OptionsServer.java +++ b/src/com/machinepublishers/jbrowserdriver/OptionsServer.java @@ -27,22 +27,22 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicReference; -import org.apache.http.client.CookieStore; import org.apache.http.impl.cookie.BasicClientCookie; import org.openqa.selenium.Cookie; +import com.sun.webkit.network.CookieManager; + class OptionsServer extends UnicastRemoteObject implements OptionsRemote, org.openqa.selenium.WebDriver.Options { + private static final CookieStore cookieStore = (CookieStore) CookieManager.getDefault(); private final Context context; private final ImeHandlerServer imeHandler = new com.machinepublishers.jbrowserdriver.ImeHandlerServer(); - private final CookieStore cookieStore; private final AtomicReference timeouts; - OptionsServer(final Context context, final CookieStore cookieStore, + OptionsServer(final Context context, final AtomicReference timeouts) throws RemoteException { this.context = context; - this.cookieStore = cookieStore; this.timeouts = timeouts; } diff --git a/src/com/machinepublishers/jbrowserdriver/Settings.java b/src/com/machinepublishers/jbrowserdriver/Settings.java index a029fe0f65b..78ddee20f51 100644 --- a/src/com/machinepublishers/jbrowserdriver/Settings.java +++ b/src/com/machinepublishers/jbrowserdriver/Settings.java @@ -26,9 +26,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.http.client.CookieStore; -import org.apache.http.impl.client.BasicCookieStore; - import com.machinepublishers.jbrowserdriver.StreamInjectors.Injector; /** @@ -378,7 +375,6 @@ public Settings build() { private final boolean saveMedia; private final boolean saveAttachments; private final String script; - private final BasicCookieStore cookieStore; private final boolean ignoreDialogs; private final boolean cache; private final File cacheDir; @@ -393,7 +389,6 @@ private Settings(Settings.Builder builder) { this.proxy = builder.proxy; this.saveMedia = builder.saveMedia; this.saveAttachments = builder.saveAttachments; - this.cookieStore = new BasicCookieStore(); this.ignoreDialogs = builder.ignoreDialogs; this.cache = builder.cache; this.cacheDir = builder.cacheDir; @@ -450,10 +445,6 @@ String script() { return script; } - CookieStore cookieStore() { - return cookieStore; - } - boolean ignoreDialogs() { return ignoreDialogs; } diff --git a/src/com/machinepublishers/jbrowserdriver/StreamConnection.java b/src/com/machinepublishers/jbrowserdriver/StreamConnection.java index 6e947f5defa..4bb26bc4f13 100644 --- a/src/com/machinepublishers/jbrowserdriver/StreamConnection.java +++ b/src/com/machinepublishers/jbrowserdriver/StreamConnection.java @@ -102,7 +102,10 @@ import org.apache.http.ssl.TrustStrategy; import org.apache.http.util.EntityUtils; +import com.sun.webkit.network.CookieManager; + class StreamConnection extends HttpURLConnection implements Closeable { + private static final CookieStore cookieStore = (CookieStore) CookieManager.getDefault(); private static final File attachmentsDir; private static final File mediaDir; private static final File cacheDir; @@ -469,7 +472,7 @@ public void connect() throws IOException { config.get().setProxy(new HttpHost(proxy.host(), proxy.port())); } } - context.get().setCookieStore(SettingsManager.settings().cookieStore()); + context.get().setCookieStore(cookieStore); context.get().setRequestConfig(config.get().build()); StatusMonitor.instance().addStatusMonitor(url, this); } diff --git a/src/com/machinepublishers/jbrowserdriver/diagnostics/Test.java b/src/com/machinepublishers/jbrowserdriver/diagnostics/Test.java index d308c9211eb..d5659ccba6d 100644 --- a/src/com/machinepublishers/jbrowserdriver/diagnostics/Test.java +++ b/src/com/machinepublishers/jbrowserdriver/diagnostics/Test.java @@ -97,7 +97,7 @@ private Test() { test(driver.findElement(By.xpath("//a[contains(@href,'1')]")).getAttribute("id").equals("anchor1")); /* - * Set cookies + * Cookie manager */ driver.manage().addCookie(new Cookie("testname", "testvalue")); test(driver.manage().getCookieNamed("testname").getValue().equals("testvalue")); @@ -155,7 +155,14 @@ private Test() { test(driver.getCurrentUrl().endsWith("/redirect/site2")); test(driver.manage().getCookieNamed("JSESSIONID").getValue().equals("ABC123")); - //TODO handle cookies set by JS + /* + * Cookies set by Javascript + */ + test("jsCookieValue1".equals(driver.manage().getCookieNamed("jsCookieName1").getValue())); + test("jsCookieValue2".equals(driver.manage().getCookieNamed("jsCookieName2").getValue())); + test("jsCookieValue3".equals(driver.manage().getCookieNamed("jsCookieName3").getValue())); + test("jsCookieValue4".equals(driver.manage().getCookieNamed("jsCookieName4").getValue())); + } catch (Throwable t) { errors.add("Test #" + (curTest + 1) + " -- " + toString(t)); } finally { diff --git a/src/com/machinepublishers/jbrowserdriver/diagnostics/iframe.htm b/src/com/machinepublishers/jbrowserdriver/diagnostics/iframe.htm index 0225e17afb6..fc6c4084ce8 100644 --- a/src/com/machinepublishers/jbrowserdriver/diagnostics/iframe.htm +++ b/src/com/machinepublishers/jbrowserdriver/diagnostics/iframe.htm @@ -3,5 +3,9 @@ test iframe + \ No newline at end of file diff --git a/src/com/machinepublishers/jbrowserdriver/diagnostics/test.htm b/src/com/machinepublishers/jbrowserdriver/diagnostics/test.htm index d0ffe2f9732..c7ffedcb126 100644 --- a/src/com/machinepublishers/jbrowserdriver/diagnostics/test.htm +++ b/src/com/machinepublishers/jbrowserdriver/diagnostics/test.htm @@ -10,7 +10,8 @@