net.anyflow.menton.http.HttpRequestRouter.java Source code

Java tutorial

Introduction

Here is the source code for net.anyflow.menton.http.HttpRequestRouter.java

Source

/*
 * Copyright 2016 The Menton Project
 * 
 * Licensed 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.
 */

package net.anyflow.menton.http;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;

import com.google.common.io.Files;

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.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion;
import net.anyflow.lannister.Settings;

/**
 * @author anyflow
 */
public class HttpRequestRouter extends SimpleChannelInboundHandler<FullHttpRequest> {

    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(HttpRequestRouter.class);

    private boolean isWebResourcePath(String path) {
        return Settings.SELF.webResourceExtensionToMimes().keySet().stream().anyMatch(s -> path.endsWith("." + s));
    }

    /*
     * (non-Javadoc)
     * 
     * @see io.netty.channel.SimpleChannelInboundHandler#channelRead0(io.netty.
     * channel.ChannelHandlerContext, java.lang.Object)
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        if (HttpHeaderValues.WEBSOCKET.toString().equalsIgnoreCase(request.headers().get(HttpHeaderNames.UPGRADE))
                && HttpHeaderValues.UPGRADE.toString()
                        .equalsIgnoreCase(request.headers().get(HttpHeaderNames.CONNECTION))) {

            if (ctx.pipeline().get(WebsocketFrameHandler.class) == null) {
                logger.error("No WebSocket Handler available.");

                ctx.channel()
                        .writeAndFlush(
                                new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN))
                        .addListener(ChannelFutureListener.CLOSE);
                return;
            }

            ctx.fireChannelRead(request.retain());
            return;
        }

        if (HttpUtil.is100ContinueExpected(request)) {
            ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE));
            return;
        }

        HttpResponse response = HttpResponse.createServerDefault(request.headers().get(HttpHeaderNames.COOKIE));

        String requestPath = new URI(request.uri()).getPath();

        if (isWebResourcePath(requestPath)) {
            handleWebResourceRequest(ctx, request, response, requestPath);
        } else {
            try {
                processRequest(ctx, request, response);
            } catch (URISyntaxException e) {
                response.setStatus(HttpResponseStatus.NOT_FOUND);
                logger.info("unexcepted URI : {}", request.uri());
            } catch (Exception e) {
                response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
                logger.error("Unknown exception was thrown in business logic handler.\r\n" + e.getMessage(), e);
            }
        }
    }

    /**
     * @param response
     * @param webResourceRequestPath
     * @throws IOException
     */
    private void handleWebResourceRequest(ChannelHandlerContext ctx, FullHttpRequest rawRequest,
            HttpResponse response, String webResourceRequestPath) throws IOException {
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(webResourceRequestPath);

        if (is == null) {
            String rootPath = (new File(Settings.SELF.webResourcePhysicalRootPath(), webResourceRequestPath))
                    .getPath();
            try {
                is = new FileInputStream(rootPath);
            } catch (FileNotFoundException e) {
                is = null;
            }
        }

        if (is == null) {
            response.setStatus(HttpResponseStatus.NOT_FOUND);
        } else {
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();

            int nRead;
            byte[] data = new byte[16384];

            while ((nRead = is.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, nRead);
            }

            buffer.flush();
            response.content().writeBytes(buffer.toByteArray());

            String ext = Files.getFileExtension(webResourceRequestPath);
            response.headers().set(HttpHeaderNames.CONTENT_TYPE,
                    Settings.SELF.webResourceExtensionToMimes().get(ext));

            is.close();
        }

        setDefaultHeaders(rawRequest, response);

        if ("true".equalsIgnoreCase(Settings.SELF.getProperty("menton.logging.writeHttpResponse"))) {
            logger.info(response.toString());
        }

        ctx.write(response);
    }

    private void processRequest(ChannelHandlerContext ctx, FullHttpRequest rawRequest, HttpResponse response)
            throws InstantiationException, IllegalAccessException, IOException, URISyntaxException {

        HttpRequestHandler.MatchedCriterion mc = HttpRequestHandler
                .findRequestHandler((new URI(rawRequest.uri())).getPath(), rawRequest.method().toString());

        if (mc.requestHandlerClass() == null) {
            response.setStatus(HttpResponseStatus.NOT_FOUND);
            logger.info("unexcepted URI : {}", rawRequest.uri());

            response.headers().add(HttpHeaderNames.CONTENT_TYPE, "text/html");

            response.setContent(response.toString());
        } else {
            HttpRequest request = new HttpRequest(rawRequest, mc.pathParameters());

            HttpRequestHandler handler = mc.requestHandlerClass().newInstance();

            String webResourcePath = handler.getClass().getAnnotation(HttpRequestHandler.Handles.class)
                    .webResourcePath();
            if ("none".equals(webResourcePath) == false) {
                handleWebResourceRequest(ctx, rawRequest, response, webResourcePath);
                return;
            }

            handler.initialize(request, response);

            if ("true".equalsIgnoreCase(Settings.SELF.getProperty("menton.logging.writeHttpRequest"))) {
                logger.info(request.toString());
            }

            response.setContent(handler.service());
        }

        setDefaultHeaders(rawRequest, response);

        if ("true".equalsIgnoreCase(Settings.SELF.getProperty("menton.logging.writeHttpResponse"))) {
            logger.info(response.toString());
        }

        ctx.write(response);
    }

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

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.error(cause.getMessage(), cause);
        ctx.close();
    }

    protected static void setDefaultHeaders(FullHttpRequest request, HttpResponse response) {
        response.headers().add(HttpHeaderNames.SERVER,

                net.anyflow.lannister.Settings.SELF.getProperty("menton.version"));

        boolean keepAlive = request.headers().get(HttpHeaderNames.CONNECTION) == HttpHeaderValues.KEEP_ALIVE
                .toString();
        if (keepAlive) {
            response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        }

        if (Settings.SELF.getProperty("menton.httpServer.allowCrossDomain", "false").equalsIgnoreCase("true")) {
            response.headers().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
            response.headers().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS, "POST, GET, PUT, DELETE");
            response.headers().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS, "X-PINGARUNER");
            response.headers().add(HttpHeaderNames.ACCESS_CONTROL_MAX_AGE, "1728000");
        }

        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
    }
}