You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2023/05/18 19:31:04 UTC

[tinkerpop] branch TINKERPOP-2947 updated (64e06042db -> d0c1346b30)

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

spmallette pushed a change to branch TINKERPOP-2947
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


 discard 64e06042db TINKERPOP-2947 Added text/plain and text serialization for graphbinary for HTTP endpoint
     new d0c1346b30 TINKERPOP-2947 Added text/plain and text serialization for graphbinary for HTTP endpoint

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (64e06042db)
            \
             N -- N -- N   refs/heads/TINKERPOP-2947 (d0c1346b30)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGELOG.asciidoc                                 |  3 ++-
 docs/src/reference/gremlin-applications.asciidoc   | 15 +++++++++++++++
 docs/src/upgrade/release-3.6.x.asciidoc            | 17 +++++++++++++++++
 .../server/GremlinServerHttpIntegrateTest.java     | 22 ++++++++++++++++++++++
 4 files changed, 56 insertions(+), 1 deletion(-)


[tinkerpop] 01/01: TINKERPOP-2947 Added text/plain and text serialization for graphbinary for HTTP endpoint

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch TINKERPOP-2947
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit d0c1346b308934b7fab442c34ec6dcf433d098c7
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Wed May 10 13:16:47 2023 -0400

    TINKERPOP-2947 Added text/plain and text serialization for graphbinary for HTTP endpoint
---
 CHANGELOG.asciidoc                                 |  3 +-
 docs/src/reference/gremlin-applications.asciidoc   | 15 ++++
 docs/src/upgrade/release-3.6.x.asciidoc            | 17 ++++
 .../handler/WebSocketGremlinRequestEncoder.java    |  2 +-
 .../driver/ser/GraphBinaryMessageSerializerV1.java | 42 +++++++++-
 .../driver/ser/GraphSONMessageSerializerV1d0.java  |  5 +-
 .../driver/ser/GraphSONMessageSerializerV2d0.java  |  5 +-
 .../driver/ser/GraphSONMessageSerializerV3d0.java  |  5 +-
 .../gremlin/driver/ser/MessageTextSerializer.java  |  5 +-
 .../gremlin/driver/TestWSGremlinInitializer.java   |  9 +-
 .../ser/GraphSONMessageSerializerV1d0Test.java     | 30 +++----
 .../ser/GraphSONMessageSerializerV2d0Test.java     | 26 +++---
 .../binary/GraphBinaryMessageSerializerV1Test.java | 37 +++++++++
 .../gremlin/server/handler/AbstractSession.java    |  2 +-
 .../handler/GremlinResponseFrameEncoder.java       |  6 +-
 .../server/handler/HttpGremlinEndpointHandler.java | 15 +++-
 .../gremlin/server/op/AbstractOpProcessor.java     |  2 +-
 .../server/util/TextPlainMessageSerializer.java    | 97 ++++++++++++++++++++++
 .../server/GremlinServerHttpIntegrateTest.java     | 70 +++++++++++++++-
 .../util/TextPlainMessageSerializerTest.java       | 48 +++++++++++
 20 files changed, 393 insertions(+), 48 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index c5fb2d0db7..6da9aa2368 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -25,7 +25,8 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 
 This release also includes changes from <<release-3-5-7, 3.5.7>>.
 
-
+* Added `text/plain` MIME type to the HTTP endpoint to return a Gremlin Console-like representation of the data.
+* Added GraphBinary serialization option to the HTTP endpoint.
 
 [[release-3-6-3]]
 === TinkerPop 3.6.3 (Release Date: May 1, 2023)
