You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by mh...@apache.org on 2015/07/23 17:31:04 UTC
[09/16] incubator-tinkerpop git commit: Better process the Accept
header in Gremlin Server for REST.
Better process the Accept header in Gremlin Server for REST.
Not a perfect implementation but better supports multiple items in the header which is typical when browsers make HTTP requests. Doesn't support wildcards in the media type but does support quality settings and *.* (which defaults to JSON).
Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/6913fb3f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/6913fb3f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/6913fb3f
Branch: refs/heads/master
Commit: 6913fb3f9d586691c9955c1f02583f630b515b4d
Parents: d0d9b80
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Wed Jul 22 15:58:23 2015 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Wed Jul 22 15:58:23 2015 -0400
----------------------------------------------------------------------
.../handler/HttpGremlinEndpointHandler.java | 36 +++++++++++++++++---
.../server/GremlinServerHttpIntegrateTest.java | 17 +++++++++
2 files changed, 48 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/6913fb3f/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/handler/HttpGremlinEndpointHandler.java
----------------------------------------------------------------------
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 a9a901c..9e9b8ef 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
@@ -53,6 +53,7 @@ import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.javatuples.Pair;
import org.javatuples.Quartet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -70,6 +71,10 @@ import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static com.codahale.metrics.MetricRegistry.name;
import static io.netty.handler.codec.http.HttpHeaders.Names.*;
@@ -109,6 +114,7 @@ public class HttpGremlinEndpointHandler extends ChannelInboundHandlerAdapter {
private final GremlinExecutor gremlinExecutor;
private final GraphManager graphManager;
+ final Pattern pattern = Pattern.compile("(.*);q=(.*)");
public HttpGremlinEndpointHandler(final Map<String, MessageSerializer> serializers,
final GremlinExecutor gremlinExecutor,
@@ -143,10 +149,9 @@ public class HttpGremlinEndpointHandler extends ChannelInboundHandlerAdapter {
}
final String acceptString = Optional.ofNullable(req.headers().get("Accept")).orElse("application/json");
- final String accept = acceptString.equals("*/*") ? "application/json" : acceptString;
- final MessageTextSerializer serializer = (MessageTextSerializer) serializers.get(accept);
+ final Pair<String, MessageTextSerializer> serializer = chooseSerializer(acceptString);
if (null == serializer) {
- sendError(ctx, BAD_REQUEST, String.format("no serializer for requested Accept header: %s", accept));
+ sendError(ctx, BAD_REQUEST, String.format("no serializer for requested Accept header: %s", acceptString));
ReferenceCountUtil.release(msg);
return;
}
@@ -169,7 +174,7 @@ public class HttpGremlinEndpointHandler extends ChannelInboundHandlerAdapter {
logger.debug("Preparing HTTP response for request with script [{}] and bindings of [{}] with result of [{}] on [{}]",
requestArguments.getValue0(), requestArguments.getValue1(), resultHolder.get(), Thread.currentThread().getName());
final FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, (ByteBuf) resultHolder.get());
- response.headers().set(CONTENT_TYPE, accept);
+ response.headers().set(CONTENT_TYPE, serializer.getValue0());
response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
// handle cors business
@@ -210,7 +215,8 @@ public class HttpGremlinEndpointHandler extends ChannelInboundHandlerAdapter {
.code(ResponseStatusCode.SUCCESS)
.result(IteratorUtils.asList(o)).create();
try {
- Object wrappedBuffer = (Object) Unpooled.wrappedBuffer(serializer.serializeResponseAsString(responseMessage).getBytes(UTF8));
+ Object wrappedBuffer = Unpooled.wrappedBuffer(
+ serializer.getValue1().serializeResponseAsString(responseMessage).getBytes(UTF8));
// http server is sessionless and must handle commit on transactions
this.graphManager.commitAll();
return wrappedBuffer;
@@ -281,6 +287,26 @@ public class HttpGremlinEndpointHandler extends ChannelInboundHandlerAdapter {
return bindings;
}
+ private Pair<String,MessageTextSerializer> chooseSerializer(final String acceptString) {
+ final List<Pair<String,Double>> ordered = Stream.of(acceptString.split(",")).map(mediaType -> {
+ // parse out each mediaType with its params - keeping it simple and just looking for "quality". if
+ // that value isn't there, default it to 1.0. not really validating here so users better get their
+ // accept headers straight
+ final Matcher matcher = pattern.matcher(mediaType);
+ return (matcher.matches()) ? Pair.with(matcher.group(1), Double.parseDouble(matcher.group(2))) : Pair.with(mediaType, 1.0);
+ }).sorted((o1, o2) -> o2.getValue0().compareTo(o1.getValue0())).collect(Collectors.toList());
+
+ for (Pair<String,Double> p : ordered) {
+ // this isn't perfect as it doesn't really account for wildcards. that level of complexity doesn't seem
+ // super useful for gremlin server really.
+ final String accept = p.getValue0().equals("*/*") ? "application/json" : p.getValue0();
+ if (serializers.containsKey(accept))
+ return Pair.with(accept, (MessageTextSerializer) serializers.get(accept));
+ }
+
+ return null;
+ }
+
private static Quartet<String, Map<String, Object>, String, Map<String,String>> getRequestArguments(final FullHttpRequest request) {
if (request.getMethod() == GET) {
final QueryStringDecoder decoder = new QueryStringDecoder(request.getUri());
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/6913fb3f/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java
----------------------------------------------------------------------
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 11ec3a6..3675b1a 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
@@ -587,6 +587,23 @@ public class GremlinServerHttpIntegrateTest extends AbstractGremlinServerIntegra
}
@Test
+ public void should200OnPOSTWithComplexAcceptHeaderDefaultResultToJson() throws Exception {
+ final CloseableHttpClient httpclient = HttpClients.createDefault();
+ final HttpPost httppost = new HttpPost("http://localhost:8182");
+ httppost.addHeader("Content-Type", "*.*;q=0.8,application/xhtml");
+ httppost.addHeader("Accept", "*/*");
+ httppost.setEntity(new StringEntity("{\"gremlin\":\"1-1\"}", Consts.UTF_8));
+
+ try (final CloseableHttpResponse response = httpclient.execute(httppost)) {
+ assertEquals(200, response.getStatusLine().getStatusCode());
+ assertEquals("application/json", response.getEntity().getContentType().getValue());
+ final String json = EntityUtils.toString(response.getEntity());
+ final JsonNode node = mapper.readTree(json);
+ assertEquals(0, node.get("result").get("data").get(0).asInt());
+ }
+ }
+
+ @Test
public void should500OnGETWithGremlinEvalFailure() throws Exception {
final CloseableHttpClient httpclient = HttpClients.createDefault();
final HttpPost httppost = new HttpPost("http://localhost:8182");