| /* |
| * 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.tomcat.jni.OS; |
| import org.apache.tomcat.jni.Socket; |
| import org.apache.tomcat.jni.Status; |
| import org.apache.tomcat.util.net.AprEndpoint; |
| import org.apache.tomcat.util.net.SocketWrapper; |
| |
| public class AprServletOutputStream extends AbstractServletOutputStream<Long> { |
| |
| private static final int SSL_OUTPUT_BUFFER_SIZE = 8192; |
| |
| private final AprEndpoint endpoint; |
| private final long socket; |
| private volatile boolean closed = false; |
| private final ByteBuffer sslOutputBuffer; |
| |
| public AprServletOutputStream(SocketWrapper<Long> socketWrapper, |
| int asyncWriteBufferSize, AprEndpoint endpoint) { |
| super(socketWrapper, asyncWriteBufferSize); |
| this.endpoint = endpoint; |
| this.socket = socketWrapper.getSocket().longValue(); |
| if (endpoint.isSSLEnabled()) { |
| sslOutputBuffer = ByteBuffer.allocateDirect(SSL_OUTPUT_BUFFER_SIZE); |
| sslOutputBuffer.position(SSL_OUTPUT_BUFFER_SIZE); |
| } else { |
| sslOutputBuffer = null; |
| } |
| } |
| |
| |
| @Override |
| protected int doWrite(boolean block, byte[] b, int off, int len) |
| throws IOException { |
| |
| if (closed) { |
| throw new IOException(sm.getString("apr.closed", Long.valueOf(socket))); |
| } |
| |
| Lock readLock = socketWrapper.getBlockingStatusReadLock(); |
| WriteLock writeLock = socketWrapper.getBlockingStatusWriteLock(); |
| |
| readLock.lock(); |
| try { |
| if (socketWrapper.getBlockingStatus() == block) { |
| return doWriteInternal(b, off, len); |
| } |
| } finally { |
| readLock.unlock(); |
| } |
| |
| writeLock.lock(); |
| try { |
| // Set the current settings for this socket |
| socketWrapper.setBlockingStatus(block); |
| if (block) { |
| Socket.timeoutSet(socket, endpoint.getSoTimeout() * 1000); |
| } else { |
| Socket.timeoutSet(socket, 0); |
| } |
| |
| // Downgrade the lock |
| readLock.lock(); |
| try { |
| writeLock.unlock(); |
| return doWriteInternal(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(); |
| } |
| } |
| } |
| |
| |
| private int doWriteInternal(byte[] b, int off, int len) throws IOException { |
| |
| int start = off; |
| int left = len; |
| int written; |
| |
| do { |
| if (endpoint.isSSLEnabled()) { |
| if (sslOutputBuffer.remaining() == 0) { |
| // Buffer was fully written last time around |
| sslOutputBuffer.clear(); |
| if (left < SSL_OUTPUT_BUFFER_SIZE) { |
| sslOutputBuffer.put(b, start, left); |
| } else { |
| sslOutputBuffer.put(b, start, SSL_OUTPUT_BUFFER_SIZE); |
| } |
| sslOutputBuffer.flip(); |
| } else { |
| // Buffer still has data from previous attempt to write |
| // APR + SSL requires that exactly the same parameters are |
| // passed when re-attempting the write |
| } |
| written = Socket.sendb(socket, sslOutputBuffer, |
| sslOutputBuffer.position(), sslOutputBuffer.limit()); |
| if (written > 0) { |
| sslOutputBuffer.position( |
| sslOutputBuffer.position() + written); |
| } |
| } else { |
| written = Socket.send(socket, b, start, left); |
| } |
| if (Status.APR_STATUS_IS_EAGAIN(-written)) { |
| written = 0; |
| } else if (-written == Status.APR_EOF) { |
| throw new EOFException(sm.getString("apr.clientAbort")); |
| } else if ((OS.IS_WIN32 || OS.IS_WIN64) && |
| (-written == Status.APR_OS_START_SYSERR + 10053)) { |
| // 10053 on Windows is connection aborted |
| throw new EOFException(sm.getString("apr.clientAbort")); |
| } else if (written < 0) { |
| throw new IOException(sm.getString("apr.write.error", |
| Integer.valueOf(-written), Long.valueOf(socket), socketWrapper)); |
| } |
| start += written; |
| left -= written; |
| } while (written > 0 && left > 0); |
| |
| if (left > 0) { |
| endpoint.getPoller().add(socket, -1, false, true); |
| } |
| return len - left; |
| } |
| |
| |
| @Override |
| protected void doFlush() throws IOException { |
| // TODO Auto-generated method stub |
| } |
| |
| |
| @Override |
| protected void doClose() throws IOException { |
| closed = true; |
| // AbstractProcessor needs to trigger the close as multiple closes for |
| // APR/native sockets will cause problems. |
| } |
| } |