SeleniumHQ/selenium — pull request #14 by curva (2013-02-12 15:35:02+00:00)
Title: [FIX] avoid UnreachableBrowserException when creating WebDriver instance

Summary:
Avoid multiple call for org.webbitserver.HttpResponse#end(), which flushes http response to tcp stream in rare cases. This causes UnreachableBrowserException when creating WebDriver instance (see exception message below [*1]).

Detail:
When a test code creates WebDriver instance, WebDriver(client) sends new session request. If WebDriver(server) receives that request, server renders response using RedirectResult#render() and flushes it using WebbitHttpResponse#end().
But there's a issue that WebbitHttpResponse#end() is called two times (see below[*2]).
WebbitHttpResponse#end() calls Webbit's NettyHttpResponse#end() that flushes http response to tcp stream. So WebDriver client gets same response two times as described below:
> HTTP/1.1 303 See Other
> Location: /wd/hub/session/

If WebDriver client gets redirect response two times, a CircularRedirectException will be thrown by apache http client which results in UnreachableBrowserException.
That's why WebbitHttpResponse should call webbit's HttpResponse#end() just one time.

Remarks:
(*1) UnreachableBrowserException Stacktrace
```java
org.openqa.selenium.remote.UnreachableBrowserException: Could not start a new session. Possible causes are invalid address of the remote server or browser start-up failure.
Build info: version: '2.29.0', revision: '58258c3', time: '2013-01-17 22:47:00'
System info: os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.6.8', java.version: '1.6.0_39'
Driver info: driver.version: AndroidDriver
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:527)
at org.openqa.selenium.remote.RemoteWebDriver.startSession(RemoteWebDriver.java:216)
at org.openqa.selenium.remote.RemoteWebDriver.(RemoteWebDriver.java:111)
at org.openqa.selenium.remote.RemoteWebDriver.(RemoteWebDriver.java:129)
at org.openqa.selenium.android.AndroidDriver.(AndroidDriver.java:81)
at org.openqa.selenium.android.AndroidDriver.(AndroidDriver.java:73)
at ReproductionTest.setup(ReproductionTest.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:24)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.apache.http.client.ClientProtocolException
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:909)
at org.openqa.selenium.remote.HttpCommandExecutor.fallBackExecute(HttpCommandExecutor.java:331)
at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:310)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:506)
... 38 more
Caused by: org.apache.http.client.CircularRedirectException: Circular redirect to 'http://192.168.0.7:8080/wd/hub/session/5559153d-0123-446d-9be4-02e01c8a33c3'
at org.apache.http.impl.client.DefaultRedirectStrategy.getLocationURI(DefaultRedirectStrategy.java:175)
at org.apache.http.impl.client.DefaultRedirectStrategy.getRedirect(DefaultRedirectStrategy.java:209)
at org.apache.http.impl.client.DefaultRequestDirector.handleResponse(DefaultRequestDirector.java:1068)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:545)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906)
... 41 more
```

(*2) Caller of WebbitHttpResponse#end()
first one is:
- org.openqa.selenium.remote.server.rest.ResultConfig#handle()

second one is either of them below:
- org.openqa.selenium.remote.server.renderer.JsonResult#render()
- org.openqa.selenium.remote.server.renderer.RedirectResult#render()
-> org.openqa.selenium.remote.server.WebbitHttpResponse#sendRedirect()

Reproduction Code:
```java
import java.util.ArrayList;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.android.AndroidDriver;
import org.openqa.selenium.remote.UnreachableBrowserException;

@RunWith(Parameterized.class)
public class ReproductionTest {
WebDriver driver = null;

public ReproductionTest(String dummy) {
}

@Parameters
public static List dummyData() {
// Dummy parameter method.
// This is just for calling setup/tearDown method many times.

int params = 1000;

List dummy = new ArrayList();
for(int i = 0; i < params; i++) {
dummy.add(new String[] { "" });
}

return dummy;
}

@Before
public void setup() throws Exception {
try {
driver = new AndroidDriver();
} catch(UnreachableBrowserException e) {
e.printStackTrace();

// sleep for a while to stop junit
Thread.sleep(10 * 60 * 1000);
}
}

@After
public void tearDown() {
driver.quit();
}

@Test
public void testDummy() throws Throwable {
// do nothing
}
}
```