reactor.io.net.impl.netty.http.NettyHttpServerHandler.java Source code

Java tutorial

Introduction

Here is the source code for reactor.io.net.impl.netty.http.NettyHttpServerHandler.java

Source

/*
 * Copyright (c) 2011-2015 Pivotal Software Inc, All Rights Reserved.
 *
 * 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 reactor.io.net.impl.netty.http;

import io.netty.buffer.ByteBufHolder;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultHttpContent;
import io.netty.handler.codec.http.DefaultLastHttpContent;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.LastHttpContent;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.io.buffer.Buffer;
import reactor.io.net.ChannelStream;
import reactor.io.net.ReactorChannelHandler;
import reactor.io.net.impl.netty.NettyChannelHandlerBridge;
import reactor.io.net.impl.netty.NettyChannelStream;
import reactor.rx.action.support.DefaultSubscriber;

/**
 * Conversion between Netty types  and Reactor types ({@link NettyHttpChannel} and {@link reactor.io.buffer.Buffer}).
 *
 * @author Stephane Maldini
 */
public class NettyHttpServerHandler<IN, OUT> extends NettyChannelHandlerBridge<IN, OUT> {

    private final NettyChannelStream<IN, OUT> tcpStream;
    protected NettyHttpChannel<IN, OUT> request;

    public NettyHttpServerHandler(ReactorChannelHandler<IN, OUT, ChannelStream<IN, OUT>> handler,
            NettyChannelStream<IN, OUT> tcpStream) {
        super(handler, tcpStream);
        this.tcpStream = tcpStream;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelActive();
        ctx.read();
    }

    @Override
    public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
        Class<?> messageClass = msg.getClass();
        if (request == null && io.netty.handler.codec.http.HttpRequest.class.isAssignableFrom(messageClass)) {
            request = new NettyHttpChannel<>(tcpStream, (io.netty.handler.codec.http.HttpRequest) msg);

            final Publisher<Void> closePublisher = handler.apply(request);
            final Subscriber<Void> closeSub = new DefaultSubscriber<Void>() {

                Subscription subscription;

                @Override
                public void onSubscribe(Subscription s) {
                    subscription = s;
                    s.request(Long.MAX_VALUE);
                }

                @Override
                public void onError(Throwable t) {
                    log.error("Error processing connection. Closing the channel.", t);
                    if (channelSubscription == null && ctx.channel().isOpen()) {
                        ctx.channel().close();
                    }
                }

                @Override
                public void onComplete() {
                    subscription.cancel();
                    if (ctx.channel().isOpen()) {
                        if (log.isDebugEnabled()) {
                            log.debug("Close Http Response ");
                        }
                        writeLast(ctx);
                        //ctx.channel().close();
                        /*
                        ctx.channel().writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(new ChannelFutureListener() {
                           @Override
                           public void operationComplete(ChannelFuture future) throws Exception {
                              if(ctx.channel().isOpen()){
                                 ctx.channel().close();
                              }
                           }
                        });
                         */
                    }
                }
            };

            if (request.checkHeader()) {
                writeFirst(ctx).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (future.isSuccess()) {
                            closePublisher.subscribe(closeSub);
                        } else {
                            closeSub.onError(future.cause());
                        }
                    }
                });
            } else {
                closePublisher.subscribe(closeSub);
            }

        }
        if (HttpContent.class.isAssignableFrom(messageClass)) {
            super.channelRead(ctx, ((ByteBufHolder) msg).content());
        }
        postRead(ctx, msg);
    }

    protected void postRead(ChannelHandlerContext ctx, Object msg) {
        if (channelSubscription != null && DefaultLastHttpContent.class.equals(msg.getClass())) {
            channelSubscription.onComplete();
            channelSubscription = null;
        }
    }

    protected void writeLast(ChannelHandlerContext ctx) {
        ctx.channel().writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE);
    }

    protected ChannelFuture writeFirst(ChannelHandlerContext ctx) {
        return ctx.writeAndFlush(request.getNettyResponse());
    }

    @Override
    protected ChannelFuture doOnWrite(final Object data, final ChannelHandlerContext ctx) {
        if (data.getClass().equals(Buffer.class)) {
            return ctx.write(new DefaultHttpContent(convertBufferToByteBuff(ctx, (Buffer) data)));
        } else {
            return ctx.write(data);
        }
    }

    NettyHttpServerHandler<IN, OUT> withWebsocketSupport(String url, String protocols) {
        //prevent further header to be sent for handshaking
        if (!request.checkHeader()) {
            log.error("Cannot enable websocket if headers have already been sent");
            return this;
        }
        return new NettyHttpWSServerHandler<>(url, protocols, this);
    }
}