com.linkedin.r2.transport.http.client.RAPRequestEncoder.java Source code

Java tutorial

Introduction

Here is the source code for com.linkedin.r2.transport.http.client.RAPRequestEncoder.java

Source

/*
   Copyright (c) 2015 LinkedIn Corp.
    
   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 com.linkedin.r2.transport.http.client;

import com.linkedin.data.ByteString;
import com.linkedin.r2.filter.R2Constants;
import com.linkedin.r2.message.stream.StreamRequest;
import com.linkedin.r2.message.stream.entitystream.ReadHandle;
import com.linkedin.r2.message.stream.entitystream.Reader;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultHttpContent;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.LastHttpContent;

/**
 * This encoder encodes StreamRequest to Netty's HttpRequest.
 *
 * @author Zhenkai Zhu
 */
/** package private */
class RAPRequestEncoder extends ChannelDuplexHandler {
    private static final int MAX_BUFFERED_CHUNKS = 10;
    // this threshold is to mitigate the effect of the inter-play of Nagle's algorithm & Delayed ACK
    // when sending requests with small entity
    private static final int FLUSH_THRESHOLD = R2Constants.DEFAULT_DATA_CHUNK_SIZE;
    private volatile BufferedReader _currentReader;

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        if (msg instanceof StreamRequest) {
            StreamRequest request = (StreamRequest) msg;
            HttpRequest nettyRequest = NettyRequestAdapter.toNettyRequest(request);
            ctx.write(nettyRequest, promise);
            _currentReader = new BufferedReader(ctx, MAX_BUFFERED_CHUNKS, FLUSH_THRESHOLD);
            request.getEntityStream().setReader(_currentReader);
        } else {
            _currentReader = null;
            ctx.write(msg, promise);
        }
    }

    @Override
    public void flush(ChannelHandlerContext ctx) throws Exception {
        if (_currentReader != null) {
            _currentReader.flush();
        } else {
            ctx.flush();
        }
    }

    /**
     * A reader that has pipelining/buffered reading
     *
     * Buffering is actually done by Netty; we just enforce the upper bound of the buffering
     */
    private class BufferedReader implements Reader {
        private final int _maxBufferedChunks;
        private final int _flushThreshold;
        private final ChannelHandlerContext _ctx;
        private volatile ReadHandle _readHandle;
        private int _notFlushedBytes;
        private int _notFlushedChunks;

        BufferedReader(ChannelHandlerContext ctx, int maxBufferedChunks, int flushThreshold) {
            _maxBufferedChunks = maxBufferedChunks;
            _flushThreshold = flushThreshold;
            _ctx = ctx;
            _notFlushedBytes = 0;
            _notFlushedChunks = 0;
        }

        public void onInit(ReadHandle rh) {
            _readHandle = rh;
        }

        public void onDataAvailable(final ByteString data) {
            HttpContent content = new DefaultHttpContent(Unpooled.wrappedBuffer(data.asByteBuffer()));
            _ctx.write(content).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    // this will not be invoked until flush() is called and the data is actually written to socket
                    _readHandle.request(1);
                }
            });

            _notFlushedBytes += data.length();
            _notFlushedChunks++;
            if (_notFlushedBytes >= _flushThreshold || _notFlushedChunks == _maxBufferedChunks) {
                _ctx.flush();
                _notFlushedBytes = 0;
                _notFlushedChunks = 0;
            }
        }

        public void onDone() {
            _currentReader = null;
            _ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
        }

        public void onError(Throwable e) {
            _currentReader = null;
            _ctx.fireExceptionCaught(e);
        }

        private void flush() {
            _readHandle.request(_maxBufferedChunks);
        }
    }
}