add possibility to use entity with http method Options in requests according to the RFC 7231 (#4837)

* add possibility to use entity with http method Options in requests according to the RFC 7231

Signed-off-by: aserkes <andrii.serkes@oracle.com>
diff --git a/connectors/apache-connector/src/test/java/org/glassfish/jersey/apache/connector/HttpMethodTest.java b/connectors/apache-connector/src/test/java/org/glassfish/jersey/apache/connector/HttpMethodTest.java
index 3edabeb..f507504 100644
--- a/connectors/apache-connector/src/test/java/org/glassfish/jersey/apache/connector/HttpMethodTest.java
+++ b/connectors/apache-connector/src/test/java/org/glassfish/jersey/apache/connector/HttpMethodTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -42,6 +42,7 @@
 
 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
 import org.junit.Test;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -147,6 +148,14 @@
     }
 
     @Test
+    public void testOptionsWithEntity() {
+        WebTarget r = getWebTarget();
+        Response response = r.request().build("OPTIONS", Entity.text("OPTIONS")).invoke();
+        assertEquals(200, response.getStatus());
+        response.close();
+    }
+
+    @Test
     public void testGet() {
         WebTarget r = getWebTarget();
         assertEquals("GET", r.request().get(String.class));
diff --git a/connectors/grizzly-connector/src/test/java/org/glassfish/jersey/grizzly/connector/HttpMethodTest.java b/connectors/grizzly-connector/src/test/java/org/glassfish/jersey/grizzly/connector/HttpMethodTest.java
new file mode 100644
index 0000000..3f90504
--- /dev/null
+++ b/connectors/grizzly-connector/src/test/java/org/glassfish/jersey/grizzly/connector/HttpMethodTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.grizzly.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests the Http methods.
+ */
+public class HttpMethodTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(HttpMethodResource.class);
+    }
+
+    protected Client createClient() {
+        ClientConfig cc = new ClientConfig();
+        cc.connectorProvider(new GrizzlyConnectorProvider());
+        return ClientBuilder.newClient(cc);
+    }
+
+    private WebTarget getWebTarget(final Client client) {
+        return client.target(getBaseUri()).path("test");
+    }
+
+    private WebTarget getWebTarget() {
+        return getWebTarget(createClient());
+    }
+
+    @Path("/test")
+    public static class HttpMethodResource {
+        @GET
+        public String get() {
+            return "GET";
+        }
+    }
+
+    @Test
+    public void testOptionsWithEntity() {
+        WebTarget r = getWebTarget();
+        Response response = r.request().build("OPTIONS", Entity.text("OPTIONS")).invoke();
+        assertEquals(200, response.getStatus());
+        response.close();
+    }
+
+    @Test
+    public void testGet() {
+        WebTarget r = getWebTarget();
+        assertEquals("GET", r.request().get(String.class));
+
+        Response cr = r.request().get();
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+}
diff --git a/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/BasicHelidonConnectorTest.java b/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/BasicHelidonConnectorTest.java
index 8b42df1..4af0ebe 100644
--- a/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/BasicHelidonConnectorTest.java
+++ b/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/BasicHelidonConnectorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -55,6 +55,8 @@
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
+import static org.junit.Assert.assertEquals;
+
 @RunWith(Parameterized.class)
 public class BasicHelidonConnectorTest extends JerseyTest {
 
@@ -305,4 +307,11 @@
             Assert.assertEquals("long", longResponse.readEntity(String.class));
         }
     }
+
+    @Test
+    public void testOptionsWithEntity() {
+        Response response = target("basic").path("get").request().build("OPTIONS", Entity.text("OPTIONS")).invoke();
+        assertEquals(200, response.getStatus());
+        response.close();
+    }
 }
diff --git a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/HttpMethodTest.java b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/HttpMethodTest.java
new file mode 100644
index 0000000..60f5f1b
--- /dev/null
+++ b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/HttpMethodTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.jdk.connector;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests the Http methods.
+ */
+public class HttpMethodTest extends JerseyTest {
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(HttpMethodResource.class);
+    }
+
+    protected Client createClient() {
+        ClientConfig cc = new ClientConfig();
+        cc.connectorProvider(new JdkConnectorProvider());
+        return ClientBuilder.newClient(cc);
+    }
+
+    private WebTarget getWebTarget(final Client client) {
+        return client.target(getBaseUri()).path("test");
+    }
+
+    private WebTarget getWebTarget() {
+        return getWebTarget(createClient());
+    }
+
+    @Path("/test")
+    public static class HttpMethodResource {
+        @GET
+        public String get() {
+            return "GET";
+        }
+    }
+
+    @Test
+    public void testOptionsWithEntity() {
+        WebTarget r = getWebTarget();
+        Response response = r.request().build("OPTIONS", Entity.text("OPTIONS")).invoke();
+        assertEquals(200, response.getStatus());
+        response.close();
+    }
+
+    @Test
+    public void testGet() {
+        WebTarget r = getWebTarget();
+        assertEquals("GET", r.request().get(String.class));
+
+        Response cr = r.request().get();
+        assertTrue(cr.hasEntity());
+        cr.close();
+    }
+}
diff --git a/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/MethodTest.java b/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/MethodTest.java
index 78393eb..6ce7a91 100644
--- a/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/MethodTest.java
+++ b/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/MethodTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -143,4 +143,11 @@
         Response response = target(PATH).request().method("PATCH", Entity.entity("PATCH", MediaType.TEXT_PLAIN));
         assertEquals("PATCH", response.readEntity(String.class));
     }
+
+    @Test
+    public void testOptionsWithEntity() {
+        Response response = target(PATH).request().build("OPTIONS", Entity.text("OPTIONS")).invoke();
+        assertEquals(200, response.getStatus());
+        response.close();
+    }
 }
diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/MethodTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/MethodTest.java
index c37cf35..f0c639b 100644
--- a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/MethodTest.java
+++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/MethodTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -98,4 +98,11 @@
         Response response = target(PATH).request().delete();
         assertEquals("DELETE", response.readEntity(String.class));
     }
+
+    @Test
+    public void testOptionsWithEntity() {
+        Response response = target(PATH).request().build("OPTIONS", Entity.text("OPTIONS")).invoke();
+        assertEquals(200, response.getStatus());
+        response.close();
+    }
 }
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java b/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java
index f6a48d7..2f59ae7 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java
@@ -123,7 +123,7 @@
         map.put("DELETE", EntityPresence.MUST_BE_NULL);
         map.put("GET", EntityPresence.MUST_BE_NULL);
         map.put("HEAD", EntityPresence.MUST_BE_NULL);
-        map.put("OPTIONS", EntityPresence.MUST_BE_NULL);
+        map.put("OPTIONS", EntityPresence.OPTIONAL);
         map.put("PATCH", EntityPresence.MUST_BE_PRESENT);
         map.put("POST", EntityPresence.OPTIONAL); // we allow to post null instead of entity
         map.put("PUT", EntityPresence.MUST_BE_PRESENT);
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpMethodEntityTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpMethodEntityTest.java
index 175ad5f..3a81437 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpMethodEntityTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpMethodEntityTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -85,7 +85,7 @@
 
     @Test
     public void testOptions() {
-        _test("OPTIONS", true, true);
+        _test("OPTIONS", true, false);
         _test("OPTIONS", false, false);
     }