io.vertx.core.http.impl.FileStreamChannel.java Source code

Java tutorial

Introduction

Here is the source code for io.vertx.core.http.impl.FileStreamChannel.java

Source

/*
 * Copyright (c) 2011-2013 The original author or authors
 *  ------------------------------------------------------
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  and Apache License v2.0 which accompanies this distribution.
 *
 *      The Eclipse Public License is available at
 *      http://www.eclipse.org/legal/epl-v10.html
 *
 *      The Apache License v2.0 is available at
 *      http://www.opensource.org/licenses/apache2.0.php
 *
 *  You may elect to redistribute this code under either of these licenses.
 */

package io.vertx.core.http.impl;

import io.netty.buffer.ByteBuf;
import io.netty.channel.AbstractChannel;
import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelConfig;
import io.netty.channel.EventLoop;
import io.netty.channel.nio.NioEventLoop;
import io.netty.handler.stream.ChunkedFile;
import io.netty.handler.stream.ChunkedStream;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;

import java.io.RandomAccessFile;
import java.net.SocketAddress;

/**
 * A channel used for writing a file in an HTTP2 stream.
 *
 * @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
 */
class FileStreamChannel extends AbstractChannel {

    private static final SocketAddress LOCAL_ADDRESS = new StreamSocketAddress();
    private static final SocketAddress REMOTE_ADDRESS = new StreamSocketAddress();
    private static final ChannelMetadata METADATA = new ChannelMetadata(true);

    private final ChannelConfig config = new DefaultChannelConfig(this);
    private boolean active;
    private boolean closed;
    private long bytesWritten;
    private final VertxHttp2Stream stream;

    FileStreamChannel(Handler<AsyncResult<Long>> resultHandler, VertxHttp2Stream stream, long offset, long length) {
        super(null, Id.INSTANCE);

        pipeline().addLast(new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();
                pipeline.addLast(new ChunkedWriteHandler());
                pipeline.addLast(new ChannelInboundHandlerAdapter() {
                    @Override
                    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                        if (evt instanceof RandomAccessFile) {
                            ChannelFuture fut = ctx.writeAndFlush(new ChunkedFile((RandomAccessFile) evt, offset,
                                    length, 8192 /* default chunk size */ ));
                            fut.addListener(f -> {
                                if (resultHandler != null) {
                                    if (f.isSuccess()) {
                                        resultHandler.handle(Future.succeededFuture(bytesWritten));
                                    } else {
                                        resultHandler.handle(Future.failedFuture(f.cause()));
                                    }
                                }
                                fut.addListener(ChannelFutureListener.CLOSE);
                            });
                        }
                    }
                });
            }
        });

        this.stream = stream;
    }

    final Handler<Void> drainHandler = v -> {
        flush();
    };

    @Override
    protected void doRegister() throws Exception {
        active = true;
    }

    @Override
    protected AbstractUnsafe newUnsafe() {
        return new DefaultUnsafe();
    }

    @Override
    protected boolean isCompatible(EventLoop loop) {
        return loop instanceof NioEventLoop;
    }

    @Override
    protected SocketAddress localAddress0() {
        return LOCAL_ADDRESS;
    }

    @Override
    protected SocketAddress remoteAddress0() {
        return REMOTE_ADDRESS;
    }

    @Override
    protected void doBind(SocketAddress localAddress) throws Exception {
    }

    @Override
    protected void doDisconnect() throws Exception {
        doClose();
    }

    @Override
    protected void doClose() throws Exception {
        active = false;
        closed = true;
    }

    @Override
    protected void doBeginRead() throws Exception {
    }

    @Override
    protected void doWrite(ChannelOutboundBuffer in) throws Exception {
        ByteBuf chunk;
        while (!stream.isNotWritable() && (chunk = (ByteBuf) in.current()) != null) {
            bytesWritten += chunk.readableBytes();
            stream.writeData(chunk.retain(), false);
            stream.handlerContext.flush();
            in.remove();
        }
    }

    @Override
    public ChannelConfig config() {
        return config;
    }

    @Override
    public boolean isOpen() {
        return !closed;
    }

    @Override
    public boolean isActive() {
        return active;
    }

    @Override
    public ChannelMetadata metadata() {
        return METADATA;
    }

    private static class StreamSocketAddress extends SocketAddress {
        @Override
        public String toString() {
            return "stream";
        }
    }

    private class DefaultUnsafe extends AbstractUnsafe {
        @Override
        public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
            safeSetSuccess(promise);
        }
    }

    static class Id implements ChannelId {

        static final ChannelId INSTANCE = new Id();

        private Id() {
        }

        @Override
        public String asShortText() {
            return toString();
        }

        @Override
        public String asLongText() {
            return toString();
        }

        @Override
        public int compareTo(ChannelId o) {
            if (o instanceof Id) {
                return 0;
            }

            return asLongText().compareTo(o.asLongText());
        }

        @Override
        public int hashCode() {
            return 0;
        }

        @Override
        public boolean equals(Object obj) {
            return obj instanceof Id;
        }

        @Override
        public String toString() {
            return "stream";
        }
    }
}