Skip to content

Commit

Permalink
Speed up URL path encoding and remove dependency on httpclient (#576) (
Browse files Browse the repository at this point in the history
…#578)

Co-authored-by: Sylvain Wallez <[email protected]>
  • Loading branch information
github-actions[bot] and swallez authored May 22, 2023
1 parent 082fe35 commit 96716b8
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
import co.elastic.clients.elasticsearch._types.ErrorResponse;
import co.elastic.clients.json.JsonpDeserializer;
import co.elastic.clients.transport.Endpoint;
import org.apache.http.client.utils.URLEncodedUtils;

import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.BitSet;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
Expand Down Expand Up @@ -145,8 +147,54 @@ public static RuntimeException noPathTemplateFound(String what) {
"Please check the API documentation, or raise an issue if this should be a valid request.");
}

public static void pathEncode(String src, StringBuilder dest) {
// TODO: avoid dependency on HttpClient here (and use something more efficient)
dest.append(URLEncodedUtils.formatSegments(src).substring(1));
private static final BitSet PATH_SAFE;
private static final char[] HEX_CHARS;

static {
PATH_SAFE = new BitSet(256);
// From RFC 3986
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
PATH_SAFE.set('a', 'z'+1);
PATH_SAFE.set('A', 'Z'+1);
PATH_SAFE.set('0', '9'+1);
PATH_SAFE.set('-');
PATH_SAFE.set('.');
PATH_SAFE.set('_');
PATH_SAFE.set('~');

// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
PATH_SAFE.set('!');
PATH_SAFE.set('$');
PATH_SAFE.set('&');
PATH_SAFE.set('\'');
PATH_SAFE.set('(');
PATH_SAFE.set(')');
PATH_SAFE.set('*');
PATH_SAFE.set('+');
PATH_SAFE.set(',');
PATH_SAFE.set(';');
PATH_SAFE.set('=');

// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
PATH_SAFE.set(':');
PATH_SAFE.set('@');

HEX_CHARS = "0123456789ABCDEF".toCharArray();
}

public static void pathEncode(final String src, StringBuilder dest) {
final ByteBuffer buf = StandardCharsets.UTF_8.encode(src);
// In UTF-8 multibyte encoding, all bytes have the high bit set. This means we can iterate
// on all bytes and percent-encode without having to care about code point context.
while (buf.hasRemaining()) {
int b = buf.get() & 0xff;
if (PATH_SAFE.get(b)) {
dest.append((char) b);
} else {
dest.append("%");
dest.append(HEX_CHARS[b >> 4 & 0xF]);
dest.append(HEX_CHARS[b & 0xF]);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import co.elastic.clients.elasticsearch._types.ErrorResponse;
import co.elastic.clients.json.JsonpDeserializer;
import co.elastic.clients.transport.JsonEndpoint;
import org.apache.http.client.utils.URLEncodedUtils;

import java.util.Map;
import java.util.function.Function;
Expand Down Expand Up @@ -88,14 +87,4 @@ public <NewResponseT> SimpleEndpoint<RequestT, NewResponseT> withResponseDeseria
newResponseParser
);
}

public static RuntimeException noPathTemplateFound(String what) {
return new RuntimeException("Could not find a request " + what + " with this set of properties. " +
"Please check the API documentation, or raise an issue if this should be a valid request.");
}

public static void pathEncode(String src, StringBuilder dest) {
// TODO: avoid dependency on HttpClient here (and use something more efficient)
dest.append(URLEncodedUtils.formatSegments(src).substring(1));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you 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 co.elastic.clients.transport.endpoints;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class EndpointBaseTest extends Assertions {

@Test
public void testPathEncoding() {
assertEquals("abCD12;-_*", pathEncode("abCD12;-_*"));
assertEquals("XYZ%5B", pathEncode("XYZ["));
assertEquals("xyz%7B", pathEncode("xyz{"));
assertEquals("foo%2Fbar", pathEncode("foo/bar"));
assertEquals("foo%20bar", pathEncode("foo bar"));
assertEquals("f%C3%AAl%C3%A9", pathEncode("fêlé"));
}

private String pathEncode(String s) {
StringBuilder sb = new StringBuilder();
EndpointBase.pathEncode(s, sb);
return sb.toString();
}
}

0 comments on commit 96716b8

Please sign in to comment.