/** * Copyright 2013-2015 Seagate Technology LLC. * * This Source Code Form is subject to the terms of the Mozilla * Public License, v. 2.0. If a copy of the MPL was not * distributed with this file, You can obtain one at * https://mozilla.org/MP:/2.0/. * * This program is distributed in the hope that it will be useful, * but is provided AS-IS, WITHOUT ANY WARRANTY; including without * the implied warranty of MERCHANTABILITY, NON-INFRINGEMENT or * FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public * License for more details. * * See www.openkinetic.org for more project information */ package com.seagate.kinetic.simulator.io.provider.nio.http; import static io.netty.handler.codec.http.HttpHeaders.getHost; import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION; import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH; import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.QueryStringDecoder; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import com.seagate.kinetic.common.lib.KineticMessage; import com.seagate.kinetic.proto.Kinetic.Command; import com.seagate.kinetic.proto.Kinetic.Message; import com.seagate.kinetic.proto.KineticIo.ExtendedMessage; import com.seagate.kinetic.simulator.io.provider.spi.MessageService; /** * * Please note: This class is for evaluation only and in prototype state. * * @author chiaming * */ public class HttpMessageServiceHandler extends SimpleChannelInboundHandler<Object> { private static final Logger logger = Logger .getLogger(HttpMessageServiceHandler.class.getName()); private MessageService lcservice = null; public HttpMessageServiceHandler(MessageService lcservice2) { this.lcservice = lcservice2; } @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { int contentLength = 0; if (msg instanceof HttpRequest) { HttpRequest request = (HttpRequest) msg; logger.finest("protocol version: " + request.getProtocolVersion()); logger.finest("host: " + getHost(request, "unknown")); logger.finest("REQUEST_URI: " + request.getUri()); List<Map.Entry<String, String>> headers = request.headers() .entries(); String lenstr = request.headers().get( HttpHeaders.Names.CONTENT_LENGTH); contentLength = Integer.parseInt(lenstr); logger.finest("content length=" + contentLength); if (!headers.isEmpty()) { for (Map.Entry<String, String> h : request.headers().entries()) { String key = h.getKey(); String value = h.getValue(); logger.finest("HEADER: " + key + " = " + value); } } QueryStringDecoder queryStringDecoder = new QueryStringDecoder( request.getUri()); Map<String, List<String>> params = queryStringDecoder.parameters(); if (!params.isEmpty()) { for (Entry<String, List<String>> p : params.entrySet()) { String key = p.getKey(); List<String> vals = p.getValue(); for (String val : vals) { logger.finest("PARAM: " + key + " = " + val); } } } } // create extended builder ExtendedMessage.Builder extendedMessage = ExtendedMessage.newBuilder(); if (msg instanceof HttpContent) { HttpContent httpContent = (HttpContent) msg; ByteBuf content = httpContent.content(); if (content.isReadable()) { byte[] body = new byte[contentLength]; content.getBytes(0, body); // read from serialized bytes extendedMessage.mergeFrom(body); } // build message ExtendedMessage extended = extendedMessage.build(); if (logger.isLoggable(Level.FINEST)) { logger.finest("received request: " + extended); } // create kinetic message for processing KineticMessage km = new KineticMessage(); // set interface message km.setMessage(extended.getInterfaceMessage()); // get command bytes ByteString commandBytes = extendedMessage.getInterfaceMessage().getCommandBytes(); // build command Command.Builder commandBuilder = Command.newBuilder(); try { commandBuilder.mergeFrom(commandBytes); km.setCommand(commandBuilder.build()); } catch (InvalidProtocolBufferException e) { logger.log(Level.WARNING, e.getMessage(), e); } // set value if (extended.hasValue()) { km.setValue(extended.getValue().toByteArray()); } // process request KineticMessage kmresp = this.lcservice.processRequest(km); // construct response message ExtendedMessage.Builder extendedResponse = ExtendedMessage .newBuilder(); // set interface message extendedResponse.setInterfaceMessage((Message.Builder) kmresp .getMessage()); // set value if (kmresp.getValue() != null) { extendedResponse.setValue(ByteString.copyFrom(kmresp .getValue())); } // get serialized value ByteBuf data = Unpooled.copiedBuffer(extendedResponse.build() .toByteArray()); FullHttpResponse httpResponse = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK, data); httpResponse.headers() .set(CONTENT_TYPE, "application/octet-stream"); httpResponse.headers().set(HttpHeaders.Names.CONTENT_ENCODING, HttpHeaders.Values.BINARY); httpResponse.headers().set(CONTENT_LENGTH, httpResponse.content().readableBytes()); httpResponse.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); // send response message ctx.writeAndFlush(httpResponse); if (logger.isLoggable(Level.FINEST)) { logger.finest("wrote and flushed response: " + kmresp); } } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.log(Level.WARNING, "Unexpected exception from downstream.", cause); ctx.close(); } }