You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by va...@apache.org on 2022/12/05 22:57:48 UTC

[tinkerpop] 01/01: merge from 3.5-dev

This is an automated email from the ASF dual-hosted git repository.

valentyn pushed a commit to branch 3.6-dev
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 4b2fff742764805533e9ae8e9c9dd5079fbe6667
Merge: ca8111140c ea67b9e64a
Author: Valentyn Kahamlyk <va...@bitquilltech.com>
AuthorDate: Mon Dec 5 14:57:38 2022 -0800

    merge from 3.5-dev

 CHANGELOG.asciidoc                                 |  4 ++
 docs/src/dev/provider/index.asciidoc               |  8 +++
 docs/src/reference/gremlin-applications.asciidoc   |  4 ++
 docs/src/reference/gremlin-variants.asciidoc       | 10 +++
 .../Gremlin.Net/Driver/ConnectionPoolSettings.cs   | 13 +++-
 .../src/Gremlin.Net/Driver/GremlinClient.cs        |  9 ++-
 .../src/Gremlin.Net/Driver/WebSocketConnection.cs  |  6 ++
 .../src/Gremlin.Net/Driver/WebSocketSettings.cs    |  2 +
 gremlin-dotnet/src/Gremlin.Net/Process/Utils.cs    | 27 +++++++-
 .../tinkerpop/gremlin/driver/Channelizer.java      | 13 ++--
 .../apache/tinkerpop/gremlin/driver/Cluster.java   | 31 ++++++++-
 .../apache/tinkerpop/gremlin/driver/Settings.java  |  8 +++
 .../apache/tinkerpop/gremlin/driver/UserAgent.java | 55 ++++++++++++++++
 .../tinkerpop/gremlin/driver/SettingsTest.java     |  2 +
 .../gremlin/driver/TestWSGremlinInitializer.java   | 37 +++++++++++
 .../WebSocketClientBehaviorIntegrateTest.java      | 38 +++++++++++
 gremlin-go/driver/client.go                        | 41 ++++++------
 gremlin-go/driver/connection.go                    | 17 ++---
 gremlin-go/driver/connection_test.go               | 17 ++---
 gremlin-go/driver/driverRemoteConnection.go        | 67 ++++++++++---------
 gremlin-go/driver/gorillaTransporter.go            |  9 ++-
 gremlin-go/driver/user_agent.go                    | 65 +++++++++++++++++++
 .../main/python/gremlin_python/driver/client.py    |  5 +-
 .../python/gremlin_python/driver/connection.py     |  9 ++-
 .../driver/driver_remote_connection.py             |  7 +-
 .../main/python/gremlin_python/driver/useragent.py | 41 ++++++++++++
 .../apache/tinkerpop/gremlin/server/Context.java   | 10 +++
 .../server/channel/WebSocketChannelizer.java       |  5 +-
 .../gremlin/server/handler/WsUserAgentHandler.java | 75 ++++++++++++++++++++++
 .../gremlin/server/util/MetricManager.java         |  8 +++
 .../gremlin/server/GremlinServerIntegrateTest.java | 70 ++++++++++++++++++++
 .../gremlin/server/channel/TestChannelizer.java    | 52 +++++++++++++++
 .../server/channel/UnifiedTestChannelizer.java     | 44 +++++++++++++
 .../server/channel/WebSocketTestChannelizer.java   | 44 +++++++++++++
 34 files changed, 768 insertions(+), 85 deletions(-)

diff --cc gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionPoolSettings.cs
index 00bd75ed46,bccd5d63b4..0f49cf964b
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionPoolSettings.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/ConnectionPoolSettings.cs
@@@ -35,10 -35,11 +35,11 @@@ namespace Gremlin.Net.Drive
          private int _maxInProcessPerConnection = DefaultMaxInProcessPerConnection;
          private int _reconnectionAttempts = DefaultReconnectionAttempts;
          private TimeSpan _reconnectionBaseDelay = DefaultReconnectionBaseDelay;
 -        private const int DefaultPoolSize = 4;
 -        private const int DefaultMaxInProcessPerConnection = 32;
 +        private const int DefaultPoolSize = 8;
 +        private const int DefaultMaxInProcessPerConnection = 16;
          private const int DefaultReconnectionAttempts = 4;
          private static readonly TimeSpan DefaultReconnectionBaseDelay = TimeSpan.FromSeconds(1);
