diff --git a/httpcore5/src/main/java/org/apache/hc/core5/annotation/Experimental.java b/httpcore5/src/main/java/org/apache/hc/core5/annotation/Experimental.java
index 6669be793..9781ca840 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/annotation/Experimental.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/annotation/Experimental.java
@@ -36,7 +36,7 @@
* The field or method to which this annotation is applied is marked as experimental.
*/
@Documented
-@Target({ElementType.METHOD, ElementType.TYPE})
+@Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Experimental {
}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/Method.java b/httpcore5/src/main/java/org/apache/hc/core5/http/Method.java
index 731e72d72..787fb405f 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/Method.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/Method.java
@@ -29,6 +29,7 @@
import java.util.Locale;
+import org.apache.hc.core5.annotation.Experimental;
import org.apache.hc.core5.util.Args;
/**
@@ -91,7 +92,21 @@ public enum Method {
/**
* The HTTP {@code PATCH} method is unsafe and non-idempotent.
*/
- PATCH(false, false);
+ PATCH(false, false),
+
+ /**
+ * The HTTP {@code QUERY} method is safe and idempotent.
+ *
+ * {@code QUERY} is a method defined to represent a safe, idempotent request that carries a body.
+ * This allows clients to send a body while enjoying the {@code GET}-like properties (such as easy caching,
+ * safe CORS handling [if supported] and bookmarking), as well as the {@code POST}-like properties (such as
+ * being able to send a query in a richer format, and not being limited by URI length and escaping restrictions).
+ *
+ * @since 5.4
+ */
+ @Experimental
+ //("QUERY method is still in DRAFT status")
+ QUERY(true, true);
private final boolean safe;
private final boolean idempotent;
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilder.java
index 5dd0c1f2f..b6619bb54 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilder.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilder.java
@@ -33,6 +33,7 @@
import java.util.Arrays;
import java.util.List;
+import org.apache.hc.core5.annotation.Experimental;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
@@ -120,6 +121,44 @@ public static ClassicRequestBuilder head(final String uri) {
return new ClassicRequestBuilder(Method.HEAD, uri);
}
+ /**
+ * Initializes a new {@link ClassicRequestBuilder} instance for the {@code QUERY} method.
+ *
+ * @see Method#QUERY for more information regarding the properties of the {@code QUERY} method.
+ *
+ * @since 5.4
+ */
+ @Experimental
+ public static ClassicRequestBuilder query() {
+ return new ClassicRequestBuilder(Method.QUERY);
+ }
+
+ /**
+ * Initializes a new {@link ClassicRequestBuilder} instance for the {@code QUERY} method.
+ *
+ * @param uri the request URI.
+ * @see Method#QUERY for more information regarding the properties of the {@code QUERY} method.
+ *
+ * @since 5.4
+ */
+ @Experimental
+ public static ClassicRequestBuilder query(final URI uri) {
+ return new ClassicRequestBuilder(Method.QUERY, uri);
+ }
+
+ /**
+ * Initializes a new {@link ClassicRequestBuilder} instance for the {@code QUERY} method.
+ *
+ * @param uri the request URI.
+ * @see Method#QUERY for more information regarding the properties of the {@code QUERY} method.
+ *
+ * @since 5.4
+ */
+ @Experimental
+ public static ClassicRequestBuilder query(final String uri) {
+ return new ClassicRequestBuilder(Method.QUERY, uri);
+ }
+
public static ClassicRequestBuilder patch() {
return new ClassicRequestBuilder(Method.PATCH);
}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/message/MessageSupport.java b/httpcore5/src/main/java/org/apache/hc/core5/http/message/MessageSupport.java
index b0c6a446b..10bf96d03 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/message/MessageSupport.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/message/MessageSupport.java
@@ -168,9 +168,12 @@ public static Header format(final String name, final String... tokens) {
}
/**
- * @since 5.3
+ * @since 5.4
*/
- public static void parseTokens(final CharSequence src, final ParserCursor cursor, final Consumer consumer) {
+ public static void parseTokens(final CharSequence src,
+ final ParserCursor cursor,
+ final Tokenizer.Delimiter delimiterPredicate,
+ final Consumer consumer) {
Args.notNull(src, "Source");
Args.notNull(cursor, "Cursor");
Args.notNull(consumer, "Consumer");
@@ -179,7 +182,7 @@ public static void parseTokens(final CharSequence src, final ParserCursor cursor
if (src.charAt(pos) == ',') {
cursor.updatePos(pos + 1);
}
- final String token = Tokenizer.INSTANCE.parseToken(src, cursor, COMMA);
+ final String token = Tokenizer.INSTANCE.parseToken(src, cursor, delimiterPredicate);
consumer.accept(token);
}
}
@@ -187,31 +190,57 @@ public static void parseTokens(final CharSequence src, final ParserCursor cursor
/**
* @since 5.3
*/
- public static void parseTokens(final Header header, final Consumer consumer) {
+ public static void parseTokens(final CharSequence src, final ParserCursor cursor, final Consumer consumer) {
+ parseTokens(src, cursor, COMMA, consumer);
+ }
+
+ /**
+ * @since 5.4
+ */
+ public static void parseTokens(final Header header,
+ final Tokenizer.Delimiter delimiterPredicate,
+ final Consumer consumer) {
Args.notNull(header, "Header");
if (header instanceof FormattedHeader) {
final CharArrayBuffer buf = ((FormattedHeader) header).getBuffer();
final ParserCursor cursor = new ParserCursor(0, buf.length());
cursor.updatePos(((FormattedHeader) header).getValuePos());
- parseTokens(buf, cursor, consumer);
+ parseTokens(buf, cursor, delimiterPredicate, consumer);
} else {
final String value = header.getValue();
final ParserCursor cursor = new ParserCursor(0, value.length());
- parseTokens(value, cursor, consumer);
+ parseTokens(value, cursor, delimiterPredicate, consumer);
}
}
/**
* @since 5.3
*/
- public static void parseTokens(final MessageHeaders headers, final String headerName, final Consumer consumer) {
+ public static void parseTokens(final Header header, final Consumer consumer) {
+ parseTokens(header, COMMA, consumer);
+ }
+
+ /**
+ * @since 5.4
+ */
+ public static void parseTokens(final MessageHeaders headers,
+ final String headerName,
+ final Tokenizer.Delimiter delimiterPredicate,
+ final Consumer consumer) {
Args.notNull(headers, "Headers");
final Iterator it = headers.headerIterator(headerName);
while (it.hasNext()) {
- parseTokens(it.next(), consumer);
+ parseTokens(it.next(), delimiterPredicate, consumer);
}
}
+ /**
+ * @since 5.3
+ */
+ public static void parseTokens(final MessageHeaders headers, final String headerName, final Consumer consumer) {
+ parseTokens(headers, headerName, COMMA, consumer);
+ }
+
public static Set parseTokens(final CharSequence src, final ParserCursor cursor) {
Args.notNull(src, "Source");
Args.notNull(cursor, "Cursor");
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncRequestBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncRequestBuilder.java
index 1c2b99b34..7b16f9774 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncRequestBuilder.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncRequestBuilder.java
@@ -33,6 +33,7 @@
import java.util.Arrays;
import java.util.List;
+import org.apache.hc.core5.annotation.Experimental;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHost;
@@ -120,6 +121,44 @@ public static AsyncRequestBuilder head(final String uri) {
return new AsyncRequestBuilder(Method.HEAD, uri);
}
+ /**
+ * Initializes a new {@link AsyncRequestBuilder} instance for the {@code QUERY} method.
+ *
+ * @see Method#QUERY for more information regarding the properties of the {@code QUERY} method.
+ *
+ * @since 5.4
+ */
+ @Experimental
+ public static AsyncRequestBuilder query() {
+ return new AsyncRequestBuilder(Method.QUERY);
+ }
+
+ /**
+ * Initializes a new {@link AsyncRequestBuilder} instance for the {@code QUERY} method.
+ *
+ * @param uri the request URI.
+ * @see Method#QUERY for more information regarding the properties of the {@code QUERY} method.
+ *
+ * @since 5.4
+ */
+ @Experimental
+ public static AsyncRequestBuilder query(final URI uri) {
+ return new AsyncRequestBuilder(Method.QUERY, uri);
+ }
+
+ /**
+ * Initializes a new {@link AsyncRequestBuilder} instance for the {@code QUERY} method.
+ *
+ * @param uri the request URI.
+ * @see Method#QUERY for more information regarding the properties of the {@code QUERY} method.
+ *
+ * @since 5.4
+ */
+ @Experimental
+ public static AsyncRequestBuilder query(final String uri) {
+ return new AsyncRequestBuilder(Method.QUERY, uri);
+ }
+
public static AsyncRequestBuilder patch() {
return new AsyncRequestBuilder(Method.PATCH);
}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/RequestContent.java b/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/RequestContent.java
index b8c22f84e..24ee1f164 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/RequestContent.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/protocol/RequestContent.java
@@ -138,7 +138,7 @@ public void process(final HttpRequest request, final EntityDetails entity, final
}
private boolean isContentEnclosingMethod(final String method) {
- return Method.POST.isSame(method) || Method.PUT.isSame(method) || Method.PATCH.isSame(method);
+ return Method.POST.isSame(method) || Method.PUT.isSame(method) || Method.PATCH.isSame(method) || Method.QUERY.isSame(method);
}
/**
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/support/BasicRequestBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/support/BasicRequestBuilder.java
index bdc0cdb85..2d2620510 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/support/BasicRequestBuilder.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/support/BasicRequestBuilder.java
@@ -33,6 +33,7 @@
import java.util.Arrays;
import java.util.List;
+import org.apache.hc.core5.annotation.Experimental;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
@@ -104,6 +105,44 @@ public static BasicRequestBuilder head(final String uri) {
return new BasicRequestBuilder(Method.HEAD, uri);
}
+ /**
+ * Initializes a new {@link BasicRequestBuilder} instance for the {@code QUERY} method.
+ *
+ * @see Method#QUERY for more information regarding the properties of the {@code QUERY} method.
+ *
+ * @since 5.4
+ */
+ @Experimental
+ public static BasicRequestBuilder query() {
+ return new BasicRequestBuilder(Method.QUERY);
+ }
+
+ /**
+ * Initializes a new {@link BasicRequestBuilder} instance for the {@code QUERY} method.
+ *
+ * @param uri the request URI.
+ * @see Method#QUERY for more information regarding the properties of the {@code QUERY} method.
+ *
+ * @since 5.4
+ */
+ @Experimental
+ public static BasicRequestBuilder query(final URI uri) {
+ return new BasicRequestBuilder(Method.QUERY, uri);
+ }
+
+ /**
+ * Initializes a new {@link BasicRequestBuilder} instance for the {@code QUERY} method.
+ *
+ * @param uri the request URI.
+ * @see Method#QUERY for more information regarding the properties of the {@code QUERY} method.
+ *
+ * @since 5.4
+ */
+ @Experimental
+ public static BasicRequestBuilder query(final String uri) {
+ return new BasicRequestBuilder(Method.QUERY, uri);
+ }
+
public static BasicRequestBuilder patch() {
return new BasicRequestBuilder(Method.PATCH);
}
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilderTest.java b/httpcore5/src/test/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilderTest.java
index 729c6b89f..685c42bdf 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilderTest.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilderTest.java
@@ -109,6 +109,19 @@ void head() throws UnknownHostException, URISyntaxException {
assertEquals("/localhost", classicRequestBuilder3.getPath());
}
+ @Test
+ void query() throws UnknownHostException, URISyntaxException {
+ final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.query();
+ assertEquals(Method.QUERY.name(), classicRequestBuilder.getMethod());
+
+ final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.query(URIBuilder.localhost().build());
+ assertEquals(Method.QUERY.name(), classicRequestBuilder1.getMethod());
+
+ final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.query("/localhost");
+ assertEquals(Method.QUERY.name(), classicRequestBuilder3.getMethod());
+ assertEquals("/localhost", classicRequestBuilder3.getPath());
+ }
+
@Test
void patch() throws UnknownHostException, URISyntaxException {
final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.patch();