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 2023/11/15 22:35:40 UTC

(tinkerpop) branch 3.6-dev updated: Bytecode support for HTTP requests (#2336)

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


The following commit(s) were added to refs/heads/3.6-dev by this push:
     new 8365421f25 Bytecode support for HTTP requests (#2336)
8365421f25 is described below

commit 8365421f2543335b043578096944e2b076e2b867
Author: Valentyn Kahamlyk <vk...@users.noreply.github.com>
AuthorDate: Wed Nov 15 14:35:35 2023 -0800

    Bytecode support for HTTP requests (#2336)
    
    Added Gremlin bytecode support for HTTP requests for Java and Python GLV's without translation to Gremlin script.
    Added serialization for entire RequestMessage.
    Payload type distinguished by Content-Type header .
---
 .../driver/handler/HttpGremlinRequestEncoder.java  |  44 ++----
 .../driver/handler/HttpGremlinResponseDecoder.java |   8 +-
 .../ser/GraphSONMessageSerializerV3d0Test.java     |  48 +++++-
 .../binary/GraphBinaryMessageSerializerV1Test.java |  27 +++-
 .../gremlin/groovy/engine/GremlinExecutor.java     |  86 +++++++++--
 .../gremlin_python/driver/aiohttp/transport.py     |   4 +-
 .../main/python/gremlin_python/driver/protocol.py  |  32 +---
 .../driver/test_driver_remote_connection_http.py   |  19 +--
 .../conf/gremlin-server-rest-modern.yaml           |   5 +-
 .../server/handler/HttpGremlinEndpointHandler.java |  66 +++++----
 .../gremlin/server/handler/HttpHandlerUtil.java    |  55 ++++++-
 .../server/util/TextPlainMessageSerializer.java    |   7 +-
 .../server/GremlinServerHttpIntegrateTest.java     |  20 ++-
 .../gremlin/server/HttpDriverIntegrateTest.java    |  31 +++-
 .../server/handler/HttpHandlerUtilTest.java        | 165 +++++++++++++++++++++
 15 files changed, 468 insertions(+), 149 deletions(-)

diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/HttpGremlinRequestEncoder.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/HttpGremlinRequestEncoder.java
index 2c271cede8..bdf0f544af 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/HttpGremlinRequestEncoder.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/HttpGremlinRequestEncoder.java
@@ -28,15 +28,12 @@ import io.netty.handler.codec.http.HttpHeaderNames;
 import io.netty.handler.codec.http.HttpMethod;
 import io.netty.handler.codec.http.HttpVersion;
 import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
-import org.apache.tinkerpop.gremlin.driver.Tokens;
 import org.apache.tinkerpop.gremlin.driver.exception.ResponseException;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
+import org.apache.tinkerpop.gremlin.driver.ser.SerTokens;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
-import org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator;
-import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
 
-import java.util.HashMap;
 import java.util.List;
 import java.util.function.UnaryOperator;
 
@@ -47,8 +44,6 @@ import java.util.function.UnaryOperator;
 public final class HttpGremlinRequestEncoder extends MessageToMessageEncoder<RequestMessage> {
     private final MessageSerializer<?> serializer;
 
-    private static final ObjectMapper mapper = new ObjectMapper();
-
     private final UnaryOperator<FullHttpRequest> interceptor;
 
     public HttpGremlinRequestEncoder(final MessageSerializer<?> serializer, final UnaryOperator<FullHttpRequest> interceptor) {
@@ -58,29 +53,22 @@ public final class HttpGremlinRequestEncoder extends MessageToMessageEncoder<Req
 
     @Override
     protected void encode(final ChannelHandlerContext channelHandlerContext, final RequestMessage requestMessage, final List<Object> objects) throws Exception {
-        try {
-            final String gremlin;
-            final Object gremlinStringOrBytecode = requestMessage.getArg(Tokens.ARGS_GREMLIN);
+        final String mimeType = serializer.mimeTypesSupported()[0];
+        // only GraphSON3 and GraphBinary recommended for serialization of Bytecode requests
+        if (requestMessage.getArg("gremlin") instanceof Bytecode &&
+                !mimeType.equals(SerTokens.MIME_GRAPHSON_V3D0) &&
+                !mimeType.equals(SerTokens.MIME_GRAPHBINARY_V1D0)) {
+            throw new ResponseException(ResponseStatusCode.REQUEST_ERROR_SERIALIZATION, String.format(
+                    "An error occurred during serialization of this request [%s] - it could not be sent to the server - Reason: only GraphSON3 and GraphBinary recommended for serialization of Bytecode requests, but used %s",
+                    requestMessage, serializer.getClass().getName()));
+        }
 
-            // the gremlin key can contain a Gremlin script or bytecode. if it's bytecode we can't submit it over
-            // http as such. it has to be converted to a script and we can do that with the Groovy translator.
-            final boolean usesBytecode = gremlinStringOrBytecode instanceof Bytecode;
-            if (usesBytecode) {
-                gremlin = GroovyTranslator.of("g").translate((Bytecode) gremlinStringOrBytecode).getScript();
-            } else {
-                gremlin = gremlinStringOrBytecode.toString();
-            }
-            final byte[] payload = mapper.writeValueAsBytes(new HashMap<String,Object>() {{
-                put(Tokens.ARGS_GREMLIN, gremlin);
-                put(Tokens.REQUEST_ID, requestMessage.getRequestId());
-                if (usesBytecode) put("op", Tokens.OPS_BYTECODE);
-            }});
-            final ByteBuf bb = channelHandlerContext.alloc().buffer(payload.length);
-            bb.writeBytes(payload);
-            final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", bb);
-            request.headers().add(HttpHeaderNames.CONTENT_TYPE, "application/json");
-            request.headers().add(HttpHeaderNames.CONTENT_LENGTH, payload.length);
-            request.headers().add(HttpHeaderNames.ACCEPT, serializer.mimeTypesSupported()[0]);
+        try {
+            final ByteBuf buffer = serializer.serializeRequestAsBinary(requestMessage, channelHandlerContext.alloc());
+            final FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/", buffer);
+            request.headers().add(HttpHeaderNames.CONTENT_TYPE, mimeType);
+            request.headers().add(HttpHeaderNames.CONTENT_LENGTH, buffer.readableBytes());
+            request.headers().add(HttpHeaderNames.ACCEPT, mimeType);
 
             objects.add(interceptor.apply(request));
         } catch (Exception ex) {
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/HttpGremlinResponseDecoder.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/HttpGremlinResponseDecoder.java
index 24ffc39de3..57a549f77f 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/HttpGremlinResponseDecoder.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/HttpGremlinResponseDecoder.java
@@ -45,12 +45,6 @@ public final class HttpGremlinResponseDecoder extends MessageToMessageDecoder<Fu
 
     @Override
     protected void decode(final ChannelHandlerContext channelHandlerContext, final FullHttpResponse httpResponse, final List<Object> objects) throws Exception {
-        final String content = httpResponse.content().toString(CharsetUtil.UTF_8);
-
-        if (!(serializer instanceof MessageTextSerializer))
-            throw new IllegalStateException(String.format("%s must be of type %s to decode responses from HTTP endpoints",
-                    serializer.getClass().getSimpleName(), HttpGremlinResponseDecoder.class.getSimpleName()));
-
-        objects.add(((MessageTextSerializer) serializer).deserializeResponse(content));
+        objects.add(serializer.deserializeResponse(httpResponse.content()));
     }
 }
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV3d0Test.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV3d0Test.java
index 701e2cdaa3..d6d0232639 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV3d0Test.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV3d0Test.java
@@ -21,7 +21,10 @@ package org.apache.tinkerpop.gremlin.driver.ser;
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufAllocator;
 import io.netty.buffer.UnpooledByteBufAllocator;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
 import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
+import org.apache.tinkerpop.gremlin.driver.Tokens;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
@@ -38,9 +41,9 @@ import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory;
 import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import org.apache.tinkerpop.shaded.jackson.databind.JsonMappingException;
-import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
 import org.junit.Test;
 
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
@@ -49,6 +52,9 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import static org.apache.tinkerpop.gremlin.driver.MockitoHamcrestMatcherAdapter.reflectionEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasItemInArray;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -67,7 +73,7 @@ public class GraphSONMessageSerializerV3d0Test {
     private final ResponseMessage.Builder responseMessageBuilder = ResponseMessage.build(requestId);
     private final static ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT;
 
-    public final MessageSerializer<ObjectMapper> serializer = new GraphSONMessageSerializerV3d0();
+    public final GraphSONMessageSerializerV3d0 serializer = new GraphSONMessageSerializerV3d0();
 
     @Test
     public void shouldSerializeIterable() throws Exception {
@@ -80,8 +86,8 @@ public class GraphSONMessageSerializerV3d0Test {
 
         final List<Integer> deserializedFunList = (List<Integer>) response.getResult().getData();
         assertEquals(2, deserializedFunList.size());
-        assertEquals(new Integer(1), deserializedFunList.get(0));
-        assertEquals(new Integer(100), deserializedFunList.get(1));
+        assertEquals(Integer.valueOf(1), deserializedFunList.get(0));
+        assertEquals(Integer.valueOf(100), deserializedFunList.get(1));
     }
 
     @Test
@@ -96,9 +102,9 @@ public class GraphSONMessageSerializerV3d0Test {
 
         final List<Integer> deserializedFunList = (List<Integer>) response.getResult().getData();
         assertEquals(3, deserializedFunList.size());
-        assertEquals(new Integer(1), deserializedFunList.get(0));
+        assertEquals(Integer.valueOf(1), deserializedFunList.get(0));
         assertNull(deserializedFunList.get(1));
-        assertEquals(new Integer(100), deserializedFunList.get(2));
+        assertEquals(Integer.valueOf(100), deserializedFunList.get(2));
     }
 
     @Test
@@ -221,7 +227,7 @@ public class GraphSONMessageSerializerV3d0Test {
 
         v.property(VertexProperty.Cardinality.single, "friends", friends);
 
-        final List list = IteratorUtils.list(graph.vertices());
+        final List<Vertex> list = IteratorUtils.list(graph.vertices());
 
         final ResponseMessage response = convert(list);
         assertCommon(response);
@@ -263,7 +269,7 @@ public class GraphSONMessageSerializerV3d0Test {
 
         final Map<Vertex, Integer> deserializedMap = (Map<Vertex, Integer>) response.getResult().getData();
         assertEquals(1, deserializedMap.size());
-        assertEquals(new Integer(1000), deserializedMap.get(v1));
+        assertEquals(Integer.valueOf(1000), deserializedMap.get(v1));
     }
 
     @Test
@@ -346,6 +352,32 @@ public class GraphSONMessageSerializerV3d0Test {
         assertNotNull(m.getArgs());
     }
 
+    @Test
+    public void shouldSerializeAndDeserializeRequest() throws Exception {
+        final GraphTraversalSource g = EmptyGraph.instance().traversal();
+        final Traversal.Admin t = g.V().hasLabel("person").out().asAdmin();
+
+        final Map<String, String> aliases = new HashMap<>();
+        aliases.put("g","g");
+
+        final RequestMessage request = RequestMessage.build(Tokens.OPS_BYTECODE)
+                .processor("traversal")
+                .overrideRequestId(UUID.randomUUID())
+                .addArg(Tokens.ARGS_GREMLIN, t.getBytecode())
+                .addArg(Tokens.ARGS_ALIASES, aliases)
+                .create();
+
+        final ByteBuf buffer = serializer.serializeRequestAsBinary(request, allocator);
+        final int mimeLen = buffer.readByte();
+        final byte[] bytes = new byte[mimeLen];
+        buffer.readBytes(bytes);
+        final String mimeType = new String(bytes, StandardCharsets.UTF_8);
+
+        final RequestMessage deserialized = serializer.deserializeRequest(buffer);
+        assertThat(request, reflectionEquals(deserialized));
+        assertThat(serializer.mimeTypesSupported(), hasItemInArray(mimeType));
+    }
+
     @Test
     public void shouldRegisterGremlinServerModuleAutomaticallyWithMapper() throws SerializationException {
         GraphSONMapper.Builder builder = GraphSONMapper.build().addCustomModule(GraphSONXModuleV3d0.build().create(false));
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryMessageSerializerV1Test.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryMessageSerializerV1Test.java
index 052bfce456..dd9af34979 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryMessageSerializerV1Test.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryMessageSerializerV1Test.java
@@ -20,14 +20,19 @@ package org.apache.tinkerpop.gremlin.driver.ser.binary;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufAllocator;
+import org.apache.tinkerpop.gremlin.driver.Tokens;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
 import org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1;
 import org.apache.tinkerpop.gremlin.driver.ser.SerializationException;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 import org.apache.tinkerpop.gremlin.structure.io.binary.TypeSerializerRegistry;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
 import org.junit.Test;
 
+import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -35,8 +40,9 @@ import java.util.UUID;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import static org.apache.tinkerpop.gremlin.driver.MockitoHamcrestMatcherAdapter.reflectionEquals;
-import static org.junit.Assert.assertEquals;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasItemInArray;
+import static org.junit.Assert.assertEquals;
 
 public class GraphBinaryMessageSerializerV1Test {
     private final ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
@@ -44,17 +50,28 @@ public class GraphBinaryMessageSerializerV1Test {
 
     @Test
     public void shouldSerializeAndDeserializeRequest() throws SerializationException {
-        final RequestMessage request = RequestMessage.build("op1")
-                .processor("proc1")
+        final GraphTraversalSource g = EmptyGraph.instance().traversal();
+        final Traversal.Admin t = g.V().hasLabel("person").out().asAdmin();
+
+        final Map<String, String> aliases = new HashMap<>();
+        aliases.put("g","g");
+
+        final RequestMessage request = RequestMessage.build(Tokens.OPS_BYTECODE)
+                .processor("traversal")
                 .overrideRequestId(UUID.randomUUID())
-                .addArg("arg1", "value1")
+                .addArg(Tokens.ARGS_GREMLIN, t.getBytecode())
+                .addArg(Tokens.ARGS_ALIASES, aliases)
                 .create();
 
         final ByteBuf buffer = serializer.serializeRequestAsBinary(request, allocator);
         final int mimeLen = buffer.readByte();
-        buffer.readBytes(new byte[mimeLen]);
+        final byte[] bytes = new byte[mimeLen];
+        buffer.readBytes(bytes);
+        final String mimeType = new String(bytes, StandardCharsets.UTF_8);
+
         final RequestMessage deserialized = serializer.deserializeRequest(buffer);
         assertThat(request, reflectionEquals(deserialized));
+        assertThat(serializer.mimeTypesSupported(), hasItemInArray(mimeType));
     }
 
     @Test
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutor.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutor.java
index 2884043aeb..9010578e53 100644
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutor.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutor.java
@@ -29,7 +29,9 @@ import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine;
 import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineManager;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.util.BytecodeHelper;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalInterruptedException;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -182,7 +184,7 @@ public class GremlinExecutor implements AutoCloseable {
      * @param boundVars the bindings to evaluate in the context of the script
      */
     public CompletableFuture<Object> eval(final String script, final String language, final Bindings boundVars) {
-        return eval(script, language, boundVars, null, null);
+        return eval(script, language, boundVars, null, null, null);
     }
 
     /**
@@ -190,14 +192,30 @@ public class GremlinExecutor implements AutoCloseable {
      * result after script evaluates but before transaction commit and before the returned {@link CompletableFuture}
      * is completed.
      *
-     * @param script the script to evaluate
+     * @param gremlin the script or bytecode to evaluate
      * @param language the language to evaluate it in
      * @param boundVars the bindings to evaluate in the context of the script
      * @param transformResult a {@link Function} that transforms the result - can be {@code null}
      */
-    public CompletableFuture<Object> eval(final String script, final String language, final Map<String, Object> boundVars,
+    public CompletableFuture<Object> eval(final Object gremlin, final String language, final Map<String, Object> boundVars,
                                           final Function<Object, Object> transformResult) {
-        return eval(script, language, new SimpleBindings(boundVars), transformResult, null);
+        return eval(gremlin, language, new SimpleBindings(boundVars), transformResult, null);
+    }
+
+    /**
+     * Evaluate a script and allow for the submission of a transform {@link Function} that will transform the
+     * result after script evaluates but before transaction commit and before the returned {@link CompletableFuture}
+     * is completed.
+     *
+     * @param gremlin the script or bytecode to evaluate
+     * @param language the language to evaluate it in
+     * @param boundVars the bindings to evaluate in the context of the script
+     * @param timeOut optional override for evaluation timeout
+     * @param transformResult a {@link Function} that transforms the result - can be {@code null}
+     */
+    public CompletableFuture<Object> eval(final Object gremlin, final String language, final Map<String, Object> boundVars,
+                                          final Long timeOut, final Function<Object, Object> transformResult) {
+        return eval(gremlin, language, new SimpleBindings(boundVars), timeOut, transformResult, null);
     }
 
     /**
@@ -222,34 +240,54 @@ public class GremlinExecutor implements AutoCloseable {
      * processing after the script evaluates and after the {@link CompletableFuture} is completed, but before the
      * transaction is committed.
      *
-     * @param script the script to evaluate
+     * @param gremlin the script or bytecode to evaluate
      * @param language the language to evaluate it in
      * @param boundVars the bindings to evaluate in the context of the script
      * @param transformResult a {@link Function} that transforms the result - can be {@code null}
      * @param withResult a {@link Consumer} that accepts the result - can be {@code null}
      */
-    public CompletableFuture<Object> eval(final String script, final String language, final Bindings boundVars,
+    public CompletableFuture<Object> eval(final Object gremlin, final String language, final Bindings boundVars,
+                                          final Function<Object, Object> transformResult, final Consumer<Object> withResult) {
+        return eval(gremlin, language, boundVars, null, transformResult, withResult);
+    }
+
+    /**
+     * Evaluate a script and allow for the submission of both a transform {@link Function} and {@link Consumer}.
+     * The {@link Function} will transform the result after script evaluates but before transaction commit and before
+     * the returned {@link CompletableFuture} is completed. The {@link Consumer} will take the result for additional
+     * processing after the script evaluates and after the {@link CompletableFuture} is completed, but before the
+     * transaction is committed.
+     *
+     * @param gremlin the script or bytecode to evaluate
+     * @param language the language to evaluate it in
+     * @param boundVars the bindings to evaluate in the context of the script
+     * @param timeOut optional override for evaluation timeout
+     * @param transformResult a {@link Function} that transforms the result - can be {@code null}
+     * @param withResult a {@link Consumer} that accepts the result - can be {@code null}
+     */
+    public CompletableFuture<Object> eval(final Object gremlin, final String language, final Bindings boundVars, final Long timeOut,
                                           final Function<Object, Object> transformResult, final Consumer<Object> withResult) {
         final LifeCycle lifeCycle = LifeCycle.build()
+                .evaluationTimeoutOverride(timeOut)
                 .transformResult(transformResult)
                 .withResult(withResult).create();
 
-        return eval(script, language, boundVars, lifeCycle);
+        return eval(gremlin, language, boundVars, lifeCycle);
     }
 
     /**
      * Evaluate a script and allow for the submission of alteration to the entire evaluation execution lifecycle.
      *
-     * @param script the script to evaluate
+     * @param gremlin the script or bytecode to evaluate
      * @param language the language to evaluate it in
      * @param boundVars the bindings to evaluate in the context of the script
      * @param lifeCycle a set of functions that can be applied at various stages of the evaluation process
      */
-    public CompletableFuture<Object> eval(final String script, final String language, final Bindings boundVars,  final LifeCycle lifeCycle) {
+    public CompletableFuture<Object> eval(final Object gremlin, final String language, final Bindings boundVars, final LifeCycle lifeCycle) {
         final String lang = Optional.ofNullable(language).orElse("gremlin-groovy");
 
         if (logger.isDebugEnabled()) {
-            logger.debug("Preparing to evaluate script - {} - in thread [{}]", script, Thread.currentThread().getName());
+            logger.debug("Preparing to evaluate script - {} - in thread [{}]", gremlin, Thread.currentThread().getName());
         }
 
         final Bindings bindings = new SimpleBindings();
@@ -258,7 +296,9 @@ public class GremlinExecutor implements AutoCloseable {
 
         // override the timeout if the lifecycle has a value assigned. if the script contains with(timeout)
         // options then allow that value to override what's provided on the lifecycle
-        final Optional<Long> timeoutDefinedInScript = GremlinScriptChecker.parse(script).getTimeout();
+        final Optional<Long> timeoutDefinedInScript = gremlin instanceof String
+                ? GremlinScriptChecker.parse((String) gremlin).getTimeout()
+                : Optional.empty();
         final long scriptEvalTimeOut = timeoutDefinedInScript.orElse(
                 lifeCycle.getEvaluationTimeoutOverride().orElse(evaluationTimeout));
 
@@ -267,9 +307,23 @@ public class GremlinExecutor implements AutoCloseable {
             try {
                 lifeCycle.getBeforeEval().orElse(beforeEval).accept(bindings);
 
-                logger.debug("Evaluating script - {} - in thread [{}]", script, Thread.currentThread().getName());
+                logger.debug("Evaluating script - {} - in thread [{}]", gremlin, Thread.currentThread().getName());
+
+                Object o;
+                if (gremlin instanceof String) {
+                    o = gremlinScriptEngineManager.getEngineByName(lang).eval((String) gremlin, bindings);
+                }
+                else if (gremlin instanceof Bytecode) {
+                    final Bytecode bytecode = (Bytecode) gremlin;
+                    final Traversal.Admin<?, ?> traversal = eval(
+                            bytecode, bindings, BytecodeHelper.getLambdaLanguage(bytecode).orElse("gremlin-groovy"), "g");
+
+                    o = IteratorUtils.asList(traversal);
+                }
+                else {
+                    throw new IllegalArgumentException("Invalid gremlin type.");
+                }
 
-                final Object o = gremlinScriptEngineManager.getEngineByName(lang).eval(script, bindings);
 
                 // apply a transformation before sending back the result - useful when trying to force serialization
                 // in the same thread that the eval took place given ThreadLocal nature of graphs as well as some
@@ -299,7 +353,7 @@ public class GremlinExecutor implements AutoCloseable {
                         || root instanceof InterruptedIOException) {
                     lifeCycle.getAfterTimeout().orElse(afterTimeout).accept(bindings, root);
                     evaluationFuture.completeExceptionally(new TimeoutException(
-                            String.format("Evaluation exceeded the configured 'evaluationTimeout' threshold of %s ms or evaluation was otherwise cancelled directly for request [%s]: %s", scriptEvalTimeOut, script, root.getMessage())));
+                            String.format("Evaluation exceeded the configured 'evaluationTimeout' threshold of %s ms or evaluation was otherwise cancelled directly for request [%s]: %s", scriptEvalTimeOut, gremlin, root.getMessage())));
                 } else {
                     lifeCycle.getAfterFailure().orElse(afterFailure).accept(bindings, root);
                     evaluationFuture.completeExceptionally(root);
@@ -318,7 +372,7 @@ public class GremlinExecutor implements AutoCloseable {
                     final CompletableFuture<Object> ef = evaluationFutureRef.get();
                     if (ef != null) {
                         ef.completeExceptionally(new TimeoutException(
-                                String.format("Evaluation exceeded the configured 'evaluationTimeout' threshold of %s ms or evaluation was otherwise cancelled directly for request [%s]", scriptEvalTimeOut, script)));
+                                String.format("Evaluation exceeded the configured 'evaluationTimeout' threshold of %s ms or evaluation was otherwise cancelled directly for request [%s]", scriptEvalTimeOut, gremlin)));
                     }
                 }
             }, scriptEvalTimeOut, TimeUnit.MILLISECONDS);
@@ -326,7 +380,7 @@ public class GremlinExecutor implements AutoCloseable {
             // Cancel the scheduled timeout if the eval future is complete or the script evaluation failed with exception
             evaluationFuture.handleAsync((v, t) -> {
                 if (!sf.isDone()) {
-                    logger.debug("Killing scheduled timeout on script evaluation - {} - as the eval completed (possibly with exception).", script);
+                    logger.debug("Killing scheduled timeout on script evaluation - {} - as the eval completed (possibly with exception).", gremlin);
                     sf.cancel(true);
                 }
 
diff --git a/gremlin-python/src/main/python/gremlin_python/driver/aiohttp/transport.py b/gremlin-python/src/main/python/gremlin_python/driver/aiohttp/transport.py
index c5facb9120..7366add5b2 100644
--- a/gremlin-python/src/main/python/gremlin_python/driver/aiohttp/transport.py
+++ b/gremlin-python/src/main/python/gremlin_python/driver/aiohttp/transport.py
@@ -187,7 +187,6 @@ class AiohttpHTTPTransport(AbstractBaseTransport):
             else:
                 self._client_session = aiohttp.ClientSession(base_url=url, headers=headers, loop=self._loop)
 
-
         # Execute the async connect synchronously.
         self._loop.run_until_complete(async_connect())
 
@@ -212,8 +211,7 @@ class AiohttpHTTPTransport(AbstractBaseTransport):
         # Inner function to perform async read.
         async def async_read():
             async with async_timeout.timeout(self._read_timeout):
-                # using http request read()
-                return await self._http_req_resp.text()
+                return await self._http_req_resp.read()
 
         return self._loop.run_until_complete(async_read())
 
diff --git a/gremlin-python/src/main/python/gremlin_python/driver/protocol.py b/gremlin-python/src/main/python/gremlin_python/driver/protocol.py
index 6aaa9b8efe..6105ad9faa 100644
--- a/gremlin-python/src/main/python/gremlin_python/driver/protocol.py
+++ b/gremlin-python/src/main/python/gremlin_python/driver/protocol.py
@@ -206,35 +206,18 @@ class GremlinServerHTTPProtocol(AbstractBaseProtocol):
     def connection_made(self, transport):
         super(GremlinServerHTTPProtocol, self).connection_made(transport)
 
-    # Transforms request message into string
     def write(self, request_id, request_message):
-        payload = {
-            'requestId': request_id
-        }
-
-        gremlinArgs = request_message.args['gremlin']
-        useBytecode = isinstance(gremlinArgs, Bytecode)
-
-        # translate bytecode into scripts
-        if useBytecode:
-            request_message.args['gremlin'] = Translator().of('g').translate(gremlinArgs)
-            payload['op'] = 'bytecode'
-
-        # add all args into the payload
-        for k, v in request_message.args.items():
-            payload[k] = v
-
-        json_data = json.dumps(payload)
 
         basic_auth = {}
         if self._username and self._password:
             basic_auth['username'] = self._username
             basic_auth['password'] = self._password
 
+        content_type = str(self._message_serializer.version, encoding='utf-8')
         message = {
-            'headers': {'CONTENT-TYPE': 'application/json',
-                        'ACCEPT': str(self._message_serializer.version, encoding='utf-8')},
-            'payload': json_data,
+            'headers': {'CONTENT-TYPE': content_type,
+                        'ACCEPT': content_type},
+            'payload': self._message_serializer.serialize_message(request_id, request_message),
             'auth': basic_auth
         }
 
@@ -247,13 +230,6 @@ class GremlinServerHTTPProtocol(AbstractBaseProtocol):
             raise GremlinServerError({'code': 500,
                                       'message': 'Server disconnected - please try to reconnect', 'attributes': {}})
 
-        # if a request query cannot be compiled by Gremlin Server, the HTTP handler will send back the exception in
-        # json string without a requestId
-        if 'message' in message and 'requestId' not in message:
-            log.error("\r\nReceived error message from server '%s'", str(message))
-            raise GremlinServerError({'code': 400,
-                                      'message': message, 'attributes': {}})
-
         message = self._message_serializer.deserialize_message(message)
         request_id = message['requestId']
         result_set = results_dict[request_id] if request_id in results_dict else ResultSet(None, None)
diff --git a/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection_http.py b/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection_http.py
index 1c7f1aa8e7..10f58fd056 100644
--- a/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection_http.py
+++ b/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection_http.py
@@ -31,20 +31,10 @@ from gremlin_python.process.strategies import SubgraphStrategy, SeedStrategy
 from gremlin_python.structure.io.util import HashableDict
 from gremlin_python.driver.serializer import GraphSONSerializersV2d0
 
-"""
-Due to the limitation of relying on python translator for sending groovy scripts over HTTP, certain tests are omitted.
-
-Some known limitation with current HTTP via groovy script translator:
-- Any known limitation with groovy script will be inherited, such as injection of 2 items with the second null, 
-    e.g. g.inject(null, null), will lead to NPE
-- Any server side NPE will cause hanging with no response, need to check server HTTP handlers
-- HTTPS only works with HttpChannelizer, need to check how WsAndHttpChannelizer handles SSL
-- No transaction support
-"""
-
 gremlin_server_url_http = os.environ.get('GREMLIN_SERVER_URL_HTTP', 'http://localhost:{}/')
 test_no_auth_http_url = gremlin_server_url_http.format(45940)
 
+
 class TestDriverRemoteConnectionHttp(object):
     def test_traversals(self, remote_connection_http):
         statics.load_statics(globals())
@@ -164,10 +154,12 @@ class TestDriverRemoteConnectionHttp(object):
         g = traversal().withRemote(remote_connection_http)
 
         assert 24.0 == g.withSack(1.0, lambda: ("x -> x + 1", "gremlin-groovy")).V().both().sack().sum_().next()
-        assert 24.0 == g.withSack(lambda: ("{1.0d}", "gremlin-groovy"), lambda: ("x -> x + 1", "gremlin-groovy")).V().both().sack().sum_().next()
+        assert 24.0 == g.withSack(lambda: ("{1.0d}", "gremlin-groovy"),
+                                  lambda: ("x -> x + 1", "gremlin-groovy")).V().both().sack().sum_().next()
 
         assert 48.0 == g.withSack(1.0, lambda: ("x, y ->  x + y + 1", "gremlin-groovy")).V().both().sack().sum_().next()
-        assert 48.0 == g.withSack(lambda: ("{1.0d}", "gremlin-groovy"), lambda: ("x, y ->  x + y + 1", "gremlin-groovy")).V().both().sack().sum_().next()
+        assert 48.0 == g.withSack(lambda: ("{1.0d}", "gremlin-groovy"),
+                                  lambda: ("x, y ->  x + y + 1", "gremlin-groovy")).V().both().sack().sum_().next()
 
     def test_strategies(self, remote_connection_http):
         statics.load_statics(globals())
@@ -220,6 +212,7 @@ class TestDriverRemoteConnectionHttp(object):
         assert 5 == t.clone().limit(5).count().next()
         assert 10 == t.clone().limit(10).count().next()
 
+
     """
     # The WsAndHttpChannelizer somehow does not distinguish the ssl handlers so authenticated https remote connection
     # only work with HttpChannelizer that is currently not in the testing set up, thus this is commented out for now
diff --git a/gremlin-server/conf/gremlin-server-rest-modern.yaml b/gremlin-server/conf/gremlin-server-rest-modern.yaml
index a34b14e453..66928acba0 100644
--- a/gremlin-server/conf/gremlin-server-rest-modern.yaml
+++ b/gremlin-server/conf/gremlin-server-rest-modern.yaml
@@ -28,7 +28,10 @@ scriptEngines: {
                org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]},
                org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/generate-modern.groovy]}}}}
 serializers:
-  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3d0] }}         # application/json
+  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3d0] }}          # application/vnd.gremlin-v3.0+json
+  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1 }                                                                                                           # application/vnd.graphbinary-v1.0
+  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1, config: { serializeResultToString: true }}                                                                 # application/vnd.graphbinary-v1.0-stringd
+
 metrics: {
   slf4jReporter: {enabled: true, interval: 180000}}
 strictTransactionManagement: false
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpGremlinEndpointHandler.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpGremlinEndpointHandler.java
index 4aaaa68fd7..59b179c84a 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpGremlinEndpointHandler.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpGremlinEndpointHandler.java
@@ -19,38 +19,43 @@
 package org.apache.tinkerpop.gremlin.server.handler;
 
 import com.codahale.metrics.Timer;
-import io.netty.handler.codec.http.*;
-import org.apache.tinkerpop.gremlin.driver.Tokens;
-import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
-import org.apache.tinkerpop.gremlin.driver.ser.SerializationException;
-import org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser;
-import org.apache.tinkerpop.gremlin.server.util.TextPlainMessageSerializer;
-import org.javatuples.Pair;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.tinkerpop.gremlin.util.ExceptionHelper;
-import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
-import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
-import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
-import org.apache.tinkerpop.gremlin.driver.ser.MessageTextSerializer;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+import io.netty.handler.codec.TooLongFrameException;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpUtil;
+import io.netty.util.ReferenceCountUtil;
 import org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor;
+import org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
 import org.apache.tinkerpop.gremlin.server.GraphManager;
 import org.apache.tinkerpop.gremlin.server.GremlinServer;
 import org.apache.tinkerpop.gremlin.server.Settings;
 import org.apache.tinkerpop.gremlin.server.auth.AuthenticatedUser;
 import org.apache.tinkerpop.gremlin.server.util.MetricManager;
+import org.apache.tinkerpop.gremlin.server.util.TextPlainMessageSerializer;
 import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.util.ExceptionHelper;
+import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
+import org.apache.tinkerpop.gremlin.driver.Tokens;
 import org.apache.tinkerpop.gremlin.util.function.FunctionUtils;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.ChannelPromise;
-import io.netty.handler.codec.TooLongFrameException;
-import io.netty.util.ReferenceCountUtil;
+import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
+import org.apache.tinkerpop.gremlin.driver.ser.MessageTextSerializer;
+import org.apache.tinkerpop.gremlin.driver.ser.SerializationException;
+import org.javatuples.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.script.Bindings;
 import javax.script.SimpleBindings;
@@ -71,12 +76,12 @@ import java.util.stream.Stream;
 import static com.codahale.metrics.MetricRegistry.name;
 import static io.netty.handler.codec.http.HttpMethod.GET;
 import static io.netty.handler.codec.http.HttpMethod.POST;
+import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
+import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
+import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
 import static io.netty.handler.codec.http.HttpResponseStatus.METHOD_NOT_ALLOWED;
 import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
 import static io.netty.handler.codec.http.HttpResponseStatus.OK;
-import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
-import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
-import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
 import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
 
 /**
@@ -142,9 +147,9 @@ public class HttpGremlinEndpointHandler extends ChannelInboundHandlerAdapter {
 
             final RequestMessage requestMessage;
             try {
-                requestMessage = HttpHandlerUtil.getRequestMessageFromHttpRequest(req);
-            } catch (IllegalArgumentException iae) {
-                HttpHandlerUtil.sendError(ctx, BAD_REQUEST, iae.getMessage(), keepAlive);
+                requestMessage = HttpHandlerUtil.getRequestMessageFromHttpRequest(req, serializers);
+            } catch (IllegalArgumentException|SerializationException ex) {
+                HttpHandlerUtil.sendError(ctx, BAD_REQUEST, ex.getMessage(), keepAlive);
                 ReferenceCountUtil.release(msg);
                 return;
             }
@@ -215,6 +220,7 @@ public class HttpGremlinEndpointHandler extends ChannelInboundHandlerAdapter {
                 // is ready to be written as a ByteBuf directly to the response.  nothing should be blocking here.
                 final CompletableFuture<Object> evalFuture = gremlinExecutor.eval(
                         requestMessage.getArg(Tokens.ARGS_GREMLIN), requestMessage.getArg(Tokens.ARGS_LANGUAGE), bindings,
+                        requestMessage.getArgOrDefault(Tokens.ARGS_EVAL_TIMEOUT, null),
                         FunctionUtils.wrapFunction(o -> {
                             // stopping the timer here is roughly equivalent to where the timer would have been stopped for
                             // this metric in other contexts.  we just want to measure eval time not serialization time.
@@ -228,7 +234,7 @@ public class HttpGremlinEndpointHandler extends ChannelInboundHandlerAdapter {
                             // results to Traverser so that GLVs can handle the results. don't quite get the same
                             // benefit here because the bulk has to be 1 since we've already resolved the result,
                             // but at least http is compatible
-                            final List<Object> results = requestMessage.getProcessor().equals(Tokens.OPS_BYTECODE) ?
+                            final List<Object> results = requestMessage.getOp().equals(Tokens.OPS_BYTECODE) ?
                                     (List<Object>) IteratorUtils.asList(o).stream().map(r -> new DefaultRemoteTraverser<Object>(r, 1)).collect(Collectors.toList()) :
                                     IteratorUtils.asList(o);
                             final ResponseMessage responseMessage = ResponseMessage.build(requestMessage.getRequestId())
@@ -242,7 +248,7 @@ public class HttpGremlinEndpointHandler extends ChannelInboundHandlerAdapter {
                             attemptCommit(requestMessage.getArg(Tokens.ARGS_ALIASES), graphManager, settings.strictTransactionManagement);
 
                             try {
-                                return Unpooled.wrappedBuffer(serializer.getValue1().serializeResponseAsString(responseMessage, ctx.alloc()).getBytes(UTF8));
+                                return Unpooled.wrappedBuffer(serializer.getValue1().serializeResponseAsBinary(responseMessage, ctx.alloc()));
                             } catch (Exception ex) {
                                 logger.warn(String.format("Error during serialization for %s", responseMessage), ex);
 
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpHandlerUtil.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpHandlerUtil.java
index f25080a5cc..748d9d39ae 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpHandlerUtil.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpHandlerUtil.java
@@ -19,6 +19,7 @@
 package org.apache.tinkerpop.gremlin.server.handler;
 
 import com.codahale.metrics.Meter;
+import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
@@ -26,11 +27,13 @@ import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.http.*;
 import io.netty.util.CharsetUtil;
 import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
 import org.apache.tinkerpop.gremlin.driver.Tokens;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.server.GremlinServer;
 import org.apache.tinkerpop.gremlin.server.op.standard.StandardOpProcessor;
 import org.apache.tinkerpop.gremlin.server.util.MetricManager;
+import org.apache.tinkerpop.gremlin.driver.ser.SerializationException;
 import org.apache.tinkerpop.shaded.jackson.databind.JsonNode;
 import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
 import org.apache.tinkerpop.shaded.jackson.databind.node.ArrayNode;
@@ -39,7 +42,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -50,6 +55,7 @@ import java.util.UUID;
 import static com.codahale.metrics.MetricRegistry.name;
 import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
 import static io.netty.handler.codec.http.HttpMethod.GET;
+import static io.netty.handler.codec.http.HttpMethod.POST;
 import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
 
 /**
@@ -70,10 +76,55 @@ public class HttpHandlerUtil {
 
     /**
      * Convert a http request into a {@link RequestMessage}.
+     * There are 2 payload types options here.
+     * 1.
+     *     existing https://tinkerpop.apache.org/docs/current/reference/#connecting-via-http
+     *     intended to use with curl, postman, etc. by users
+     *     both GET and POST
+     *     Content-Type header can be empty or application/json
+     *     Accept header can be any, most useful can be application/json, text/plain, application/vnd.gremlin-v3.0+json and application/vnd.gremlin-v3.0+json;types=false
+     *     Request body example: { "gremlin": "g.V()" }
+     * 2.
+     *     experimental payload with serialized RequestMessage
+     *     intended for drivers/GLV's. Support both gremlin and bytecode queries.
+     *     only POST
+     *     Content-Type is defined by used serializer, expected type GraphSON application/vnd.gremlin-v3.0+json or GraphBinary application/vnd.graphbinary-v1.0. Untyped GraphSON is not supported, it can't deserialize bytecode
+     *     Accept header can be any.
+     *     Request body contains serialized RequestMessage
      */
-    public static RequestMessage getRequestMessageFromHttpRequest(final FullHttpRequest request) {
-        final String contentType = Optional.ofNullable(request.headers().get(HttpHeaderNames.CONTENT_TYPE)).orElse("application/json");
+    public static RequestMessage getRequestMessageFromHttpRequest(final FullHttpRequest request,
+                                                                  Map<String, MessageSerializer<?>> serializers) throws SerializationException {
+        final String contentType = request.headers().get(HttpHeaderNames.CONTENT_TYPE);
+
+        if (request.method() == POST && contentType != null && !contentType.equals("application/json") && serializers.containsKey(contentType)) {
+            final MessageSerializer<?> serializer = serializers.get(contentType);
+
+            final ByteBuf buffer = request.content();
+
+            // additional validation for header
+            final int first = buffer.readByte();
+            // payload can be plain json or can start with additional header with content type.
+            // if first character is not "{" (0x7b) then need to verify is correct serializer selected.
+            if (first != 0x7b) {
+                final byte[] bytes = new byte[first];
+                buffer.readBytes(bytes);
+                final String mimeType = new String(bytes, StandardCharsets.UTF_8);
+
+                if (Arrays.stream(serializer.mimeTypesSupported()).noneMatch(t -> t.equals(mimeType)))
+                    throw new IllegalArgumentException("Mime type mismatch. Value in content-type header is not equal payload header.");
+            } else
+                buffer.resetReaderIndex();
+
+            return serializer.deserializeRequest(buffer);
+        }
 
+        return getRequestMessageFromHttpRequest(request);
+    }
+
+    /**
+     * Convert a http request into a {@link RequestMessage}.
+     */
+    public static RequestMessage getRequestMessageFromHttpRequest(final FullHttpRequest request) {
         // default is just the StandardOpProcessor which maintains compatibility with older versions which only
         // processed scripts.
         final RequestMessage.Builder msgBuilder = RequestMessage.build(StandardOpProcessor.OP_PROCESSOR_NAME);
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializer.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializer.java
index 81b3662b96..ea0332de48 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializer.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializer.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.server.util;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufAllocator;
+import io.netty.util.CharsetUtil;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.driver.ser.MessageTextSerializer;
@@ -42,7 +43,11 @@ public class TextPlainMessageSerializer implements MessageTextSerializer<Functio
 
     @Override
     public ByteBuf serializeResponseAsBinary(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException {
-        throw new UnsupportedOperationException("text/plain does not produce binary");
+        final String payload = serializeResponseAsString(responseMessage, allocator);
+        final ByteBuf encodedMessage = allocator.buffer(payload.length());
+        encodedMessage.writeCharSequence(payload, CharsetUtil.UTF_8);
+
+        return encodedMessage;
     }
 
     @Override
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java
index b9c17d27cc..6d01dd45e6 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java
@@ -18,7 +18,10 @@
  */
 package org.apache.tinkerpop.gremlin.server;
 
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
 import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.http.HttpEntity;
 import org.apache.http.HttpHeaders;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1;
@@ -47,6 +50,7 @@ import org.apache.tinkerpop.shaded.jackson.databind.JsonNode;
 import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
 import org.junit.Test;
 
+import java.io.IOException;
 import java.time.Instant;
 import java.util.Base64;
 import java.util.HashMap;
@@ -401,9 +405,9 @@ public class GremlinServerHttpIntegrateTest extends AbstractGremlinServerIntegra
         try (final CloseableHttpResponse response = httpclient.execute(httpget)) {
             assertEquals(200, response.getStatusLine().getStatusCode());
             assertEquals(mime, response.getEntity().getContentType().getValue());
-            final String base64 = EntityUtils.toString(response.getEntity());
+
             final GraphBinaryMessageSerializerV1 serializer = new GraphBinaryMessageSerializerV1(TypeSerializerRegistry.INSTANCE);
-            final ResponseMessage msg = serializer.deserializeResponse(base64);
+            final ResponseMessage msg = serializer.deserializeResponse(toByteBuf(response.getEntity()));
             final List<Object> data = (List<Object>) msg.getResult().getData();
             assertEquals(6, data.size());
             for (Object o : data) {
@@ -422,9 +426,9 @@ public class GremlinServerHttpIntegrateTest extends AbstractGremlinServerIntegra
         try (final CloseableHttpResponse response = httpclient.execute(httpget)) {
             assertEquals(200, response.getStatusLine().getStatusCode());
             assertEquals(mime, response.getEntity().getContentType().getValue());
-            final String base64 = EntityUtils.toString(response.getEntity());
+
             final GraphBinaryMessageSerializerV1 serializer = new GraphBinaryMessageSerializerV1(TypeSerializerRegistry.INSTANCE);
-            final ResponseMessage msg = serializer.deserializeResponse(base64);
+            final ResponseMessage msg = serializer.deserializeResponse(toByteBuf(response.getEntity()));
             final List<Object> data = (List<Object>) msg.getResult().getData();
             assertEquals(6, data.size());
             for (Object o : data) {
@@ -1070,4 +1074,12 @@ public class GremlinServerHttpIntegrateTest extends AbstractGremlinServerIntegra
 
         threadPool.shutdown();
     }
+
+    private static ByteBuf toByteBuf(final HttpEntity httpEntity) throws IOException {
+        final byte[] asArray = new byte[(int)httpEntity.getContentLength()];
+
+        httpEntity.getContent().read(asArray);
+
+        return Unpooled.wrappedBuffer(asArray);
+    }
 }
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/HttpDriverIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/HttpDriverIntegrateTest.java
index 1d0fbc6fb5..528e325d8a 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/HttpDriverIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/HttpDriverIntegrateTest.java
@@ -18,18 +18,23 @@
  */
 package org.apache.tinkerpop.gremlin.server;
 
+import io.netty.handler.codec.EncoderException;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.tinkerpop.gremlin.driver.Channelizer;
 import org.apache.tinkerpop.gremlin.driver.Client;
 import org.apache.tinkerpop.gremlin.driver.Cluster;
 import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection;
-import org.apache.tinkerpop.gremlin.driver.ser.Serializers;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 import org.apache.tinkerpop.gremlin.server.channel.HttpChannelizer;
 import org.apache.tinkerpop.gremlin.structure.Transaction;
+import org.apache.tinkerpop.gremlin.driver.ser.Serializers;
 import org.junit.Test;
 
 import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.core.AllOf.allOf;
+import static org.hamcrest.core.StringContains.containsString;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
@@ -81,7 +86,8 @@ public class HttpDriverIntegrateTest extends AbstractGremlinServerIntegrationTes
                 .create();
         try {
             final GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster));
-            assertEquals("2", g.inject("2").toList().get(0));
+            final String result = g.inject("2").toList().get(0);
+            assertEquals("2", result);
         } catch (Exception ex) {
             throw ex;
         } finally {
@@ -89,6 +95,24 @@ public class HttpDriverIntegrateTest extends AbstractGremlinServerIntegrationTes
         }
     }
 
+    @Test
+    public void shouldGetErrorForBytecodeWithUntypedGraphSON() throws Exception {
+        final Cluster cluster = TestClientFactory.build()
+                .channelizer(Channelizer.HttpChannelizer.class)
+                .serializer(Serializers.GRAPHSON_V2D0_UNTYPED)
+                .create();
+        try {
+            final GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster));
+            g.inject("2").toList();
+            fail("Exception expected");
+        } catch (EncoderException ex) {
+            assertThat(ex.getMessage(), allOf(containsString("An error occurred during serialization of this request"),
+                    containsString("it could not be sent to the server - Reason: only GraphSON3 and GraphBinary recommended for serialization of Bytecode requests, but used org.apache.tinkerpop.gremlin.")));
+        } finally {
+            cluster.close();
+        }
+    }
+
     @Test
     public void shouldSubmitBytecodeWithGraphBinary() throws Exception {
         final Cluster cluster = TestClientFactory.build()
@@ -97,7 +121,8 @@ public class HttpDriverIntegrateTest extends AbstractGremlinServerIntegrationTes
                 .create();
         try {
             final GraphTraversalSource g = traversal().withRemote(DriverRemoteConnection.using(cluster));
-            assertEquals("2", g.inject("2").toList().get(0));
+            final String result = g.inject("2").toList().get(0);
+            assertEquals("2", result);
         } catch (Exception ex) {
             throw ex;
         } finally {
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/handler/HttpHandlerUtilTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/handler/HttpHandlerUtilTest.java
new file mode 100644
index 0000000000..6b911d9b2e
--- /dev/null
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/handler/HttpHandlerUtilTest.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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 org.apache.tinkerpop.gremlin.server.handler;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import io.netty.handler.codec.http.DefaultHttpHeaders;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpHeaders;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpVersion;
+import io.netty.handler.codec.http.QueryStringEncoder;
+import io.netty.util.CharsetUtil;
+import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
+import org.apache.tinkerpop.gremlin.driver.Tokens;
+import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
+import org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1;
+import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0;
+import org.apache.tinkerpop.gremlin.driver.ser.SerTokens;
+import org.apache.tinkerpop.gremlin.driver.ser.SerializationException;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.samePropertyValuesAs;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class HttpHandlerUtilTest {
+
+    private final ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
+    private final GraphBinaryMessageSerializerV1 graphBinarySerializer = new GraphBinaryMessageSerializerV1();
+    public final GraphSONMessageSerializerV3d0 graphSONSerializer = new GraphSONMessageSerializerV3d0();
+
+    @Test
+    public void shouldFailWhenIncorrectSerializerUsed() throws SerializationException {
+        final RequestMessage request = RequestMessage.build(Tokens.OPS_BYTECODE)
+                .processor("traversal")
+                .overrideRequestId(UUID.randomUUID())
+                .addArg(Tokens.ARGS_GREMLIN, "g.V()")
+                .create();
+
+        final ByteBuf buffer = graphSONSerializer.serializeRequestAsBinary(request, allocator);
+
+        final HttpHeaders headers = new DefaultHttpHeaders();
+        headers.add(HttpHeaderNames.CONTENT_TYPE, SerTokens.MIME_GRAPHBINARY_V1D0);
+
+        final FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "some uri",
+                buffer, headers, new DefaultHttpHeaders());
+
+        final Map<String, MessageSerializer<?>> serializers = new HashMap<>();
+        serializers.put(SerTokens.MIME_GRAPHBINARY_V1D0, graphBinarySerializer);
+
+        try {
+            HttpHandlerUtil.getRequestMessageFromHttpRequest(httpRequest, serializers);
+            fail("SerializationException expected");
+        } catch (IllegalArgumentException ex) {
+            assertEquals("Mime type mismatch. Value in content-type header is not equal payload header.", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldCorrectlyDeserializeRequestMessage() throws SerializationException {
+        final RequestMessage request = RequestMessage.build(Tokens.OPS_BYTECODE)
+                .processor("traversal")
+                .overrideRequestId(UUID.randomUUID())
+                .addArg(Tokens.ARGS_GREMLIN, "g.V()")
+                .create();
+
+        final ByteBuf buffer = graphBinarySerializer.serializeRequestAsBinary(request, allocator);
+
+        final HttpHeaders headers = new DefaultHttpHeaders();
+        headers.add(HttpHeaderNames.CONTENT_TYPE, SerTokens.MIME_GRAPHBINARY_V1D0);
+
+        final FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "some uri",
+                buffer, headers, new DefaultHttpHeaders());
+
+        final Map<String, MessageSerializer<?>> serializers = new HashMap<>();
+        serializers.put(SerTokens.MIME_GRAPHBINARY_V1D0, graphBinarySerializer);
+
+        final RequestMessage deserialized = HttpHandlerUtil.getRequestMessageFromHttpRequest(httpRequest, serializers);
+        assertThat(request, samePropertyValuesAs(deserialized));
+    }
+
+    @Test
+    public void shouldCorrectlyDeserializeGremlinFromPostRequest() throws SerializationException {
+        final String gremlin = "g.V().hasLabel('person')";
+        final UUID requestId = UUID.randomUUID();
+        final ByteBuf buffer = allocator.buffer();
+        buffer.writeCharSequence("{ \"gremlin\": \"" + gremlin +
+                        "\", \"requestId\": \"" + requestId +
+                        "\", \"language\":  \"gremlin-groovy\"}",
+                CharsetUtil.UTF_8);
+
+        final HttpHeaders headers = new DefaultHttpHeaders();
+        headers.add(HttpHeaderNames.CONTENT_TYPE, SerTokens.MIME_JSON);
+
+        final FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "some uri",
+                buffer, headers, new DefaultHttpHeaders());
+
+        final Map<String, MessageSerializer<?>> serializers = new HashMap<>();
+        serializers.put(SerTokens.MIME_GRAPHBINARY_V1D0, graphBinarySerializer);
+
+        final RequestMessage deserialized = HttpHandlerUtil.getRequestMessageFromHttpRequest(httpRequest, serializers);
+        assertEquals(gremlin, deserialized.getArgs().get(Tokens.ARGS_GREMLIN));
+        assertEquals(requestId, deserialized.getRequestId());
+        assertEquals("gremlin-groovy", deserialized.getArg(Tokens.ARGS_LANGUAGE));
+    }
+
+    @Test
+    public void shouldCorrectlyDeserializeGremlinFromGetRequest() throws SerializationException {
+        final String gremlin = "g.V().hasLabel('person')";
+        final UUID requestId = UUID.randomUUID();
+        final ByteBuf buffer = allocator.buffer();
+
+        final List<String> headerOptions = Arrays.asList("", null, SerTokens.MIME_JSON, "some invalid value");
+
+        for (final String contentTypeValue : headerOptions) {
+            final HttpHeaders headers = new DefaultHttpHeaders();
+            if (contentTypeValue != null) {
+                headers.add(HttpHeaderNames.CONTENT_TYPE, contentTypeValue);
+            }
+
+            final QueryStringEncoder encoder = new QueryStringEncoder("/");
+            encoder.addParam("gremlin", gremlin);
+            encoder.addParam("requestId", requestId.toString());
+            encoder.addParam("language", "gremlin-groovy");
+
+            final FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
+                    encoder.toString(), buffer, headers, new DefaultHttpHeaders());
+
+            final Map<String, MessageSerializer<?>> serializers = new HashMap<>();
+            serializers.put(SerTokens.MIME_GRAPHBINARY_V1D0, graphBinarySerializer);
+
+            final RequestMessage deserialized = HttpHandlerUtil.getRequestMessageFromHttpRequest(httpRequest, serializers);
+            assertEquals(gremlin, deserialized.getArgs().get(Tokens.ARGS_GREMLIN));
+            assertEquals(requestId, deserialized.getRequestId());
+            assertEquals("gremlin-groovy", deserialized.getArg(Tokens.ARGS_LANGUAGE));
+        }
+    }
+}