package io.blobkeeper.server.handler; /* * Copyright (C) 2015 by Denis M. Gabaydulin * * 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. */ import com.google.common.collect.ImmutableMap; import io.blobkeeper.cluster.service.ClusterMembershipService; import io.blobkeeper.common.domain.api.ReturnValue; import io.blobkeeper.common.logging.MdcContext; import io.blobkeeper.server.util.ErrorCodeResolver; import io.blobkeeper.server.util.JsonUtils; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.handler.codec.http.*; import io.netty.util.CharsetUtil; import org.jetbrains.annotations.NotNull; import javax.inject.Inject; import static io.blobkeeper.common.logging.MdcContext.SRC_NODE; import static io.blobkeeper.common.util.MdcUtils.setCurrentContext; import static io.netty.buffer.Unpooled.copiedBuffer; import static io.netty.channel.ChannelFutureListener.CLOSE; import static io.netty.handler.codec.http.HttpHeaders.Names.*; import static io.netty.handler.codec.http.HttpHeaders.Values.KEEP_ALIVE; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_0; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import static io.netty.util.CharsetUtil.UTF_8; public abstract class BaseFileHandler<T> extends SimpleChannelInboundHandler<T> { @Inject private JsonUtils jsonUtils; @Inject private ErrorCodeResolver errorCodeResolver; @Inject protected ClusterMembershipService clusterMembershipService; protected void writeResponse(Channel channel, String result, HttpRequest request) { // Convert the response content to a ChannelBuffer. ByteBuf buf = copiedBuffer(result, CharsetUtil.UTF_8); // Decide whether to close the connection or not. boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request.headers().get(CONNECTION)) || request.getProtocolVersion().equals(HTTP_1_0) && !KEEP_ALIVE.equalsIgnoreCase(request.headers().get(CONNECTION)); // Build the response object. FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, buf); response.headers().set(CONTENT_TYPE, "application/json; charset=UTF-8"); if (!close) { // There's no need to add 'Content-Length' header // if this is the last response. response.headers().set(CONTENT_LENGTH, buf.readableBytes()); } // Write the response. ChannelFuture future = channel.writeAndFlush(response); // Close the connection after the write operation is done if necessary. if (close) { future.addListener(ChannelFutureListener.CLOSE); } } protected void sendError(ChannelHandlerContext ctx, HttpResponseStatus status, io.blobkeeper.common.domain.Error error) { sendError(ctx, status, new ReturnValue<>(error)); } protected void sendError(ChannelHandlerContext ctx, HttpResponseStatus status, ReturnValue<?> returnValue) { FullHttpResponse response = new DefaultFullHttpResponse( HTTP_1_1, status, Unpooled.copiedBuffer(getJson(returnValue), UTF_8) ); response.headers().set(CONTENT_TYPE, "application/json; charset=UTF-8"); // Close the connection as soon as the error message is sent. ctx.writeAndFlush(response).addListener(CLOSE); } protected void writeResponse(ChannelHandlerContext ctx, ReturnValue<?> returnValue, HttpRequest request) { if (returnValue.hasError()) { HttpResponseStatus responseStatus = errorCodeResolver.getResponseStatus(returnValue.getError().getCode()); sendError(ctx, responseStatus, returnValue); } else { writeResponse(ctx.channel(), getJson(returnValue), request); } } protected String getJson(@NotNull Object responseContent) { return jsonUtils.toJson(responseContent); } protected void setContext() { setCurrentContext(new MdcContext(ImmutableMap.of(SRC_NODE, clusterMembershipService.getSelfNode().toString()))); } }