/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.coyote.http11.upgrade; import java.io.EOFException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.Selector; import org.apache.tomcat.util.net.NioChannel; import org.apache.tomcat.util.net.NioEndpoint; import org.apache.tomcat.util.net.NioSelectorPool; import org.apache.tomcat.util.net.SocketWrapper; public class NioServletInputStream extends AbstractServletInputStream { private final NioChannel channel; private final NioSelectorPool pool; public NioServletInputStream(SocketWrapper<NioChannel> wrapper, NioSelectorPool pool) { this.channel = wrapper.getSocket(); this.pool = pool; } @Override protected boolean doIsReady() throws IOException { ByteBuffer readBuffer = channel.getBufHandler().getReadBuffer(); if (readBuffer.remaining() > 0) { return true; } readBuffer.clear(); fillReadBuffer(false); boolean isReady = readBuffer.position() > 0; readBuffer.flip(); return isReady; } @Override protected int doRead(boolean block, byte[] b, int off, int len) throws IOException { ByteBuffer readBuffer = channel.getBufHandler().getReadBuffer(); int remaining = readBuffer.remaining(); // Is there enough data in the read buffer to satisfy this request? if (remaining >= len) { readBuffer.get(b, off, len); return len; } // Copy what data there is in the read buffer to the byte array int leftToWrite = len; int newOffset = off; if (remaining > 0) { readBuffer.get(b, off, remaining); leftToWrite -= remaining; newOffset += remaining; } // Fill the read buffer as best we can readBuffer.clear(); int nRead = fillReadBuffer(block); // Full as much of the remaining byte array as possible with the data // that was just read if (nRead > 0) { readBuffer.flip(); if (nRead > leftToWrite) { readBuffer.get(b, newOffset, leftToWrite); leftToWrite = 0; } else { readBuffer.get(b, newOffset, nRead); leftToWrite -= nRead; } } else if (nRead == 0) { readBuffer.flip(); } else if (nRead == -1) { // TODO i18n throw new EOFException(); } return len - leftToWrite; } @Override protected void doClose() throws IOException { channel.close(); } private int fillReadBuffer(boolean block) throws IOException { int nRead; if (block) { Selector selector = null; try { selector = pool.get(); } catch ( IOException x ) { // Ignore } try { NioEndpoint.KeyAttachment att = (NioEndpoint.KeyAttachment) channel.getAttachment(); if (att == null) { throw new IOException("Key must be cancelled."); } nRead = pool.read(channel.getBufHandler().getReadBuffer(), channel, selector, att.getTimeout()); } catch (EOFException eof) { nRead = -1; } finally { if (selector != null) { pool.put(selector); } } } else { nRead = channel.read(channel.getBufHandler().getReadBuffer()); } return nRead; } }