| Index: java/src/org/apache/tomcat/jni/socket/AprSocket.java
|
| diff --git a/java/src/org/apache/tomcat/jni/socket/AprSocket.java b/java/src/org/apache/tomcat/jni/socket/AprSocket.java
|
| deleted file mode 100644
|
| index 1800bab4490809e2de6ebca0a0d020c8a08550fa..0000000000000000000000000000000000000000
|
| --- a/java/src/org/apache/tomcat/jni/socket/AprSocket.java
|
| +++ /dev/null
|
| @@ -1,922 +0,0 @@
|
| -/*
|
| - * 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.tomcat.jni.socket;
|
| -
|
| -import java.io.ByteArrayInputStream;
|
| -import java.io.IOException;
|
| -import java.security.cert.CertificateException;
|
| -import java.security.cert.CertificateFactory;
|
| -import java.security.cert.X509Certificate;
|
| -import java.util.logging.Level;
|
| -import java.util.logging.Logger;
|
| -
|
| -import org.apache.tomcat.jni.Address;
|
| -import org.apache.tomcat.jni.Error;
|
| -import org.apache.tomcat.jni.Poll;
|
| -import org.apache.tomcat.jni.SSL;
|
| -import org.apache.tomcat.jni.SSLExt;
|
| -import org.apache.tomcat.jni.SSLSocket;
|
| -import org.apache.tomcat.jni.Sockaddr;
|
| -import org.apache.tomcat.jni.Socket;
|
| -import org.apache.tomcat.jni.Status;
|
| -import org.apache.tomcat.jni.socket.AprSocketContext.AprPoller;
|
| -import org.apache.tomcat.jni.socket.AprSocketContext.BlockingPollHandler;
|
| -
|
| -/**
|
| - * Native socket, using JNI + APR + openssl.
|
| - *
|
| - * The socket is non-blocking - you can register either a blocking or non
|
| - * blocking callback.
|
| - *
|
| - * There is no explicit method to register/unregister poll interest -
|
| - * it is done automatically, when read/write methods return 0.
|
| - *
|
| - * To keep the socket polling you must read all the available data, until
|
| - * read() returns 0. If you want to pause - don't read all input. To resume -
|
| - * read again until it returns 0.
|
| - *
|
| - * Same for write - when write() returns 0 the socket is registered for
|
| - * write interest.
|
| - *
|
| - * You can also use the blocking read/write methods.
|
| - */
|
| -public class AprSocket implements Runnable {
|
| -
|
| - private static final Logger log =
|
| - Logger.getLogger("org.apache.tomcat.jni.socket.AprSocket");
|
| -
|
| - private static final byte[][] NO_CERTS = new byte[0][];
|
| -
|
| - static final int CONNECTING = 0x1;
|
| - static final int CONNECTED = 0x2;
|
| -
|
| - // Current ( real ) poll status
|
| - static final int POLLIN_ACTIVE = 0x4;
|
| - static final int POLLOUT_ACTIVE = 0x8;
|
| -
|
| - static final int POLL = 0x10;
|
| -
|
| - static final int SSL_ATTACHED = 0x40;
|
| -
|
| - // Requested poll status. Set by read/write when needed.
|
| - // Cleared when polled
|
| - static final int POLLIN = 0x80;
|
| - static final int POLLOUT = 0x100;
|
| -
|
| - static final int ACCEPTED = 0x200;
|
| - static final int ERROR = 0x400;
|
| - static final int CLOSED = 0x800;
|
| -
|
| - static final int READING = 0x1000;
|
| - static final int WRITING = 0x2000;
|
| -
|
| - // Not null
|
| - private final AprSocketContext context;
|
| -
|
| - // only one - to save per/socket memory - context has similar callbacks.
|
| - BlockingPollHandler handler;
|
| -
|
| - // Set while it's associated with a poller - it'll stay associated after
|
| - // connect until close. Destroy will happen in the poller.
|
| - // POLL bit indicates if the socket is actually polling.
|
| - AprPoller poller;
|
| -
|
| - // Bit field indicating the status and socket should only be accessed with
|
| - // socketLock protection
|
| - private int status;
|
| -
|
| - long socket;
|
| -
|
| - //long to = 10000;
|
| -
|
| - // Persistent info about the peer ( SSL, etc )
|
| - private HostInfo hostInfo;
|
| -
|
| - AprSocket(AprSocketContext context) {
|
| - this.context = context;
|
| - }
|
| -
|
| - public void recycle() {
|
| - status = 0;
|
| - hostInfo = null;
|
| - handler = null;
|
| - socket = 0;
|
| - poller = null;
|
| - }
|
| -
|
| - @Override
|
| - public String toString() {
|
| - return (context.isServer() ? "AprSrv-" : "AprCli-") +
|
| - Long.toHexString(socket) + " " + Integer.toHexString(status);
|
| - }
|
| -
|
| - public void setHandler(BlockingPollHandler l) {
|
| - handler = l;
|
| - }
|
| -
|
| - private void setNonBlocking() {
|
| - if (socket != 0 && context.running) {
|
| - Socket.optSet(socket, Socket.APR_SO_NONBLOCK, 1);
|
| - Socket.timeoutSet(socket, 0);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Check if the socket is currently registered with a poller.
|
| - */
|
| - public boolean isPolling() {
|
| - synchronized (this) {
|
| - return (status & POLL) != 0;
|
| - }
|
| - }
|
| -
|
| - public BlockingPollHandler getHandler() {
|
| - return handler;
|
| - }
|
| -
|
| - public AprSocketContext getContext() {
|
| - return context;
|
| - }
|
| -
|
| - AprSocket setHost(HostInfo hi) {
|
| - hostInfo = hi;
|
| - return this;
|
| - }
|
| -
|
| - /**
|
| - */
|
| - public void connect() throws IOException {
|
| - if (isBlocking()) {
|
| - // will call handleConnected() at the end.
|
| - context.connectBlocking(this);
|
| - } else {
|
| - synchronized(this) {
|
| - if ((status & CONNECTING) != 0) {
|
| - return;
|
| - }
|
| - status |= CONNECTING;
|
| - }
|
| - context.connectExecutor.execute(this);
|
| - }
|
| - }
|
| -
|
| -
|
| - // after connection is done, called from a thread pool ( not IO thread )
|
| - // may block for handshake.
|
| - void afterConnect() throws IOException {
|
| - if (hostInfo.secure) {
|
| - blockingStartTLS();
|
| - }
|
| -
|
| - setNonBlocking(); // call again, to set the bits ( connect was blocking )
|
| -
|
| - setStatus(CONNECTED);
|
| - clearStatus(CONNECTING);
|
| -
|
| - notifyConnected(false);
|
| - }
|
| -
|
| - public HostInfo getHost() {
|
| - return hostInfo;
|
| - }
|
| -
|
| - /**
|
| - * Write as much data as possible to the socket.
|
| - *
|
| - * @param data
|
| - * @param off
|
| - * @param len
|
| - * @return For both blocking and non-blocking, returns the number of bytes
|
| - * written. If no data can be written (e.g. if the buffers are
|
| - * full) 0 will be returned.
|
| - * @throws IOException
|
| - */
|
| - public int write(byte[] data, int off, int len, long to) throws IOException {
|
| - long max = System.currentTimeMillis() + to;
|
| -
|
| - while (true) {
|
| - int rc = writeInternal(data, off, len);
|
| - if (rc < 0) {
|
| - throw new IOException("Write error " + rc);
|
| - } else if (rc == 0) {
|
| - // need poll out - do we need to update polling ?
|
| - context.findPollerAndAdd(this);
|
| - } else {
|
| - return rc;
|
| - }
|
| -
|
| - try {
|
| - long waitTime = max - System.currentTimeMillis();
|
| - if (waitTime <= 0) {
|
| - return 0;
|
| - }
|
| - wait(waitTime);
|
| - } catch (InterruptedException e) {
|
| - return 0;
|
| - }
|
| - }
|
| - }
|
| -
|
| - public int write(byte[] data, int off, int len) throws IOException {
|
| - // In SSL mode, read/write can't be called at the same time.
|
| - int rc = writeInternal(data, off, len);
|
| - if (rc < 0) {
|
| - throw new IOException("Write error " + rc);
|
| - } else if (rc == 0) {
|
| - // need poll out - do we need to update polling ?
|
| - synchronized (this) {
|
| - context.findPollerAndAdd(this);
|
| - }
|
| - }
|
| - return rc;
|
| - }
|
| -
|
| - private int writeInternal(byte[] data, int off, int len) throws IOException {
|
| - int rt = 0;
|
| - int sent = 0;
|
| - synchronized(this) {
|
| - if ((status & CLOSED) != 0
|
| - || socket == 0
|
| - || !context.running) {
|
| - throw new IOException("Closed");
|
| - }
|
| - if ((status & WRITING) != 0) {
|
| - throw new IOException("Write from 2 threads not allowed");
|
| - }
|
| - status |= WRITING;
|
| -
|
| - while (len > 0) {
|
| - sent = Socket.send(socket, data, off, len);
|
| - if (sent <= 0) {
|
| - break;
|
| - }
|
| - off += sent;
|
| - len -= sent;
|
| - }
|
| -
|
| - status &= ~WRITING;
|
| - }
|
| -
|
| - if (context.rawDataHandler != null) {
|
| - context.rawData(this, false, data, off, sent, len, false);
|
| - }
|
| -
|
| - if (sent <= 0) {
|
| - if (sent == -Status.TIMEUP || sent == -Status.EAGAIN || sent == 0) {
|
| - setStatus(POLLOUT);
|
| - updatePolling();
|
| - return rt;
|
| - }
|
| - log.warning("apr.send(): Failed to send, closing " + sent);
|
| - reset();
|
| - throw new IOException("Error sending " + sent + " " + Error.strerror(-sent));
|
| - } else {
|
| - off += sent;
|
| - len -= sent;
|
| - rt += sent;
|
| - return sent;
|
| - }
|
| - }
|
| -
|
| - public int read(byte[] data, int off, int len, long to) throws IOException {
|
| - int rd = readNB(data, off, len);
|
| - if (rd == 0) {
|
| - synchronized(this) {
|
| - try {
|
| - wait(to);
|
| - } catch (InterruptedException e) {
|
| - return 0;
|
| - }
|
| - }
|
| - rd = readNB(data, off, len);
|
| - }
|
| - return processReadResult(data, off, len, rd);
|
| - }
|
| -
|
| - public int read(byte[] data, int off, int len) throws IOException {
|
| - return readNB(data, off, len);
|
| - }
|
| -
|
| - private int processReadResult(byte[] data, int off, int len, int read)
|
| - throws IOException {
|
| - if (context.rawDataHandler != null) {
|
| - context.rawData(this, true, data, off, read, len, false);
|
| - }
|
| -
|
| - if (read > 0) {
|
| - return read;
|
| - }
|
| -
|
| - if (read == 0 || read == -Status.TIMEUP || read == -Status.ETIMEDOUT
|
| - || read == -Status.EAGAIN) {
|
| - read = 0;
|
| - setStatus(POLLIN);
|
| - updatePolling();
|
| - return 0;
|
| - }
|
| -
|
| - if (read == -Status.APR_EOF || read == -1) {
|
| - close();
|
| - return -1;
|
| - }
|
| - // abrupt close
|
| - reset();
|
| - throw new IOException("apr.read(): " + read + " " + Error.strerror(-read));
|
| - }
|
| -
|
| - public int readNB(byte[] data, int off, int len) throws IOException {
|
| - int read;
|
| - synchronized(this) {
|
| - if ((status & CLOSED) != 0
|
| - || socket == 0
|
| - || !context.running) {
|
| - return -1;
|
| - }
|
| - if ((status & READING) != 0) {
|
| - throw new IOException("Read from 2 threads not allowed");
|
| - }
|
| - status |= READING;
|
| -
|
| - read = Socket.recv(socket, data, off, len);
|
| - status &= ~READING;
|
| - }
|
| - return processReadResult(data, off, len, read);
|
| - }
|
| -
|
| - /*
|
| - No support for shutdownOutput: SSL is quite tricky.
|
| - Use close() instead - no read/write will be allowed after.
|
| -
|
| - */
|
| -
|
| - public void close() {
|
| - synchronized (this) {
|
| - if ((status & CLOSED) != 0 || socket == 0) {
|
| - return;
|
| - }
|
| - status |= CLOSED;
|
| - status &= ~POLLIN;
|
| - status &= ~POLLOUT;
|
| - }
|
| - if (context.rawDataHandler != null) {
|
| - context.rawDataHandler.rawData(this, false, null, 0, 0, 0, true);
|
| - }
|
| - Socket.close(socket);
|
| - if (poller == null) {
|
| - maybeDestroy();
|
| - } else {
|
| - try {
|
| - poller.requestUpdate(this);
|
| - } catch (IOException e) {
|
| - e.printStackTrace();
|
| - }
|
| - }
|
| - }
|
| -
|
| - void maybeDestroy() {
|
| - synchronized(this) {
|
| - if (socket == 0 ||
|
| - (status & CONNECTING) != 0 || !context.running) {
|
| - // closed or operation in progress
|
| - // if context stopped, pool will be destroyed and close
|
| - // all sockets automatically.
|
| - return;
|
| - }
|
| - if ((status & CLOSED) == 0) {
|
| - return; // not closed
|
| - }
|
| - if ((status & (WRITING | READING)) != 0) {
|
| - return; // not closed
|
| - }
|
| -
|
| - if (context.rawDataHandler != null) {
|
| - context.rawDataHandler.rawData(this, false, null, -1, -1, -1, true);
|
| - }
|
| - if (log.isLoggable(Level.FINE)) {
|
| - log.info("closing: context.open=" + context.open.get() + " " + this);
|
| - }
|
| -
|
| - context.open.decrementAndGet();
|
| -
|
| - if (socket != 0 && (status & CLOSED) == 0) {
|
| - Socket.close(socket);
|
| - status |= CLOSED;
|
| - }
|
| -
|
| - if (handler != null) {
|
| - if (isBlocking()) {
|
| - context.getExecutor().execute(this);
|
| - } else {
|
| - handler.closed(this);
|
| - }
|
| - }
|
| -
|
| - context.destroySocket(this);
|
| - }
|
| - }
|
| -
|
| -
|
| -
|
| - /**
|
| - * Close input and output, potentially sending RST, than close the socket.
|
| - *
|
| - * The proper way to close when gracefully done is by calling writeEnd() and
|
| - * reading all remaining input until -1 (EOF) is received.
|
| - *
|
| - * If EOF is received, the proper way to close is send whatever is remaining and
|
| - * call writeEnd();
|
| - */
|
| - public void reset() {
|
| - setStatus(ERROR);
|
| - close();
|
| - }
|
| -
|
| -
|
| - /**
|
| - */
|
| - public boolean isClosed() {
|
| - synchronized(this) {
|
| - if ((status & CLOSED) != 0 || socket == 0 || !context.running) {
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - public long getIOTimeout() throws IOException {
|
| - if (socket != 0 && context.running) {
|
| - try {
|
| - return Socket.timeoutGet(socket) / 1000;
|
| - } catch (Exception e) {
|
| - throw new IOException(e);
|
| - }
|
| - } else {
|
| - throw new IOException("Socket is closed");
|
| - }
|
| - }
|
| -
|
| - // Cert is in DER format
|
| - // Called after handshake
|
| - public byte[][] getPeerCert(boolean check) throws IOException {
|
| - getHost();
|
| - if (hostInfo.certs != null && hostInfo.certs != NO_CERTS && !check) {
|
| - return hostInfo.certs;
|
| - }
|
| - if (!checkBitAndSocket(SSL_ATTACHED)) {
|
| - return NO_CERTS;
|
| - }
|
| - try {
|
| - int certLength = SSLSocket.getInfoI(socket,
|
| - SSL.SSL_INFO_CLIENT_CERT_CHAIN);
|
| - // TODO: if resumed, old certs are good.
|
| - // If not - warn if certs changed, remember first cert, etc.
|
| - if (certLength <= 0) {
|
| - // Can also happen on session resume - keep the old
|
| - if (hostInfo.certs == null) {
|
| - hostInfo.certs = NO_CERTS;
|
| - }
|
| - return hostInfo.certs;
|
| - }
|
| - hostInfo.certs = new byte[certLength + 1][];
|
| -
|
| - hostInfo.certs[0] = SSLSocket.getInfoB(socket,
|
| - SSL.SSL_INFO_CLIENT_CERT);
|
| - for (int i = 0; i < certLength; i++) {
|
| - hostInfo.certs[i + 1] = SSLSocket.getInfoB(socket,
|
| - SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
|
| - }
|
| - return hostInfo.certs;
|
| - } catch (Exception e) {
|
| - throw new IOException(e);
|
| - }
|
| - }
|
| -
|
| - public X509Certificate[] getPeerX509Cert() throws IOException {
|
| - byte[][] certs = getPeerCert(false);
|
| - X509Certificate[] xcerts = new X509Certificate[certs.length];
|
| - if (certs.length == 0) {
|
| - return xcerts;
|
| - }
|
| - try {
|
| - CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
| - for (int i = 0; i < certs.length; i++) {
|
| - if (certs[i] != null) {
|
| - ByteArrayInputStream bis = new ByteArrayInputStream(
|
| - certs[i]);
|
| - xcerts[i] = (X509Certificate) cf.generateCertificate(bis);
|
| - bis.close();
|
| - }
|
| - }
|
| - } catch (CertificateException ex) {
|
| - throw new IOException(ex);
|
| - }
|
| - return xcerts;
|
| - }
|
| -
|
| - public String getCipherSuite() throws IOException {
|
| - if (checkBitAndSocket(SSL_ATTACHED)) {
|
| - return null;
|
| - }
|
| - try {
|
| - return SSLSocket.getInfoS(socket, SSL.SSL_INFO_CIPHER);
|
| - } catch (Exception e) {
|
| - throw new IOException(e);
|
| - }
|
| - }
|
| -
|
| - public int getKeySize() throws IOException {
|
| - if (checkBitAndSocket(SSL_ATTACHED)) {
|
| - return -1;
|
| - }
|
| - try {
|
| - return SSLSocket.getInfoI(socket, SSL.SSL_INFO_CIPHER_USEKEYSIZE);
|
| - } catch (Exception e) {
|
| - throw new IOException(e);
|
| - }
|
| - }
|
| -
|
| - public int getRemotePort() throws IOException {
|
| - if (socket != 0 && context.running) {
|
| - try {
|
| - long sa = Address.get(Socket.APR_REMOTE, socket);
|
| - Sockaddr addr = Address.getInfo(sa);
|
| - return addr.port;
|
| - } catch (Exception ex) {
|
| - throw new IOException(ex);
|
| - }
|
| - }
|
| - throw new IOException("Socket closed");
|
| - }
|
| -
|
| - public String getRemoteAddress() throws IOException {
|
| - if (socket != 0 && context.running) {
|
| - try {
|
| - long sa = Address.get(Socket.APR_REMOTE, socket);
|
| - return Address.getip(sa);
|
| - } catch (Exception ex) {
|
| - throw new IOException(ex);
|
| - }
|
| - }
|
| - throw new IOException("Socket closed");
|
| - }
|
| -
|
| - public String getRemoteHostname() throws IOException {
|
| - if (socket != 0 && context.running) {
|
| - try {
|
| - long sa = Address.get(Socket.APR_REMOTE, socket);
|
| - String remoteHost = Address.getnameinfo(sa, 0);
|
| - if (remoteHost == null) {
|
| - remoteHost = Address.getip(sa);
|
| - }
|
| - return remoteHost;
|
| - } catch (Exception ex) {
|
| - throw new IOException(ex);
|
| - }
|
| - }
|
| - throw new IOException("Socket closed");
|
| - }
|
| -
|
| - public int getLocalPort() throws IOException {
|
| - if (socket != 0 && context.running) {
|
| - try {
|
| - long sa = Address.get(Socket.APR_LOCAL, socket);
|
| - Sockaddr addr = Address.getInfo(sa);
|
| - return addr.port;
|
| - } catch (Exception ex) {
|
| - throw new IOException(ex);
|
| - }
|
| - }
|
| - throw new IOException("Socket closed");
|
| - }
|
| -
|
| - public String getLocalAddress() throws IOException {
|
| - if (socket != 0 && context.running) {
|
| - try {
|
| - long sa = Address.get(Socket.APR_LOCAL, socket);
|
| - return Address.getip(sa);
|
| - } catch (Exception ex) {
|
| - throw new IOException(ex);
|
| - }
|
| - }
|
| - throw new IOException("Socket closed");
|
| - }
|
| -
|
| - public String getLocalHostname() throws IOException {
|
| - if (socket != 0 && context.running) {
|
| - try {
|
| - long sa = Address.get(Socket.APR_LOCAL, socket);
|
| - return Address.getnameinfo(sa, 0);
|
| - } catch (Exception ex) {
|
| - throw new IOException(ex);
|
| - }
|
| - }
|
| - throw new IOException("Socket closed");
|
| - }
|
| -
|
| - public boolean isBlocking() {
|
| - return ! (handler instanceof AprSocketContext.NonBlockingPollHandler);
|
| - }
|
| -
|
| - public boolean isError() {
|
| - return checkPreConnect(ERROR);
|
| - }
|
| -
|
| - void notifyError(Throwable err, boolean needsThread) {
|
| - if (handler instanceof AprSocketContext.NonBlockingPollHandler) {
|
| - if (err != null) {
|
| - ((AprSocketContext.NonBlockingPollHandler) handler).error(this, err);
|
| - }
|
| - } else {
|
| - // poller destroyed, etc
|
| - if (needsThread) {
|
| - context.getExecutor().execute(this);
|
| - } else {
|
| - try {
|
| - notifyIO();
|
| - } catch (IOException e) {
|
| - log.log(Level.SEVERE, this + " error ", e);
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Called after connect and from poller.
|
| - void notifyIO() throws IOException {
|
| - long t0 = System.currentTimeMillis();
|
| - try {
|
| - if (handler != null) {
|
| - handler.process(this, true, false, false);
|
| - }
|
| - } catch (Throwable t) {
|
| - throw new IOException(t);
|
| - } finally {
|
| - long t1 = System.currentTimeMillis();
|
| - t1 -= t0;
|
| - if (t1 > context.maxHandlerTime.get()) {
|
| - context.maxHandlerTime.set(t1);
|
| - }
|
| - context.totalHandlerTime.addAndGet(t1);
|
| - context.handlerCount.incrementAndGet();
|
| - }
|
| - }
|
| -
|
| - private void notifyConnected(boolean server) throws IOException {
|
| - // Will set the handler on the channel for accepted
|
| - context.onSocket(this);
|
| -
|
| - if (handler instanceof AprSocketContext.NonBlockingPollHandler) {
|
| - ((AprSocketContext.NonBlockingPollHandler) handler).connected(this);
|
| -
|
| - ((AprSocketContext.NonBlockingPollHandler) handler).process(this, true, true, false);
|
| - // Now register for polling - unless process() set suspendRead and
|
| - // doesn't need out notifications
|
| - updatePolling();
|
| - } else {
|
| - if (server) {
|
| - // client will block in connect().
|
| - // Server: call process();
|
| - notifyIO();
|
| - }
|
| - }
|
| - }
|
| -
|
| - private void updatePolling() throws IOException {
|
| - synchronized (this) {
|
| - if ((status & CLOSED) != 0) {
|
| - maybeDestroy();
|
| - return;
|
| - }
|
| - }
|
| - context.findPollerAndAdd(this);
|
| - }
|
| -
|
| - @Override
|
| - public void run() {
|
| - if (!context.running) {
|
| - return;
|
| - }
|
| - if (checkPreConnect(CLOSED)) {
|
| - if (handler != null) {
|
| - handler.closed(this);
|
| - }
|
| - return;
|
| - }
|
| - if (!checkPreConnect(CONNECTED)) {
|
| - if (checkBitAndSocket(ACCEPTED)) {
|
| - try {
|
| - context.open.incrementAndGet();
|
| -
|
| - if (log.isLoggable(Level.FINE)) {
|
| - log.info("Accept: " + context.open.get() + " " + this + " " +
|
| - getRemotePort());
|
| - }
|
| - if (context.tcpNoDelay) {
|
| - Socket.optSet(socket, Socket.APR_TCP_NODELAY, 1);
|
| - }
|
| -
|
| - setStatus(CONNECTED);
|
| - if (context.sslMode) {
|
| - Socket.timeoutSet(socket,
|
| - context.connectTimeout * 1000L);
|
| - blockingStartTLS();
|
| - }
|
| - setNonBlocking(); // call again, to set the bits ( connect was blocking )
|
| -
|
| - notifyConnected(true);
|
| - return;
|
| - } catch (Throwable t) {
|
| - t.printStackTrace(); // no error handler yet
|
| - reset();
|
| - notifyError(t, false);
|
| - return;
|
| - }
|
| - }
|
| - if (checkPreConnect(CONNECTING)) {
|
| - // Non-blocking connect - will call 'afterConnection' at the end.
|
| - try {
|
| - context.connectBlocking(this);
|
| - } catch (IOException t) {
|
| - reset(); // also sets status ERROR
|
| - if (handler instanceof AprSocketContext.NonBlockingPollHandler) {
|
| - ((AprSocketContext.NonBlockingPollHandler) handler).process(this, false, false, true);
|
| - }
|
| - notifyError(t, false);
|
| - }
|
| - }
|
| - } else {
|
| - if (handler != null) {
|
| - try {
|
| - notifyIO();
|
| - } catch (Throwable e) {
|
| - log.log(Level.SEVERE, this + " error ", e);
|
| - reset();
|
| - // no notifyIO - just did it.
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * This is a blocking call ! ( can be made non-blocking, but too complex )
|
| - *
|
| - * Will be called automatically after connect() or accept if 'secure' is
|
| - * true.
|
| - *
|
| - * Can be called manually to upgrade the channel
|
| - * @throws IOException
|
| - */
|
| - public void blockingStartTLS() throws IOException {
|
| - synchronized(this) {
|
| - if (socket == 0 || !context.running) {
|
| - return;
|
| - }
|
| - if ((status & SSL_ATTACHED) != 0) {
|
| - return;
|
| - }
|
| - status |= SSL_ATTACHED;
|
| - }
|
| -
|
| - try {
|
| - if (log.isLoggable(Level.FINE)) {
|
| - log.info(this + " StartSSL");
|
| - }
|
| -
|
| - AprSocketContext aprCon = context;
|
| - SSLSocket.attach(aprCon.getSslCtx(), socket);
|
| -
|
| - if (context.debugSSL) {
|
| - SSLExt.debug(socket);
|
| - }
|
| - if (!getContext().isServer()) {
|
| - if (context.USE_TICKETS && hostInfo.ticketLen > 0) {
|
| - SSLExt.setTicket(socket, hostInfo.ticket,
|
| - hostInfo.ticketLen);
|
| - } else if (hostInfo.sessDer != null) {
|
| - SSLExt.setSessionData(socket, hostInfo.sessDer,
|
| - hostInfo.sessDer.length);
|
| - }
|
| - }
|
| - SSLExt.sslSetMode(socket, SSLExt.SSL_MODE_ENABLE_PARTIAL_WRITE |
|
| - SSLExt.SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
| -
|
| - int rc = SSLSocket.handshake(socket);
|
| -
|
| - // At this point we have the session ID, remote certs, etc
|
| - // we can lookup host info
|
| - if (hostInfo == null) {
|
| - hostInfo = new HostInfo();
|
| - }
|
| -
|
| - if (rc != Status.APR_SUCCESS) {
|
| - throw new IOException(this + " Handshake failed " + rc + " "
|
| - + Error.strerror(rc) + " SSLL "
|
| - + SSL.getLastError());
|
| - } else { // SUCCESS
|
| - handshakeDone();
|
| - }
|
| - } catch (IOException e) {
|
| - throw e;
|
| - } catch (Exception e) {
|
| - throw new IOException(e);
|
| - }
|
| - }
|
| -
|
| - private void handshakeDone() throws IOException {
|
| - getHost();
|
| - if (socket == 0 || !context.running) {
|
| - throw new IOException("Socket closed");
|
| - }
|
| - if (context.USE_TICKETS && ! context.isServer()) {
|
| - if (hostInfo.ticket == null) {
|
| - hostInfo.ticket = new byte[2048];
|
| - }
|
| - int ticketLen = SSLExt.getTicket(socket, hostInfo.ticket);
|
| - if (ticketLen > 0) {
|
| - hostInfo.ticketLen = ticketLen;
|
| - if (log.isLoggable(Level.FINE)) {
|
| - log.info("Received ticket: " + ticketLen);
|
| - }
|
| - }
|
| - }
|
| -
|
| - // TODO: if the ticket, session id or session changed - callback to
|
| - // save the session again
|
| - try {
|
| - hostInfo.sessDer = SSLExt.getSessionData(socket);
|
| - getPeerCert(true);
|
| - hostInfo.sessionId = SSLSocket.getInfoS(socket,
|
| - SSL.SSL_INFO_SESSION_ID);
|
| - } catch (Exception e) {
|
| - throw new IOException(e);
|
| - }
|
| -
|
| - hostInfo.npn = new byte[32];
|
| - hostInfo.npnLen = SSLExt.getNPN(socket, hostInfo.npn);
|
| -
|
| - // If custom verification is used - should check the certificates
|
| - if (context.tlsCertVerifier != null) {
|
| - context.tlsCertVerifier.handshakeDone(this);
|
| - }
|
| - }
|
| -
|
| - int requestedPolling() {
|
| - synchronized(this) {
|
| - if (socket == 0 || ((status & CLOSED) != 0)) {
|
| - return 0;
|
| - }
|
| - // Implicit:
|
| - //Poll.APR_POLLNVAL | Poll.APR_POLLHUP | Poll.APR_POLLERR |
|
| - int res = 0;
|
| - if ((status & POLLIN) != 0) {
|
| - res = Poll.APR_POLLIN;
|
| - }
|
| - if ((status & POLLOUT) != 0) {
|
| - res |= Poll.APR_POLLOUT;
|
| - }
|
| - return res;
|
| - }
|
| - }
|
| -
|
| - boolean checkBitAndSocket(int bit) {
|
| - synchronized (this) {
|
| - return ((status & bit) != 0 && socket != 0 &&
|
| - (status & CLOSED) == 0 && context.running);
|
| - }
|
| - }
|
| -
|
| - boolean checkPreConnect(int bit) {
|
| - synchronized (this) {
|
| - return ((status & bit) != 0);
|
| - }
|
| - }
|
| -
|
| - void clearStatus(int bit) {
|
| - synchronized (this) {
|
| - status &= ~bit;
|
| - }
|
| - }
|
| -
|
| - boolean setStatus(int bit) {
|
| - synchronized (this) {
|
| - int old = status & bit;
|
| - status |= bit;
|
| - return old != 0;
|
| - }
|
| - }
|
| -
|
| -
|
| -}
|
|
|