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

com.stony.reactor.jersey.HttpContentInputStream Maven / Gradle / Ivy

package com.stony.reactor.jersey;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 

reactor-netty-ext *

com.stony.reactor.jersey * * @author stony * @version 上午11:30 * @since 2018/1/19 * @see netflix.karyon.transport.util.HttpContentInputStream */ public class HttpContentInputStream extends InputStream { private static final Logger logger = LoggerFactory.getLogger(HttpContentInputStream.class); private final Lock lock = new ReentrantLock(); private volatile boolean isClosed = false; private volatile boolean isCompleted = false; private volatile Throwable completedWithError = null; private final Condition contentAvailabilityMonitor = lock.newCondition(); private final ByteBuf contentBuffer; public HttpContentInputStream(final ByteBufAllocator allocator, final Flux content) { contentBuffer = allocator.buffer(); content.subscribe(byteBuf -> { lock.lock(); try { //This is not only to stop writing 0 bytes as it might seems //In case of no payload request, like GET //We are getting onNext( 0 bytes ), onComplete during the stress conditions //AFTER we say subscriber.onCompleted() and tiered down and close //request stream, this doesn't contradict logic, in fact 0 bytes is just the same as nothing to write //but that save us annoying log record every time this happens if (byteBuf.readableBytes() > 0) { contentBuffer.writeBytes(byteBuf); } contentAvailabilityMonitor.signalAll(); } catch (Exception e) { logger.error("Error on server", e); } finally { lock.unlock(); } }, throwable -> { lock.lock(); try { completedWithError = throwable; isCompleted = true; logger.error("Flux Observer, got error: " + throwable.getMessage()); contentAvailabilityMonitor.signalAll(); } finally { lock.unlock(); } }, () -> { lock.lock(); try { isCompleted = true; logger.debug("Processing complete"); contentAvailabilityMonitor.signalAll(); } finally { lock.unlock(); } }); } @Override public int available() throws IOException { return isCompleted ? contentBuffer.readableBytes() : 0; } @Override public void mark(int readlimit) { contentBuffer.markReaderIndex(); } @Override public boolean markSupported() { return true; } @Override public int read() throws IOException { lock.lock(); try { if (!await()) { return -1; } return contentBuffer.readByte() & 0xff; } finally { lock.unlock(); } } @Override public int read(final byte[] b, final int off, final int len) throws IOException { if (b == null) { throw new NullPointerException("Null buffer"); } if (len < 0 || off < 0 || len > b.length - off) { throw new IndexOutOfBoundsException("Invalid index"); } if (len == 0) { return 0; } lock.lock(); try { if (!await()) { return -1; } int size = Math.min(len, contentBuffer.readableBytes()); contentBuffer.readBytes(b, off, size); return size; } finally { lock.unlock(); } } @Override public void close() throws IOException { //we need a sync block here, as the double close sometimes is reality and we want to decrement ref. counter only once synchronized (this) { if (isClosed) { return; } isClosed = true; } contentBuffer.release(); } @Override public void reset() throws IOException { contentBuffer.resetReaderIndex(); } @Override public long skip(long n) throws IOException { //per InputStream contract, if n is negative, 0 bytes are skipped if (n <= 0) { return 0; } if (n > Integer.MAX_VALUE) { return skipBytes(Integer.MAX_VALUE); } else { return skipBytes((int) n); } } private int skipBytes(int n) throws IOException { int nBytes = Math.min(available(), n); contentBuffer.skipBytes(nBytes); return nBytes; } private boolean await() throws IOException { //await in here while (!isCompleted && !contentBuffer.isReadable()) { try { contentAvailabilityMonitor.await(); } catch (InterruptedException e) { // Restore interrupt status and bailout Thread.currentThread().interrupt(); logger.error("Interrupted: " + e.getMessage()); throw new IOException(e); } } if (completedWithError != null) { throw new IOException(completedWithError); } if (isCompleted && !contentBuffer.isReadable()) { return false; } return true; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy