package io.gitlab.leibnizhu.sbnetty.request; import com.google.common.primitives.Ints; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.LastHttpContent; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import java.io.IOException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static com.google.common.base.Preconditions.checkNotNull; /** * 将Netty的http 解码结果解析成Servlet支持的 */ public class HttpRequestInputStream extends ServletInputStream { private final Channel channel; //netty ChannelHandlerContext的channel private AtomicBoolean closed; //输入流是否已经关闭,保证线程安全 private final BlockingQueue<HttpContent> queue; //HttpContent的队列,一次请求可能有多次加入 private HttpContent current; private int currentLength; private ReadListener readListener; public HttpRequestInputStream(Channel channel) { this.channel = checkNotNull(channel); this.closed = new AtomicBoolean(); queue = new LinkedBlockingQueue<>(); } public void addContent(HttpContent httpContent) { checkNotClosed(); queue.offer(httpContent.retain()); } public int getCurrentLength() { return currentLength; } @Override public int readLine(byte[] b, int off, int len) throws IOException { checkNotNull(b); return super.readLine(b, off, len); //模板方法,会调用当前类实现的read()方法 } /** * 本次请求没再有新的HttpContent输入,而且当前的内容全部被读完 * @return true=读取完毕 反之false */ @Override public boolean isFinished() { checkNotClosed(); return isLastContent() && current.content().readableBytes() == 0; } /** * 已经传入本次请求所有HttpContent */ private boolean isLastContent() { return current instanceof LastHttpContent; } /** * 已读入至少一次HttpContent且未读取完所有内容,或者HttpContent队列非空 */ @Override public boolean isReady() { checkNotClosed(); return (current != null && current.content().readableBytes() > 0) || !queue.isEmpty(); } @Override public void setReadListener(ReadListener readListener) { checkNotClosed(); checkNotNull(readListener); this.readListener = readListener; } /** * 跳过n个字节 */ @Override public long skip(long n) throws IOException { checkNotClosed(); ByteBuf content = current.content(); long skipLen = Math.min(content.readableBytes(), n); //实际可以跳过的字节数 content.skipBytes(Ints.checkedCast(skipLen)); return skipLen; } /** * @return 可读字节数 */ @Override public int available() throws IOException { return null == current ? 0 : current.content().readableBytes(); } @Override public void close() throws IOException { if (closed.compareAndSet(false, true)) { closeHttpContentQueue(); closeCurrentHttpContent(); } } /** * 关闭当前HttpContent */ public void closeCurrentHttpContent() { if(current != null){ current.release(); current = null; } } /** * 关闭HttpContent队列 */ private void closeHttpContentQueue() { while(!queue.isEmpty()){ HttpContent content = queue.poll(); if(content != null){ content.release(); } } queue.clear(); } /** * 尝试更新current,然后读取len个字节并复制到b中(off下标开始) * @return 实际读取的字节数 */ @Override public int read(byte[] b, int off, int len) throws IOException { checkNotNull(b); if (0 == len) { return 0; } poll(); if (isFinished()) { return -1; } ByteBuf byteBuf = readContent(len);//读取len个字节 int readableBytes = byteBuf.readableBytes(); byteBuf.readBytes(b, off, readableBytes);//复制到b return readableBytes - byteBuf.readableBytes();//返回实际读取的字节数 } /** * 尝试更新current,然后读取一个字节,并返回 */ @Override public int read() throws IOException { poll(); if (isFinished()) { return -1; } return readContent(1).getByte(0); } /** * 从current的HttpContent中读取length个字节 */ private ByteBuf readContent(int length) { ByteBuf content = current.content(); if (length < content.readableBytes()) { return content.readSlice(length); } else { return content; } } /** * 如果没有可读字节了,从HttpContent队列中获取一个到current中 * 如果readListener非空,则非阻塞,读不到数据也直接返回 * @throws IOException channel非激活状态 */ private void poll() throws IOException { checkNotClosed(); if (null == current || current.content().readableBytes() == 0) { boolean blocking = null == readListener; while (!isLastContent()) { //current为空,或者current不是当前请求最后一个HttpContent try { current = queue.poll(1000, TimeUnit.NANOSECONDS); } catch (InterruptedException ignored) { } if(current != null){ this.currentLength = current.content().readableBytes(); } if (current != null || !blocking) { //队列中读取到数据,或者readListener非空(非阻塞),则退出 break; } if (!channel.isActive()) { throw new IOException("Channel is not active"); } } } } private void checkNotClosed() { if (closed.get()) { throw new IllegalStateException("Stream is closed"); } } }