| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Licensed to the Apache Software Foundation (ASF) under one or more | |
| 3 * contributor license agreements. See the NOTICE file distributed with | |
| 4 * this work for additional information regarding copyright ownership. | |
| 5 * The ASF licenses this file to You under the Apache License, Version 2.0 | |
| 6 * (the "License"); you may not use this file except in compliance with | |
| 7 * the License. You may obtain a copy of the License at | |
| 8 * | |
| 9 * http://www.apache.org/licenses/LICENSE-2.0 | |
| 10 * | |
| 11 * Unless required by applicable law or agreed to in writing, software | |
| 12 * distributed under the License is distributed on an "AS IS" BASIS, | |
| 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 14 * See the License for the specific language governing permissions and | |
| 15 * limitations under the License. | |
| 16 */ | |
| 17 package org.apache.tomcat.jni.socket; | |
| 18 | |
| 19 import java.io.ByteArrayInputStream; | |
| 20 import java.io.IOException; | |
| 21 import java.security.cert.CertificateException; | |
| 22 import java.security.cert.CertificateFactory; | |
| 23 import java.security.cert.X509Certificate; | |
| 24 import java.util.logging.Level; | |
| 25 import java.util.logging.Logger; | |
| 26 | |
| 27 import org.apache.tomcat.jni.Address; | |
| 28 import org.apache.tomcat.jni.Error; | |
| 29 import org.apache.tomcat.jni.Poll; | |
| 30 import org.apache.tomcat.jni.SSL; | |
| 31 import org.apache.tomcat.jni.SSLExt; | |
| 32 import org.apache.tomcat.jni.SSLSocket; | |
| 33 import org.apache.tomcat.jni.Sockaddr; | |
| 34 import org.apache.tomcat.jni.Socket; | |
| 35 import org.apache.tomcat.jni.Status; | |
| 36 import org.apache.tomcat.jni.socket.AprSocketContext.AprPoller; | |
| 37 import org.apache.tomcat.jni.socket.AprSocketContext.BlockingPollHandler; | |
| 38 | |
| 39 /** | |
| 40 * Native socket, using JNI + APR + openssl. | |
| 41 * | |
| 42 * The socket is non-blocking - you can register either a blocking or non | |
| 43 * blocking callback. | |
| 44 * | |
| 45 * There is no explicit method to register/unregister poll interest - | |
| 46 * it is done automatically, when read/write methods return 0. | |
| 47 * | |
| 48 * To keep the socket polling you must read all the available data, until | |
| 49 * read() returns 0. If you want to pause - don't read all input. To resume - | |
| 50 * read again until it returns 0. | |
| 51 * | |
| 52 * Same for write - when write() returns 0 the socket is registered for | |
| 53 * write interest. | |
| 54 * | |
| 55 * You can also use the blocking read/write methods. | |
| 56 */ | |
| 57 public class AprSocket implements Runnable { | |
| 58 | |
| 59 private static final Logger log = | |
| 60 Logger.getLogger("org.apache.tomcat.jni.socket.AprSocket"); | |
| 61 | |
| 62 private static final byte[][] NO_CERTS = new byte[0][]; | |
| 63 | |
| 64 static final int CONNECTING = 0x1; | |
| 65 static final int CONNECTED = 0x2; | |
| 66 | |
| 67 // Current ( real ) poll status | |
| 68 static final int POLLIN_ACTIVE = 0x4; | |
| 69 static final int POLLOUT_ACTIVE = 0x8; | |
| 70 | |
| 71 static final int POLL = 0x10; | |
| 72 | |
| 73 static final int SSL_ATTACHED = 0x40; | |
| 74 | |
| 75 // Requested poll status. Set by read/write when needed. | |
| 76 // Cleared when polled | |
| 77 static final int POLLIN = 0x80; | |
| 78 static final int POLLOUT = 0x100; | |
| 79 | |
| 80 static final int ACCEPTED = 0x200; | |
| 81 static final int ERROR = 0x400; | |
| 82 static final int CLOSED = 0x800; | |
| 83 | |
| 84 static final int READING = 0x1000; | |
| 85 static final int WRITING = 0x2000; | |
| 86 | |
| 87 // Not null | |
| 88 private final AprSocketContext context; | |
| 89 | |
| 90 // only one - to save per/socket memory - context has similar callbacks. | |
| 91 BlockingPollHandler handler; | |
| 92 | |
| 93 // Set while it's associated with a poller - it'll stay associated after | |
| 94 // connect until close. Destroy will happen in the poller. | |
| 95 // POLL bit indicates if the socket is actually polling. | |
| 96 AprPoller poller; | |
| 97 | |
| 98 // Bit field indicating the status and socket should only be accessed with | |
| 99 // socketLock protection | |
| 100 private int status; | |
| 101 | |
| 102 long socket; | |
| 103 | |
| 104 //long to = 10000; | |
| 105 | |
| 106 // Persistent info about the peer ( SSL, etc ) | |
| 107 private HostInfo hostInfo; | |
| 108 | |
| 109 AprSocket(AprSocketContext context) { | |
| 110 this.context = context; | |
| 111 } | |
| 112 | |
| 113 public void recycle() { | |
| 114 status = 0; | |
| 115 hostInfo = null; | |
| 116 handler = null; | |
| 117 socket = 0; | |
| 118 poller = null; | |
| 119 } | |
| 120 | |
| 121 @Override | |
| 122 public String toString() { | |
| 123 return (context.isServer() ? "AprSrv-" : "AprCli-") + | |
| 124 Long.toHexString(socket) + " " + Integer.toHexString(status); | |
| 125 } | |
| 126 | |
| 127 public void setHandler(BlockingPollHandler l) { | |
| 128 handler = l; | |
| 129 } | |
| 130 | |
| 131 private void setNonBlocking() { | |
| 132 if (socket != 0 && context.running) { | |
| 133 Socket.optSet(socket, Socket.APR_SO_NONBLOCK, 1); | |
| 134 Socket.timeoutSet(socket, 0); | |
| 135 } | |
| 136 } | |
| 137 | |
| 138 /** | |
| 139 * Check if the socket is currently registered with a poller. | |
| 140 */ | |
| 141 public boolean isPolling() { | |
| 142 synchronized (this) { | |
| 143 return (status & POLL) != 0; | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 public BlockingPollHandler getHandler() { | |
| 148 return handler; | |
| 149 } | |
| 150 | |
| 151 public AprSocketContext getContext() { | |
| 152 return context; | |
| 153 } | |
| 154 | |
| 155 AprSocket setHost(HostInfo hi) { | |
| 156 hostInfo = hi; | |
| 157 return this; | |
| 158 } | |
| 159 | |
| 160 /** | |
| 161 */ | |
| 162 public void connect() throws IOException { | |
| 163 if (isBlocking()) { | |
| 164 // will call handleConnected() at the end. | |
| 165 context.connectBlocking(this); | |
| 166 } else { | |
| 167 synchronized(this) { | |
| 168 if ((status & CONNECTING) != 0) { | |
| 169 return; | |
| 170 } | |
| 171 status |= CONNECTING; | |
| 172 } | |
| 173 context.connectExecutor.execute(this); | |
| 174 } | |
| 175 } | |
| 176 | |
| 177 | |
| 178 // after connection is done, called from a thread pool ( not IO thread ) | |
| 179 // may block for handshake. | |
| 180 void afterConnect() throws IOException { | |
| 181 if (hostInfo.secure) { | |
| 182 blockingStartTLS(); | |
| 183 } | |
| 184 | |
| 185 setNonBlocking(); // call again, to set the bits ( connect was blocking
) | |
| 186 | |
| 187 setStatus(CONNECTED); | |
| 188 clearStatus(CONNECTING); | |
| 189 | |
| 190 notifyConnected(false); | |
| 191 } | |
| 192 | |
| 193 public HostInfo getHost() { | |
| 194 return hostInfo; | |
| 195 } | |
| 196 | |
| 197 /** | |
| 198 * Write as much data as possible to the socket. | |
| 199 * | |
| 200 * @param data | |
| 201 * @param off | |
| 202 * @param len | |
| 203 * @return For both blocking and non-blocking, returns the number of bytes | |
| 204 * written. If no data can be written (e.g. if the buffers are | |
| 205 * full) 0 will be returned. | |
| 206 * @throws IOException | |
| 207 */ | |
| 208 public int write(byte[] data, int off, int len, long to) throws IOException
{ | |
| 209 long max = System.currentTimeMillis() + to; | |
| 210 | |
| 211 while (true) { | |
| 212 int rc = writeInternal(data, off, len); | |
| 213 if (rc < 0) { | |
| 214 throw new IOException("Write error " + rc); | |
| 215 } else if (rc == 0) { | |
| 216 // need poll out - do we need to update polling ? | |
| 217 context.findPollerAndAdd(this); | |
| 218 } else { | |
| 219 return rc; | |
| 220 } | |
| 221 | |
| 222 try { | |
| 223 long waitTime = max - System.currentTimeMillis(); | |
| 224 if (waitTime <= 0) { | |
| 225 return 0; | |
| 226 } | |
| 227 wait(waitTime); | |
| 228 } catch (InterruptedException e) { | |
| 229 return 0; | |
| 230 } | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 public int write(byte[] data, int off, int len) throws IOException { | |
| 235 // In SSL mode, read/write can't be called at the same time. | |
| 236 int rc = writeInternal(data, off, len); | |
| 237 if (rc < 0) { | |
| 238 throw new IOException("Write error " + rc); | |
| 239 } else if (rc == 0) { | |
| 240 // need poll out - do we need to update polling ? | |
| 241 synchronized (this) { | |
| 242 context.findPollerAndAdd(this); | |
| 243 } | |
| 244 } | |
| 245 return rc; | |
| 246 } | |
| 247 | |
| 248 private int writeInternal(byte[] data, int off, int len) throws IOException
{ | |
| 249 int rt = 0; | |
| 250 int sent = 0; | |
| 251 synchronized(this) { | |
| 252 if ((status & CLOSED) != 0 | |
| 253 || socket == 0 | |
| 254 || !context.running) { | |
| 255 throw new IOException("Closed"); | |
| 256 } | |
| 257 if ((status & WRITING) != 0) { | |
| 258 throw new IOException("Write from 2 threads not allowed"); | |
| 259 } | |
| 260 status |= WRITING; | |
| 261 | |
| 262 while (len > 0) { | |
| 263 sent = Socket.send(socket, data, off, len); | |
| 264 if (sent <= 0) { | |
| 265 break; | |
| 266 } | |
| 267 off += sent; | |
| 268 len -= sent; | |
| 269 } | |
| 270 | |
| 271 status &= ~WRITING; | |
| 272 } | |
| 273 | |
| 274 if (context.rawDataHandler != null) { | |
| 275 context.rawData(this, false, data, off, sent, len, false); | |
| 276 } | |
| 277 | |
| 278 if (sent <= 0) { | |
| 279 if (sent == -Status.TIMEUP || sent == -Status.EAGAIN || sent == 0) { | |
| 280 setStatus(POLLOUT); | |
| 281 updatePolling(); | |
| 282 return rt; | |
| 283 } | |
| 284 log.warning("apr.send(): Failed to send, closing " + sent); | |
| 285 reset(); | |
| 286 throw new IOException("Error sending " + sent + " " + Error.strerror
(-sent)); | |
| 287 } else { | |
| 288 off += sent; | |
| 289 len -= sent; | |
| 290 rt += sent; | |
| 291 return sent; | |
| 292 } | |
| 293 } | |
| 294 | |
| 295 public int read(byte[] data, int off, int len, long to) throws IOException { | |
| 296 int rd = readNB(data, off, len); | |
| 297 if (rd == 0) { | |
| 298 synchronized(this) { | |
| 299 try { | |
| 300 wait(to); | |
| 301 } catch (InterruptedException e) { | |
| 302 return 0; | |
| 303 } | |
| 304 } | |
| 305 rd = readNB(data, off, len); | |
| 306 } | |
| 307 return processReadResult(data, off, len, rd); | |
| 308 } | |
| 309 | |
| 310 public int read(byte[] data, int off, int len) throws IOException { | |
| 311 return readNB(data, off, len); | |
| 312 } | |
| 313 | |
| 314 private int processReadResult(byte[] data, int off, int len, int read) | |
| 315 throws IOException { | |
| 316 if (context.rawDataHandler != null) { | |
| 317 context.rawData(this, true, data, off, read, len, false); | |
| 318 } | |
| 319 | |
| 320 if (read > 0) { | |
| 321 return read; | |
| 322 } | |
| 323 | |
| 324 if (read == 0 || read == -Status.TIMEUP || read == -Status.ETIMEDOUT | |
| 325 || read == -Status.EAGAIN) { | |
| 326 read = 0; | |
| 327 setStatus(POLLIN); | |
| 328 updatePolling(); | |
| 329 return 0; | |
| 330 } | |
| 331 | |
| 332 if (read == -Status.APR_EOF || read == -1) { | |
| 333 close(); | |
| 334 return -1; | |
| 335 } | |
| 336 // abrupt close | |
| 337 reset(); | |
| 338 throw new IOException("apr.read(): " + read + " " + Error.strerror(-read
)); | |
| 339 } | |
| 340 | |
| 341 public int readNB(byte[] data, int off, int len) throws IOException { | |
| 342 int read; | |
| 343 synchronized(this) { | |
| 344 if ((status & CLOSED) != 0 | |
| 345 || socket == 0 | |
| 346 || !context.running) { | |
| 347 return -1; | |
| 348 } | |
| 349 if ((status & READING) != 0) { | |
| 350 throw new IOException("Read from 2 threads not allowed"); | |
| 351 } | |
| 352 status |= READING; | |
| 353 | |
| 354 read = Socket.recv(socket, data, off, len); | |
| 355 status &= ~READING; | |
| 356 } | |
| 357 return processReadResult(data, off, len, read); | |
| 358 } | |
| 359 | |
| 360 /* | |
| 361 No support for shutdownOutput: SSL is quite tricky. | |
| 362 Use close() instead - no read/write will be allowed after. | |
| 363 | |
| 364 */ | |
| 365 | |
| 366 public void close() { | |
| 367 synchronized (this) { | |
| 368 if ((status & CLOSED) != 0 || socket == 0) { | |
| 369 return; | |
| 370 } | |
| 371 status |= CLOSED; | |
| 372 status &= ~POLLIN; | |
| 373 status &= ~POLLOUT; | |
| 374 } | |
| 375 if (context.rawDataHandler != null) { | |
| 376 context.rawDataHandler.rawData(this, false, null, 0, 0, 0, true); | |
| 377 } | |
| 378 Socket.close(socket); | |
| 379 if (poller == null) { | |
| 380 maybeDestroy(); | |
| 381 } else { | |
| 382 try { | |
| 383 poller.requestUpdate(this); | |
| 384 } catch (IOException e) { | |
| 385 e.printStackTrace(); | |
| 386 } | |
| 387 } | |
| 388 } | |
| 389 | |
| 390 void maybeDestroy() { | |
| 391 synchronized(this) { | |
| 392 if (socket == 0 || | |
| 393 (status & CONNECTING) != 0 || !context.running) { | |
| 394 // closed or operation in progress | |
| 395 // if context stopped, pool will be destroyed and close | |
| 396 // all sockets automatically. | |
| 397 return; | |
| 398 } | |
| 399 if ((status & CLOSED) == 0) { | |
| 400 return; // not closed | |
| 401 } | |
| 402 if ((status & (WRITING | READING)) != 0) { | |
| 403 return; // not closed | |
| 404 } | |
| 405 | |
| 406 if (context.rawDataHandler != null) { | |
| 407 context.rawDataHandler.rawData(this, false, null, -1, -1, -1, tr
ue); | |
| 408 } | |
| 409 if (log.isLoggable(Level.FINE)) { | |
| 410 log.info("closing: context.open=" + context.open.get() + " " + t
his); | |
| 411 } | |
| 412 | |
| 413 context.open.decrementAndGet(); | |
| 414 | |
| 415 if (socket != 0 && (status & CLOSED) == 0) { | |
| 416 Socket.close(socket); | |
| 417 status |= CLOSED; | |
| 418 } | |
| 419 | |
| 420 if (handler != null) { | |
| 421 if (isBlocking()) { | |
| 422 context.getExecutor().execute(this); | |
| 423 } else { | |
| 424 handler.closed(this); | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 context.destroySocket(this); | |
| 429 } | |
| 430 } | |
| 431 | |
| 432 | |
| 433 | |
| 434 /** | |
| 435 * Close input and output, potentially sending RST, than close the socket. | |
| 436 * | |
| 437 * The proper way to close when gracefully done is by calling writeEnd() and | |
| 438 * reading all remaining input until -1 (EOF) is received. | |
| 439 * | |
| 440 * If EOF is received, the proper way to close is send whatever is remaining
and | |
| 441 * call writeEnd(); | |
| 442 */ | |
| 443 public void reset() { | |
| 444 setStatus(ERROR); | |
| 445 close(); | |
| 446 } | |
| 447 | |
| 448 | |
| 449 /** | |
| 450 */ | |
| 451 public boolean isClosed() { | |
| 452 synchronized(this) { | |
| 453 if ((status & CLOSED) != 0 || socket == 0 || !context.running) { | |
| 454 return true; | |
| 455 } | |
| 456 return false; | |
| 457 } | |
| 458 } | |
| 459 | |
| 460 public long getIOTimeout() throws IOException { | |
| 461 if (socket != 0 && context.running) { | |
| 462 try { | |
| 463 return Socket.timeoutGet(socket) / 1000; | |
| 464 } catch (Exception e) { | |
| 465 throw new IOException(e); | |
| 466 } | |
| 467 } else { | |
| 468 throw new IOException("Socket is closed"); | |
| 469 } | |
| 470 } | |
| 471 | |
| 472 // Cert is in DER format | |
| 473 // Called after handshake | |
| 474 public byte[][] getPeerCert(boolean check) throws IOException { | |
| 475 getHost(); | |
| 476 if (hostInfo.certs != null && hostInfo.certs != NO_CERTS && !check) { | |
| 477 return hostInfo.certs; | |
| 478 } | |
| 479 if (!checkBitAndSocket(SSL_ATTACHED)) { | |
| 480 return NO_CERTS; | |
| 481 } | |
| 482 try { | |
| 483 int certLength = SSLSocket.getInfoI(socket, | |
| 484 SSL.SSL_INFO_CLIENT_CERT_CHAIN); | |
| 485 // TODO: if resumed, old certs are good. | |
| 486 // If not - warn if certs changed, remember first cert, etc. | |
| 487 if (certLength <= 0) { | |
| 488 // Can also happen on session resume - keep the old | |
| 489 if (hostInfo.certs == null) { | |
| 490 hostInfo.certs = NO_CERTS; | |
| 491 } | |
| 492 return hostInfo.certs; | |
| 493 } | |
| 494 hostInfo.certs = new byte[certLength + 1][]; | |
| 495 | |
| 496 hostInfo.certs[0] = SSLSocket.getInfoB(socket, | |
| 497 SSL.SSL_INFO_CLIENT_CERT); | |
| 498 for (int i = 0; i < certLength; i++) { | |
| 499 hostInfo.certs[i + 1] = SSLSocket.getInfoB(socket, | |
| 500 SSL.SSL_INFO_CLIENT_CERT_CHAIN + i); | |
| 501 } | |
| 502 return hostInfo.certs; | |
| 503 } catch (Exception e) { | |
| 504 throw new IOException(e); | |
| 505 } | |
| 506 } | |
| 507 | |
| 508 public X509Certificate[] getPeerX509Cert() throws IOException { | |
| 509 byte[][] certs = getPeerCert(false); | |
| 510 X509Certificate[] xcerts = new X509Certificate[certs.length]; | |
| 511 if (certs.length == 0) { | |
| 512 return xcerts; | |
| 513 } | |
| 514 try { | |
| 515 CertificateFactory cf = CertificateFactory.getInstance("X.509"); | |
| 516 for (int i = 0; i < certs.length; i++) { | |
| 517 if (certs[i] != null) { | |
| 518 ByteArrayInputStream bis = new ByteArrayInputStream( | |
| 519 certs[i]); | |
| 520 xcerts[i] = (X509Certificate) cf.generateCertificate(bis); | |
| 521 bis.close(); | |
| 522 } | |
| 523 } | |
| 524 } catch (CertificateException ex) { | |
| 525 throw new IOException(ex); | |
| 526 } | |
| 527 return xcerts; | |
| 528 } | |
| 529 | |
| 530 public String getCipherSuite() throws IOException { | |
| 531 if (checkBitAndSocket(SSL_ATTACHED)) { | |
| 532 return null; | |
| 533 } | |
| 534 try { | |
| 535 return SSLSocket.getInfoS(socket, SSL.SSL_INFO_CIPHER); | |
| 536 } catch (Exception e) { | |
| 537 throw new IOException(e); | |
| 538 } | |
| 539 } | |
| 540 | |
| 541 public int getKeySize() throws IOException { | |
| 542 if (checkBitAndSocket(SSL_ATTACHED)) { | |
| 543 return -1; | |
| 544 } | |
| 545 try { | |
| 546 return SSLSocket.getInfoI(socket, SSL.SSL_INFO_CIPHER_USEKEYSIZE); | |
| 547 } catch (Exception e) { | |
| 548 throw new IOException(e); | |
| 549 } | |
| 550 } | |
| 551 | |
| 552 public int getRemotePort() throws IOException { | |
| 553 if (socket != 0 && context.running) { | |
| 554 try { | |
| 555 long sa = Address.get(Socket.APR_REMOTE, socket); | |
| 556 Sockaddr addr = Address.getInfo(sa); | |
| 557 return addr.port; | |
| 558 } catch (Exception ex) { | |
| 559 throw new IOException(ex); | |
| 560 } | |
| 561 } | |
| 562 throw new IOException("Socket closed"); | |
| 563 } | |
| 564 | |
| 565 public String getRemoteAddress() throws IOException { | |
| 566 if (socket != 0 && context.running) { | |
| 567 try { | |
| 568 long sa = Address.get(Socket.APR_REMOTE, socket); | |
| 569 return Address.getip(sa); | |
| 570 } catch (Exception ex) { | |
| 571 throw new IOException(ex); | |
| 572 } | |
| 573 } | |
| 574 throw new IOException("Socket closed"); | |
| 575 } | |
| 576 | |
| 577 public String getRemoteHostname() throws IOException { | |
| 578 if (socket != 0 && context.running) { | |
| 579 try { | |
| 580 long sa = Address.get(Socket.APR_REMOTE, socket); | |
| 581 String remoteHost = Address.getnameinfo(sa, 0); | |
| 582 if (remoteHost == null) { | |
| 583 remoteHost = Address.getip(sa); | |
| 584 } | |
| 585 return remoteHost; | |
| 586 } catch (Exception ex) { | |
| 587 throw new IOException(ex); | |
| 588 } | |
| 589 } | |
| 590 throw new IOException("Socket closed"); | |
| 591 } | |
| 592 | |
| 593 public int getLocalPort() throws IOException { | |
| 594 if (socket != 0 && context.running) { | |
| 595 try { | |
| 596 long sa = Address.get(Socket.APR_LOCAL, socket); | |
| 597 Sockaddr addr = Address.getInfo(sa); | |
| 598 return addr.port; | |
| 599 } catch (Exception ex) { | |
| 600 throw new IOException(ex); | |
| 601 } | |
| 602 } | |
| 603 throw new IOException("Socket closed"); | |
| 604 } | |
| 605 | |
| 606 public String getLocalAddress() throws IOException { | |
| 607 if (socket != 0 && context.running) { | |
| 608 try { | |
| 609 long sa = Address.get(Socket.APR_LOCAL, socket); | |
| 610 return Address.getip(sa); | |
| 611 } catch (Exception ex) { | |
| 612 throw new IOException(ex); | |
| 613 } | |
| 614 } | |
| 615 throw new IOException("Socket closed"); | |
| 616 } | |
| 617 | |
| 618 public String getLocalHostname() throws IOException { | |
| 619 if (socket != 0 && context.running) { | |
| 620 try { | |
| 621 long sa = Address.get(Socket.APR_LOCAL, socket); | |
| 622 return Address.getnameinfo(sa, 0); | |
| 623 } catch (Exception ex) { | |
| 624 throw new IOException(ex); | |
| 625 } | |
| 626 } | |
| 627 throw new IOException("Socket closed"); | |
| 628 } | |
| 629 | |
| 630 public boolean isBlocking() { | |
| 631 return ! (handler instanceof AprSocketContext.NonBlockingPollHandler); | |
| 632 } | |
| 633 | |
| 634 public boolean isError() { | |
| 635 return checkPreConnect(ERROR); | |
| 636 } | |
| 637 | |
| 638 void notifyError(Throwable err, boolean needsThread) { | |
| 639 if (handler instanceof AprSocketContext.NonBlockingPollHandler) { | |
| 640 if (err != null) { | |
| 641 ((AprSocketContext.NonBlockingPollHandler) handler).error(this,
err); | |
| 642 } | |
| 643 } else { | |
| 644 // poller destroyed, etc | |
| 645 if (needsThread) { | |
| 646 context.getExecutor().execute(this); | |
| 647 } else { | |
| 648 try { | |
| 649 notifyIO(); | |
| 650 } catch (IOException e) { | |
| 651 log.log(Level.SEVERE, this + " error ", e); | |
| 652 } | |
| 653 } | |
| 654 } | |
| 655 } | |
| 656 | |
| 657 // Called after connect and from poller. | |
| 658 void notifyIO() throws IOException { | |
| 659 long t0 = System.currentTimeMillis(); | |
| 660 try { | |
| 661 if (handler != null) { | |
| 662 handler.process(this, true, false, false); | |
| 663 } | |
| 664 } catch (Throwable t) { | |
| 665 throw new IOException(t); | |
| 666 } finally { | |
| 667 long t1 = System.currentTimeMillis(); | |
| 668 t1 -= t0; | |
| 669 if (t1 > context.maxHandlerTime.get()) { | |
| 670 context.maxHandlerTime.set(t1); | |
| 671 } | |
| 672 context.totalHandlerTime.addAndGet(t1); | |
| 673 context.handlerCount.incrementAndGet(); | |
| 674 } | |
| 675 } | |
| 676 | |
| 677 private void notifyConnected(boolean server) throws IOException { | |
| 678 // Will set the handler on the channel for accepted | |
| 679 context.onSocket(this); | |
| 680 | |
| 681 if (handler instanceof AprSocketContext.NonBlockingPollHandler) { | |
| 682 ((AprSocketContext.NonBlockingPollHandler) handler).connected(this); | |
| 683 | |
| 684 ((AprSocketContext.NonBlockingPollHandler) handler).process(this, tr
ue, true, false); | |
| 685 // Now register for polling - unless process() set suspendRead and | |
| 686 // doesn't need out notifications | |
| 687 updatePolling(); | |
| 688 } else { | |
| 689 if (server) { | |
| 690 // client will block in connect(). | |
| 691 // Server: call process(); | |
| 692 notifyIO(); | |
| 693 } | |
| 694 } | |
| 695 } | |
| 696 | |
| 697 private void updatePolling() throws IOException { | |
| 698 synchronized (this) { | |
| 699 if ((status & CLOSED) != 0) { | |
| 700 maybeDestroy(); | |
| 701 return; | |
| 702 } | |
| 703 } | |
| 704 context.findPollerAndAdd(this); | |
| 705 } | |
| 706 | |
| 707 @Override | |
| 708 public void run() { | |
| 709 if (!context.running) { | |
| 710 return; | |
| 711 } | |
| 712 if (checkPreConnect(CLOSED)) { | |
| 713 if (handler != null) { | |
| 714 handler.closed(this); | |
| 715 } | |
| 716 return; | |
| 717 } | |
| 718 if (!checkPreConnect(CONNECTED)) { | |
| 719 if (checkBitAndSocket(ACCEPTED)) { | |
| 720 try { | |
| 721 context.open.incrementAndGet(); | |
| 722 | |
| 723 if (log.isLoggable(Level.FINE)) { | |
| 724 log.info("Accept: " + context.open.get() + " " + this +
" " + | |
| 725 getRemotePort()); | |
| 726 } | |
| 727 if (context.tcpNoDelay) { | |
| 728 Socket.optSet(socket, Socket.APR_TCP_NODELAY, 1); | |
| 729 } | |
| 730 | |
| 731 setStatus(CONNECTED); | |
| 732 if (context.sslMode) { | |
| 733 Socket.timeoutSet(socket, | |
| 734 context.connectTimeout * 1000L); | |
| 735 blockingStartTLS(); | |
| 736 } | |
| 737 setNonBlocking(); // call again, to set the bits ( connect w
as blocking ) | |
| 738 | |
| 739 notifyConnected(true); | |
| 740 return; | |
| 741 } catch (Throwable t) { | |
| 742 t.printStackTrace(); // no error handler yet | |
| 743 reset(); | |
| 744 notifyError(t, false); | |
| 745 return; | |
| 746 } | |
| 747 } | |
| 748 if (checkPreConnect(CONNECTING)) { | |
| 749 // Non-blocking connect - will call 'afterConnection' at the end
. | |
| 750 try { | |
| 751 context.connectBlocking(this); | |
| 752 } catch (IOException t) { | |
| 753 reset(); // also sets status ERROR | |
| 754 if (handler instanceof AprSocketContext.NonBlockingPollHandl
er) { | |
| 755 ((AprSocketContext.NonBlockingPollHandler) handler).proc
ess(this, false, false, true); | |
| 756 } | |
| 757 notifyError(t, false); | |
| 758 } | |
| 759 } | |
| 760 } else { | |
| 761 if (handler != null) { | |
| 762 try { | |
| 763 notifyIO(); | |
| 764 } catch (Throwable e) { | |
| 765 log.log(Level.SEVERE, this + " error ", e); | |
| 766 reset(); | |
| 767 // no notifyIO - just did it. | |
| 768 } | |
| 769 } | |
| 770 } | |
| 771 } | |
| 772 | |
| 773 /** | |
| 774 * This is a blocking call ! ( can be made non-blocking, but too complex ) | |
| 775 * | |
| 776 * Will be called automatically after connect() or accept if 'secure' is | |
| 777 * true. | |
| 778 * | |
| 779 * Can be called manually to upgrade the channel | |
| 780 * @throws IOException | |
| 781 */ | |
| 782 public void blockingStartTLS() throws IOException { | |
| 783 synchronized(this) { | |
| 784 if (socket == 0 || !context.running) { | |
| 785 return; | |
| 786 } | |
| 787 if ((status & SSL_ATTACHED) != 0) { | |
| 788 return; | |
| 789 } | |
| 790 status |= SSL_ATTACHED; | |
| 791 } | |
| 792 | |
| 793 try { | |
| 794 if (log.isLoggable(Level.FINE)) { | |
| 795 log.info(this + " StartSSL"); | |
| 796 } | |
| 797 | |
| 798 AprSocketContext aprCon = context; | |
| 799 SSLSocket.attach(aprCon.getSslCtx(), socket); | |
| 800 | |
| 801 if (context.debugSSL) { | |
| 802 SSLExt.debug(socket); | |
| 803 } | |
| 804 if (!getContext().isServer()) { | |
| 805 if (context.USE_TICKETS && hostInfo.ticketLen > 0) { | |
| 806 SSLExt.setTicket(socket, hostInfo.ticket, | |
| 807 hostInfo.ticketLen); | |
| 808 } else if (hostInfo.sessDer != null) { | |
| 809 SSLExt.setSessionData(socket, hostInfo.sessDer, | |
| 810 hostInfo.sessDer.length); | |
| 811 } | |
| 812 } | |
| 813 SSLExt.sslSetMode(socket, SSLExt.SSL_MODE_ENABLE_PARTIAL_WRITE | | |
| 814 SSLExt.SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); | |
| 815 | |
| 816 int rc = SSLSocket.handshake(socket); | |
| 817 | |
| 818 // At this point we have the session ID, remote certs, etc | |
| 819 // we can lookup host info | |
| 820 if (hostInfo == null) { | |
| 821 hostInfo = new HostInfo(); | |
| 822 } | |
| 823 | |
| 824 if (rc != Status.APR_SUCCESS) { | |
| 825 throw new IOException(this + " Handshake failed " + rc + " " | |
| 826 + Error.strerror(rc) + " SSLL " | |
| 827 + SSL.getLastError()); | |
| 828 } else { // SUCCESS | |
| 829 handshakeDone(); | |
| 830 } | |
| 831 } catch (IOException e) { | |
| 832 throw e; | |
| 833 } catch (Exception e) { | |
| 834 throw new IOException(e); | |
| 835 } | |
| 836 } | |
| 837 | |
| 838 private void handshakeDone() throws IOException { | |
| 839 getHost(); | |
| 840 if (socket == 0 || !context.running) { | |
| 841 throw new IOException("Socket closed"); | |
| 842 } | |
| 843 if (context.USE_TICKETS && ! context.isServer()) { | |
| 844 if (hostInfo.ticket == null) { | |
| 845 hostInfo.ticket = new byte[2048]; | |
| 846 } | |
| 847 int ticketLen = SSLExt.getTicket(socket, hostInfo.ticket); | |
| 848 if (ticketLen > 0) { | |
| 849 hostInfo.ticketLen = ticketLen; | |
| 850 if (log.isLoggable(Level.FINE)) { | |
| 851 log.info("Received ticket: " + ticketLen); | |
| 852 } | |
| 853 } | |
| 854 } | |
| 855 | |
| 856 // TODO: if the ticket, session id or session changed - callback to | |
| 857 // save the session again | |
| 858 try { | |
| 859 hostInfo.sessDer = SSLExt.getSessionData(socket); | |
| 860 getPeerCert(true); | |
| 861 hostInfo.sessionId = SSLSocket.getInfoS(socket, | |
| 862 SSL.SSL_INFO_SESSION_ID); | |
| 863 } catch (Exception e) { | |
| 864 throw new IOException(e); | |
| 865 } | |
| 866 | |
| 867 hostInfo.npn = new byte[32]; | |
| 868 hostInfo.npnLen = SSLExt.getNPN(socket, hostInfo.npn); | |
| 869 | |
| 870 // If custom verification is used - should check the certificates | |
| 871 if (context.tlsCertVerifier != null) { | |
| 872 context.tlsCertVerifier.handshakeDone(this); | |
| 873 } | |
| 874 } | |
| 875 | |
| 876 int requestedPolling() { | |
| 877 synchronized(this) { | |
| 878 if (socket == 0 || ((status & CLOSED) != 0)) { | |
| 879 return 0; | |
| 880 } | |
| 881 // Implicit: | |
| 882 //Poll.APR_POLLNVAL | Poll.APR_POLLHUP | Poll.APR_POLLERR | | |
| 883 int res = 0; | |
| 884 if ((status & POLLIN) != 0) { | |
| 885 res = Poll.APR_POLLIN; | |
| 886 } | |
| 887 if ((status & POLLOUT) != 0) { | |
| 888 res |= Poll.APR_POLLOUT; | |
| 889 } | |
| 890 return res; | |
| 891 } | |
| 892 } | |
| 893 | |
| 894 boolean checkBitAndSocket(int bit) { | |
| 895 synchronized (this) { | |
| 896 return ((status & bit) != 0 && socket != 0 && | |
| 897 (status & CLOSED) == 0 && context.running); | |
| 898 } | |
| 899 } | |
| 900 | |
| 901 boolean checkPreConnect(int bit) { | |
| 902 synchronized (this) { | |
| 903 return ((status & bit) != 0); | |
| 904 } | |
| 905 } | |
| 906 | |
| 907 void clearStatus(int bit) { | |
| 908 synchronized (this) { | |
| 909 status &= ~bit; | |
| 910 } | |
| 911 } | |
| 912 | |
| 913 boolean setStatus(int bit) { | |
| 914 synchronized (this) { | |
| 915 int old = status & bit; | |
| 916 status |= bit; | |
| 917 return old != 0; | |
| 918 } | |
| 919 } | |
| 920 | |
| 921 | |
| 922 } | |
| OLD | NEW |