use of io.netty.handler.codec.http.HttpHeaderNames.CONNECTION in project ignite-3 by apache.
the class RestApiHandler method channelRead0.
/**
* {@inheritDoc}
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
CompletableFuture<DefaultFullHttpResponse> responseFuture = router.route(request).map(route -> {
var response = new RestApiHttpResponse(new DefaultHttpResponse(HttpVersion.HTTP_1_1, OK));
return route.handle(request, response).thenApply(resp -> {
ByteBuf content = resp.content() != null ? Unpooled.wrappedBuffer(resp.content()) : new EmptyByteBuf(UnpooledByteBufAllocator.DEFAULT);
return new DefaultFullHttpResponse(resp.protocolVersion(), resp.status(), content, resp.headers(), EmptyHttpHeaders.INSTANCE);
});
}).orElseGet(() -> CompletableFuture.completedFuture(new DefaultFullHttpResponse(request.protocolVersion(), HttpResponseStatus.NOT_FOUND)));
responseFuture.whenCompleteAsync((response, e) -> {
if (e != null) {
exceptionCaught(ctx, e);
return;
}
response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
boolean keepAlive = HttpUtil.isKeepAlive(request);
if (keepAlive) {
if (!request.protocolVersion().isKeepAliveDefault()) {
response.headers().set(CONNECTION, KEEP_ALIVE);
}
} else {
response.headers().set(CONNECTION, CLOSE);
}
ChannelFuture f = ctx.writeAndFlush(response);
if (!keepAlive) {
f.addListener(ChannelFutureListener.CLOSE);
}
}, ctx.executor());
}
use of io.netty.handler.codec.http.HttpHeaderNames.CONNECTION in project GraphScope by alibaba.
the class MaxGraphHttpGremlinEndpointHandler method channelRead.
@Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) {
if (msg instanceof FullHttpRequest) {
final FullHttpRequest req = (FullHttpRequest) msg;
if ("/favicon.ico".equals(req.uri())) {
sendError(ctx, NOT_FOUND, "Gremlin Server doesn't have a favicon.ico");
ReferenceCountUtil.release(msg);
return;
}
if (HttpUtil.is100ContinueExpected(req)) {
ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));
}
if (req.method() != GET && req.method() != POST && req.method() != HEAD) {
sendError(ctx, METHOD_NOT_ALLOWED, METHOD_NOT_ALLOWED.toString());
ReferenceCountUtil.release(msg);
return;
}
final Quartet<String, Map<String, Object>, String, Map<String, String>> requestArguments;
try {
requestArguments = getRequestArguments(req);
} catch (IllegalArgumentException iae) {
sendError(ctx, BAD_REQUEST, iae.getMessage());
ReferenceCountUtil.release(msg);
return;
}
final String acceptString = Optional.ofNullable(req.headers().get("Accept")).orElse("application/json");
final Pair<String, MessageTextSerializer> serializer = chooseSerializer(acceptString);
if (null == serializer) {
sendError(ctx, BAD_REQUEST, String.format("no serializer for requested Accept header: %s", acceptString));
ReferenceCountUtil.release(msg);
return;
}
final String origin = req.headers().get(ORIGIN);
final boolean keepAlive = HttpUtil.isKeepAlive(req);
// not using the req any where below here - assume it is safe to release at this point.
ReferenceCountUtil.release(msg);
try {
logger.debug("Processing request containing script [{}] and bindings of [{}] on {}", requestArguments.getValue0(), requestArguments.getValue1(), Thread.currentThread().getName());
if (settings.authentication.enableAuditLog) {
String address = ctx.channel().remoteAddress().toString();
if (address.startsWith("/") && address.length() > 1)
address = address.substring(1);
auditLogger.info("User with address {} requested: {}", address, requestArguments.getValue0());
}
final ChannelPromise promise = ctx.channel().newPromise();
final AtomicReference<Object> resultHolder = new AtomicReference<>();
promise.addListener(future -> {
// processing of the exception
if (future.isSuccess()) {
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());
ByteBuf content = (ByteBuf) resultHolder.get();
final FullHttpResponse response = req.method() == GET ? new DefaultFullHttpResponse(HTTP_1_1, OK, content) : new DefaultFullHttpResponse(HTTP_1_1, OK);
response.headers().set(CONTENT_TYPE, serializer.getValue0());
response.headers().set(CONTENT_LENGTH, content.readableBytes());
// handle cors business
if (origin != null)
response.headers().set(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
if (!keepAlive) {
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
} else {
response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE);
ctx.writeAndFlush(response);
}
}
});
final Timer.Context timerContext = evalOpTimer.time();
final Bindings bindings;
try {
bindings = createBindings(requestArguments.getValue1(), requestArguments.getValue3());
} catch (IllegalStateException iae) {
sendError(ctx, BAD_REQUEST, iae.getMessage());
ReferenceCountUtil.release(msg);
return;
}
// provide a transform function to serialize to message - this will force
// serialization to occur
// in the same thread as the eval. after the CompletableFuture is returned from the
// eval the result
// is ready to be written as a ByteBuf directly to the response. nothing should be
// blocking here.
String script = requestArguments.getValue0();
final CompletableFuture<Object> evalFuture = gremlinExecutor.eval(script, requestArguments.getValue2(), bindings, FunctionUtils.wrapFunction(o -> {
// stopping the timer here is roughly equivalent to
// where the timer would have been stopped for
// this metric in other contexts. we just want to
// measure eval time not serialization time.
timerContext.stop();
logger.debug("Transforming result of request with script" + " [{}] and bindings of [{}] with result" + " of [{}] on [{}]", requestArguments.getValue0(), requestArguments.getValue1(), o, Thread.currentThread().getName());
List<Object> resultList = opProcessor.processHttpGraphTraversal(script, o, settings.scriptEvaluationTimeout, req);
final ResponseMessage responseMessage = ResponseMessage.build(UUID.randomUUID()).code(ResponseStatusCode.SUCCESS).result(resultList).create();
// http server is sessionless and must handle commit on
// transactions. the commit occurs
// before serialization to be consistent with how things
// work for websocket based
// communication. this means that failed serialization
// does not mean that you won't get
// a commit to the database
attemptCommit(requestArguments.getValue3(), graphManager, settings.strictTransactionManagement);
try {
return Unpooled.wrappedBuffer(serializer.getValue1().serializeResponseAsString(responseMessage).getBytes(UTF8));
} catch (Exception ex) {
logger.warn(String.format("Error during serialization for %s", responseMessage), ex);
throw ex;
}
}));
evalFuture.exceptionally(t -> {
if (t.getMessage() != null)
sendError(ctx, INTERNAL_SERVER_ERROR, t.getMessage(), Optional.of(t));
else
sendError(ctx, INTERNAL_SERVER_ERROR, String.format("Error encountered evaluating script: %s", requestArguments.getValue0()), Optional.of(t));
promise.setFailure(t);
return null;
});
evalFuture.thenAcceptAsync(r -> {
// now that the eval/serialization is done in the same thread - complete
// the promise so we can
// write back the HTTP response on the same thread as the original
// request
resultHolder.set(r);
promise.setSuccess();
}, gremlinExecutor.getExecutorService());
} catch (Exception ex) {
// tossed to exceptionCaught which delegates to sendError method
final Throwable t = ExceptionUtils.getRootCause(ex);
throw new RuntimeException(null == t ? ex : t);
}
}
}
Aggregations