diff --git a/docs/src/reference/gremlin-applications.asciidoc b/docs/src/reference/gremlin-applications.asciidoc
index 8db0b6bca5..2ee5c214fd 100644
--- a/docs/src/reference/gremlin-applications.asciidoc
+++ b/docs/src/reference/gremlin-applications.asciidoc
@@ -901,6 +901,21 @@ Gremlin script.  The caveat is that these arguments will always be treated as `S
 types are preserved or to pass complex objects such as lists or maps, use `POST` which will at least support the
 allowed JSON data types.
 
+Passing the `Accept` header with a valid MIME type will trigger the server to return the result in a particular format.
+Note that in addition to the formats available given the server's `serializers` configuration, there is also a basic
+`text/plain` format which produces a text representation of results similar to the Gremlin Console:
+
+[source,text]
+----
+$ curl -H "Accept:text/plain" -X POST -d "{\"gremlin\":\"g.V()\"}" "http://localhost:8182"
+==>v[1]
+==>v[2]
+==>v[3]
+==>v[4]
+==>v[5]
+==>v[6]
+----
+
 Finally, as Gremlin Server can host multiple `ScriptEngine` instances (e.g. `gremlin-groovy`, `nashorn`), it is
 possible to define the language to utilize to process the request:
 
diff --git a/docs/src/upgrade/release-3.6.x.asciidoc b/docs/src/upgrade/release-3.6.x.asciidoc
index 184ed9734b..00e4c5955d 100644
--- a/docs/src/upgrade/release-3.6.x.asciidoc
+++ b/docs/src/upgrade/release-3.6.x.asciidoc
@@ -29,7 +29,24 @@ Please see the link:https://github.com/apache/tinkerpop/blob/3.6.4/CHANGELOG.asc
 
 === Upgrading for Users
 
+==== HTTP Plain Text
 
+A `text/plain` MIME type has been added to the HTTP endpoint to return Gremlin Console formatted results in plain text.
+This format can be helpful for a variety of reasons. Reading JSON formatted results can be difficult sometimes and
+`text/plain` is a more simple, readable representation for when that is helpful.
+
+[source,text]
+----
+$ curl -H "Accept:text/plain" -X POST -d "{\"gremlin\":\"g.V()\"}" "http://localhost:8182"
+==>v[1]
+==>v[2]
+==>v[3]
+==>v[4]
+==>v[5]
+==>v[6]
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2947[TINKERPOP-2947]
 
 == TinkerPop 3.6.3
 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketGremlinRequestEncoder.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketGremlinRequestEncoder.java
index 92dedab8ac..8bb8173660 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketGremlinRequestEncoder.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/handler/WebSocketGremlinRequestEncoder.java
@@ -54,7 +54,7 @@ public final class WebSocketGremlinRequestEncoder extends MessageToMessageEncode
                 objects.add(new BinaryWebSocketFrame(encodedMessage));
             } else {
                 final MessageTextSerializer<?> textSerializer = (MessageTextSerializer<?>) serializer;
-                objects.add(new TextWebSocketFrame(textSerializer.serializeRequestAsString(requestMessage)));
+                objects.add(new TextWebSocketFrame(textSerializer.serializeRequestAsString(requestMessage, channelHandlerContext.alloc())));
             }
         } catch (Exception ex) {
             throw new ResponseException(ResponseStatusCode.REQUEST_ERROR_SERIALIZATION, String.format(
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryMessageSerializerV1.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryMessageSerializerV1.java
index 2eea896d0e..1c7a69a238 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryMessageSerializerV1.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryMessageSerializerV1.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.driver.ser;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.Unpooled;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryIo;
@@ -36,6 +37,7 @@ import org.javatuples.Pair;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
+import java.util.Base64;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -43,7 +45,7 @@ import java.util.stream.Collectors;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
-public class GraphBinaryMessageSerializerV1 extends AbstractMessageSerializer<GraphBinaryMapper> {
+public class GraphBinaryMessageSerializerV1 extends AbstractMessageSerializer<GraphBinaryMapper> implements MessageTextSerializer<GraphBinaryMapper> {
 
     public static final String TOKEN_CUSTOM = "custom";
     public static final String TOKEN_BUILDER = "builder";
@@ -52,6 +54,9 @@ public class GraphBinaryMessageSerializerV1 extends AbstractMessageSerializer<Gr
     private static final String MIME_TYPE = SerTokens.MIME_GRAPHBINARY_V1D0;
     private static final String MIME_TYPE_STRINGD = SerTokens.MIME_GRAPHBINARY_V1D0 + "-stringd";
 
+    private static final Base64.Encoder base64Encoder = Base64.getEncoder();
+    private static final Base64.Decoder base64Decoder = Base64.getDecoder();
+
     private byte[] header = MIME_TYPE.getBytes(UTF_8);
     private boolean serializeToString = false;
     private GraphBinaryReader reader;
@@ -185,6 +190,41 @@ public class GraphBinaryMessageSerializerV1 extends AbstractMessageSerializer<Gr
         return new String[]{serializeToString ? MIME_TYPE_STRINGD : MIME_TYPE};
     }
 
+    @Override
+    public String serializeResponseAsString(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException {
+        final ByteBuf bb = serializeResponseAsBinary(responseMessage, allocator);
+        return base64Encoder.encodeToString(convertToBytes(bb));
+    }
+
+    @Override
+    public String serializeRequestAsString(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException {
+        final ByteBuf bb = serializeRequestAsBinary(requestMessage, allocator);
+        return base64Encoder.encodeToString(convertToBytes(bb));
+    }
+
+    @Override
+    public RequestMessage deserializeRequest(final String msg) throws SerializationException {
+        return deserializeRequest(convertToByteBuf(msg));
+    }
+
+    @Override
+    public ResponseMessage deserializeResponse(final String msg) throws SerializationException {
+        return deserializeResponse(convertToByteBuf(msg));
+    }
+
+    private byte[] convertToBytes(final ByteBuf bb) {
+        byte[] bytes = new byte[bb.readableBytes()];
+        bb.getBytes(bb.readerIndex(), bytes);
+        return bytes;
+    }
+
+    private ByteBuf convertToByteBuf(final String msg) {
+        final byte[] b = base64Decoder.decode(msg);
+        final ByteBuf bb = Unpooled.buffer(b.length);
+        bb.writeBytes(b);
+        return bb;
+    }
+
     private void addCustomClasses(final Map<String, Object> config, final TypeSerializerRegistry.Builder builder) {
         final List<String> classNameList = getListStringFromConfig(TOKEN_CUSTOM, config);
 
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0.java
index d3a15f52c6..60a24868e4 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.driver.ser;
 
+import io.netty.buffer.ByteBufAllocator;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
@@ -95,7 +96,7 @@ public final class GraphSONMessageSerializerV1d0 extends AbstractGraphSONMessage
     }
 
     @Override
-    public String serializeResponseAsString(final ResponseMessage responseMessage) throws SerializationException {
+    public String serializeResponseAsString(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException {
         try {
             return mapper.writeValueAsString(responseMessage);
         } catch (Exception ex) {
@@ -115,7 +116,7 @@ public final class GraphSONMessageSerializerV1d0 extends AbstractGraphSONMessage
     }
 
     @Override
-    public String serializeRequestAsString(final RequestMessage requestMessage) throws SerializationException {
+    public String serializeRequestAsString(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException {
         try {
             return mapper.writeValueAsString(requestMessage);
         } catch (Exception ex) {
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0.java
index 6717a00192..3442205923 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.driver.ser;
 
+import io.netty.buffer.ByteBufAllocator;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
@@ -110,7 +111,7 @@ public final class GraphSONMessageSerializerV2d0 extends AbstractGraphSONMessage
     }
 
     @Override
-    public String serializeResponseAsString(final ResponseMessage responseMessage) throws SerializationException {
+    public String serializeResponseAsString(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException {
         try {
             return mapper.writeValueAsString(responseMessage);
         } catch (Exception ex) {
@@ -130,7 +131,7 @@ public final class GraphSONMessageSerializerV2d0 extends AbstractGraphSONMessage
     }
 
     @Override
-    public String serializeRequestAsString(final RequestMessage requestMessage) throws SerializationException {
+    public String serializeRequestAsString(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException {
         try {
             return mapper.writeValueAsString(requestMessage);
         } catch (Exception ex) {
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV3d0.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV3d0.java
index da3eea7999..5aebf77abc 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV3d0.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV3d0.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.driver.ser;
 
+import io.netty.buffer.ByteBufAllocator;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper;
@@ -106,7 +107,7 @@ public final class GraphSONMessageSerializerV3d0 extends AbstractGraphSONMessage
     }
 
     @Override
-    public String serializeResponseAsString(final ResponseMessage responseMessage) throws SerializationException {
+    public String serializeResponseAsString(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException {
         try {
             return mapper.writeValueAsString(responseMessage);
         } catch (Exception ex) {
@@ -126,7 +127,7 @@ public final class GraphSONMessageSerializerV3d0 extends AbstractGraphSONMessage
     }
 
     @Override
-    public String serializeRequestAsString(final RequestMessage requestMessage) throws SerializationException {
+    public String serializeRequestAsString(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException {
         try {
             return mapper.writeValueAsString(requestMessage);
         } catch (Exception ex) {
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/MessageTextSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/MessageTextSerializer.java
index 289d56da82..6ef0885a9b 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/MessageTextSerializer.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/MessageTextSerializer.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.driver.ser;
 
+import io.netty.buffer.ByteBufAllocator;
 import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
@@ -31,9 +32,9 @@ import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 public interface MessageTextSerializer<M> extends MessageSerializer<M> {
-    public String serializeResponseAsString(final ResponseMessage responseMessage) throws SerializationException;
+    public String serializeResponseAsString(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException;
 
-    public String serializeRequestAsString(final RequestMessage requestMessage) throws SerializationException;
+    public String serializeRequestAsString(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException;
 
     public RequestMessage deserializeRequest(final String msg) throws SerializationException;
 
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/TestWSGremlinInitializer.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/TestWSGremlinInitializer.java
index b9e9b520c4..eda187c213 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/TestWSGremlinInitializer.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/TestWSGremlinInitializer.java
@@ -18,6 +18,8 @@
  */
 package org.apache.tinkerpop.gremlin.driver;
 
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.UnpooledByteBufAllocator;
 import io.netty.handler.codec.http.HttpHeaders;
 import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
 import org.slf4j.Logger;
@@ -82,6 +84,7 @@ public class TestWSGremlinInitializer extends TestChannelizers.TestWebSocketServ
      * Gremlin serializer used for serializing/deserializing the request/response. This should be same as client.
      */
     private static final GraphSONMessageSerializerV2d0 SERIALIZER = new GraphSONMessageSerializerV2d0();
+    private final static ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT;
 
     @Override
     public void postInit(ChannelPipeline pipeline) {
@@ -128,7 +131,7 @@ public class TestWSGremlinInitializer extends TestChannelizers.TestWebSocketServ
                 final ResponseMessage responseMessage = ResponseMessage.build(msg)
                         .code(ResponseStatusCode.SERVER_ERROR)
                         .statusAttributeException(new RuntimeException()).create();
-                ctx.channel().writeAndFlush(new TextWebSocketFrame(SERIALIZER.serializeResponseAsString(responseMessage)));
+                ctx.channel().writeAndFlush(new TextWebSocketFrame(SERIALIZER.serializeResponseAsString(responseMessage, allocator)));
             } else if (msg.getRequestId().equals(CLOSE_CONNECTION_REQUEST_ID)) {
                 Thread.sleep(1000);
                 ctx.channel().writeAndFlush(new CloseWebSocketFrame());
@@ -152,7 +155,7 @@ public class TestWSGremlinInitializer extends TestChannelizers.TestWebSocketServ
             final GraphTraversalSource g = graph.traversal();
             final Vertex t = g.V().limit(1).next();
 
-            return SERIALIZER.serializeResponseAsString(ResponseMessage.build(requestID).result(t).create());
+            return SERIALIZER.serializeResponseAsString(ResponseMessage.build(requestID).result(t).create(), allocator);
         }
 
         /**
@@ -160,7 +163,7 @@ public class TestWSGremlinInitializer extends TestChannelizers.TestWebSocketServ
          * @throws SerializationException
          */
         private String returnSimpleStringResponse(final UUID requestID, String message) throws SerializationException {
-            return SERIALIZER.serializeResponseAsString(ResponseMessage.build(requestID).result(message).create());
+            return SERIALIZER.serializeResponseAsString(ResponseMessage.build(requestID).result(message).create(), allocator);
         }
 
         /**
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0Test.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0Test.java
index 4c4b7a2686..001ce7ed22 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0Test.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV1d0Test.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.driver.ser;
 
+import io.netty.buffer.ByteBufAllocator;
 import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
@@ -74,6 +75,7 @@ public class GraphSONMessageSerializerV1d0Test {
     private static final RequestMessage msg = RequestMessage.build("op")
             .overrideRequestId(UUID.fromString("2D62161B-9544-4F39-AF44-62EC49F9A595")).create();
     private static final ObjectMapper mapper = new ObjectMapper();
+    private static final ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
 
     @Test
     public void shouldConfigureIoRegistry() throws Exception {
@@ -86,7 +88,7 @@ public class GraphSONMessageSerializerV1d0Test {
 
         final ResponseMessage toSerialize = ResponseMessage.build(UUID.fromString("2D62161B-9544-4F39-AF44-62EC49F9A595"))
                 .result(Color.RED).create();
-        final String results = serializer.serializeResponseAsString(toSerialize);
+        final String results = serializer.serializeResponseAsString(toSerialize, allocator);
         final JsonNode json = mapper.readTree(results);
         assertNotNull(json);
         assertThat(json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA).booleanValue(), is(true));
@@ -95,7 +97,7 @@ public class GraphSONMessageSerializerV1d0Test {
     @Test
     public void shouldSerializeToJsonNullResultReturnsNull() throws Exception {
         final ResponseMessage message = ResponseMessage.build(msg).create();
-        final String results = SERIALIZER.serializeResponseAsString(message);
+        final String results = SERIALIZER.serializeResponseAsString(message, allocator);
         final JsonNode json = mapper.readTree(results);
         assertNotNull(json);
         assertEquals(msg.getRequestId().toString(), json.path(SerTokens.TOKEN_REQUEST).asText());
@@ -108,7 +110,7 @@ public class GraphSONMessageSerializerV1d0Test {
         funList.add(new FunObject("x"));
         funList.add(new FunObject("y"));
 
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -127,7 +129,7 @@ public class GraphSONMessageSerializerV1d0Test {
         funList.add(new FunObject("x"));
         funList.add(new FunObject("y"));
 
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList.iterator()).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList.iterator()).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -148,7 +150,7 @@ public class GraphSONMessageSerializerV1d0Test {
         funList.add(null);
         funList.add(new FunObject("y"));
 
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList.iterator()).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList.iterator()).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -172,7 +174,7 @@ public class GraphSONMessageSerializerV1d0Test {
         map.put("y", "some");
         map.put("z", innerMap);
 
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -199,7 +201,7 @@ public class GraphSONMessageSerializerV1d0Test {
         map.put(v1, 100);
         map.put(d, "test");
 
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(IteratorUtils.asList(map)).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(IteratorUtils.asList(map)).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -226,7 +228,7 @@ public class GraphSONMessageSerializerV1d0Test {
         e.property("abc", 123);
 
         final Iterable<Edge> iterable = IteratorUtils.list(g.edges());
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create(), allocator);
 
         final JsonNode json = mapper.readTree(results);
 
@@ -260,7 +262,7 @@ public class GraphSONMessageSerializerV1d0Test {
         e.property("abc", 123);
 
         final Iterable<Property<Object>> iterable = IteratorUtils.list(e.properties("abc"));
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create(), allocator);
 
         final JsonNode json = mapper.readTree(results);
 
@@ -293,7 +295,7 @@ public class GraphSONMessageSerializerV1d0Test {
         v.property(VertexProperty.Cardinality.single, "friends", friends);
 
         final Iterable iterable = IteratorUtils.list(g.vertices());
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -334,7 +336,7 @@ public class GraphSONMessageSerializerV1d0Test {
         final Map<Vertex, Integer> map = new HashMap<>();
         map.put(g.V().has("name", "marko").next(), 1000);
 
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -393,7 +395,7 @@ public class GraphSONMessageSerializerV1d0Test {
                 .statusMessage("worked")
                 .create();
 
-        final String results = SERIALIZER.serializeResponseAsString(response);
+        final String results = SERIALIZER.serializeResponseAsString(response, allocator);
         final ResponseMessage deserialized = SERIALIZER.deserializeResponse(results);
 
         assertEquals(id, deserialized.getRequestId());
@@ -423,7 +425,7 @@ public class GraphSONMessageSerializerV1d0Test {
                 .statusMessage(null)
                 .create();
 
-        final String results = SERIALIZER.serializeResponseAsString(response);
+        final String results = SERIALIZER.serializeResponseAsString(response, allocator);
         final ResponseMessage deserialized = SERIALIZER.deserializeResponse(results);
         Assert.assertNotNull(SERIALIZER.getClass().getSimpleName() + " should be able to deserialize ResponseMessage "
                         + "with null message field", deserialized);
@@ -436,7 +438,7 @@ public class GraphSONMessageSerializerV1d0Test {
         final Tree t = g.V(1).out().properties("name").tree().next();
 
         
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(t).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(t).create(), allocator);
 
         final JsonNode json = mapper.readTree(results);
 
diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0Test.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0Test.java
index a497400dd1..3d01af41e3 100644
--- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0Test.java
+++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0Test.java
@@ -104,7 +104,7 @@ public class GraphSONMessageSerializerV2d0Test {
 
         final ResponseMessage toSerialize = ResponseMessage.build(UUID.fromString("2D62161B-9544-4F39-AF44-62EC49F9A595"))
                 .result(Color.RED).create();
-        final String results = serializer.serializeResponseAsString(toSerialize);
+        final String results = serializer.serializeResponseAsString(toSerialize, allocator);
         final JsonNode json = mapper.readTree(results);
         assertNotNull(json);
         assertThat(json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA).booleanValue(), is(true));
@@ -113,7 +113,7 @@ public class GraphSONMessageSerializerV2d0Test {
     @Test
     public void shouldSerializeToJsonNullResultReturnsNull() throws Exception {
         final ResponseMessage message = ResponseMessage.build(msg).create();
-        final String results = SERIALIZER.serializeResponseAsString(message);
+        final String results = SERIALIZER.serializeResponseAsString(message, allocator);
         final JsonNode json = mapper.readTree(results);
         assertNotNull(json);
         assertEquals(msg.getRequestId().toString(), json.path(SerTokens.TOKEN_REQUEST).asText());
@@ -126,7 +126,7 @@ public class GraphSONMessageSerializerV2d0Test {
         funList.add(new FunObject("x"));
         funList.add(new FunObject("y"));
 
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -145,7 +145,7 @@ public class GraphSONMessageSerializerV2d0Test {
         funList.add(new FunObject("x"));
         funList.add(new FunObject("y"));
 
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -166,7 +166,7 @@ public class GraphSONMessageSerializerV2d0Test {
         funList.add(null);
         funList.add(new FunObject("y"));
 
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList.iterator()).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList.iterator()).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -190,7 +190,7 @@ public class GraphSONMessageSerializerV2d0Test {
         map.put("y", "some");
         map.put("z", innerMap);
 
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -217,7 +217,7 @@ public class GraphSONMessageSerializerV2d0Test {
         map.put(v1, 100);
         map.put(d, "test");
 
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(IteratorUtils.asList(map)).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(IteratorUtils.asList(map)).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -244,7 +244,7 @@ public class GraphSONMessageSerializerV2d0Test {
         e.property("abc", 123);
 
         final Iterable<Edge> iterable = IteratorUtils.list(g.edges());
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create(), allocator);
 
         final JsonNode json = mapper.readTree(results);
 
@@ -277,7 +277,7 @@ public class GraphSONMessageSerializerV2d0Test {
         e.property("abc", 123);
 
         final Iterable<Property<Object>> iterable = IteratorUtils.list(e.properties("abc"));
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create(), allocator);
 
         final JsonNode json = mapper.readTree(results);
 
@@ -310,7 +310,7 @@ public class GraphSONMessageSerializerV2d0Test {
         v.property(VertexProperty.Cardinality.single, "friends", friends);
 
         final Iterable iterable = IteratorUtils.list(g.vertices());
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -351,7 +351,7 @@ public class GraphSONMessageSerializerV2d0Test {
         final Map<Vertex, Integer> map = new HashMap<>();
         map.put(g.V().has("name", "marko").next(), 1000);
 
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create(), allocator);
         final JsonNode json = mapper.readTree(results);
 
         assertNotNull(json);
@@ -410,7 +410,7 @@ public class GraphSONMessageSerializerV2d0Test {
                 .statusMessage("worked")
                 .create();
 
-        final String results = SERIALIZER.serializeResponseAsString(response);
+        final String results = SERIALIZER.serializeResponseAsString(response, allocator);
         final ResponseMessage deserialized = SERIALIZER.deserializeResponse(results);
 
         assertEquals(id, deserialized.getRequestId());
@@ -429,7 +429,7 @@ public class GraphSONMessageSerializerV2d0Test {
         final GraphTraversalSource g = graph.traversal();
         final Tree t = g.V(1).out().properties("name").tree().next();
 
-        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(t).create());
+        final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(t).create(), allocator);
 
         final JsonNode json = mapper.readTree(results);
 
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 d77de484b9..6cd37b87ce 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
@@ -175,6 +175,43 @@ public class GraphBinaryMessageSerializerV1Test {
         assertEquals(java.awt.Color.RED.toString(), deserialized.getResult().getData());
     }
 
+    @Test
+    public void shouldToStringSerializeAsText() throws SerializationException {
+        final GraphBinaryMessageSerializerV1 serializer = new GraphBinaryMessageSerializerV1();
+        final Map<String,Object> conf = new HashMap<String,Object>() {{
+            put(GraphBinaryMessageSerializerV1.TOKEN_SERIALIZE_RESULT_TO_STRING, true);
+        }};
+        serializer.configure(conf, Collections.emptyMap());
+
+        final ResponseMessage messageWithUnexpectedType = ResponseMessage.build(UUID.randomUUID()).
+                result(java.awt.Color.RED).create();
+        final String base64 = serializer.serializeResponseAsString(messageWithUnexpectedType, allocator);
+        final ResponseMessage deserialized = serializer.deserializeResponse(base64);
+
+        assertEquals(java.awt.Color.RED.toString(), deserialized.getResult().getData());
+    }
+
+    @Test
+    public void shouldSerializeAndDeserializeRequestAsText() throws SerializationException {
+        final GraphBinaryMessageSerializerV1 serializer = new GraphBinaryMessageSerializerV1();
+        final Map<String,Object> conf = new HashMap<String,Object>() {{
+            put(GraphBinaryMessageSerializerV1.TOKEN_SERIALIZE_RESULT_TO_STRING, true);
+        }};
+        serializer.configure(conf, Collections.emptyMap());
+
+        final RequestMessage request = RequestMessage.build("op1")
+                .processor("proc1")
+                .overrideRequestId(UUID.randomUUID())
+                .addArg("arg1", "value1")
+                .create();
+
+        final ByteBuf buffer = serializer.serializeRequestAsBinary(request, allocator);
+        final int mimeLen = buffer.readByte();
+        buffer.readBytes(new byte[mimeLen]);
+        final RequestMessage deserialized = serializer.deserializeRequest(buffer);
+        assertThat(request, reflectionEquals(deserialized));
+    }
+
     private static void assertResponseEquals(ResponseMessage expected, ResponseMessage actual) {
         assertEquals(expected.getRequestId(), actual.getRequestId());
         // Status
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/AbstractSession.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/AbstractSession.java
index d891e50a17..4b137f3dc2 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/AbstractSession.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/AbstractSession.java
@@ -751,7 +751,7 @@ public abstract class AbstractSession implements Session, AutoCloseable {
                         .code(code)
                         .statusAttributes(statusAttributes)
                         .responseMetaData(responseMetaData)
-                        .result(aggregate).create()));
+                        .result(aggregate).create(), nettyContext.alloc()));
             }
         } catch (Exception ex) {
             logger.warn("The result [{}] in the request {} could not be serialized and returned.", aggregate, msg.getRequestId(), ex);
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GremlinResponseFrameEncoder.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GremlinResponseFrameEncoder.java
index 6020b365ec..54fe16b75f 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GremlinResponseFrameEncoder.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GremlinResponseFrameEncoder.java
@@ -84,9 +84,9 @@ public class GremlinResponseFrameEncoder extends MessageToMessageEncoder<Respons
                 // if the request came in on a session then the serialization must occur that same thread except
                 // in the case of errors for reasons described above.
                 if (null == session || !o.getStatus().getCode().isSuccess())
-                    serialized = new Frame(textSerializer.serializeResponseAsString(o));
+                    serialized = new Frame(textSerializer.serializeResponseAsString(o, ctx.alloc()));
                 else
-                    serialized = new Frame(session.getExecutor().submit(() -> textSerializer.serializeResponseAsString(o)).get());
+                    serialized = new Frame(session.getExecutor().submit(() -> textSerializer.serializeResponseAsString(o, ctx.alloc())).get());
 
                 objects.add(serialized);
             }
@@ -102,7 +102,7 @@ public class GremlinResponseFrameEncoder extends MessageToMessageEncoder<Respons
                 objects.add(serializer.serializeResponseAsBinary(error, ctx.alloc()));
             } else {
                 final MessageTextSerializer<?> textSerializer = (MessageTextSerializer<?>) serializer;
-                objects.add(textSerializer.serializeResponseAsString(error));
+                objects.add(textSerializer.serializeResponseAsString(error, ctx.alloc()));
             }
         }
     }
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 634d88f949..6995b21583 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,7 +19,10 @@
 package org.apache.tinkerpop.gremlin.server.handler;
 
 import com.codahale.metrics.Timer;
+import io.netty.buffer.ByteBufAllocator;
+import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.driver.ser.SerializationException;
+import org.apache.tinkerpop.gremlin.server.util.TextPlainMessageSerializer;
 import org.javatuples.Pair;
 import org.javatuples.Quartet;
 import org.slf4j.Logger;
@@ -60,10 +63,12 @@ import java.nio.charset.StandardCharsets;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -99,6 +104,11 @@ public class HttpGremlinEndpointHandler extends ChannelInboundHandlerAdapter {
      */
     private final Map<String, MessageSerializer<?>> serializers;
 
+    /**
+     * Serializer for {@code text/plain} which is a serializer exclusive to HTTP.
+     */
+    private static final TextPlainMessageSerializer textPlainSerializer = new TextPlainMessageSerializer();
+
     private final GremlinExecutor gremlinExecutor;
     private final GraphManager graphManager;
     private final Settings settings;
@@ -223,7 +233,7 @@ public class HttpGremlinEndpointHandler extends ChannelInboundHandlerAdapter {
                             attemptCommit(requestArguments.getValue3(), graphManager, settings.strictTransactionManagement);
 
                             try {
-                                return Unpooled.wrappedBuffer(serializer.getValue1().serializeResponseAsString(responseMessage).getBytes(UTF8));
+                                return Unpooled.wrappedBuffer(serializer.getValue1().serializeResponseAsString(responseMessage, ctx.alloc()).getBytes(UTF8));
                             } catch (Exception ex) {
                                 logger.warn(String.format("Error during serialization for %s", responseMessage), ex);
 
@@ -331,6 +341,9 @@ public class HttpGremlinEndpointHandler extends ChannelInboundHandlerAdapter {
             final String accept = p.getValue0().equals("*/*") ? "application/json" : p.getValue0();
             if (serializers.containsKey(accept))
                 return Pair.with(accept, (MessageTextSerializer<?>) serializers.get(accept));
+            else if (accept.equals("text/plain")) {
+                return Pair.with(accept, textPlainSerializer);
+            }
         }
 
         return null;
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractOpProcessor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractOpProcessor.java
index 383d074553..a0c3a37a6c 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractOpProcessor.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractOpProcessor.java
@@ -297,7 +297,7 @@ public abstract class AbstractOpProcessor implements OpProcessor {
                         .code(code)
                         .statusAttributes(statusAttributes)
                         .responseMetaData(responseMetaData)
-                        .result(aggregate).create()));
+                        .result(aggregate).create(), nettyContext.alloc()));
             }
         } catch (Exception ex) {
             logger.warn("The result [{}] in the request {} could not be serialized and returned.", aggregate, msg.getRequestId(), ex);
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
new file mode 100644
index 0000000000..81b3662b96
--- /dev/null
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializer.java
@@ -0,0 +1,97 @@
+/*
+ * 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.util;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
+import org.apache.tinkerpop.gremlin.driver.ser.MessageTextSerializer;
+import org.apache.tinkerpop.gremlin.driver.ser.SerializationException;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+
+/**
+ * A highly use-case specific serializer that only has context for HTTP where results simply need to be converted
+ * to string in a line by line fashion for text based returns.
+ */
+public class TextPlainMessageSerializer implements MessageTextSerializer<Function<Object, String>> {
+
+    @Override
+    public Function<Object, String> getMapper() {
+        return Objects::toString;
+    }
+
+    @Override
+    public ByteBuf serializeResponseAsBinary(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException {
+        throw new UnsupportedOperationException("text/plain does not produce binary");
+    }
+
+    @Override
+    public ByteBuf serializeRequestAsBinary(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException {
+        throw new UnsupportedOperationException("text/plain does not produce binary");
+    }
+
+    @Override
+    public RequestMessage deserializeRequest(final ByteBuf msg) throws SerializationException {
+        throw new UnsupportedOperationException("text/plain does not have deserialization functions");
+    }
+
+    @Override
+    public ResponseMessage deserializeResponse(final ByteBuf msg) throws SerializationException {
+        throw new UnsupportedOperationException("text/plain does not have deserialization functions");
+    }
+
+    @Override
+    public String[] mimeTypesSupported() {
+        return new String[] { "text/plain" };
+    }
+
+    @Override
+    public String serializeResponseAsString(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException {
+        final StringBuilder sb = new StringBuilder();
+
+        // this should only serialize success conditions so all should have data in List form
+        final List<Object> data = (List<Object>) responseMessage.getResult().getData();
+        for (int ix = 0; ix < data.size(); ix ++) {
+            sb.append("==>");
+            sb.append(data.get(ix));
+            if (ix < data.size() - 1)
+                sb.append(System.lineSeparator());
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public String serializeRequestAsString(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException {
+        throw new UnsupportedOperationException("text/plain does not have any need to serialize requests");
+    }
+
+    @Override
+    public RequestMessage deserializeRequest(final String msg) throws SerializationException {
+        throw new UnsupportedOperationException("text/plain does not have deserialization functions");
+    }
+
+    @Override
+    public ResponseMessage deserializeResponse(final String msg) throws SerializationException {
+        throw new UnsupportedOperationException("text/plain does not have deserialization functions");
+    }
+}
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 7a33bb0bd0..4f44158dd6 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
@@ -20,6 +20,8 @@ package org.apache.tinkerpop.gremlin.server;
 
 import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.http.HttpHeaders;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
+import org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1;
 import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0;
 import org.apache.tinkerpop.gremlin.driver.Tokens;
 import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV2d0;
@@ -36,15 +38,17 @@ import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.util.EntityUtils;
 import org.apache.tinkerpop.gremlin.server.handler.SaslAndHttpBasicAuthenticationHandler;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.io.binary.TypeSerializerRegistry;
 import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens;
 import org.apache.tinkerpop.shaded.jackson.databind.JsonNode;
 import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
 import org.junit.Test;
 
-import java.io.File;
 import java.time.Instant;
 import java.util.Base64;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Executors;
@@ -54,7 +58,9 @@ import java.util.concurrent.TimeUnit;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.hamcrest.core.StringContains.containsString;
+import static org.hamcrest.core.StringRegularExpression.matchesRegex;
 import static org.hamcrest.core.StringStartsWith.startsWith;
 import static org.junit.Assert.assertEquals;
 
@@ -365,6 +371,68 @@ public class GremlinServerHttpIntegrateTest extends AbstractGremlinServerIntegra
         }
     }
 
+    @Test
+    public void should200OnGETWithGremlinQueryStringArgumentWithIteratorResultGraphBinary() throws Exception {
+        final CloseableHttpClient httpclient = HttpClients.createDefault();
+        final HttpGet httpget = new HttpGet(TestClientFactory.createURLString("?gremlin=gclassic.V()"));
+        final String mime = SerTokens.MIME_GRAPHBINARY_V1D0;
+        httpget.addHeader("Accept", mime);
+
+        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 List<Object> data = (List<Object>) msg.getResult().getData();
+            assertEquals(6, data.size());
+            for (Object o : data) {
+                assertThat(o, instanceOf(Vertex.class));
+            }
+        }
+    }
+
+    @Test
+    public void should200OnGETWithGremlinQueryStringArgumentWithIteratorResultGraphBinaryToString() throws Exception {
+        final CloseableHttpClient httpclient = HttpClients.createDefault();
+        final HttpGet httpget = new HttpGet(TestClientFactory.createURLString("?gremlin=gclassic.V()"));
+        final String mime = SerTokens.MIME_GRAPHBINARY_V1D0 + "-stringd";
+        httpget.addHeader("Accept", mime);
+
+        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 List<Object> data = (List<Object>) msg.getResult().getData();
+            assertEquals(6, data.size());
+            for (Object o : data) {
+                assertThat(o, instanceOf(String.class));
+                assertThat((String) o, matchesRegex("v\\[\\d\\]"));
+            }
+        }
+    }
+
+    @Test
+    public void should200OnGETWithGremlinQueryStringArgumentWithIteratorResultTextPlain() throws Exception {
+        final CloseableHttpClient httpclient = HttpClients.createDefault();
+        final HttpGet httpget = new HttpGet(TestClientFactory.createURLString("?gremlin=gclassic.V()"));
+        final String mime = "text/plain";
+        httpget.addHeader("Accept", mime);
+
+        try (final CloseableHttpResponse response = httpclient.execute(httpget)) {
+            assertEquals(200, response.getStatusLine().getStatusCode());
+            assertEquals(mime, response.getEntity().getContentType().getValue());
+            final String text = EntityUtils.toString(response.getEntity());
+            final String[] split = text.split(System.lineSeparator());
+            assertEquals(6, split.length);
+            for (String line : split) {
+                assertThat(line, matchesRegex("==>v\\[\\d\\]"));
+            }
+        }
+    }
+
     @Test
     public void should200OnGETWithGremlinQueryStringArgument() throws Exception {
         final CloseableHttpClient httpclient = HttpClients.createDefault();
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializerTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializerTest.java
new file mode 100644
index 0000000000..803d1495fa
--- /dev/null
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/util/TextPlainMessageSerializerTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.util;
+
+import io.netty.buffer.ByteBufAllocator;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.junit.Assert.assertEquals;
+
+public class TextPlainMessageSerializerTest {
+
+    @Test
+    public void shouldProducePlainText() throws Exception {
+        final Map<String, Object> m = new HashMap<>();
+        final ResponseMessage msg = ResponseMessage.build(UUID.randomUUID()).
+                result(Arrays.asList(1, new DetachedVertex(100, "person", m), java.awt.Color.RED)).create();
+
+        final TextPlainMessageSerializer messageSerializer = new TextPlainMessageSerializer();
+        final String output = messageSerializer.serializeResponseAsString(msg, ByteBufAllocator.DEFAULT);
+        final String exp = "==>1" + System.lineSeparator() +
+                           "==>v[100]" + System.lineSeparator() +
+                           "==>java.awt.Color[r=255,g=0,b=0]";
+        assertEquals(exp, output);
+    }
+}