All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.blade.server.netty.HttpServerHandler Maven / Gradle / Ivy

There is a newer version: 2.0.15.RELEASE
Show newest version
package com.blade.server.netty;

import com.blade.Blade;
import com.blade.exception.BladeException;
import com.blade.exception.ExceptionResolve;
import com.blade.kit.BladeKit;
import com.blade.mvc.WebContext;
import com.blade.mvc.handler.RouteViewResolve;
import com.blade.mvc.hook.Signature;
import com.blade.mvc.hook.WebHook;
import com.blade.mvc.http.HttpRequest;
import com.blade.mvc.http.HttpResponse;
import com.blade.mvc.http.Request;
import com.blade.mvc.http.Response;
import com.blade.mvc.route.Route;
import com.blade.mvc.route.RouteHandler;
import com.blade.mvc.route.RouteMatcher;
import com.blade.mvc.ui.DefaultUI;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
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.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import lombok.extern.slf4j.Slf4j;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import static com.blade.mvc.Const.ENV_KEY_PAGE_404;
import static com.blade.mvc.Const.ENV_KEY_PAGE_500;
import static io.netty.handler.codec.http.HttpUtil.is100ContinueExpected;

/**
 * @author biezhi
 * 2017/5/31
 */
@Slf4j
@ChannelHandler.Sharable
public class HttpServerHandler extends SimpleChannelInboundHandler {

    private final Blade             blade;
    private final RouteMatcher      routeMatcher;
    private final Set       statics;
    private final Optional  page404;
    private final Optional  page500;
    private final SessionHandler    sessionHandler;
    private final StaticFileHandler staticFileHandler;
    private final RouteViewResolve  routeViewResolve;
    private final ExceptionResolve  exceptionResolve;

    HttpServerHandler(Blade blade, ExceptionResolve exceptionResolve) {
        this.blade = blade;
        this.statics = blade.getStatics();
        this.exceptionResolve = exceptionResolve;

        this.page404 = Optional.ofNullable(blade.environment().get(ENV_KEY_PAGE_404, null));
        this.page500 = Optional.ofNullable(blade.environment().get(ENV_KEY_PAGE_500, null));

        this.routeMatcher = blade.routeMatcher();
        this.routeViewResolve = new RouteViewResolve(blade);
        this.staticFileHandler = new StaticFileHandler(blade);
        this.sessionHandler = blade.sessionManager() != null ? new SessionHandler(blade) : null;
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest) throws Exception {
        if (is100ContinueExpected(fullHttpRequest)) {
            ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE));
        }

        Request  request  = HttpRequest.build(ctx, fullHttpRequest, sessionHandler);
        Response response = HttpResponse.build(ctx, blade.templateEngine());

        // route signature
        Signature signature = Signature.builder().request(request).response(response).build();

        try {
            // request uri
            String uri = request.uri();
            log.debug("{}\t{}\t{}", request.protocol(), request.method(), uri);

            // write session
            WebContext.set(new WebContext(request, response));

            if (isStaticFile(uri)) {
                staticFileHandler.handle(ctx, request, response);
                return;
            }

            Route route = routeMatcher.lookupRoute(request.method(), uri);
            if (null == route) {
                // 404
                response.notFound();
                if (page404.isPresent()) {
                    response.render(page404.get());
                } else {
                    String html = String.format(DefaultUI.VIEW_404, uri);
                    response.html(html);
                }
                return;
            }
            request.initPathParams(route);

            // get method parameters
            signature.setRoute(route);

            // middleware
            if (!invokeMiddleware(routeMatcher.getMiddleware(), signature)) {
                this.sendFinish(response);
                return;
            }

            // web hook before
            if (!invokeHook(routeMatcher.getBefore(uri), signature)) {
                this.sendFinish(response);
                return;
            }

            // execute
            signature.setRoute(route);
            this.routeHandle(signature);

            // webHook
            this.invokeHook(routeMatcher.getAfter(uri), signature);

        } catch (Exception e) {
            if (null != exceptionResolve && !exceptionResolve.handle(e, signature)) {
            } else {
                throw e;
            }
        } finally {
            this.sendFinish(response);
            WebContext.remove();
        }
    }

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

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("error", cause);
        if (!ctx.channel().isActive()) {
            ctx.close();
            return;
        }
        Response     response = WebContext.response();
        String       error    = cause.getMessage();
        StringWriter sw       = new StringWriter();
        PrintWriter  writer   = new PrintWriter(sw);
        if (page500.isPresent()) {
            cause.printStackTrace(writer);
            WebContext.request().attribute("err_message", error);
            WebContext.request().attribute("err_stackTrace", sw.toString());
            response.render(page500.get());
        } else {
            if (blade.devMode()) {
                writer.write(String.format(DefaultUI.ERROR_START, cause.getClass() + " : " + cause.getMessage()));
                writer.write("\r\n");
                cause.printStackTrace(writer);
                writer.println(DefaultUI.HTML_FOOTER);
                error = sw.toString();
                response.html(error);
            } else {
                response.body("Internal Server Error");
            }
        }
    }

    private boolean isStaticFile(String uri) {
        Optional result = statics.stream().filter(s -> s.equals(uri) || uri.startsWith(s)).findFirst();
        return result.isPresent();
    }

    /**
     * Actual routing method execution
     *
     * @param signature signature
     */
    private boolean routeHandle(Signature signature) throws Exception {
        Object target = signature.getRoute().getTarget();
        if (null == target) {
            Class clazz = signature.getAction().getDeclaringClass();
            target = blade.getBean(clazz);
            signature.getRoute().setTarget(target);
        }
        if (signature.getRoute().getTargetType() == RouteHandler.class) {
            RouteHandler routeHandler = (RouteHandler) target;
            routeHandler.handle(signature.request(), signature.response());
            return false;
        } else {
            return routeViewResolve.handle(signature);
        }
    }

    private boolean invokeMiddleware(List middleware, Signature signature) throws BladeException {
        if (BladeKit.isEmpty(middleware)) {
            return true;
        }
        for (Route route : middleware) {
            WebHook webHook = (WebHook) route.getTarget();
            boolean flag    = webHook.before(signature);
            if (!flag) return false;
        }
        return true;
    }

    /**
     * invoke hooks
     *
     * @param hooks     webHook list
     * @param signature http request
     * @return
     * @throws BladeException
     */
    private boolean invokeHook(List hooks, Signature signature) throws BladeException {
        for (Route route : hooks) {
            if (route.getTargetType() == RouteHandler.class) {
                RouteHandler routeHandler = (RouteHandler) route.getTarget();
                routeHandler.handle(signature.request(), signature.response());
            } else {
                boolean flag = routeViewResolve.invokeHook(signature, route);
                if (!flag) return false;
            }
        }
        return true;
    }

    private void sendFinish(Response response) {
        if (!response.isCommit()) {
            response.body(Unpooled.EMPTY_BUFFER);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy