| /* |
| * 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.util.concurrent.locks.Lock; |
| import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; |
| |
| import org.apache.juli.logging.Log; |
| import org.apache.juli.logging.LogFactory; |
| import org.apache.tomcat.jni.OS; |
| import org.apache.tomcat.jni.Socket; |
| import org.apache.tomcat.jni.Status; |
| import org.apache.tomcat.util.net.SocketWrapper; |
| |
| public class AprServletInputStream extends AbstractServletInputStream { |
| |
| private static final Log log = LogFactory.getLog(AprServletInputStream.class); |
| |
| private final SocketWrapper<Long> wrapper; |
| private final long socket; |
| private ByteBuffer leftoverInput; |
| private volatile boolean eagain = false; |
| private volatile boolean closed = false; |
| |
| |
| public AprServletInputStream(SocketWrapper<Long> wrapper, ByteBuffer leftoverInput) { |
| this.wrapper = wrapper; |
| this.socket = wrapper.getSocket().longValue(); |
| if (leftoverInput != null) { |
| this.leftoverInput = ByteBuffer.allocate(leftoverInput.remaining()); |
| this.leftoverInput.put(leftoverInput); |
| } |
| } |
| |
| |
| @Override |
| protected int doRead(boolean block, byte[] b, int off, int len) |
| throws IOException { |
| |
| if (closed) { |
| throw new IOException(sm.getString("apr.closed", Long.valueOf(socket))); |
| } |
| |
| if (leftoverInput != null) { |
| if (leftoverInput.remaining() < len) { |
| len = leftoverInput.remaining(); |
| } |
| leftoverInput.get(b, off, len); |
| if (leftoverInput.remaining() == 0) { |
| leftoverInput = null; |
| } |
| return len; |
| } |
| |
| Lock readLock = wrapper.getBlockingStatusReadLock(); |
| WriteLock writeLock = wrapper.getBlockingStatusWriteLock(); |
| |
| boolean readDone = false; |
| int result = 0; |
| readLock.lock(); |
| try { |
| if (wrapper.getBlockingStatus() == block) { |
| result = Socket.recv(socket, b, off, len); |
| readDone = true; |
| } |
| } finally { |
| readLock.unlock(); |
| } |
| |
| if (!readDone) { |
| writeLock.lock(); |
| try { |
| wrapper.setBlockingStatus(block); |
| // Set the current settings for this socket |
| Socket.optSet(socket, Socket.APR_SO_NONBLOCK, (block ? 0 : 1)); |
| // Downgrade the lock |
| readLock.lock(); |
| try { |
| writeLock.unlock(); |
| result = Socket.recv(socket, b, off, len); |
| } finally { |
| readLock.unlock(); |
| } |
| } finally { |
| // Should have been released above but may not have been on some |
| // exception paths |
| if (writeLock.isHeldByCurrentThread()) { |
| writeLock.unlock(); |
| } |
| } |
| } |
| |
| if (result > 0) { |
| eagain = false; |
| return result; |
| } else if (-result == Status.EAGAIN) { |
| eagain = true; |
| return 0; |
| } else if (-result == Status.APR_EGENERAL && wrapper.isSecure()) { |
| // Not entirely sure why this is necessary. Testing to date has not |
| // identified any issues with this but log it so it can be tracked |
| // if it is suspected of causing issues in the future. |
| if (log.isDebugEnabled()) { |
| log.debug(sm.getString("apr.read.sslGeneralError", |
| Long.valueOf(socket), wrapper)); |
| } |
| eagain = true; |
| return 0; |
| } else if (-result == Status.APR_EOF) { |
| throw new EOFException(sm.getString("apr.clientAbort")); |
| } else if ((OS.IS_WIN32 || OS.IS_WIN64) && |
| (-result == Status.APR_OS_START_SYSERR + 10053)) { |
| // 10053 on Windows is connection aborted |
| throw new EOFException(sm.getString("apr.clientAbort")); |
| } else { |
| throw new IOException(sm.getString("apr.read.error", |
| Integer.valueOf(-result), Long.valueOf(socket), wrapper)); |
| } |
| } |
| |
| |
| @Override |
| protected boolean doIsReady() { |
| return !eagain; |
| } |
| |
| |
| @Override |
| protected void doClose() throws IOException { |
| closed = true; |
| // AbstractProcessor needs to trigger the close as multiple closes for |
| // APR/native sockets will cause problems. |
| } |
| } |