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

org.bridje.http.impl.HttpServerChannelHandler Maven / Gradle / Ivy

/*
 * 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.http.HttpBridletContext;
import org.bridje.http.HttpBridletRequest;
import org.bridje.http.HttpBridletResponse;
import org.bridje.ioc.Ioc;

class HttpServerChannelHandler extends SimpleChannelInboundHandler
{
    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 headers = resp.getHeadersMap();
        for (Map.Entry entry : headers.entrySet())
        {
            String key = entry.getKey();
            Object value = entry.getValue();
            if(value != null)
            {
                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 parseCookies(String cookiesHeader)
    {
        Set 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()));
            });
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy