com.intuit.karate.netty.FeatureServerHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.intuit.karate.netty.FeatureServerHandler.java

Source

/*
 * The MIT License
 *
 * Copyright 2017 Intuit Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.intuit.karate.netty;

import com.intuit.karate.StringUtils;
import com.intuit.karate.core.FeatureBackend;
import com.intuit.karate.http.HttpRequest;
import com.intuit.karate.http.HttpResponse;
import com.intuit.karate.http.HttpUtils;
import com.intuit.karate.http.MultiValuedMap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
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.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.CharsetUtil;

/**
 *
 * @author pthomas3
 */
public class FeatureServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    private final FeatureBackend backend;
    private final Runnable stopFunction;
    private final boolean ssl;

    public FeatureServerHandler(FeatureBackend backend, boolean ssl, Runnable stopFunction) {
        this.backend = backend;
        this.ssl = ssl;
        this.stopFunction = stopFunction;
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    private static final String STOP_URI = "/__admin/stop";

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) {
        long startTime = System.currentTimeMillis();
        backend.getContext().logger.debug("handling method: {}, uri: {}", msg.method(), msg.uri());
        FullHttpResponse nettyResponse;
        if (msg.uri().startsWith(STOP_URI)) {
            backend.getContext().logger.info("stop uri invoked, shutting down");
            ByteBuf responseBuf = Unpooled.copiedBuffer("stopped", CharsetUtil.UTF_8);
            nettyResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, responseBuf);
            stopFunction.run();
        } else {
            StringUtils.Pair url = HttpUtils.parseUriIntoUrlBaseAndPath(msg.uri());
            HttpRequest request = new HttpRequest();
            if (url.left == null) {
                String requestScheme = ssl ? "https" : "http";
                String host = msg.headers().get(HttpUtils.HEADER_HOST);
                request.setUrlBase(requestScheme + "://" + host);
            } else {
                request.setUrlBase(url.left);
            }
            request.setUri(url.right);
            request.setMethod(msg.method().name());
            msg.headers().forEach(h -> request.addHeader(h.getKey(), h.getValue()));
            QueryStringDecoder decoder = new QueryStringDecoder(url.right);
            decoder.parameters().forEach((k, v) -> request.putParam(k, v));
            HttpContent httpContent = (HttpContent) msg;
            ByteBuf content = httpContent.content();
            if (content.isReadable()) {
                byte[] bytes = new byte[content.readableBytes()];
                content.readBytes(bytes);
                request.setBody(bytes);
            }
            HttpResponse response = backend.buildResponse(request, startTime);
            HttpResponseStatus httpResponseStatus = HttpResponseStatus.valueOf(response.getStatus());
            byte[] responseBody = response.getBody();
            if (responseBody != null) {
                ByteBuf responseBuf = Unpooled.copiedBuffer(responseBody);
                nettyResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, httpResponseStatus, responseBuf);
            } else {
                nettyResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, httpResponseStatus);
            }
            MultiValuedMap karateHeaders = response.getHeaders();
            if (karateHeaders != null) {
                HttpHeaders nettyHeaders = nettyResponse.headers();
                karateHeaders.forEach((k, v) -> nettyHeaders.add(k, v));
            }
        }
        ctx.write(nettyResponse);
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        if (cause.getMessage() == null) {
            cause.printStackTrace();
        } else {
            backend.getContext().logger.error("error, closing connection: {}", cause.getMessage());
        }
        ctx.close();
    }

}