mirror of
https://github.com/microsoft/playwright-java.git
synced 2025-09-08 21:01:00 +00:00
feat: implement remaining Response methods (#46)
This commit is contained in:
parent
24b101d071
commit
c134a6355f
@ -243,6 +243,8 @@ class Method extends Element {
|
|||||||
"void route(Pattern url, BiConsumer<Route, Request> handler);",
|
"void route(Pattern url, BiConsumer<Route, Request> handler);",
|
||||||
"void route(Predicate<String> url, BiConsumer<Route, Request> handler);",
|
"void route(Predicate<String> url, BiConsumer<Route, Request> handler);",
|
||||||
});
|
});
|
||||||
|
// There is no standard JSON type in Java.
|
||||||
|
customSignature.put("Response.json", new String[0]);
|
||||||
customSignature.put("Page.frame", new String[]{
|
customSignature.put("Page.frame", new String[]{
|
||||||
"Frame frameByName(String name);",
|
"Frame frameByName(String name);",
|
||||||
"Frame frameByUrl(String glob);",
|
"Frame frameByUrl(String glob);",
|
||||||
|
@ -273,6 +273,7 @@ class Types {
|
|||||||
add("ElementHandle.screenshot", "Promise<Buffer>", "byte[]");
|
add("ElementHandle.screenshot", "Promise<Buffer>", "byte[]");
|
||||||
add("Request.postDataBuffer", "null|Buffer", "byte[]");
|
add("Request.postDataBuffer", "null|Buffer", "byte[]");
|
||||||
add("Response.body", "Promise<Buffer>", "byte[]");
|
add("Response.body", "Promise<Buffer>", "byte[]");
|
||||||
|
add("Response.finished", "Promise<null|Error>", "String");
|
||||||
add("ChromiumBrowser.stopTracing", "Promise<Buffer>", "byte[]");
|
add("ChromiumBrowser.stopTracing", "Promise<Buffer>", "byte[]");
|
||||||
|
|
||||||
// JSON type
|
// JSON type
|
||||||
|
@ -20,10 +20,9 @@ import java.util.*;
|
|||||||
|
|
||||||
public interface Response {
|
public interface Response {
|
||||||
byte[] body();
|
byte[] body();
|
||||||
Error finished();
|
String finished();
|
||||||
Frame frame();
|
Frame frame();
|
||||||
Map<String, String> headers();
|
Map<String, String> headers();
|
||||||
Object json();
|
|
||||||
boolean ok();
|
boolean ok();
|
||||||
Request request();
|
Request request();
|
||||||
int status();
|
int status();
|
||||||
|
@ -16,20 +16,27 @@
|
|||||||
|
|
||||||
package com.microsoft.playwright.impl;
|
package com.microsoft.playwright.impl;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.microsoft.playwright.Frame;
|
import com.microsoft.playwright.Frame;
|
||||||
import com.microsoft.playwright.Request;
|
import com.microsoft.playwright.Request;
|
||||||
import com.microsoft.playwright.Response;
|
import com.microsoft.playwright.Response;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static com.microsoft.playwright.impl.Serialization.deserialize;
|
|
||||||
|
|
||||||
public class ResponseImpl extends ChannelOwner implements Response {
|
public class ResponseImpl extends ChannelOwner implements Response {
|
||||||
|
private final Map<String, String> headers = new HashMap<>();
|
||||||
|
|
||||||
ResponseImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
ResponseImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||||
super(parent, type, guid, initializer);
|
super(parent, type, guid, initializer);
|
||||||
|
|
||||||
|
for (JsonElement e : initializer.getAsJsonArray("headers")) {
|
||||||
|
JsonObject item = e.getAsJsonObject();
|
||||||
|
headers.put(item.get("name").getAsString().toLowerCase(), item.get("value").getAsString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -39,7 +46,11 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Error finished() {
|
public String finished() {
|
||||||
|
JsonObject json = sendMessage("finished").getAsJsonObject();
|
||||||
|
if (json.has("error")) {
|
||||||
|
return json.get("error").getAsString();
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,12 +61,7 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> headers() {
|
public Map<String, String> headers() {
|
||||||
return null;
|
return headers;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object json() {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -80,7 +86,7 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String text() {
|
public String text() {
|
||||||
return null;
|
return new String(body(), StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -25,6 +25,7 @@ import com.microsoft.playwright.*;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
@ -38,7 +39,7 @@ class Serialization {
|
|||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
e.printStackTrace(new PrintStream(out));
|
e.printStackTrace(new PrintStream(out));
|
||||||
result.error.stack = new String(out.toByteArray());
|
result.error.stack = new String(out.toByteArray(), StandardCharsets.UTF_8);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,8 +24,7 @@ import java.nio.file.FileSystems;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
|
|
||||||
public class Server implements HttpHandler {
|
public class Server implements HttpHandler {
|
||||||
private final HttpServer server;
|
private final HttpServer server;
|
||||||
@ -40,6 +39,7 @@ public class Server implements HttpHandler {
|
|||||||
private final Map<String, Auth> auths = Collections.synchronizedMap(new HashMap<>());
|
private final Map<String, Auth> auths = Collections.synchronizedMap(new HashMap<>());
|
||||||
private final Map<String, String> csp = Collections.synchronizedMap(new HashMap<>());
|
private final Map<String, String> csp = Collections.synchronizedMap(new HashMap<>());
|
||||||
private final Map<String, HttpHandler> routes = Collections.synchronizedMap(new HashMap<>());
|
private final Map<String, HttpHandler> routes = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
private final Set<String> gzipRoutes = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
|
||||||
private static class Auth {
|
private static class Auth {
|
||||||
public final String user;
|
public final String user;
|
||||||
@ -93,6 +93,10 @@ public class Server implements HttpHandler {
|
|||||||
this.csp.put(path, csp);
|
this.csp.put(path, csp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enableGzip(String path) {
|
||||||
|
gzipRoutes.add(path);
|
||||||
|
}
|
||||||
|
|
||||||
static class Request {
|
static class Request {
|
||||||
public final String method;
|
public final String method;
|
||||||
// TODO: make a copy to ensure thread safety?
|
// TODO: make a copy to ensure thread safety?
|
||||||
@ -135,6 +139,7 @@ public class Server implements HttpHandler {
|
|||||||
auths.clear();
|
auths.clear();
|
||||||
csp.clear();
|
csp.clear();
|
||||||
routes.clear();
|
routes.clear();
|
||||||
|
gzipRoutes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -154,7 +159,7 @@ public class Server implements HttpHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!authorized) {
|
if (!authorized) {
|
||||||
exchange.getResponseHeaders().put("WWW-Authenticate", Arrays.asList("Basic realm=\"Secure Area\""));
|
exchange.getResponseHeaders().add("WWW-Authenticate", "Basic realm=\"Secure Area\"");
|
||||||
exchange.sendResponseHeaders(401, 0);
|
exchange.sendResponseHeaders(401, 0);
|
||||||
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||||
writer.write("HTTP Error 401 Unauthorized: Access is denied");
|
writer.write("HTTP Error 401 Unauthorized: Access is denied");
|
||||||
@ -180,20 +185,27 @@ public class Server implements HttpHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (csp.containsKey(path)) {
|
if (csp.containsKey(path)) {
|
||||||
exchange.getResponseHeaders().put("Content-Security-Policy", singletonList(csp.get(path)));
|
exchange.getResponseHeaders().add("Content-Security-Policy", csp.get(path));
|
||||||
}
|
}
|
||||||
File file = new File(resourcesDir, path.substring(1));
|
File file = new File(resourcesDir, path.substring(1));
|
||||||
exchange.getResponseHeaders().put("Content-Type", singletonList(mimeType(file)));
|
exchange.getResponseHeaders().add("Content-Type", mimeType(file));
|
||||||
|
OutputStream output = exchange.getResponseBody();
|
||||||
|
if (gzipRoutes.contains(path)) {
|
||||||
|
exchange.getResponseHeaders().add("Content-Encoding", "gzip");
|
||||||
|
}
|
||||||
try (FileInputStream input = new FileInputStream(file)) {
|
try (FileInputStream input = new FileInputStream(file)) {
|
||||||
exchange.sendResponseHeaders(200, 0);
|
exchange.sendResponseHeaders(200, 0);
|
||||||
copy(input, exchange.getResponseBody());
|
if (gzipRoutes.contains(path)) {
|
||||||
|
output = new GZIPOutputStream(output);
|
||||||
|
}
|
||||||
|
copy(input, output);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
exchange.sendResponseHeaders(404, 0);
|
exchange.sendResponseHeaders(404, 0);
|
||||||
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||||
writer.write("File not found: " + file.getCanonicalPath());
|
writer.write("File not found: " + file.getCanonicalPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exchange.getResponseBody().close();
|
output.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void copy(InputStream in, OutputStream out) throws IOException {
|
private static void copy(InputStream in, OutputStream out) throws IOException {
|
||||||
|
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* 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.Test;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
import static com.microsoft.playwright.Page.EventType.REQUESTFINISHED;
|
||||||
|
import static com.microsoft.playwright.Page.EventType.RESPONSE;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class TestNetworkResponse extends TestBase {
|
||||||
|
@Test
|
||||||
|
void shouldWork() {
|
||||||
|
server.setRoute("/empty.html", exchange -> {
|
||||||
|
exchange.getResponseHeaders().add("foo", "bar");
|
||||||
|
exchange.getResponseHeaders().add("BaZ", "bAz");
|
||||||
|
exchange.sendResponseHeaders(200, 0);
|
||||||
|
exchange.getResponseBody().close();
|
||||||
|
});
|
||||||
|
Response response = page.navigate(server.EMPTY_PAGE);
|
||||||
|
assertEquals("bar", response.headers().get("foo"));
|
||||||
|
assertEquals("bAz", response.headers().get("baz"));
|
||||||
|
assertNull(response.headers().get("BaZ"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnText() {
|
||||||
|
Response response = page.navigate(server.PREFIX + "/simple.json");
|
||||||
|
assertEquals("{\"foo\": \"bar\"}\n", response.text());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnUncompressedText() {
|
||||||
|
server.enableGzip("/simple.json");
|
||||||
|
Response response = page.navigate(server.PREFIX + "/simple.json");
|
||||||
|
assertEquals("gzip", response.headers().get("content-encoding"));
|
||||||
|
assertEquals("{\"foo\": \"bar\"}\n", response.text());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowWhenRequestingBodyOfRedirectedResponse() {
|
||||||
|
server.setRedirect("/foo.html", "/empty.html");
|
||||||
|
Response response = page.navigate(server.PREFIX + "/foo.html");
|
||||||
|
Request redirectedFrom = response.request().redirectedFrom();
|
||||||
|
assertNotNull(redirectedFrom);
|
||||||
|
Response redirected = redirectedFrom.response();
|
||||||
|
assertEquals(302, redirected.status());
|
||||||
|
try {
|
||||||
|
redirected.text();
|
||||||
|
fail("did not throw");
|
||||||
|
} catch (PlaywrightException e) {
|
||||||
|
assertTrue(e.getMessage().contains("Response body is unavailable for redirect responses"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldWaitUntilResponseCompletes() {
|
||||||
|
page.navigate(server.EMPTY_PAGE);
|
||||||
|
server.setRoute("/get", exchange -> {
|
||||||
|
// In Firefox, |fetch| will be hanging until it receives |Content-Type| header
|
||||||
|
// from server.
|
||||||
|
exchange.getResponseHeaders().add("Content-Type", "text/plain; charset=utf-8");
|
||||||
|
exchange.sendResponseHeaders(200, 0);
|
||||||
|
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||||
|
writer.write("hello ");
|
||||||
|
writer.flush();
|
||||||
|
writer.write("wor");
|
||||||
|
writer.flush();
|
||||||
|
writer.write("ld!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Setup page to trap response.
|
||||||
|
boolean[] requestFinished = {false};
|
||||||
|
page.addListener(REQUESTFINISHED, event -> {
|
||||||
|
requestFinished[0] |= ((Request) event.data()).url().contains("/get");
|
||||||
|
});
|
||||||
|
// send request and wait for server response
|
||||||
|
Deferred<Event<Page.EventType>> responseEvent = page.waitForEvent(RESPONSE);
|
||||||
|
Future<Server.Request> request = server.waitForRequest("/get");
|
||||||
|
page.evaluate("() => fetch('./get', { method: 'GET'})");
|
||||||
|
assertNotNull(responseEvent.get());
|
||||||
|
Response pageResponse = (Response) responseEvent.get().data();
|
||||||
|
assertEquals(200, pageResponse.status());
|
||||||
|
assertEquals(false, requestFinished[0]);
|
||||||
|
assertEquals("hello world!", pageResponse.text());
|
||||||
|
}
|
||||||
|
|
||||||
|
void shouldReturnJson() {
|
||||||
|
// Not exposed in Java.
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void shouldReturnBody() throws IOException {
|
||||||
|
Response response = page.navigate(server.PREFIX + "/pptr.png");
|
||||||
|
byte[] expected = Files.readAllBytes(new File("src/test/resources/pptr.png").toPath());
|
||||||
|
assertTrue(Arrays.equals(expected, response.body()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnBodyWithCompression() throws IOException {
|
||||||
|
server.enableGzip("/pptr.png");
|
||||||
|
Response response = page.navigate(server.PREFIX + "/pptr.png");
|
||||||
|
byte[] expected = Files.readAllBytes(new File("src/test/resources/pptr.png").toPath());
|
||||||
|
assertTrue(Arrays.equals(expected, response.body()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnStatusText() {
|
||||||
|
server.setRoute("/cool", exchange -> {
|
||||||
|
exchange.sendResponseHeaders(200, 0);
|
||||||
|
exchange.getResponseBody().close();
|
||||||
|
});
|
||||||
|
Response response = page.navigate(server.PREFIX + "/cool");
|
||||||
|
assertEquals("OK", response.statusText());
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user