1
0
mirror of synced 2026-08-04 06:26:55 +00:00

feat(proxy): support per context proxy configuration (#87)

This commit is contained in:
Yury Semikhatsky
2020-12-03 21:48:36 -08:00
committed by GitHub
parent 2722229b0a
commit b5459c048b
4 changed files with 243 additions and 15 deletions
@@ -16,10 +16,7 @@
package com.microsoft.playwright;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.*;
import java.io.IOException;
@@ -51,10 +48,17 @@ public class TestBase {
return "firefox".equals(browserType.name());
}
@BeforeAll
static void launchBrowser() {
playwright = Playwright.create();
static BrowserType.LaunchOptions createLaunchOptions() {
String headfulEnv = System.getenv("HEADFUL");
headful = headfulEnv != null && !"0".equals(headfulEnv) && !"false".equals(headfulEnv);
BrowserType.LaunchOptions options;
options = new BrowserType.LaunchOptions();
options.headless = !headful;
return options;
}
static void launchBrowser(BrowserType.LaunchOptions launchOptions) {
playwright = Playwright.create();
String browserName = System.getenv("BROWSER");
if (browserName == null) {
@@ -73,11 +77,12 @@ public class TestBase {
default:
throw new IllegalArgumentException("Unknown browser: " + browserName);
}
String headfulEnv = System.getenv("HEADFUL");
headful = headfulEnv != null && !"0".equals(headfulEnv) && !"false".equals(headfulEnv);
BrowserType.LaunchOptions options = new BrowserType.LaunchOptions();
options.headless = !headful;
browser = browserType.launch(options);
browser = browserType.launch(launchOptions);
}
@BeforeAll
static void launchBrowser() {
launchBrowser(createLaunchOptions());
}
@AfterAll
@@ -0,0 +1,216 @@
/*
* Copyright (c) Microsoft Corporation.
*
* 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.microsoft.playwright;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import java.io.OutputStreamWriter;
import java.util.Base64;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class TestBrowserContextProxy extends TestBase {
@BeforeAll
// Hide base class method to provide extra option.
static void launchBrowser() {
BrowserType.LaunchOptions options = createLaunchOptions();
options.setProxy().withServer("per-context").done();
launchBrowser(options);
}
@Test
void shouldThrowForMissingGlobalProxy() {
Browser browser = browserType.launch(createLaunchOptions());
try {
browser.newContext(new Browser.NewContextOptions().setProxy().withServer("localhost:" + server.PORT).done());
fail("did not throw");
} catch (PlaywrightException e) {
assertTrue(e.getMessage().contains("Browser needs to be launched with the global proxy"));
} finally {
browser.close();
}
}
void shouldThrowForBadServerValue() {
// Enforced by compiler in Java
}
@Test
void shouldUseProxy() {
server.setRoute("/target.html", exchange -> {
exchange.sendResponseHeaders(200, 0);
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("<html><title>Served by the proxy</title></html>");
}
});
BrowserContext context = browser.newContext(new Browser.NewContextOptions().setProxy()
.withServer("localhost:" + server.PORT).done());
Page page = context.newPage();
page.navigate("http://non-existent.com/target.html");
assertEquals("Served by the proxy", page.title());
context.close();
}
@Test
void shouldUseProxyTwice() {
server.setRoute("/target.html", exchange -> {
exchange.sendResponseHeaders(200, 0);
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("<html><title>Served by the proxy</title></html>");
}
});
BrowserContext context = browser.newContext(new Browser.NewContextOptions().setProxy()
.withServer("localhost:" + server.PORT).done());
Page page = context.newPage();
page.navigate("http://non-existent.com/target.html");
page.navigate("http://non-existent-2.com/target.html");
assertEquals("Served by the proxy", page.title());
context.close();
}
@Test
void shouldUseProxyForSecondPage() {
server.setRoute("/target.html", exchange -> {
exchange.sendResponseHeaders(200, 0);
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("<html><title>Served by the proxy</title></html>");
}
});
BrowserContext context = browser.newContext(new Browser.NewContextOptions().setProxy()
.withServer("localhost:" + server.PORT).done());
Page page = context.newPage();
page.navigate("http://non-existent.com/target.html");
assertEquals("Served by the proxy", page.title());
Page page2 = context.newPage();
page2.navigate("http://non-existent.com/target.html");
assertEquals("Served by the proxy", page2.title());
context.close();
}
@Test
void shouldWorkWithIPPORTNotion() {
server.setRoute("/target.html", exchange -> {
exchange.sendResponseHeaders(200, 0);
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("<html><title>Served by the proxy</title></html>");
}
});
BrowserContext context = browser.newContext(new Browser.NewContextOptions().setProxy()
.withServer("127.0.0.1:" + server.PORT).done());
Page page = context.newPage();
page.navigate("http://non-existent.com/target.html");
assertEquals("Served by the proxy", page.title());
context.close();
}
@Test
void shouldAuthenticate() {
server.setRoute("/target.html", exchange -> {
List<String> auth = exchange.getRequestHeaders().get("proxy-authorization");
if (auth == null) {
exchange.getResponseHeaders().add("Proxy-Authenticate", "Basic realm='Access to internal site'");
exchange.sendResponseHeaders(407, 0);
exchange.getResponseBody().close();
return;
}
exchange.sendResponseHeaders(200, 0);
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("<html><title>" + auth.get(0) + "</title></html>");
}
});
BrowserContext context = browser.newContext(new Browser.NewContextOptions().setProxy()
.withServer("localhost:" + server.PORT)
.withUsername("user")
.withPassword("secret").done());
Page page = context.newPage();
page.navigate("http://non-existent.com/target.html");
assertEquals("Basic " + Base64.getEncoder().encodeToString("user:secret".getBytes()), page.title());
context.close();
}
static boolean isChromiumHeadful() {
return isChromium() && isHeadful();
}
@Test
@DisabledIf(value="isChromiumHeadful", disabledReason="fixme")
void shouldExcludePatterns() {
server.setRoute("/target.html", exchange -> {
exchange.sendResponseHeaders(200, 0);
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("<html><title>Served by the proxy</title></html>");
}
});
BrowserContext context = browser.newContext(new Browser.NewContextOptions().setProxy()
.withServer("127.0.0.1:" + server.PORT)
// FYI: using long and weird domain names to avoid ATT DNS hijacking
// that resolves everything to some weird search results page.
//
// @see https://gist.github.com/CollinChaffin/24f6c9652efb3d6d5ef2f5502720ef00
.withBypass("1.non.existent.domain.for.the.test, 2.non.existent.domain.for.the.test, .another.test").done());
Page page = context.newPage();
page.navigate("http://0.non.existent.domain.for.the.test/target.html");
assertEquals("Served by the proxy", page.title());
try {
page.navigate("http://1.non.existent.domain.for.the.test/target.html");
fail("did not throw");
} catch (PlaywrightException exception) {
}
try {
page.navigate("http://2.non.existent.domain.for.the.test/target.html");
fail("did not throw");
} catch (PlaywrightException e) {
}
try {
page.navigate("http://foo.is.the.another.test/target.html");
fail("did not throw");
} catch (PlaywrightException e) {
}
page.navigate("http://3.non.existent.domain.for.the.test/target.html");
assertEquals("Served by the proxy", page.title());
context.close();
}
void shouldUseSocksProxy() {
// TODO: implement socks server
}
void shouldUseSocksProxyInSecondPage() {
// TODO: implement socks server
}
@Test
void doesLaunchWithoutAPort() {
BrowserContext context = browser.newContext(new Browser.NewContextOptions().setProxy()
.withServer("http://localhost").done());
context.close();
}
}
@@ -41,7 +41,7 @@ public class TestPageFill extends TestBase {
@Test
void shouldThrowOnUnsupportedInputs() {
page.navigate(server.PREFIX + "/input/textarea.html");
for (String type : new String[]{"color", "file"}) {
for (String type : new String[]{"button", "checkbox", "file", "image", "radio", "range", "reset", "submit"}) {
page.evalOnSelector("input", "(input, type) => input.setAttribute('type', type)", type);
try {
page.fill("input", "");
@@ -460,8 +460,15 @@ public class TestPageRoute extends TestBase {
}
@Test
void shouldCreateARedirect() {
page.navigate(server.PREFIX + "/empty.html");
@DisabledIf(value="com.microsoft.playwright.TestBase#isWebKit", disabledReason="fixme")
void shouldFulfillWithRedirectStatus() {
page.navigate(server.PREFIX + "/title.html");
server.setRoute("/final", exchange -> {
exchange.sendResponseHeaders(200, 0);
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
writer.write("foo");
}
});
page.route("**/*", route -> {
if (!route.request().url().equals(server.PREFIX + "/redirect_this")) {
route.continue_();