+         internal const bool DefaultEnableUserAgentOnConnect = true;
  
          /// <summary>
          ///     Gets or sets the size of the connection pool.
diff --cc gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
index acda9d04e6,a688c53879..361cf57357
--- a/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Driver/GremlinClient.cs
@@@ -169,10 -172,11 +173,11 @@@ namespace Gremlin.Net.Drive
              Action<ClientWebSocketOptions> webSocketConfiguration = null, string sessionId = null,
              bool disableCompression = false, ILoggerFactory loggerFactory = null)
          {
 -            messageSerializer ??= new GraphSON3MessageSerializer();
 +            messageSerializer ??= new GraphBinaryMessageSerializer();
              var webSocketSettings = new WebSocketSettings
              {
-                 WebSocketConfigurationCallback = webSocketConfiguration
+                 WebSocketConfigurationCallback = webSocketConfiguration,
+                 EnableUserAgentOnConnect = connectionPoolSettings?.EnableUserAgentOnConnect ?? ConnectionPoolSettings.DefaultEnableUserAgentOnConnect
  #if NET6_0_OR_GREATER
                  , UseCompression = !disableCompression
  #endif
diff --cc gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/WebSocketClientBehaviorIntegrateTest.java
index a9e5105376,2ce10c2653..5a56418ff1
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/WebSocketClientBehaviorIntegrateTest.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/WebSocketClientBehaviorIntegrateTest.java
@@@ -75,8 -73,59 +75,46 @@@ public class WebSocketClientBehaviorInt
      @After
      public void shutdown() {
          server.stop();
 -
 -        // reset logger
 -        final org.apache.log4j.Logger rootLogger = org.apache.log4j.Logger.getRootLogger();
 -
 -        if (name.getMethodName().equals("shouldRemoveConnectionFromPoolWhenServerClose_WithPendingRequests") ||
 -                name.getMethodName().equals("shouldNotCreateReplacementConnectionWhenClientClosesConnection")) {
 -            final org.apache.log4j.Logger connectionPoolLogger = org.apache.log4j.Logger.getLogger(ConnectionPool.class);
 -            final org.apache.log4j.Logger connectionLogger = org.apache.log4j.Logger.getLogger(Connection.class);
 -            connectionPoolLogger.setLevel(previousLogLevel);
 -            connectionLogger.setLevel(previousLogLevel);
 -        }
 -
 -        rootLogger.removeAppender(recordingAppender);
      }
  
+     /**
+      * Tests that client is correctly sending user agent during web socket handshake by having the server return
+      * the captured user agent.
+      */
+     @Test
+     public void shouldIncludeUserAgentInHandshakeRequest() {
+         final Cluster cluster = Cluster.build("localhost").port(SimpleSocketServer.PORT)
+                 .minConnectionPoolSize(1)
+                 .maxConnectionPoolSize(1)
+                 .serializer(Serializers.GRAPHSON_V2D0)
+                 .create();
+         final Client.ClusteredClient client = cluster.connect();
+ 
+         // trigger the testing server to return captured user agent
+         String returnedUserAgent = client.submit("1", RequestOptions.build()
+                         .overrideRequestId(TestWSGremlinInitializer.USER_AGENT_REQUEST_ID).create()).one().getString();
+         assertEquals(UserAgent.USER_AGENT, returnedUserAgent);
+     }
+ 
+     /**
+      * Tests that no user agent is sent to server when that behaviour is disabled.
+      */
+     @Test
+     public void shouldNotIncludeUserAgentInHandshakeRequestIfDisabled() {
+         final Cluster cluster = Cluster.build("localhost").port(SimpleSocketServer.PORT)
+                 .minConnectionPoolSize(1)
+                 .maxConnectionPoolSize(1)
+                 .serializer(Serializers.GRAPHSON_V2D0)
+                 .enableUserAgentOnConnect(false)
+                 .create();
+         final Client.ClusteredClient client = cluster.connect();
+ 
+         // trigger the testing server to return captured user agent
+         String returnedUserAgent = client.submit("1", RequestOptions.build()
+                 .overrideRequestId(TestWSGremlinInitializer.USER_AGENT_REQUEST_ID).create()).one().getString();
+         assertEquals("", returnedUserAgent);
+     }
+ 
      /**
       * Constructs a deadlock situation when initializing a {@link Client} object in sessionless form that leads to
       * hanging behavior in low resource environments (TINKERPOP-2504) and for certain configurations of the
diff --cc gremlin-python/src/main/python/gremlin_python/driver/client.py
index 084b86f45c,61bead15a9..3a8e45c147
--- a/gremlin-python/src/main/python/gremlin_python/driver/client.py
+++ b/gremlin-python/src/main/python/gremlin_python/driver/client.py
@@@ -43,8 -41,8 +43,8 @@@ class Client
                   transport_factory=None, pool_size=None, max_workers=None,
                   message_serializer=None, username="", password="",
                   kerberized_service="", headers=None, session=None,
-                  **transport_kwargs):
+                  enable_user_agent_on_connect=True, **transport_kwargs):
 -        logging.info("Creating Client with url '%s'", url)
 +        log.info("Creating Client with url '%s'", url)
          self._closed = False
          self._url = url
          self._headers = headers
diff --cc gremlin-python/src/main/python/gremlin_python/driver/connection.py
index c26a09e627,92ebb2795d..ca43d66d20
--- a/gremlin-python/src/main/python/gremlin_python/driver/connection.py
+++ b/gremlin-python/src/main/python/gremlin_python/driver/connection.py
@@@ -15,10 -15,10 +15,10 @@@
  # specific language governing permissions and limitations
  # under the License.
  import uuid
 +import queue
  from concurrent.futures import Future
 -from six.moves import queue
  
- from gremlin_python.driver import resultset
+ from gremlin_python.driver import resultset, useragent
  
  __author__ = 'David M. Brown (davebshow@gmail.com)'
  
diff --cc gremlin-python/src/main/python/gremlin_python/driver/driver_remote_connection.py
index 4156bf2822,e7768746fe..858c85b92e
--- a/gremlin-python/src/main/python/gremlin_python/driver/driver_remote_connection.py
+++ b/gremlin-python/src/main/python/gremlin_python/driver/driver_remote_connection.py
@@@ -39,8 -37,8 +39,8 @@@ class DriverRemoteConnection(RemoteConn
                   username="", password="", kerberized_service='',
                   message_serializer=None, graphson_reader=None,
                   graphson_writer=None, headers=None, session=None,
-                  **transport_kwargs):
-         log.info("Creating DriverRemoteConnection with url '%s'", str(url))
+                  enable_user_agent_on_connect=True, **transport_kwargs):
 -        logging.info("Creating DriverRemoteConnection with url '%s'", str(url))
++        logg.info("Creating DriverRemoteConnection with url '%s'", str(url))
          self.__url = url
          self.__traversal_source = traversal_source
          self.__protocol_factory = protocol_factory
diff --cc gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/Context.java
index 955182ce93,ad15772509..da1c666bc0
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/Context.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/Context.java
@@@ -24,9 -24,10 +24,10 @@@ import org.apache.tinkerpop.gremlin.dri
  import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
  import org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor;
  import io.netty.channel.ChannelHandlerContext;
 -import org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinScriptChecker;
 +import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptChecker;
  import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
  import org.apache.tinkerpop.gremlin.server.handler.Frame;
+ import org.apache.tinkerpop.gremlin.server.handler.WsUserAgentHandler;
  import org.apache.tinkerpop.gremlin.structure.Graph;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
diff --cc gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
index 5f237a3d56,f5264886fe..5ab40a265e
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
@@@ -18,14 -18,12 +18,16 @@@
   */
  package org.apache.tinkerpop.gremlin.server;
  
 +import ch.qos.logback.classic.Level;
 +import ch.qos.logback.classic.Logger;
+ import io.netty.channel.ChannelHandlerContext;
+ import io.netty.util.AttributeKey;
 +import nl.altindag.log.LogCaptor;
  import org.apache.commons.configuration2.BaseConfiguration;
  import org.apache.commons.configuration2.Configuration;
 -import org.apache.log4j.Level;
 -import org.apache.log4j.Logger;
 +import org.apache.tinkerpop.gremlin.server.channel.UnifiedChannelizer;
 +import org.apache.tinkerpop.gremlin.server.channel.WebSocketChannelizer;
 +import org.apache.tinkerpop.gremlin.util.ExceptionHelper;
  import org.apache.tinkerpop.gremlin.TestHelper;
  import org.apache.tinkerpop.gremlin.driver.Client;
  import org.apache.tinkerpop.gremlin.driver.Cluster;
@@@ -49,12 -47,21 +52,19 @@@ import org.apache.tinkerpop.gremlin.str
  import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
  import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
  import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 -import org.apache.tinkerpop.gremlin.server.handler.OpSelectorHandler;
  import org.apache.tinkerpop.gremlin.server.op.AbstractEvalOpProcessor;
  import org.apache.tinkerpop.gremlin.server.op.standard.StandardOpProcessor;
+ import org.apache.tinkerpop.gremlin.server.channel.TestChannelizer;
+ import org.apache.tinkerpop.gremlin.server.channel.UnifiedChannelizer;
+ import org.apache.tinkerpop.gremlin.server.channel.UnifiedTestChannelizer;
+ import org.apache.tinkerpop.gremlin.server.channel.WebSocketChannelizer;
+ import org.apache.tinkerpop.gremlin.server.channel.WebSocketTestChannelizer;
+ import org.apache.tinkerpop.gremlin.server.handler.WsUserAgentHandler;
  import org.apache.tinkerpop.gremlin.structure.Graph;
  import org.apache.tinkerpop.gremlin.structure.T;
  import org.apache.tinkerpop.gremlin.structure.Vertex;
 -import org.apache.tinkerpop.gremlin.util.Log4jRecordingAppender;
  import org.apache.tinkerpop.gremlin.util.function.Lambda;
+ import org.apache.tinkerpop.gremlin.util.ExceptionHelper;
  import org.hamcrest.CoreMatchers;
  import org.hamcrest.Matchers;
  import org.junit.After;
@@@ -122,42 -128,42 +132,46 @@@ public class GremlinServerIntegrateTes
      }};
      private static final int POOL_SIZE_FOR_TIMEOUT_TESTS = 1;
  
 +    private static LogCaptor logCaptor;
 +
 +    @BeforeClass
 +    public static void setupLogCaptor() {
 +        logCaptor = LogCaptor.forRoot();
 +    }
 +
 +    @AfterClass
 +    public static void tearDownAfterClass() {
 +        logCaptor.close();
 +    }
 +
      @Before
      public void setupForEachTest() {
 -        recordingAppender = new Log4jRecordingAppender();
 -        final Logger rootLogger = Logger.getRootLogger();
  
          if (name.getMethodName().equals("shouldPingChannelIfClientDies") ||
 -                name.getMethodName().equals("shouldCloseChannelIfClientDoesntRespond") ||
 -                name.getMethodName().equals("shouldCaptureUserAgentFromClient")) {
 -            final org.apache.log4j.Logger opSelectorHandlerLogger = org.apache.log4j.Logger.getLogger(OpSelectorHandler.class);
 -            final org.apache.log4j.Logger unifiedHandlerLogger = org.apache.log4j.Logger.getLogger(UnifiedHandler.class);
 -            final org.apache.log4j.Logger wsUserAgentHandlerLogger = org.apache.log4j.Logger.getLogger(WsUserAgentHandler.class);
 +                name.getMethodName().equals("shouldCloseChannelIfClientDoesntRespond")) {
 +            final Logger opSelectorHandlerLogger = (Logger) LoggerFactory.getLogger(OpSelectorHandler.class);
 +            final Logger unifiedHandlerLogger = (Logger) LoggerFactory.getLogger(UnifiedHandler.class);
++            final Logger wsUserAgentHandlerLogger = (Logger) LoggerFactory.getLogger(WsUserAgentHandler.class);
              previousLogLevel = opSelectorHandlerLogger.getLevel();
              opSelectorHandlerLogger.setLevel(Level.INFO);
              unifiedHandlerLogger.setLevel(Level.INFO);
+             wsUserAgentHandlerLogger.setLevel(Level.DEBUG);
          }
  
 -        rootLogger.addAppender(recordingAppender);
 +        logCaptor.clearLogs();
      }
  
      @After
      public void teardownForEachTest() {
 -        final Logger rootLogger = Logger.getRootLogger();
 -
          if (name.getMethodName().equals("shouldPingChannelIfClientDies")||
 -                name.getMethodName().equals("shouldCloseChannelIfClientDoesntRespond") ||
 -                name.getMethodName().equals("shouldCaptureUserAgentFromClient")) {
 -            final org.apache.log4j.Logger opSelectorHandlerLogger = org.apache.log4j.Logger.getLogger(OpSelectorHandler.class);
 +                name.getMethodName().equals("shouldCloseChannelIfClientDoesntRespond")) {
 +            final Logger opSelectorHandlerLogger = (Logger) LoggerFactory.getLogger(OpSelectorHandler.class);
              opSelectorHandlerLogger.setLevel(previousLogLevel);
 -            final org.apache.log4j.Logger unifiedHandlerLogger = org.apache.log4j.Logger.getLogger(UnifiedHandler.class);
 +            final Logger unifiedHandlerLogger = (Logger) LoggerFactory.getLogger(UnifiedHandler.class);
              unifiedHandlerLogger.setLevel(previousLogLevel);
 -            final org.apache.log4j.Logger wsUserAgentHandlerLogger = org.apache.log4j.Logger.getLogger(WsUserAgentHandler.class);
++            final Logger wsUserAgentHandlerLogger = (Logger) LoggerFactory.getLogger(WsUserAgentHandler.class);
+             wsUserAgentHandlerLogger.setLevel(previousLogLevel);
          }
 -
 -        rootLogger.removeAppender(recordingAppender);
      }
  
      /**
@@@ -290,6 -302,55 +310,56 @@@
          return scriptEngineConf;
      }
  
+     @Test
+     public void shouldCaptureUserAgentFromClient() {
+         final Cluster cluster = TestClientFactory.build().enableUserAgentOnConnect(true).create();
+         final Client client = cluster.connect();
+         client.submit("test");
+ 
 -        assertThat(recordingAppender.logContainsAny(".*New Connection on channel .* with user agent.*$"), is(true));
++        assertThat(logCaptor.getLogs().stream().anyMatch(m -> m.matches(
++                ".*New Connection on channel .* with user agent.*$")), is(true));
+ 
+         cluster.close();
+     }
+ 
+     @Test
+     public void shouldStoreUserAgentInContextWebSocket() throws InterruptedException {
+         shouldStoreUserAgentInContext();
+     }
+ 
+     @Test
+     public void shouldStoreUserAgentInContextUnified() throws InterruptedException {
+         shouldStoreUserAgentInContext();
+     }
+ 
+     private void shouldStoreUserAgentInContext() throws InterruptedException {
+         if(server.getChannelizer() instanceof TestChannelizer) {
+             assertNull(getUserAgentIfAvailable());
+             final Cluster cluster = TestClientFactory.build().enableUserAgentOnConnect(true).create();
+             final Client client = cluster.connect();
+ 
+             client.submit("test");
+             assertEquals(UserAgent.USER_AGENT, getUserAgentIfAvailable());
+             client.submit("test");
+             assertEquals(UserAgent.USER_AGENT, getUserAgentIfAvailable());
+             client.close();
+             cluster.close();
+         }
+     }
+ 
+     private String getUserAgentIfAvailable() {
+         final AttributeKey<String> userAgentAttrKey = AttributeKey.valueOf(UserAgent.USER_AGENT_HEADER_NAME);
+         if(!(server.getChannelizer() instanceof TestChannelizer)) {
+             return null;
+         }
+         final TestChannelizer channelizer = (TestChannelizer) server.getChannelizer();
+         final ChannelHandlerContext ctx = channelizer.getMostRecentChannelHandlerContext();
+         if(ctx == null || !ctx.channel().hasAttr(userAgentAttrKey)) {
+             return null;
+         }
+         return channelizer.getMostRecentChannelHandlerContext().channel().attr(userAgentAttrKey).get();
+     }
+ 
      @Test
      public void shouldBlowTheWorkQueueSize() throws Exception {
          final Cluster cluster = TestClientFactory.open();