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

io.higgs.http.client.HTTPStreamingRequest Maven / Gradle / Ivy

package io.higgs.http.client;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.higgs.core.func.Function1;
import io.higgs.http.client.readers.Reader;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoopGroup;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.HttpChunkedInput;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.stream.ChunkedInput;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.concurrent.GenericFutureListener;

import java.net.URI;
import java.util.LinkedList;
import java.util.Queue;

/**
 * @author Courtney Robinson 
 */
public class HTTPStreamingRequest extends Request {
    public HTTPStreamingRequest(HttpRequestBuilder builder, EventLoopGroup group, URI uri, Reader f) {
        super(builder, group, uri, HttpMethod.POST, HttpVersion.HTTP_1_1, f);
    }

    @Override
    protected void newNettyRequest(URI uri, HttpMethod method, HttpVersion version) {
        request = new DefaultHttpRequest(version, method, uri.getRawPath());
        headers().set(HttpHeaders.Names.REFERER, originalUri == null ? uri.toString() : originalUri.toString());
    }

    public FutureResponse execute(Function1 conf) {
        if (!request.headers().contains(HttpHeaders.Names.CONTENT_TYPE)) {
            request.headers().set(HttpHeaders.Names.CONTENT_TYPE, "application/json");
        }
        if (!request.headers().contains(HttpHeaders.Names.CONTENT_LENGTH)) {
            request.headers().remove(HttpHeaders.Names.CONTENT_LENGTH);
        }
        request.headers().set(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
        return super.execute(conf);
    }

    public void onReady(final Function1 listener) {
        if (connectFuture == null) {
            throw new IllegalStateException("Not connected");
        }
        connectFuture.addListener(new GenericFutureListener() {
            public void operationComplete(ChannelFuture future) throws Exception {
                if (future.isSuccess()) {
                    StreamSender sender = new StreamSender(channel);
                    for (String name : channel.pipeline().names()) {
                        ChannelHandler handler = channel.pipeline().get(name);
                        if (handler instanceof ChunkedWriteHandler) {
                            sender.setWriteHandler((ChunkedWriteHandler) handler);
                            break;
                        }
                    }
                    if (!sender.hasChunkedHandler()) {
                        throw new IllegalStateException("A chunked write handler must be in the pipeline");
                    }
                    listener.apply(sender);
                }
            }
        });
    }

    public static class StreamSender {
        protected final ObjectMapper MAPPER = new ObjectMapper();
        private final Channel channel;
        protected boolean stopped;
        protected ChunkedWriteHandler chunkedHandler;
        protected Queue queue = new LinkedList<>();
        protected boolean needToResume;
        protected ChunkedInput input = new ChunkedInput() {

            @Override
            public boolean isEndOfInput() throws Exception {
                return stopped;
            }

            @Override
            public void close() throws Exception {
            }

            @Override
            public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception {
                if (stopped || queue.size() == 0) {
                    needToResume = true;
                    return null;
                } else {
                    needToResume = false;
                    return queue.poll();
                }
            }
        };

        public StreamSender(Channel channel) {
            if (channel == null) {
                throw new IllegalArgumentException("Channel cannot be null");
            }
            this.channel = channel;
        }

        public StreamSender send(Object content) throws JsonProcessingException {
            return send(Unpooled.wrappedBuffer(MAPPER.writeValueAsBytes(content)));
        }

        public StreamSender send(final String content) {
            return send(Unpooled.wrappedBuffer(content.getBytes()));
        }

        public synchronized StreamSender send(final ByteBuf content) {
            if (chunkedHandler == null) {
                throw new IllegalStateException("ChunkedWriteHandler must be present in the pipeline");
            }
            queue.add(content);
            channel.pipeline().writeAndFlush(new HttpChunkedInput(input));
            if (needToResume) {
                chunkedHandler.resumeTransfer();
            }
            return this;
        }

        public void setWriteHandler(ChunkedWriteHandler writeHandler) {
            this.chunkedHandler = writeHandler;
        }

        public boolean hasChunkedHandler() {
            return chunkedHandler != null;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy