This is an automated email from the ASF dual-hosted git repository.
kenhuuu pushed a commit to branch master-http
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/master-http by this push:
new fe6fd0dba6 Introduce Netty handler for managing request IDs.
fe6fd0dba6 is described below
commit fe6fd0dba6514134079f72ad1fedcde0fad23584
Author: Ken Hu <[email protected]>
AuthorDate: Wed Apr 17 16:44:24 2024 -0700
Introduce Netty handler for managing request IDs.
Request IDs are now generated by the server and returned as a header.
This handler handles both of these operations to centralize the
implementation.
---
.../apache/tinkerpop/gremlin/driver/Client.java | 4 +-
.../gremlin/server/channel/HttpChannelizer.java | 4 ++
.../server/handler/HttpGremlinEndpointHandler.java | 41 +++++------
.../gremlin/server/handler/HttpHandlerUtil.java | 3 +-
.../server/handler/HttpRequestIdHandler.java | 50 +++++++++++++
.../server/handler/HttpRequestMessageDecoder.java | 5 +-
.../tinkerpop/gremlin/server/handler/StateKey.java | 7 ++
.../server/GremlinServerHttpIntegrateTest.java | 35 +++++----
.../server/handler/HttpRequestIdHandlerTest.java | 82 ++++++++++++++++++++++
.../handler/HttpRequestMessageDecoderTest.java | 8 +--
.../org/apache/tinkerpop/gremlin/util/Tokens.java | 3 +-
.../gremlin/util/message/RequestMessageV4.java | 12 +---
.../gremlin/util/message/ResponseMessage.java | 4 --
.../ser/AbstractGraphSONMessageSerializerV4.java | 3 -
.../ser/binary/RequestMessageSerializerV4.java | 3 -
.../gremlin/util/message/RequestMessageV4Test.java | 11 +--
16 files changed, 196 insertions(+), 79 deletions(-)
diff --git
a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
index 88f935fd20..bc718945c0 100644
---
a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
+++
b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
@@ -359,7 +359,7 @@ public abstract class Client {
// options.getTimeout().ifPresent(timeout ->
request.add(Tokens.ARGS_EVAL_TIMEOUT, timeout));
options.getParameters().ifPresent(params ->
request.addBindings(params));
options.getAliases().ifPresent(aliases -> {if (aliases.get("g") !=
null) request.addG(aliases.get("g")); });
- options.getOverrideRequestId().ifPresent(request::overrideRequestId);
+// options.getOverrideRequestId().ifPresent(request::overrideRequestId);
// options.getUserAgent().ifPresent(userAgent ->
request.addArg(Tokens.ARGS_USER_AGENT, userAgent));
options.getLanguage().ifPresent(lang -> request.addLanguage(lang));
// options.getMaterializeProperties().ifPresent(mp ->
request.addArg(Tokens.ARGS_MATERIALIZE_PROPERTIES, mp));
@@ -656,7 +656,7 @@ public abstract class Client {
// apply settings if they were made available
options.getBatchSize().ifPresent(batchSize ->
request.addChunkSize(batchSize));
// options.getTimeout().ifPresent(timeout ->
request.add(Tokens.ARGS_EVAL_TIMEOUT, timeout));
-
options.getOverrideRequestId().ifPresent(request::overrideRequestId);
+//
options.getOverrideRequestId().ifPresent(request::overrideRequestId);
// options.getUserAgent().ifPresent(userAgent ->
request.add(Tokens.ARGS_USER_AGENT, userAgent));
// options.getMaterializeProperties().ifPresent(mp ->
request.addArg(Tokens.ARGS_MATERIALIZE_PROPERTIES, mp));
diff --git
a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/channel/HttpChannelizer.java
b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/channel/HttpChannelizer.java
index 4d631bfdd0..e512e993fa 100644
---
a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/channel/HttpChannelizer.java
+++
b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/channel/HttpChannelizer.java
@@ -30,6 +30,7 @@ import
org.apache.tinkerpop.gremlin.server.handler.AbstractAuthenticationHandler
import
org.apache.tinkerpop.gremlin.server.handler.HttpBasicAuthenticationHandler;
import
org.apache.tinkerpop.gremlin.server.handler.HttpBasicAuthorizationHandler;
import org.apache.tinkerpop.gremlin.server.handler.HttpRequestCheckingHandler;
+import org.apache.tinkerpop.gremlin.server.handler.HttpRequestIdHandler;
import org.apache.tinkerpop.gremlin.server.handler.HttpRequestMessageDecoder;
import org.apache.tinkerpop.gremlin.server.handler.HttpUserAgentHandler;
import org.apache.tinkerpop.gremlin.server.handler.HttpGremlinEndpointHandler;
@@ -55,6 +56,8 @@ public class HttpChannelizer extends AbstractChannelizer {
private HttpRequestCheckingHandler httpRequestCheckingHandler = new
HttpRequestCheckingHandler();
private HttpRequestMessageDecoder httpRequestMessageDecoder = new
HttpRequestMessageDecoder(serializers);
+ private HttpRequestIdHandler httpRequestIdHandler = new
HttpRequestIdHandler();
+
@Override
public void init(final ServerGremlinExecutor serverGremlinExecutor) {
super.init(serverGremlinExecutor);
@@ -71,6 +74,7 @@ public class HttpChannelizer extends AbstractChannelizer {
if (logger.isDebugEnabled())
pipeline.addLast(new LoggingHandler("http-io", LogLevel.DEBUG));
+ pipeline.addLast("http-requestid-handler", httpRequestIdHandler);
pipeline.addLast("http-keepalive-handler", new
HttpServerKeepAliveHandler());
pipeline.addLast("http-cors-handler", new
CorsHandler(CorsConfigBuilder.forAnyOrigin().build()));
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 a6909e908a..2196795250 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
@@ -81,6 +81,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionException;
@@ -219,7 +220,7 @@ public class HttpGremlinEndpointHandler extends
SimpleChannelInboundHandler<Requ
final String msgInvalid =
String.format("Message could not be parsed.
Check the format of the request. [%s]", requestMessage);
throw new ProcessingException(msgInvalid,
- ResponseMessage.build(requestMessage)
+ ResponseMessage.buildV4()
.code(ResponseStatusCode.REQUEST_ERROR_MALFORMED_REQUEST)
.statusMessage(msgInvalid)
.create());
@@ -227,7 +228,7 @@ public class HttpGremlinEndpointHandler extends
SimpleChannelInboundHandler<Requ
final String msgDefault =
String.format("Message with gremlin of type
[%s] is not recognized.", requestMessage.getGremlinType());
throw new ProcessingException(msgDefault,
- ResponseMessage.build(requestMessage)
+ ResponseMessage.buildV4()
.code(ResponseStatusCode.REQUEST_ERROR_MALFORMED_REQUEST)
.statusMessage(msgDefault)
.create());
@@ -258,7 +259,7 @@ public class HttpGremlinEndpointHandler extends
SimpleChannelInboundHandler<Requ
if (!requestCtx.getStartedResponse()) {
final String errorMessage = String.format("A timeout
occurred during traversal evaluation of [%s] - consider increasing the limit
given to evaluationTimeout", requestMessage);
writeError(requestCtx,
- ResponseMessage.build(requestMessage)
+ ResponseMessage.buildV4()
.code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
.statusMessage(errorMessage)
.create(),
@@ -268,7 +269,7 @@ public class HttpGremlinEndpointHandler extends
SimpleChannelInboundHandler<Requ
}
} catch (RejectedExecutionException ree) {
writeError(requestCtx,
-
ResponseMessage.build(requestMessage).code(ResponseStatusCode.TOO_MANY_REQUESTS).statusMessage("Rate
limiting").create(),
+
ResponseMessage.buildV4().code(ResponseStatusCode.TOO_MANY_REQUESTS).statusMessage("Rate
limiting").create(),
serializer.getValue1());
}
}
@@ -284,7 +285,7 @@ public class HttpGremlinEndpointHandler extends
SimpleChannelInboundHandler<Requ
final Optional<Throwable> possibleSpecialException =
determineIfSpecialException(t);
if (possibleSpecialException.isPresent()) {
final Throwable special = possibleSpecialException.get();
- final ResponseMessage.Builder specialResponseMsg =
ResponseMessage.build(requestMessage).
+ final ResponseMessage.Builder specialResponseMsg =
ResponseMessage.buildV4().
statusMessage(special.getMessage()).
statusAttributeException(special);
if (special instanceof TemporaryException) {
@@ -301,13 +302,13 @@ public class HttpGremlinEndpointHandler extends
SimpleChannelInboundHandler<Requ
t = ExceptionHelper.getRootCause(t);
if (t instanceof TooLongFrameException) {
- return ResponseMessage.build(requestMessage)
+ return ResponseMessage.buildV4()
.code(ResponseStatusCode.SERVER_ERROR)
.statusMessage(t.getMessage() + " - increase the
maxContentLength")
.create();
} else if (t instanceof InterruptedException || t instanceof
TraversalInterruptedException) {
final String errorMessage = String.format("A timeout occurred
during traversal evaluation of [%s] - consider increasing the limit given to
evaluationTimeout", requestMessage);
- errorResponseMessage = ResponseMessage.build(requestMessage)
+ errorResponseMessage = ResponseMessage.buildV4()
.code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
.statusMessage(errorMessage)
.statusAttributeException(t)
@@ -315,23 +316,23 @@ public class HttpGremlinEndpointHandler extends
SimpleChannelInboundHandler<Requ
} else if (t instanceof TimedInterruptTimeoutException) {
// occurs when the TimedInterruptCustomizerProvider is in play
logMessage = String.format("A timeout occurred within the
script during evaluation of [%s] - consider increasing the limit given to
TimedInterruptCustomizerProvider", requestMessage);
- errorResponseMessage =
ResponseMessage.build(requestMessage).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
+ errorResponseMessage =
ResponseMessage.buildV4().code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
.statusMessage("Timeout during script evaluation
triggered by TimedInterruptCustomizerProvider")
.statusAttributeException(t).create();
} else if (t instanceof TimeoutException) {
logMessage = String.format("Script evaluation exceeded the
configured threshold for request [%s]", requestMessage);
- errorResponseMessage =
ResponseMessage.build(requestMessage).code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
+ errorResponseMessage =
ResponseMessage.buildV4().code(ResponseStatusCode.SERVER_ERROR_TIMEOUT)
.statusMessage(t.getMessage())
.statusAttributeException(t).create();
} else if (t instanceof MultipleCompilationErrorsException &&
t.getMessage().contains("Method too large") &&
((MultipleCompilationErrorsException)
t).getErrorCollector().getErrorCount() == 1) {
final String errorMessage = String.format("The Gremlin
statement that was submitted exceeds the maximum compilation size allowed by
the JVM, please split it into multiple smaller statements - %s",
requestMessage.trimMessage(1021));
logMessage = errorMessage;
- errorResponseMessage =
ResponseMessage.build(requestMessage).code(ResponseStatusCode.SERVER_ERROR_EVALUATION)
+ errorResponseMessage =
ResponseMessage.buildV4().code(ResponseStatusCode.SERVER_ERROR_EVALUATION)
.statusMessage(errorMessage)
.statusAttributeException(t).create();
} else {
- errorResponseMessage = ResponseMessage.build(requestMessage)
+ errorResponseMessage = ResponseMessage.buildV4()
.code(ResponseStatusCode.SERVER_ERROR_EVALUATION)
.statusMessage((t.getMessage() == null) ? t.toString()
: t.getMessage())
.statusAttributeException(t)
@@ -349,20 +350,20 @@ public class HttpGremlinEndpointHandler extends
SimpleChannelInboundHandler<Requ
final Map bindings = (Map)
message.getFields().get(Tokens.ARGS_BINDINGS);
if (IteratorUtils.anyMatch(bindings.keySet().iterator(), k -> null
== k || !(k instanceof String))) {
final String msg = String.format("The [%s] message is using
one or more invalid binding keys - they must be of type String and cannot be
null", Tokens.OPS_EVAL);
- throw new ProcessingException(msg,
ResponseMessage.build(message).code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(msg).create());
+ throw new ProcessingException(msg,
ResponseMessage.buildV4().code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(msg).create());
}
final Set<String> badBindings =
IteratorUtils.set(IteratorUtils.<String>filter(bindings.keySet().iterator(),
INVALID_BINDINGS_KEYS::contains));
if (!badBindings.isEmpty()) {
final String msg = String.format("The [%s] message supplies
one or more invalid parameters key of [%s] - these are reserved names.",
Tokens.OPS_EVAL, badBindings);
- throw new ProcessingException(msg,
ResponseMessage.build(message).code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(msg).create());
+ throw new ProcessingException(msg,
ResponseMessage.buildV4().code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(msg).create());
}
// ignore control bindings that get passed in with the "#jsr223"
prefix - those aren't used in compilation
if
(IteratorUtils.count(IteratorUtils.filter(bindings.keySet().iterator(), k ->
!k.toString().startsWith("#jsr223"))) > settings.maxParameters) {
final String msg = String.format("The [%s] message contains %s
bindings which is more than is allowed by the server %s configuration",
Tokens.OPS_EVAL, bindings.size(),
settings.maxParameters);
- throw new ProcessingException(msg,
ResponseMessage.build(message).code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(msg).create());
+ throw new ProcessingException(msg,
ResponseMessage.buildV4().code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(msg).create());
}
}
@@ -381,19 +382,19 @@ public class HttpGremlinEndpointHandler extends
SimpleChannelInboundHandler<Requ
if (!(requestMsg.getGremlin() instanceof Bytecode)) {
final String msg = String.format("A [%s] message requires a
gremlin argument that is of type %s.",
Tokens.OPS_BYTECODE, Tokens.ARGS_GREMLIN,
Bytecode.class.getSimpleName());
- throw new ProcessingException(msg,
ResponseMessage.build(requestMsg).code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(msg).create());
+ throw new ProcessingException(msg,
ResponseMessage.buildV4().code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(msg).create());
}
final Optional<String> alias = requestMsg.optionalField(Tokens.ARGS_G);
if (!alias.isPresent()) {
final String msg = String.format("A [%s] message requires a [%s]
argument.", Tokens.OPS_BYTECODE, Tokens.ARGS_G);
- throw new ProcessingException(msg,
ResponseMessage.build(requestMsg).code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(msg).create());
+ throw new ProcessingException(msg,
ResponseMessage.buildV4().code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(msg).create());
}
final String traversalSourceName = alias.get();
if
(!ctx.getGraphManager().getTraversalSourceNames().contains(traversalSourceName))
{
final String msg = String.format("The traversal source [%s] for
alias [%s] is not configured on the server.", traversalSourceName,
Tokens.VAL_TRAVERSAL_SOURCE_ALIAS);
- throw new ProcessingException(msg,
ResponseMessage.build(requestMsg).code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(msg).create());
+ throw new ProcessingException(msg,
ResponseMessage.buildV4().code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(msg).create());
}
final TraversalSource g =
ctx.getGraphManager().getTraversalSource(traversalSourceName);
@@ -407,13 +408,13 @@ public class HttpGremlinEndpointHandler extends
SimpleChannelInboundHandler<Requ
} catch (ScriptException ex) {
logger.error("Traversal contains a lambda that cannot be
compiled", ex);
throw new ProcessingException("Traversal contains a lambda that
cannot be compiled",
-
ResponseMessage.build(requestMsg).code(ResponseStatusCode.SERVER_ERROR_EVALUATION)
+
ResponseMessage.buildV4().code(ResponseStatusCode.SERVER_ERROR_EVALUATION)
.statusMessage(ex.getMessage())
.statusAttributeException(ex).create());
} catch (Exception ex) {
logger.error("Could not deserialize the Traversal instance", ex);
throw new ProcessingException("Could not deserialize the Traversal
instance",
-
ResponseMessage.build(requestMsg).code(ResponseStatusCode.SERVER_ERROR_SERIALIZATION)
+
ResponseMessage.buildV4().code(ResponseStatusCode.SERVER_ERROR_SERIALIZATION)
.statusMessage(ex.getMessage())
.statusAttributeException(ex).create());
}
@@ -457,7 +458,7 @@ public class HttpGremlinEndpointHandler extends
SimpleChannelInboundHandler<Requ
if (!found) {
final String error = String.format("Could not alias [%s] to
[%s] as [%s] not in the Graph or TraversalSource global bindings",
Tokens.ARGS_G, aliased, aliased);
- throw new ProcessingException(error, ResponseMessage.build(msg)
+ throw new ProcessingException(error, ResponseMessage.buildV4()
.code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(error).create());
}
}
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 209503a0d3..61f16280cc 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
@@ -80,8 +80,7 @@ import static
io.netty.handler.codec.http.LastHttpContent.EMPTY_LAST_CONTENT;
public class HttpHandlerUtil {
private static final Logger logger =
LoggerFactory.getLogger(HttpHandlerUtil.class);
static final Meter errorMeter =
MetricManager.INSTANCE.getMeter(name(GremlinServer.class, "errors"));
- private static final String ARGS_BINDINGS_DOT = Tokens.ARGS_BINDINGS + ".";
- private static final String ARGS_ALIASES_DOT = Tokens.ARGS_ALIASES + ".";
+
/**
* A generic mapper to return JSON errors in specific cases.
*/
diff --git
a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestIdHandler.java
b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestIdHandler.java
new file mode 100644
index 0000000000..30e103b56c
--- /dev/null
+++
b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestIdHandler.java
@@ -0,0 +1,50 @@
+/*
+ * 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.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPromise;
+import io.netty.handler.codec.http.HttpResponse;
+
+import java.util.UUID;
+
+/**
+ * A handler that generates a Request ID for the incoming HTTP request and
injects the same ID into the response header.
+ */
[email protected]
+public class HttpRequestIdHandler extends ChannelDuplexHandler {
+ public static String REQUEST_ID_HEADER_NAME = "Gremlin-RequestId";
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws
Exception {
+ ctx.attr(StateKey.REQUEST_ID).set(UUID.randomUUID());
+ ctx.fireChannelRead(msg);
+ }
+
+ @Override
+ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise
promise) throws Exception {
+ if (msg instanceof HttpResponse) {
+ HttpResponse response = (HttpResponse) msg;
+ response.headers().add(REQUEST_ID_HEADER_NAME,
ctx.attr(StateKey.REQUEST_ID).get());
+ }
+ ctx.write(msg, promise);
+ }
+}
diff --git
a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestMessageDecoder.java
b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestMessageDecoder.java
index 29595107c7..cefc04f41f 100644
---
a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestMessageDecoder.java
+++
b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestMessageDecoder.java
@@ -218,13 +218,10 @@ public class HttpRequestMessageDecoder extends
MessageToMessageDecoder<FullHttpR
final JsonNode languageNode = body.get(Tokens.ARGS_LANGUAGE);
final String language = null == languageNode ? "gremlin-groovy" :
languageNode.asText();
- final JsonNode requestIdNode = body.get(Tokens.REQUEST_ID);
- final UUID requestId = null == requestIdNode ? UUID.randomUUID() :
UUID.fromString(requestIdNode.asText());
-
final JsonNode chunkSizeNode = body.get(Tokens.ARGS_BATCH_SIZE);
final Integer chunkSize = null == chunkSizeNode ? null :
chunkSizeNode.asInt();
- final RequestMessageV4.Builder builder =
RequestMessageV4.build(scriptNode.asText()).overrideRequestId(requestId)
+ final RequestMessageV4.Builder builder =
RequestMessageV4.build(scriptNode.asText())
.addBindings(bindings).addLanguage(language);
if (null != g) builder.addG(g);
if (null != chunkSize) builder.addChunkSize(chunkSize);
diff --git
a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/StateKey.java
b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/StateKey.java
index 9e83999160..7ee47f87e3 100644
---
a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/StateKey.java
+++
b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/StateKey.java
@@ -25,6 +25,8 @@ import org.apache.tinkerpop.gremlin.server.auth.Authenticator;
import org.apache.tinkerpop.gremlin.util.ser.MessageTextSerializerV4;
import org.javatuples.Pair;
+import java.util.UUID;
+
/**
* Keys used in the various handlers to store state in the pipeline.
*
@@ -54,6 +56,11 @@ public final class StateKey {
*/
public static final AttributeKey<RequestMessage> REQUEST_MESSAGE =
AttributeKey.valueOf("request");
+ /**
+ * The key for the current request ID.
+ */
+ public static final AttributeKey<UUID> REQUEST_ID =
AttributeKey.valueOf("requestId");
+
/**
* The key for the current {@link AuthenticatedUser}.
*/
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 23c77c1ad3..bd7e85170b 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
@@ -70,12 +70,14 @@ import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import static
org.apache.tinkerpop.gremlin.server.handler.HttpRequestIdHandler.REQUEST_ID_HEADER_NAME;
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.StringRegularExpression.matchesRegex;
import static org.hamcrest.core.StringStartsWith.startsWith;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
/**
@@ -229,6 +231,7 @@ public class GremlinServerHttpIntegrateTest extends
AbstractGremlinServerIntegra
try (final CloseableHttpResponse response =
httpclient.execute(httpget)) {
assertEquals(405, response.getStatusLine().getStatusCode());
+ assertTrue(response.containsHeader(REQUEST_ID_HEADER_NAME));
}
}
@@ -241,18 +244,7 @@ public class GremlinServerHttpIntegrateTest extends
AbstractGremlinServerIntegra
try (final CloseableHttpResponse response =
httpclient.execute(httppost)) {
assertEquals(401, response.getStatusLine().getStatusCode());
- }
- }
-
- @Test
- public void should400OnPOSTWithNonUUIDRequestId() throws Exception {
- final CloseableHttpClient httpclient = HttpClients.createDefault();
- final HttpPost httppost = new
HttpPost(TestClientFactory.createURLString());
- httppost.addHeader("Content-Type", "application/json");
- httppost.setEntity(new StringEntity("{\"gremlin\":\"2-1\",
\"requestId\":\"nonsense\"}", Consts.UTF_8));
-
- try (final CloseableHttpResponse response =
httpclient.execute(httppost)) {
- assertEquals(400, response.getStatusLine().getStatusCode());
+ assertTrue(response.containsHeader(REQUEST_ID_HEADER_NAME));
}
}
@@ -865,6 +857,7 @@ public class GremlinServerHttpIntegrateTest extends
AbstractGremlinServerIntegra
try (final CloseableHttpResponse response =
httpclient.execute(httppost)) {
assertEquals(200, response.getStatusLine().getStatusCode()); //
Temporarily 200 OK.
+ assertTrue(response.containsHeader(REQUEST_ID_HEADER_NAME));
final String json = EntityUtils.toString(response.getEntity());
final JsonNode node = mapper.readTree(json);
assertEquals("Division by zero",
node.get("status").get("message").asText());
@@ -1196,6 +1189,7 @@ public class GremlinServerHttpIntegrateTest extends
AbstractGremlinServerIntegra
try (final CloseableHttpResponse response =
httpclient.execute(httppost)) {
assertEquals(200, response.getStatusLine().getStatusCode());
+ assertTrue(response.containsHeader(REQUEST_ID_HEADER_NAME));
assertTrue(response.getEntity().isChunked());
final String json = EntityUtils.toString(response.getEntity());
@@ -1223,6 +1217,7 @@ public class GremlinServerHttpIntegrateTest extends
AbstractGremlinServerIntegra
try (final CloseableHttpResponse response =
httpclient.execute(httppost)) {
assertEquals(200, response.getStatusLine().getStatusCode());
+ assertTrue(response.containsHeader(REQUEST_ID_HEADER_NAME));
assertTrue(response.getEntity().isChunked());
final String json = EntityUtils.toString(response.getEntity());
@@ -1383,6 +1378,22 @@ public class GremlinServerHttpIntegrateTest extends
AbstractGremlinServerIntegra
}
}
+ @Test
+ public void shouldIgnoreRequestIdInPostRequest() throws Exception {
+ final UUID requestId =
UUID.fromString("1e55c495-22d5-4a39-934a-a2744ba010ef");
+ final String body = "{ \"gremlin\": \"" + "g.V()" + "\",
\"requestId\": \"" + requestId + "\", \"g\": \"gmodern" + "\", \"language\":
\"gremlin-lang\"}";
+ final CloseableHttpClient httpclient = HttpClients.createDefault();
+ final HttpPost httppost = new
HttpPost(TestClientFactory.createURLString());
+ httppost.addHeader("Content-Type", "application/json");
+ httppost.setEntity(new StringEntity(body, Consts.UTF_8));
+
+ try (final CloseableHttpResponse response =
httpclient.execute(httppost)) {
+ assertEquals(200, response.getStatusLine().getStatusCode());
+ assertTrue(response.containsHeader(REQUEST_ID_HEADER_NAME));
+ assertNotEquals(requestId,
UUID.fromString(response.getLastHeader(REQUEST_ID_HEADER_NAME).getValue()));
+ }
+ }
+
@Test
public void should100onExpectContinueRequest() throws Exception {
final GraphSONMessageSerializerV4 serializer = new
GraphSONMessageSerializerV4();
diff --git
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestIdHandlerTest.java
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestIdHandlerTest.java
new file mode 100644
index 0000000000..0fc2247b6e
--- /dev/null
+++
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestIdHandlerTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.channel.embedded.EmbeddedChannel;
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+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.HttpMethod;
+import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.HttpResponse;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpServerCodec;
+import io.netty.handler.codec.http.HttpVersion;
+import io.netty.util.CharsetUtil;
+import org.junit.Test;
+
+import java.util.UUID;
+
+import static
org.apache.tinkerpop.gremlin.server.handler.HttpRequestIdHandler.REQUEST_ID_HEADER_NAME;
+import static org.junit.Assert.assertTrue;
+
+public class HttpRequestIdHandlerTest {
+ @Test
+ public void shouldProvideRequestIdToFollowingHandlers() {
+ final HttpRequestIdHandler httpRequestIdHandler = new
HttpRequestIdHandler();
+ final EmbeddedChannel testChannel = new EmbeddedChannel(new
HttpServerCodec(), httpRequestIdHandler);
+
+ final ByteBuf buffer = testChannel.alloc().buffer();
+ buffer.writeCharSequence("abc", CharsetUtil.UTF_8);
+ final FullHttpRequest httpRequest = new
DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/gremlin",
buffer);
+
+ testChannel.writeInbound(httpRequest);
+ testChannel.finish();
+
+ assertTrue(testChannel.readInbound() instanceof HttpRequest);
+ assertTrue(testChannel.attr(StateKey.REQUEST_ID) != null);
+
+ buffer.release();
+ }
+
+ @Test
+ public void shouldInjectRequestIdHeaderToOutgoingWrites() {
+ final HttpRequestIdHandler httpRequestIdHandler = new
HttpRequestIdHandler();
+ final EmbeddedChannel testServerChannel = new
EmbeddedChannel(httpRequestIdHandler);
+
+ final ByteBuf buffer = testServerChannel.alloc().buffer();
+ buffer.writeCharSequence("abc", CharsetUtil.UTF_8);
+ final FullHttpRequest httpRequest = new
DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/gremlin",
buffer);
+
+ final FullHttpResponse httpResponse = new
DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
+
+ testServerChannel.writeInbound(httpRequest);
+ testServerChannel.writeOutbound(httpResponse);
+ final HttpResponse updatedResponse = testServerChannel.readOutbound();
+
+ testServerChannel.finish();
+ assertTrue(updatedResponse.headers().contains(REQUEST_ID_HEADER_NAME));
+ UUID.fromString(updatedResponse.headers().get(REQUEST_ID_HEADER_NAME));
+
+ httpRequest.release();
+ httpResponse.release();
+ }
+}
diff --git
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestMessageDecoderTest.java
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestMessageDecoderTest.java
index e7e43467b1..f3c421c28e 100644
---
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestMessageDecoderTest.java
+++
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/handler/HttpRequestMessageDecoderTest.java
@@ -72,9 +72,7 @@ public class HttpRequestMessageDecoderTest {
final HttpRequestMessageDecoder requestMessageDecoder = new
HttpRequestMessageDecoder(serializers);
final EmbeddedChannel testChannel = new EmbeddedChannel(new
HttpServerCodec(), new HttpObjectAggregator(Integer.MAX_VALUE),
requestMessageDecoder);
- final RequestMessageV4 request = RequestMessageV4.build("g.V()")
- .overrideRequestId(UUID.randomUUID())
- .create();
+ final RequestMessageV4 request =
RequestMessageV4.build("g.V()").create();
final ByteBuf buffer =
graphSONSerializer.serializeRequestMessageV4(request, allocator);
@@ -98,9 +96,7 @@ public class HttpRequestMessageDecoderTest {
final HttpRequestMessageDecoder requestMessageDecoder = new
HttpRequestMessageDecoder(serializers);
final EmbeddedChannel testChannel = new EmbeddedChannel(new
HttpServerCodec(), new HttpObjectAggregator(Integer.MAX_VALUE),
requestMessageDecoder);
- final RequestMessageV4 request = RequestMessageV4.build("g.V()")
- .overrideRequestId(UUID.randomUUID())
- .create();
+ final RequestMessageV4 request =
RequestMessageV4.build("g.V()").addLanguage("gremlin-lang").create();
final ByteBuf buffer =
graphBinarySerializer.serializeRequestMessageV4(request, allocator);
diff --git
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/Tokens.java
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/Tokens.java
index 04064357d5..e1a122e143 100644
--- a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/Tokens.java
+++ b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/Tokens.java
@@ -55,8 +55,7 @@ public final class Tokens {
public static final String ARGS_BINDINGS = "bindings";
/**
- * Argument name that allows definition of alias names for {@link Graph}
and {@link TraversalSource} objects on
- * the remote system.
+ * @Deprecated
*/
public static final String ARGS_ALIASES = "aliases";
diff --git
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/message/RequestMessageV4.java
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/message/RequestMessageV4.java
index 4718716ae4..65c2b0029b 100644
---
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/message/RequestMessageV4.java
+++
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/message/RequestMessageV4.java
@@ -29,7 +29,7 @@ import java.util.Optional;
import java.util.UUID;
/**
- * The model for a request message in the HTTP body that is sent to the server.
+ * The model for a request message in the HTTP body that is sent to the server
beginning in 4.0.0.
*/
public final class RequestMessageV4 {
/**
@@ -152,16 +152,6 @@ public final class RequestMessageV4 {
this.fields.put(Tokens.REQUEST_ID, UUID.randomUUID());
}
- /**
- * Override the request identifier with a specified one, otherwise the
{@link Builder} will randomly generate
- * a {@link UUID}.
- */
- public Builder overrideRequestId(final UUID requestId) {
- Objects.requireNonNull(requestId, "requestId argument cannot be
null.");
- this.fields.put(Tokens.REQUEST_ID, requestId);
- return this;
- }
-
public Builder addLanguage(final String language) {
Objects.requireNonNull(language, "language argument cannot be
null.");
this.fields.put(Tokens.ARGS_LANGUAGE, language);
diff --git
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/message/ResponseMessage.java
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/message/ResponseMessage.java
index 6dc2780dd5..94dbca4a3a 100644
---
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/message/ResponseMessage.java
+++
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/message/ResponseMessage.java
@@ -111,10 +111,6 @@ public final class ResponseMessage {
return new Builder(requestMessage);
}
- public static Builder build(final RequestMessageV4 requestMessage) {
- return new Builder(requestMessage);
- }
-
public static Builder build(final UUID requestId) {
return new Builder(requestId);
}
diff --git
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV4.java
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV4.java
index 513d8cca80..489bf0ac4f 100644
---
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV4.java
+++
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV4.java
@@ -286,9 +286,6 @@ public abstract class AbstractGraphSONMessageSerializerV4
extends AbstractGraphS
public RequestMessageV4 createObject(final Map<String, Object> data) {
RequestMessageV4.Builder builder =
RequestMessageV4.build(data.get(SerTokens.TOKEN_GREMLIN));
- if (data.containsKey(SerTokens.TOKEN_REQUEST)) {
-
builder.overrideRequestId(UUID.fromString(data.get(SerTokens.TOKEN_REQUEST).toString()));
- }
if (data.containsKey(SerTokens.TOKEN_LANGUAGE)) {
builder.addLanguage(data.get(SerTokens.TOKEN_LANGUAGE).toString());
}
diff --git
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/binary/RequestMessageSerializerV4.java
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/binary/RequestMessageSerializerV4.java
index addf5cbc53..5c083f5cd1 100644
---
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/binary/RequestMessageSerializerV4.java
+++
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/binary/RequestMessageSerializerV4.java
@@ -63,9 +63,6 @@ public class RequestMessageSerializerV4 {
}
final RequestMessageV4.Builder builder =
RequestMessageV4.build(gremlin);
- if (fields.containsKey(SerTokens.TOKEN_REQUEST)) {
-
builder.overrideRequestId(UUID.fromString(fields.get(SerTokens.TOKEN_REQUEST).toString()));
- }
if (fields.containsKey(SerTokens.TOKEN_LANGUAGE)) {
builder.addLanguage(fields.get(SerTokens.TOKEN_LANGUAGE).toString());
}
diff --git
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/message/RequestMessageV4Test.java
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/message/RequestMessageV4Test.java
index f9e9de312f..5b17aabde6 100644
---
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/message/RequestMessageV4Test.java
+++
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/message/RequestMessageV4Test.java
@@ -30,13 +30,6 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class RequestMessageV4Test {
- @Test
- public void shouldOverrideRequest() {
- final UUID request = UUID.randomUUID();
- final RequestMessageV4 msg =
RequestMessageV4.build("x").overrideRequestId(request).create();
- assertEquals(request, msg.getRequestId());
- }
-
@Test
public void shouldSetScriptGremlin() {
final String script = "g.V().both()";
@@ -93,17 +86,15 @@ public class RequestMessageV4Test {
@Test
public void shouldGetFields() {
final String g = "gmodern";
- final UUID rId = UUID.randomUUID();
final String lang = "lang";
final String query = "g.V()";
final Map<String, Object> bindings = new HashMap<>();
bindings.put("b", "c");
bindings.put("g", "gmodern");
- final RequestMessageV4 msg =
RequestMessageV4.build(query).addG(g).addBindings(bindings).addLanguage(lang).overrideRequestId(rId).create();
+ final RequestMessageV4 msg =
RequestMessageV4.build(query).addG(g).addBindings(bindings).addLanguage(lang).create();
final Map<String, Object> fields = msg.getFields();
assertEquals(g, fields.get(Tokens.ARGS_G));
- assertEquals(rId, fields.get(Tokens.REQUEST_ID));
assertEquals(lang, fields.get(Tokens.ARGS_LANGUAGE));
assertEquals(bindings, fields.get(Tokens.ARGS_BINDINGS));
assertEquals(query, msg.getGremlin());