Skip to content

Commit 1d40fab

Browse files
committed
chore: follow upstream url matcher
1 parent eb08046 commit 1d40fab

File tree

4 files changed

+141
-44
lines changed

4 files changed

+141
-44
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ scripts/download_driver.sh
2929
### Building and running the tests with Maven
3030

3131
```bash
32-
mvn compile
32+
mvn -B install -D skipTest
3333
mvn test
3434
# Executing a single test
3535
BROWSER=chromium mvn test --projects=playwright -Dtest=TestPageNetworkSizes#shouldHaveTheCorrectResponseBodySize

playwright/src/main/java/com/microsoft/playwright/impl/UrlMatcher.java

Lines changed: 93 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,29 @@
1616

1717
package com.microsoft.playwright.impl;
1818

19-
import com.google.gson.JsonArray;
20-
import com.google.gson.JsonObject;
2119
import com.microsoft.playwright.PlaywrightException;
2220

2321
import java.net.MalformedURLException;
22+
import java.net.URI;
23+
import java.net.URISyntaxException;
2424
import java.net.URL;
25-
import java.util.Objects;
2625
import java.util.function.Predicate;
2726
import java.util.regex.Pattern;
27+
import java.util.ArrayList;
28+
import java.util.List;
2829

2930
import static com.microsoft.playwright.impl.Utils.globToRegex;
3031
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
3132

3233
class UrlMatcher {
33-
final Object rawSource;
34-
private final Predicate<String> predicate;
35-
36-
private static Predicate<String> toPredicate(Pattern pattern) {
37-
return s -> pattern.matcher(s).find();
38-
}
39-
40-
static UrlMatcher any() {
41-
return new UrlMatcher((Object) null, null);
42-
}
34+
private final URL baseURL;
35+
public final String glob;
36+
public final Pattern pattern;
37+
public final Predicate<String> predicate;
4338

4439
static UrlMatcher forOneOf(URL baseUrl, Object object) {
4540
if (object == null) {
46-
return UrlMatcher.any();
41+
return new UrlMatcher(baseUrl, (String) null);
4742
}
4843
if (object instanceof String) {
4944
return new UrlMatcher(baseUrl, (String) object);
@@ -58,6 +53,16 @@ static UrlMatcher forOneOf(URL baseUrl, Object object) {
5853
}
5954

6055
static String resolveUrl(URL baseUrl, String spec) {
56+
try {
57+
URL specURL = new URL(spec);
58+
// We want to follow HTTP spec, so we enforce a slash if there is no path.
59+
if (specURL.getPath().isEmpty()) {
60+
spec = specURL.toString() + "/";
61+
}
62+
} catch (MalformedURLException e) {
63+
// Ignore - we end up here if spec is e.g. a relative path.
64+
}
65+
6166
if (baseUrl == null) {
6267
return spec;
6368
}
@@ -68,50 +73,102 @@ static String resolveUrl(URL baseUrl, String spec) {
6873
}
6974
}
7075

71-
UrlMatcher(URL base, String url) {
72-
this(url, toPredicate(Pattern.compile(globToRegex(resolveUrl(base, url)))).or(s -> url == null || url.equals(s)));
76+
static private String normaliseUrl(String url) {
77+
URI parsedUrl;
78+
try {
79+
parsedUrl = new URI(url);
80+
} catch (URISyntaxException e) {
81+
return url;
82+
}
83+
// Align with the Node.js URL parser which automatically adds a slash to the path if it is empty.
84+
if (parsedUrl.getScheme() != null && (
85+
parsedUrl.getScheme().equals("http") || parsedUrl.getScheme().equals("https") ||
86+
parsedUrl.getScheme().equals("ws") || parsedUrl.getScheme().equals("wss")
87+
) && parsedUrl.getPath().isEmpty()) {
88+
try {
89+
return new URI(parsedUrl.getScheme(), parsedUrl.getAuthority(), "/", parsedUrl.getQuery(), parsedUrl.getFragment()).toString();
90+
} catch (URISyntaxException e) {
91+
return url;
92+
}
93+
}
94+
return url;
95+
}
96+
97+
UrlMatcher(URL baseURL, String glob) {
98+
this(baseURL, null, null, glob);
7399
}
74100

75101
UrlMatcher(Pattern pattern) {
76-
this(pattern, toPredicate(pattern));
102+
this(null, pattern, null, null);
77103
}
104+
78105
UrlMatcher(Predicate<String> predicate) {
79-
this(predicate, predicate);
106+
this(null, null, predicate, null);
80107
}
81108

82-
private UrlMatcher(Object rawSource, Predicate<String> predicate) {
83-
this.rawSource = rawSource;
109+
private UrlMatcher(URL baseURL, Pattern pattern, Predicate<String> predicate, String glob) {
110+
this.baseURL = baseURL;
111+
this.pattern = pattern;
84112
this.predicate = predicate;
113+
this.glob = glob;
85114
}
86115

87116
boolean test(String value) {
88-
return predicate == null || predicate.test(value);
117+
return testImpl(baseURL, pattern, predicate, glob, value);
118+
}
119+
120+
private static boolean testImpl(URL baseURL, Pattern pattern, Predicate<String> predicate, String glob, String value) {
121+
if (pattern != null) {
122+
return pattern.matcher(value).find();
123+
}
124+
if (predicate != null) {
125+
return predicate.test(value);
126+
}
127+
if (glob != null) {
128+
if (!glob.startsWith("*")) {
129+
glob = normaliseUrl(glob);
130+
// Allow http(s) baseURL to match ws(s) urls.
131+
if (baseURL != null && Pattern.compile("^https?://").matcher(baseURL.getProtocol()).find() && Pattern.compile("^wss?://").matcher(value).find()) {
132+
try {
133+
baseURL = new URL(baseURL.toString().replaceFirst("^http", "ws"));
134+
} catch (MalformedURLException e) {
135+
// Handle exception
136+
}
137+
}
138+
glob = resolveUrl(baseURL, glob);
139+
}
140+
return Pattern.compile(globToRegex(glob)).matcher(value).find();
141+
}
142+
return true;
89143
}
90144

91145
@Override
92146
public boolean equals(Object o) {
93147
if (this == o) return true;
94148
if (o == null || getClass() != o.getClass()) return false;
95149
UrlMatcher that = (UrlMatcher) o;
96-
if (rawSource instanceof Pattern && that.rawSource instanceof Pattern) {
97-
Pattern a = (Pattern) rawSource;
98-
Pattern b = (Pattern) that.rawSource;
99-
return a.pattern().equals(b.pattern()) && a.flags() == b.flags();
150+
List<Boolean> matches = new ArrayList<>();
151+
if (baseURL != null && that.baseURL != null) {
152+
matches.add(baseURL.equals(that.baseURL));
100153
}
101-
return Objects.equals(rawSource, that.rawSource);
102-
}
103-
104-
@Override
105-
public int hashCode() {
106-
return Objects.hash(rawSource);
154+
if (pattern != null && that.pattern != null) {
155+
matches.add(pattern.pattern().equals(that.pattern.pattern()) && pattern.flags() == that.pattern.flags());
156+
}
157+
if (predicate != null && that.predicate != null) {
158+
matches.add(predicate.equals(that.predicate));
159+
}
160+
if (glob != null && that.glob != null) {
161+
matches.add(glob.equals(that.glob));
162+
}
163+
return matches.stream().allMatch(m -> m);
107164
}
108165

109166
@Override
110167
public String toString() {
111-
if (rawSource == null)
112-
return "<any>";
113-
if (rawSource instanceof Predicate)
114-
return "matching predicate";
115-
return rawSource.toString();
168+
if (pattern != null)
169+
return String.format("<regex pattern=\"%s\" flags=\"%s\">", pattern.pattern(), toJsRegexFlags(pattern));
170+
if (predicate != null)
171+
return "<predicate>";
172+
return String.format("<glob pattern=\"%s\">", glob);
116173
}
117174
}

playwright/src/main/java/com/microsoft/playwright/impl/Utils.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -462,13 +462,11 @@ static JsonObject interceptionPatterns(List<UrlMatcher> matchers) {
462462
JsonArray jsonPatterns = new JsonArray();
463463
for (UrlMatcher matcher: matchers) {
464464
JsonObject jsonPattern = new JsonObject();
465-
Object urlFilter = matcher.rawSource;
466-
if (urlFilter instanceof String) {
467-
jsonPattern.addProperty("glob", (String) urlFilter);
468-
} else if (urlFilter instanceof Pattern) {
469-
Pattern pattern = (Pattern) urlFilter;
470-
jsonPattern.addProperty("regexSource", pattern.pattern());
471-
jsonPattern.addProperty("regexFlags", toJsRegexFlags(pattern));
465+
if (matcher.glob != null) {
466+
jsonPattern.addProperty("glob", matcher.glob);
467+
} else if (matcher.pattern != null) {
468+
jsonPattern.addProperty("regexSource", matcher.pattern.pattern());
469+
jsonPattern.addProperty("regexFlags", toJsRegexFlags(matcher.pattern));
472470
} else {
473471
// Match all requests.
474472
jsonPattern.addProperty("glob", "**/*");

playwright/src/test/java/com/microsoft/playwright/TestRouteWebSocket.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99

1010
import java.io.IOException;
1111
import java.nio.charset.StandardCharsets;
12+
import java.util.ArrayList;
1213
import java.util.concurrent.ExecutionException;
1314
import java.util.concurrent.Future;
15+
import java.util.List;
1416
import java.util.regex.Pattern;
1517

1618
import static com.microsoft.playwright.Utils.mapOf;
@@ -317,4 +319,44 @@ public void shouldWorkWithoutServer(Page page) {
317319
"close code=3008 reason=oops"),
318320
page.evaluate("window.log"));
319321
}
322+
323+
@Test
324+
public void shouldWorkWithNoTrailingSlash(Page page) throws Exception {
325+
List<String> log = new ArrayList<>();
326+
327+
// No trailing slash in the route pattern
328+
page.routeWebSocket("ws://localhost:" + webSocketServer.getPort(), ws -> {
329+
ws.onMessage(message -> {
330+
log.add(message.text());
331+
ws.send("response");
332+
});
333+
});
334+
335+
page.navigate("about:blank");
336+
page.evaluate("({ port }) => {\n" +
337+
" window.log = [];\n" +
338+
" // No trailing slash in WebSocket URL\n" +
339+
" window.ws = new WebSocket('ws://localhost:' + port);\n" +
340+
" window.ws.addEventListener('message', event => window.log.push(event.data));\n" +
341+
"}", mapOf("port", webSocketServer.getPort()));
342+
343+
// Wait for WebSocket to be ready (readyState === 1)
344+
page.waitForCondition(() -> {
345+
Integer result = (Integer) page.evaluate("() => window.ws.readyState");
346+
return result == 1;
347+
});
348+
349+
page.evaluate("() => window.ws.send('query')");
350+
351+
// Wait and verify server received message
352+
page.waitForCondition(() -> log.size() >= 1);
353+
assertEquals(asList("query"), log);
354+
355+
// Wait and verify client received response
356+
page.waitForCondition(() -> {
357+
Boolean result = (Boolean) page.evaluate("() => window.log.length >= 1");
358+
return result;
359+
});
360+
assertEquals(asList("response"), page.evaluate("window.log"));
361+
}
320362
}

0 commit comments

Comments
 (0)