Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 2 additions & 40 deletions java/src/org/openqa/selenium/remote/http/jdk/JdkHttpMessages.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

package org.openqa.selenium.remote.http.jdk;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.openqa.selenium.remote.http.HttpHeader.UserAgent;

import com.google.common.net.MediaType;
Expand All @@ -43,8 +42,6 @@ class JdkHttpMessages {
private final ClientConfig config;
private static final List<String> IGNORE_HEADERS =
List.of("content-length", "connection", "host");
private static final String ALLOWED_IN_URI = "-._~!$&'()*+,;=:@/?%";
private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();

public JdkHttpMessages(ClientConfig config) {
this.config = Objects.requireNonNull(config, "Client config");
Expand Down Expand Up @@ -138,52 +135,17 @@ public URI getRawUri(HttpRequest req) {
throw new IllegalStateException(
"Unable to resolve relative URI " + uri + ": base URI is not set in ClientConfig");
}
String path = quoteIllegalCharacters(uri);
Comment thread
titusfortner marked this conversation as resolved.
String base = baseUrl.toString();
if (base.endsWith("/")) {
rawUrl = base.substring(0, base.length() - 1) + path;
rawUrl = base.substring(0, base.length() - 1) + uri;
} else {
rawUrl = base + path;
rawUrl = base + uri;
}
}

return URI.create(rawUrl);
}

/**
* The URI of a request may hold characters that {@link URI} refuses, most commonly a space in the
* name of a file to download from Grid. This happens because the server receiving the request
* decodes the path ("%20" becomes a literal space), and the decoded value is kept in the {@link
* HttpRequest} that is handed over to this client for proxying. Quote every character that is not
* allowed in a URI, and leave all the others untouched, so that URIs which were valid before are
* sent exactly as they were.
*/
private static String quoteIllegalCharacters(String uri) {
StringBuilder quoted = new StringBuilder(uri.length());
for (int i = 0; i < uri.length(); ) {
int codePoint = uri.codePointAt(i);
i += Character.charCount(codePoint);
if (isAllowedInUri(codePoint)) {
quoted.appendCodePoint(codePoint);
continue;
}
for (byte b : new String(Character.toChars(codePoint)).getBytes(UTF_8)) {
quoted.append('%').append(HEX_DIGITS[(b >> 4) & 0xf]).append(HEX_DIGITS[b & 0xf]);
}
}
return quoted.toString();
}

private static boolean isAllowedInUri(int codePoint) {
if (codePoint >= 128) {
return false;
}
return (codePoint >= 'a' && codePoint <= 'z')
|| (codePoint >= 'A' && codePoint <= 'Z')
|| (codePoint >= '0' && codePoint <= '9')
|| ALLOWED_IN_URI.indexOf(codePoint) >= 0;
}

public HttpResponse createResponse(java.net.http.HttpResponse<InputStream> response) {
HttpResponse res = new HttpResponse();
res.setStatus(response.statusCode());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,37 +320,6 @@ public void shouldStopRequestOnCancel() throws InterruptedException {
}
}

@Test
void shouldSendRequestWithPathThatNeedsQuoting() {
// A proxied request holds the decoded path of the original request, which may contain
// characters that are not allowed in a URI, e.g. spaces in the name of a file downloaded
// from Grid. The client must quote them, and the server must decode them back.
String path =
"/session/772f83578930be5dce8f626ecf8ea935/se/files/attestation pour l'employeur.pdf";

HttpResponse response =
executeWithinServer(
new HttpRequest(GET, path),
req -> new HttpResponse().setContent(Contents.utf8String(req.getUri())));

assertThat(response.contentAsString()).isEqualTo(path);
}

@Test
void shouldSendRequestWithPathThatNeedsQuotingForNonAsciiCharacters() {
// Same scenario as above, but for a file name that also holds non-ASCII characters,
// including one outside the Basic Multilingual Plane (a surrogate pair), to exercise the
// UTF-8 percent-encoding of a multi-byte code point rather than just a single ASCII byte.
String path = "/session/772f83578930be5dce8f626ecf8ea935/se/files/файл tähtedega 😀.pdf";

HttpResponse response =
executeWithinServer(
new HttpRequest(GET, path),
req -> new HttpResponse().setContent(Contents.utf8String(req.getUri())));

assertThat(response.contentAsString()).isEqualTo(path);
}

private HttpResponse getResponseWithHeaders(final Multimap<String, String> headers) {
return executeWithinServer(
new HttpRequest(GET, "/foo"),
Expand Down
Loading