org.bridje.http.impl.HttpServerChannelHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.bridje.http.impl.HttpServerChannelHandler.java

Source

/*
 * Copyright 2016 Bridje Framework.
 *
 * 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 org.bridje.http.impl;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
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 static io.netty.handler.codec.http.HttpHeaders.Names.COOKIE;
import static io.netty.handler.codec.http.HttpHeaders.Names.SERVER;
import static io.netty.handler.codec.http.HttpHeaders.Names.SET_COOKIE;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
import io.netty.handler.codec.http.multipart.Attribute;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.DiskAttribute;
import io.netty.handler.codec.http.multipart.DiskFileUpload;
import io.netty.handler.codec.http.multipart.FileUpload;
import io.netty.handler.codec.http.multipart.HttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bridje.ioc.Ioc;
import org.bridje.http.HttpBridletContext;
import org.bridje.http.HttpBridletRequest;
import org.bridje.http.HttpBridletResponse;

/**
 *
 */
class HttpServerChannelHandler extends SimpleChannelInboundHandler<HttpObject> {
    private static final Logger LOG = Logger.getLogger(HttpServerChannelHandler.class.getName());

    private HttpBridletContext context;

    private HttpBridletRequestImpl req;

    private HttpBridletResponseImpl resp;

    private final HttpServerImpl server;

    private HttpPostRequestDecoder decoder;

    private static final HttpDataFactory FACTORY = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); // Disk if size exceed

    static {
        DiskFileUpload.deleteOnExitTemporaryFile = true; // should delete file
                                                         // on exit (in normal
                                                         // exit)
        DiskFileUpload.baseDirectory = null; // system temp directory
        DiskAttribute.deleteOnExitTemporaryFile = true; // should delete file on
                                                        // exit (in normal exit)
        DiskAttribute.baseDirectory = null; // system temp directory
    }

    public HttpServerChannelHandler(HttpServerImpl server) {
        this.server = server;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws IOException {
        if (!msg.getDecoderResult().isSuccess()) {
            sendBadRequest(ctx);
            return;
        }
        if (msg instanceof HttpRequest) {
            HttpRequest httpReq = (HttpRequest) msg;
            if (HttpHeaders.is100ContinueExpected(httpReq)) {
                ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE));
            }
            readHeaders(ctx, httpReq);
        } else if (msg instanceof HttpContent) {
            HttpContent httpCont = (HttpContent) msg;
            readContent(ctx, httpCont);
        } else {
            sendBadRequest(ctx);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        LOG.log(Level.SEVERE, cause.getMessage(), cause);
        ctx.close();
        closeAll();
    }

    private void sendResponse(ChannelHandlerContext ctx) throws IOException {
        resp.close();
        int length = resp.getBuffer().readableBytes();
        DefaultHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
                HttpResponseStatus.valueOf(resp.getStatusCode()), resp.getBuffer());
        resp.setHeader(SERVER, server.getServerName());
        resp.setHeader(CONTENT_TYPE, resp.getContentType());
        resp.setHeader(CONTENT_LENGTH, length);
        resp.setHeader(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
        Map<String, Object> headers = resp.getHeadersMap();
        for (Map.Entry<String, Object> entry : headers.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            if (value instanceof Iterable) {
                response.headers().set(key, (Iterable<?>) value);
            } else {
                response.headers().set(key, value);
            }
        }
        writeCookies(response);
        ctx.write(response);
        ctx.flush();
        closeAll();
    }

    private void sendBadRequest(ChannelHandlerContext ctx) {
        LOG.log(Level.WARNING, "Bad Request Received....");
        DefaultHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST);
        response.headers().set(SERVER, server.getServerName());
        response.headers().set(CONTENT_TYPE, "text/html");
        response.headers().set(CONTENT_LENGTH, 0);
        response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
        ctx.write(response);
        ctx.flush();
        closeAll();
    }

    private void readHeaders(ChannelHandlerContext ctx, HttpRequest msg) {
        if (req == null && context == null) {
            context = new HttpBridletContextImpl();
            req = new HttpBridletRequestImpl(msg);
            QueryStringDecoder decoderQuery = new QueryStringDecoder(msg.getUri());
            req.setQueryString(decoderQuery.parameters());
            req.setCookies(parseCookies(msg.headers().get(COOKIE)));
            // new getMethod

            if (req.isForm()) {
                decoder = new HttpPostRequestDecoder(FACTORY, msg);
            }
        } else {
            sendBadRequest(ctx);
        }
    }

    private void readContent(ChannelHandlerContext ctx, HttpContent msg) throws IOException {
        if (req != null) {
            if (req.isForm()) {
                try {
                    decoder.offer(msg);
                    readHttpDataChunkByChunk();
                } catch (HttpPostRequestDecoder.EndOfDataDecoderException ex) {
                    handleRequest(ctx);
                    return;
                } catch (DecoderException e) {
                    LOG.log(Level.WARNING, e.getMessage());
                    sendBadRequest(ctx);
                    return;
                }
            } else {
                req.setContent(msg.content());
            }

            //if is the last http content
            if (msg instanceof LastHttpContent) {
                handleRequest(ctx);
            }
        } else {
            sendBadRequest(ctx);
        }
    }

    private void readHttpDataChunkByChunk() throws IOException {
        while (decoder.hasNext()) {
            InterfaceHttpData data = decoder.next();
            if (data != null) {
                try {
                    // new value
                    writeHttpData(data);
                } finally {
                    data.release();
                }
            }
        }
    }

    private void writeHttpData(InterfaceHttpData data) throws IOException {
        if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
            Attribute attribute = (Attribute) data;
            String value = attribute.getValue();

            if (value.length() > 65535) {
                throw new IOException("Data too long");
            }
            req.addPostParameter(attribute.getName(), value);
        } else {
            if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) {
                FileUpload fileUpload = (FileUpload) data;
                req.addFileUpload(fileUpload);
            }
        }
    }

    private void handleRequest(ChannelHandlerContext ctx) throws IOException {
        if (resp == null) {
            resp = new HttpBridletResponseImpl(ctx.alloc().buffer());
        }
        RootHttpBridlet rootHandler = Ioc.context().find(RootHttpBridlet.class);
        context.set(HttpBridletRequest.class, req);
        context.set(HttpBridletResponse.class, resp);
        rootHandler.handle(context);
        sendResponse(ctx);
    }

    private void closeAll() {
        if (resp != null) {
            resp.release();
        }
        if (req != null) {
            req.release();
        }
        context = null;
        req = null;
        resp = null;
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) {
        if (decoder != null) {
            decoder.cleanFiles();
        }
    }

    public Set<Cookie> parseCookies(String cookiesHeader) {
        Set<Cookie> cookies;
        if (cookiesHeader == null) {
            cookies = Collections.emptySet();
        } else {
            cookies = ServerCookieDecoder.STRICT.decode(cookiesHeader);
        }
        return cookies;
    }

    private void writeCookies(DefaultHttpResponse response) {
        if (resp.getCookies() != null && !resp.getCookies().isEmpty()) {
            // Reset the cookies if necessary.
            resp.getCookies().forEach((name, cookie) -> {
                response.headers().add(SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie.getInternalCookie()));
            });
        }
    }
}