001/* 002 * JGrapes Event Driven Framework 003 * Copyright (C) 2017-2019 Michael N. Lipp 004 * 005 * This program is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU Affero General Public License as published by 007 * the Free Software Foundation; either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, but 011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 013 * for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License along 016 * with this program; if not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jgrapes.net; 020 021import java.net.InetSocketAddress; 022import java.net.SocketAddress; 023import java.nio.ByteBuffer; 024import java.security.KeyManagementException; 025import java.security.NoSuchAlgorithmException; 026import java.security.cert.X509Certificate; 027import java.util.Collections; 028import java.util.List; 029import java.util.Optional; 030import java.util.concurrent.ExecutionException; 031import java.util.concurrent.FutureTask; 032import java.util.logging.Level; 033import javax.net.ssl.ExtendedSSLSession; 034import javax.net.ssl.SNIServerName; 035import javax.net.ssl.SSLContext; 036import javax.net.ssl.SSLEngine; 037import javax.net.ssl.SSLEngineResult; 038import javax.net.ssl.SSLEngineResult.HandshakeStatus; 039import javax.net.ssl.SSLEngineResult.Status; 040import javax.net.ssl.SSLException; 041import javax.net.ssl.TrustManager; 042import javax.net.ssl.X509TrustManager; 043import org.jgrapes.core.Channel; 044import org.jgrapes.core.ClassChannel; 045import org.jgrapes.core.Component; 046import org.jgrapes.core.Components; 047import org.jgrapes.core.EventPipeline; 048import org.jgrapes.core.annotation.Handler; 049import org.jgrapes.core.annotation.HandlerDefinition.ChannelReplacements; 050import org.jgrapes.io.IOSubchannel; 051import org.jgrapes.io.events.Close; 052import org.jgrapes.io.events.Closed; 053import org.jgrapes.io.events.HalfClosed; 054import org.jgrapes.io.events.IOError; 055import org.jgrapes.io.events.Input; 056import org.jgrapes.io.events.OpenSocketConnection; 057import org.jgrapes.io.events.Output; 058import org.jgrapes.io.events.Purge; 059import org.jgrapes.io.util.LinkedIOSubchannel; 060import org.jgrapes.io.util.ManagedBuffer; 061import org.jgrapes.io.util.ManagedBufferPool; 062import org.jgrapes.net.events.Accepted; 063import org.jgrapes.net.events.ClientConnected; 064import org.jgrapes.net.events.Connected; 065 066/** 067 * A component that receives and sends byte buffers on an 068 * encrypted channel and sends and receives the corresponding 069 * decrypted data on a plain channel. 070 * 071 * The encrypted channel is assumed to be the network side 072 * ("upstream") and therefore {@link Input} events represent 073 * encrypted data and are decoded to {@link Output} events on 074 * the plain channel ("downstream") and vice versa. 075 */ 076@SuppressWarnings({ "PMD.ExcessiveImports" }) 077public class SslCodec extends Component { 078 079 private final Channel encryptedChannel; 080 private final SSLContext sslContext; 081 082 /** 083 * Represents the encrypted channel in annotations. 084 */ 085 private class EncryptedChannel extends ClassChannel { 086 } 087 088 /** 089 * Creates a new codec that uses the given {@link SSLContext}. 090 * 091 * @param plainChannel the component's channel 092 * @param encryptedChannel the channel with the encrypted data 093 * @param sslContext the SSL context to use 094 */ 095 public SslCodec(Channel plainChannel, Channel encryptedChannel, 096 SSLContext sslContext) { 097 super(plainChannel, ChannelReplacements.create() 098 .add(EncryptedChannel.class, encryptedChannel)); 099 this.encryptedChannel = encryptedChannel; 100 this.sslContext = sslContext; 101 } 102 103 /** 104 * Creates a new codec to be used as client. 105 * 106 * @param plainChannel the component's channel 107 * @param encryptedChannel the channel with the encrypted data 108 * @param dontValidate if `true` accept all kinds of certificates 109 */ 110 @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.CommentRequired", 111 "PMD.ReturnEmptyArrayRatherThanNull", "PMD.UncommentedEmptyMethodBody", 112 "PMD.AvoidDuplicateLiterals" }) 113 public SslCodec(Channel plainChannel, Channel encryptedChannel, 114 boolean dontValidate) { 115 super(plainChannel, ChannelReplacements.create() 116 .add(EncryptedChannel.class, encryptedChannel)); 117 this.encryptedChannel = encryptedChannel; 118 try { 119 final SSLContext sslContext = SSLContext.getInstance("SSL"); 120 if (dontValidate) { 121 // Create a trust manager that does not validate certificate 122 // chains 123 final TrustManager[] trustAllCerts = { 124 new X509TrustManager() { 125 public X509Certificate[] getAcceptedIssuers() { 126 return new X509Certificate[0]; 127 } 128 129 public void checkClientTrusted( 130 X509Certificate[] certs, String authType) { 131 } 132 133 public void checkServerTrusted( 134 X509Certificate[] certs, String authType) { 135 } 136 } 137 }; 138 sslContext.init(null, trustAllCerts, null); 139 } else { 140 sslContext.init(null, null, null); 141 } 142 this.sslContext = sslContext; 143 } catch (NoSuchAlgorithmException | KeyManagementException e) { 144 throw new IllegalArgumentException(e); 145 } 146 } 147 148 /** 149 * Creates a new downstream connection as {@link LinkedIOSubchannel} 150 * of the network connection together with an {@link SSLEngine}. 151 * 152 * @param event 153 * the accepted event 154 */ 155 @Handler(channels = EncryptedChannel.class, excludeSelf = true) 156 public void onAccepted(Accepted event, IOSubchannel encryptedChannel) { 157 new PlainChannel(event, encryptedChannel); 158 } 159 160 /** 161 * Forward the connection request to the encrypted network. 162 * 163 * @param event the event 164 */ 165 @Handler 166 public void onOpenConnection(OpenSocketConnection event) { 167 fire(new OpenSocketConnection(event.address()) 168 .setAssociated(SslCodec.class, event), encryptedChannel); 169 } 170 171 /** 172 * Creates a new downstream connection as {@link LinkedIOSubchannel} 173 * of the network connection together with an {@link SSLEngine}. 174 * 175 * @param event 176 * the accepted event 177 */ 178 @Handler(channels = EncryptedChannel.class, excludeSelf = true) 179 public void onConnected(ClientConnected event, 180 IOSubchannel encryptedChannel) { 181 if (event.openEvent() 182 .associated(SslCodec.class, Object.class).isPresent()) { 183 new PlainChannel(event, encryptedChannel); 184 } 185 } 186 187 /** 188 * Handles encrypted data from upstream (the network). The data is 189 * send through the {@link SSLEngine} and events are sent downstream 190 * (and in the initial phases upstream) according to the conversion 191 * results. 192 * 193 * @param event the event 194 * @param encryptedChannel the channel for exchanging the encrypted data 195 * @throws InterruptedException 196 * @throws SSLException 197 * @throws ExecutionException 198 */ 199 @Handler(channels = EncryptedChannel.class, excludeSelf = true) 200 public void onInput(Input<ByteBuffer> event, IOSubchannel encryptedChannel) 201 throws InterruptedException, SSLException, ExecutionException { 202 @SuppressWarnings({ "unchecked", "PMD.AvoidDuplicateLiterals" }) 203 final Optional<PlainChannel> plainChannel 204 = (Optional<PlainChannel>) LinkedIOSubchannel 205 .downstreamChannel(this, encryptedChannel); 206 if (plainChannel.isPresent()) { 207 plainChannel.get().sendDownstream(event); 208 } 209 } 210 211 /** 212 * Handles a half close event from the encrypted channel (client). 213 * 214 * @param event the event 215 * @param encryptedChannel the channel for exchanging the encrypted data 216 * @throws InterruptedException 217 * @throws SSLException 218 */ 219 @Handler(channels = EncryptedChannel.class, excludeSelf = true) 220 public void onHalfClosed(HalfClosed event, IOSubchannel encryptedChannel) 221 throws SSLException, InterruptedException { 222 @SuppressWarnings("unchecked") 223 final Optional<PlainChannel> plainChannel 224 = (Optional<PlainChannel>) LinkedIOSubchannel 225 .downstreamChannel(this, encryptedChannel); 226 if (plainChannel.isPresent()) { 227 plainChannel.get().upstreamHalfClosed(); 228 } 229 } 230 231 /** 232 * Handles a close event from the encrypted channel (client). 233 * 234 * @param event the event 235 * @param encryptedChannel the channel for exchanging the encrypted data 236 * @throws InterruptedException 237 * @throws SSLException 238 */ 239 @Handler(channels = EncryptedChannel.class, excludeSelf = true) 240 public void onClosed(Closed<Void> event, IOSubchannel encryptedChannel) 241 throws SSLException, InterruptedException { 242 @SuppressWarnings("unchecked") 243 final Optional<PlainChannel> plainChannel 244 = (Optional<PlainChannel>) LinkedIOSubchannel 245 .downstreamChannel(this, encryptedChannel); 246 if (plainChannel.isPresent()) { 247 plainChannel.get().upstreamClosed(); 248 } 249 } 250 251 /** 252 * Forwards a {@link Purge} event downstream. 253 * 254 * @param event the event 255 * @param encryptedChannel the encrypted channel 256 */ 257 @Handler(channels = EncryptedChannel.class, excludeSelf = true) 258 public void onPurge(Purge event, IOSubchannel encryptedChannel) { 259 @SuppressWarnings("unchecked") 260 final Optional<PlainChannel> plainChannel 261 = (Optional<PlainChannel>) LinkedIOSubchannel 262 .downstreamChannel(this, encryptedChannel); 263 if (plainChannel.isPresent()) { 264 plainChannel.get().purge(); 265 } 266 } 267 268 /** 269 * Handles an {@link IOError} event from the encrypted channel (client) 270 * by sending it downstream. 271 * 272 * @param event the event 273 * @param encryptedChannel the channel for exchanging the encrypted data 274 * @throws InterruptedException 275 * @throws SSLException 276 */ 277 @Handler(channels = EncryptedChannel.class, excludeSelf = true) 278 public void onIOError(IOError event, IOSubchannel encryptedChannel) 279 throws SSLException, InterruptedException { 280 @SuppressWarnings("unchecked") 281 final Optional<PlainChannel> plainChannel 282 = (Optional<PlainChannel>) LinkedIOSubchannel 283 .downstreamChannel(this, encryptedChannel); 284 plainChannel.ifPresent(channel -> fire(new IOError(event), channel)); 285 } 286 287 /** 288 * Sends plain data through the engine and then upstream. 289 * 290 * @param event 291 * the event with the data 292 * @throws InterruptedException if the execution was interrupted 293 * @throws SSLException if some SSL related problem occurs 294 * @throws ExecutionException 295 */ 296 @Handler 297 public void onOutput(Output<ByteBuffer> event, PlainChannel plainChannel) 298 throws InterruptedException, SSLException, ExecutionException { 299 if (plainChannel.hub() != this) { 300 return; 301 } 302 plainChannel.sendUpstream(event); 303 } 304 305 /** 306 * Forwards a close event upstream. 307 * 308 * @param event 309 * the close event 310 * @throws SSLException if an SSL related problem occurs 311 * @throws InterruptedException if the execution was interrupted 312 */ 313 @Handler 314 public void onClose(Close event, PlainChannel plainChannel) 315 throws InterruptedException, SSLException { 316 if (plainChannel.hub() != this) { 317 return; 318 } 319 plainChannel.close(event); 320 } 321 322 /** 323 * Represents the plain channel. 324 */ 325 @SuppressWarnings("PMD.DataflowAnomalyAnalysis") 326 private class PlainChannel extends LinkedIOSubchannel 327 implements SocketIOChannel { 328 public SocketAddress localAddress; 329 public SocketAddress remoteAddress; 330 public SSLEngine sslEngine; 331 private EventPipeline downPipeline; 332 private ManagedBufferPool<ManagedBuffer<ByteBuffer>, 333 ByteBuffer> downstreamPool; 334 private ByteBuffer carryOver; 335 private boolean[] inputProcessed = { false }; 336 337 /** 338 * Instantiates a new plain channel from an accepted connection. 339 * 340 * @param event the event 341 * @param upstreamChannel the upstream channel 342 */ 343 public PlainChannel(Accepted event, IOSubchannel upstreamChannel) { 344 super(SslCodec.this, channel(), upstreamChannel, 345 newEventPipeline()); 346 localAddress = event.localAddress(); 347 remoteAddress = event.remoteAddress(); 348 init(); 349 sslEngine.setUseClientMode(false); 350 } 351 352 /** 353 * Instantiates a new plain channel from an established connection. 354 * 355 * @param event the event 356 * @param upstreamChannel the upstream channel 357 */ 358 public PlainChannel(Connected<?> event, IOSubchannel upstreamChannel) { 359 super(SslCodec.this, channel(), upstreamChannel, 360 newEventPipeline()); 361 localAddress = event.localAddress(); 362 remoteAddress = event.remoteAddress(); 363 init(); 364 sslEngine.setUseClientMode(true); 365 366 // Forward downstream 367 if (event instanceof ClientConnected) { 368 downPipeline.fire(new ClientConnected( 369 ((ClientConnected) event).openEvent().associated( 370 SslCodec.class, OpenSocketConnection.class).get(), 371 event.localAddress(), event.remoteAddress()), this); 372 373 } else { 374 downPipeline.fire(new Connected<Void>( 375 event.localAddress(), event.remoteAddress()), this); 376 } 377 } 378 379 private void init() { 380 if (remoteAddress instanceof InetSocketAddress) { 381 sslEngine = sslContext.createSSLEngine( 382 ((InetSocketAddress) remoteAddress).getAddress() 383 .getHostAddress(), 384 ((InetSocketAddress) remoteAddress).getPort()); 385 } else { 386 sslEngine = sslContext.createSSLEngine(); 387 } 388 String channelName = Components.objectName(SslCodec.this) 389 + "." + Components.objectName(this); 390 // Create buffer pools, adding 50 to decoded application buffer 391 // size, see 392 // https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/samples/sslengine/SSLEngineSimpleDemo.java 393 final int appBufSize 394 = sslEngine.getSession().getApplicationBufferSize(); 395 downstreamPool = new ManagedBufferPool<>(ManagedBuffer::new, 396 () -> ByteBuffer.allocate(appBufSize + 50), 2) 397 .setName(channelName + ".downstream.buffers"); 398 // Provide buffers with application buffer size 399 // for use by downstream components. 400 setByteBufferPool(new ManagedBufferPool<>(ManagedBuffer::new, 401 () -> ByteBuffer.allocate(appBufSize), 2) 402 .setName(channelName + ".upstream.buffers")); 403 downPipeline = newEventPipeline(); 404 // Buffers for sending encrypted data upstream will be 405 // obtained from upstream() and resized if required. 406 } 407 408 /** 409 * Sends input downstream. 410 * 411 * @param event the event 412 * @throws SSLException the SSL exception 413 * @throws InterruptedException the interrupted exception 414 * @throws ExecutionException the execution exception 415 */ 416 public void sendDownstream(Input<ByteBuffer> event) 417 throws SSLException, InterruptedException, ExecutionException { 418 ByteBuffer input = event.buffer().duplicate(); 419 if (carryOver != null) { 420 if (carryOver.remaining() < input.remaining()) { 421 // Shouldn't happen with carryOver having packet size 422 // bytes left, have seen it happen nevertheless. 423 carryOver.flip(); 424 ByteBuffer extCarryOver = ByteBuffer.allocate( 425 carryOver.remaining() + input.remaining()); 426 extCarryOver.put(carryOver); 427 carryOver = extCarryOver; 428 } 429 carryOver.put(input); 430 carryOver.flip(); 431 input = carryOver; 432 carryOver = null; 433 } 434 435 // Main processing 436 processInput(input); 437 438 // Check if data from incomplete packet remains in input buffer 439 if (input.hasRemaining()) { 440 // Actually, packet buffer size should be sufficient, 441 // but since this is hard to test and doesn't really matter... 442 carryOver = ByteBuffer.allocate(input.remaining() 443 + sslEngine.getSession().getPacketBufferSize() + 50); 444 carryOver.put(input); 445 } 446 } 447 448 @SuppressWarnings({ "PMD.CyclomaticComplexity", "PMD.NcssCount", 449 "PMD.AvoidInstantiatingObjectsInLoops", "PMD.ExcessiveMethodLength", 450 "PMD.NPathComplexity", "PMD.CognitiveComplexity" }) 451 private SSLEngineResult processInput(ByteBuffer input) 452 throws SSLException, InterruptedException, ExecutionException { 453 SSLEngineResult unwrapResult; 454 ManagedBuffer<ByteBuffer> unwrapped = downstreamPool.acquire(); 455 while (true) { 456 unwrapResult 457 = sslEngine.unwrap(input, unwrapped.backingBuffer()); 458 synchronized (inputProcessed) { 459 inputProcessed[0] = true; 460 inputProcessed.notifyAll(); 461 } 462 // Handle any handshaking procedures 463 switch (unwrapResult.getHandshakeStatus()) { 464 case NEED_TASK: 465 while (true) { 466 Runnable runnable = sslEngine.getDelegatedTask(); 467 if (runnable == null) { 468 break; 469 } 470 // Having this handled by the response thread is 471 // probably not really necessary, but as the delegated 472 // task usually includes sending upstream... 473 @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") 474 FutureTask<Boolean> task 475 = new FutureTask<>(runnable, true); 476 upstreamChannel().responsePipeline() 477 .executorService().submit(task); 478 task.get(); 479 } 480 continue; 481 482 case NEED_WRAP: 483 ManagedBuffer<ByteBuffer> feedback 484 = acquireUpstreamBuffer(); 485 synchronized (sslEngine) { 486 SSLEngineResult wrapResult = sslEngine.wrap( 487 ManagedBuffer.EMPTY_BYTE_BUFFER.backingBuffer(), 488 feedback.backingBuffer()); 489 // JDK11 sometimes returns NEED_WRAP (together 490 // with status is CLOSED) but does not produce 491 // anything when wrapping (all of which does 492 // not make sense). 493 if (feedback.position() == 0 494 && unwrapResult.getStatus() == Status.CLOSED) { 495 feedback.unlockBuffer(); 496 break; 497 } 498 upstreamChannel() 499 .respond(Output.fromSink(feedback, false)); 500 if (wrapResult 501 .getHandshakeStatus() == HandshakeStatus.FINISHED) { 502 fireAccepted(); 503 } 504 } 505 continue; 506 507 case FINISHED: 508 fireAccepted(); 509 break; 510 511 default: 512 break; 513 } 514 515 // Anything to forward downstream? 516 if (unwrapped.position() > 0) { 517 // forward unwrapped data 518 downPipeline.fire(Input.fromSink(unwrapped, 519 sslEngine.isInboundDone()), this); 520 unwrapped = null; 521 } 522 523 // If we have a buffer overflow or everything was okay 524 // and there's data left, we try again, else we quit. 525 if (unwrapResult.getStatus() != Status.BUFFER_OVERFLOW 526 && (unwrapResult.getStatus() != Status.OK 527 || !input.hasRemaining())) { 528 // Underflow or closed 529 if (unwrapped != null) { 530 unwrapped.unlockBuffer(); 531 } 532 break; 533 } 534 535 // Make sure we have an output buffer. 536 if (unwrapped == null) { 537 unwrapped = downstreamPool.acquire(); 538 } 539 } 540 return unwrapResult; 541 } 542 543 @SuppressWarnings("PMD.DataflowAnomalyAnalysis") 544 private void fireAccepted() { 545 List<SNIServerName> snis = Collections.emptyList(); 546 if (sslEngine.getSession() instanceof ExtendedSSLSession) { 547 snis = ((ExtendedSSLSession) sslEngine.getSession()) 548 .getRequestedServerNames(); 549 } 550 downPipeline.fire(new Accepted( 551 localAddress, remoteAddress, true, snis), this); 552 } 553 554 /** 555 * Send output upstream. 556 * 557 * @param event the event 558 * @throws SSLException the SSL exception 559 * @throws InterruptedException the interrupted exception 560 * @throws ExecutionException 561 */ 562 public void sendUpstream(Output<ByteBuffer> event) 563 throws SSLException, InterruptedException, ExecutionException { 564 ByteBuffer output = event.buffer().backingBuffer().duplicate(); 565 processOutput(output, event.isEndOfRecord()); 566 } 567 568 @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.NcssCount", 569 "PMD.CyclomaticComplexity", "PMD.NPathComplexity", 570 "PMD.CognitiveComplexity" }) 571 private void processOutput(ByteBuffer output, boolean eor) 572 throws InterruptedException, SSLException, ExecutionException { 573 ManagedBuffer<ByteBuffer> wrapped = acquireUpstreamBuffer(); 574 while (true) { 575 SSLEngineResult wrapResult; 576 // Cheap synchronization: no (relevant) input 577 inputProcessed[0] = false; 578 synchronized (sslEngine) { 579 wrapResult 580 = sslEngine.wrap(output, wrapped.backingBuffer()); 581 // Anything to be sent upstream? 582 if (wrapped.position() > 0) { 583 upstreamChannel().respond(Output.fromSink(wrapped, 584 sslEngine.isInboundDone() 585 || eor && !output.hasRemaining())); 586 wrapped = null; 587 } 588 } 589 switch (wrapResult.getHandshakeStatus()) { 590 case NEED_TASK: 591 while (true) { 592 Runnable runnable = sslEngine.getDelegatedTask(); 593 if (runnable == null) { 594 break; 595 } 596 runnable.run(); 597 } 598 continue; 599 600 case NEED_UNWRAP: 601 // Input required. Wait until 602 // input becomes available and retry. 603 synchronized (inputProcessed) { 604 while (!inputProcessed[0]) { 605 inputProcessed.wait(); 606 } 607 } 608 break; 609 610 default: 611 break; 612 } 613 614 // If we have a buffer overflow or everything was okay 615 // and there's data left, we try again, else we quit. 616 if (wrapResult.getStatus() != Status.BUFFER_OVERFLOW 617 && (wrapResult.getStatus() != Status.OK 618 || !output.hasRemaining())) { 619 // Underflow or closed 620 if (wrapped != null) { 621 wrapped.unlockBuffer(); 622 } 623 // Warn if data is discarded 624 if (output.hasRemaining()) { 625 logger.warning(() -> toString() 626 + ": Upstream data discarded, SSLEngine status: " 627 + wrapResult.getStatus()); 628 } 629 break; 630 } 631 632 // Was handshake (or partial content), get new buffer and try 633 // again 634 if (wrapped == null) { 635 wrapped = acquireUpstreamBuffer(); 636 } 637 } 638 } 639 640 /** 641 * Close the connection. 642 * 643 * @param event the event 644 * @throws InterruptedException the interrupted exception 645 * @throws SSLException the SSL exception 646 */ 647 public void close(Close event) 648 throws InterruptedException, SSLException { 649 sslEngine.closeOutbound(); 650 while (!sslEngine.isOutboundDone()) { 651 ManagedBuffer<ByteBuffer> feedback = acquireUpstreamBuffer(); 652 sslEngine.wrap(ManagedBuffer.EMPTY_BYTE_BUFFER 653 .backingBuffer(), feedback.backingBuffer()); 654 upstreamChannel().respond(Output.fromSink(feedback, false)); 655 } 656 upstreamChannel().respond(new Close()); 657 } 658 659 /** 660 * Handles the {@link HalfClosed} event. 661 * 662 * @throws SSLException the SSL exception 663 * @throws InterruptedException the interrupted exception 664 */ 665 @SuppressWarnings("PMD.GuardLogStatement") 666 public void upstreamHalfClosed() 667 throws SSLException, InterruptedException { 668 if (sslEngine.isInboundDone()) { 669 // Was properly closed on SSL level 670 return; 671 } 672 try { 673 sslEngine.closeInbound(); 674 sslEngine.closeOutbound(); 675 while (!sslEngine.isOutboundDone()) { 676 ManagedBuffer<ByteBuffer> feedback 677 = acquireUpstreamBuffer(); 678 SSLEngineResult result = sslEngine.wrap( 679 ManagedBuffer.EMPTY_BYTE_BUFFER.backingBuffer(), 680 feedback.backingBuffer()); 681 // This is required for/since JDK 11. It claims that 682 // outbound is not done, but doesn't produce any 683 // additional 684 // data. 685 if (result.getStatus() == Status.CLOSED 686 || feedback.position() == 0) { 687 feedback.unlockBuffer(); 688 break; 689 } 690 upstreamChannel() 691 .respond(Output.fromSink(feedback, false)); 692 } 693 } catch (SSLException e) { 694 // Several clients (notably chromium, see 695 // https://bugs.chromium.org/p/chromium/issues/detail?id=118366 696 // don't close the connection properly. So nobody is really 697 // interested in this message 698 logger.log(Level.FINEST, e.getMessage(), e); 699 } catch (InterruptedException e) { 700 logger.log(Level.WARNING, e.getMessage(), e); 701 } 702 } 703 704 /** 705 * Forwards the {@link Closed} event downstream. 706 * 707 * @throws SSLException the SSL exception 708 * @throws InterruptedException the interrupted exception 709 */ 710 public void upstreamClosed() 711 throws SSLException, InterruptedException { 712 downPipeline.fire(new Closed<Void>(), this); 713 } 714 715 private ManagedBuffer<ByteBuffer> acquireUpstreamBuffer() 716 throws InterruptedException { 717 ManagedBuffer<ByteBuffer> feedback 718 = upstreamChannel().byteBufferPool().acquire(); 719 int encSize 720 = sslEngine.getSession().getPacketBufferSize() + 50; 721 if (feedback.capacity() < encSize) { 722 feedback.replaceBackingBuffer(ByteBuffer.allocate( 723 encSize)); 724 } 725 return feedback; 726 } 727 728 /** 729 * Fire a {@link Purge} event downstream. 730 */ 731 public void purge() { 732 downPipeline.fire(new Purge(), this); 733 } 734 735 @Override 736 public SocketAddress localAddress() { 737 return localAddress; 738 } 739 740 @Override 741 public SocketAddress remoteAddress() { 742 return remoteAddress; 743 } 744 745 @Override 746 public boolean isPurgeable() { 747 return false; 748 } 749 750 @Override 751 public long purgeableSince() { 752 return 0; 753 } 754 755 } 756}