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) 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) 179 public void onConnected(Connected<?> event, IOSubchannel encryptedChannel) { 180 new PlainChannel(event, encryptedChannel); 181 } 182 183 /** 184 * Handles encrypted data from upstream (the network). The data is 185 * send through the {@link SSLEngine} and events are sent downstream 186 * (and in the initial phases upstream) according to the conversion 187 * results. 188 * 189 * @param event the event 190 * @param encryptedChannel the channel for exchanging the encrypted data 191 * @throws InterruptedException 192 * @throws SSLException 193 * @throws ExecutionException 194 */ 195 @Handler(channels = EncryptedChannel.class) 196 public void onInput( 197 Input<ByteBuffer> event, IOSubchannel encryptedChannel) 198 throws InterruptedException, SSLException, ExecutionException { 199 @SuppressWarnings({ "unchecked", "PMD.AvoidDuplicateLiterals" }) 200 final Optional<PlainChannel> plainChannel 201 = (Optional<PlainChannel>) LinkedIOSubchannel 202 .downstreamChannel(this, encryptedChannel); 203 if (plainChannel.isPresent()) { 204 plainChannel.get().sendDownstream(event); 205 } 206 } 207 208 /** 209 * Handles a half close event from the encrypted channel (client). 210 * 211 * @param event the event 212 * @param encryptedChannel the channel for exchanging the encrypted data 213 * @throws InterruptedException 214 * @throws SSLException 215 */ 216 @Handler(channels = EncryptedChannel.class) 217 public void onHalfClosed(HalfClosed event, IOSubchannel encryptedChannel) 218 throws SSLException, InterruptedException { 219 @SuppressWarnings("unchecked") 220 final Optional<PlainChannel> plainChannel 221 = (Optional<PlainChannel>) LinkedIOSubchannel 222 .downstreamChannel(this, encryptedChannel); 223 if (plainChannel.isPresent()) { 224 plainChannel.get().upstreamHalfClosed(); 225 } 226 } 227 228 /** 229 * Handles a close event from the encrypted channel (client). 230 * 231 * @param event the event 232 * @param encryptedChannel the channel for exchanging the encrypted data 233 * @throws InterruptedException 234 * @throws SSLException 235 */ 236 @Handler(channels = EncryptedChannel.class) 237 public void onClosed(Closed<Void> event, IOSubchannel encryptedChannel) 238 throws SSLException, InterruptedException { 239 @SuppressWarnings("unchecked") 240 final Optional<PlainChannel> plainChannel 241 = (Optional<PlainChannel>) LinkedIOSubchannel 242 .downstreamChannel(this, encryptedChannel); 243 if (plainChannel.isPresent()) { 244 plainChannel.get().upstreamClosed(); 245 } 246 } 247 248 /** 249 * Forwards a {@link Purge} event downstream. 250 * 251 * @param event the event 252 * @param encryptedChannel the encrypted channel 253 */ 254 @Handler(channels = EncryptedChannel.class) 255 public void onPurge(Purge event, IOSubchannel encryptedChannel) { 256 @SuppressWarnings("unchecked") 257 final Optional<PlainChannel> plainChannel 258 = (Optional<PlainChannel>) LinkedIOSubchannel 259 .downstreamChannel(this, encryptedChannel); 260 if (plainChannel.isPresent()) { 261 plainChannel.get().purge(); 262 } 263 } 264 265 /** 266 * Handles an {@link IOError} event from the encrypted channel (client) 267 * by sending it downstream. 268 * 269 * @param event the event 270 * @param encryptedChannel the channel for exchanging the encrypted data 271 * @throws InterruptedException 272 * @throws SSLException 273 */ 274 @Handler(channels = EncryptedChannel.class) 275 public void onIOError(IOError event, IOSubchannel encryptedChannel) 276 throws SSLException, InterruptedException { 277 @SuppressWarnings("unchecked") 278 final Optional<PlainChannel> plainChannel 279 = (Optional<PlainChannel>) LinkedIOSubchannel 280 .downstreamChannel(this, encryptedChannel); 281 plainChannel.ifPresent(channel -> fire(new IOError(event), channel)); 282 } 283 284 /** 285 * Sends plain data through the engine and then upstream. 286 * 287 * @param event 288 * the event with the data 289 * @throws InterruptedException if the execution was interrupted 290 * @throws SSLException if some SSL related problem occurs 291 * @throws ExecutionException 292 */ 293 @Handler 294 public void onOutput(Output<ByteBuffer> event, 295 PlainChannel plainChannel) 296 throws InterruptedException, SSLException, ExecutionException { 297 if (plainChannel.hub() != this) { 298 return; 299 } 300 plainChannel.sendUpstream(event); 301 } 302 303 /** 304 * Forwards a close event upstream. 305 * 306 * @param event 307 * the close event 308 * @throws SSLException if an SSL related problem occurs 309 * @throws InterruptedException if the execution was interrupted 310 */ 311 @Handler 312 public void onClose(Close event, PlainChannel plainChannel) 313 throws InterruptedException, SSLException { 314 if (plainChannel.hub() != this) { 315 return; 316 } 317 plainChannel.close(event); 318 } 319 320 /** 321 * Represents the plain channel. 322 */ 323 @SuppressWarnings("PMD.DataflowAnomalyAnalysis") 324 private class PlainChannel extends LinkedIOSubchannel { 325 public SocketAddress localAddress; 326 public SocketAddress remoteAddress; 327 public SSLEngine sslEngine; 328 private EventPipeline downPipeline; 329 private ManagedBufferPool<ManagedBuffer<ByteBuffer>, 330 ByteBuffer> downstreamPool; 331 private ByteBuffer carryOver; 332 private boolean[] inputProcessed = { false }; 333 334 /** 335 * Instantiates a new plain channel from an accepted connection. 336 * 337 * @param event the event 338 * @param upstreamChannel the upstream channel 339 */ 340 public PlainChannel(Accepted event, IOSubchannel upstreamChannel) { 341 super(SslCodec.this, channel(), upstreamChannel, 342 newEventPipeline()); 343 localAddress = event.localAddress(); 344 remoteAddress = event.remoteAddress(); 345 init(); 346 sslEngine.setUseClientMode(false); 347 } 348 349 /** 350 * Instantiates a new plain channel from an established connection. 351 * 352 * @param event the event 353 * @param upstreamChannel the upstream channel 354 */ 355 public PlainChannel(Connected<?> event, IOSubchannel upstreamChannel) { 356 super(SslCodec.this, channel(), upstreamChannel, 357 newEventPipeline()); 358 localAddress = event.localAddress(); 359 remoteAddress = event.remoteAddress(); 360 init(); 361 sslEngine.setUseClientMode(true); 362 363 // Forward downstream 364 if (event instanceof ClientConnected) { 365 downPipeline.fire(new ClientConnected( 366 ((ClientConnected) event).openEvent().associated( 367 SslCodec.class, OpenSocketConnection.class).get(), 368 event.localAddress(), event.remoteAddress()), this); 369 370 } else { 371 downPipeline.fire(new Connected<Void>( 372 event.localAddress(), event.remoteAddress()), this); 373 } 374 } 375 376 private void init() { 377 if (remoteAddress instanceof InetSocketAddress) { 378 sslEngine = sslContext.createSSLEngine( 379 ((InetSocketAddress) remoteAddress).getAddress() 380 .getHostAddress(), 381 ((InetSocketAddress) remoteAddress).getPort()); 382 } else { 383 sslEngine = sslContext.createSSLEngine(); 384 } 385 String channelName = Components.objectName(SslCodec.this) 386 + "." + Components.objectName(this); 387 // Create buffer pools, adding 50 to decoded application buffer 388 // size, see 389 // https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/samples/sslengine/SSLEngineSimpleDemo.java 390 final int appBufSize 391 = sslEngine.getSession().getApplicationBufferSize(); 392 downstreamPool = new ManagedBufferPool<>(ManagedBuffer::new, 393 () -> ByteBuffer.allocate(appBufSize + 50), 2) 394 .setName(channelName + ".downstream.buffers"); 395 // Provide buffers with application buffer size 396 // for use by downstream components. 397 setByteBufferPool(new ManagedBufferPool<>(ManagedBuffer::new, 398 () -> ByteBuffer.allocate(appBufSize), 2) 399 .setName(channelName + ".upstream.buffers")); 400 downPipeline = newEventPipeline(); 401 // Buffers for sending encrypted data upstream will be 402 // obtained from upstream() and resized if required. 403 } 404 405 /** 406 * Sends input downstream. 407 * 408 * @param event the event 409 * @throws SSLException the SSL exception 410 * @throws InterruptedException the interrupted exception 411 * @throws ExecutionException the execution exception 412 */ 413 public void sendDownstream(Input<ByteBuffer> event) 414 throws SSLException, InterruptedException, ExecutionException { 415 ByteBuffer input = event.buffer().duplicate(); 416 if (carryOver != null) { 417 if (carryOver.remaining() < input.remaining()) { 418 // Shouldn't happen with carryOver having packet size 419 // bytes left, have seen it happen nevertheless. 420 carryOver.flip(); 421 ByteBuffer extCarryOver = ByteBuffer.allocate( 422 carryOver.remaining() + input.remaining()); 423 extCarryOver.put(carryOver); 424 carryOver = extCarryOver; 425 } 426 carryOver.put(input); 427 carryOver.flip(); 428 input = carryOver; 429 carryOver = null; 430 } 431 432 // Main processing 433 processInput(input); 434 435 // Check if data from incomplete packet remains in input buffer 436 if (input.hasRemaining()) { 437 // Actually, packet buffer size should be sufficient, 438 // but since this is hard to test and doesn't really matter... 439 carryOver = ByteBuffer.allocate(input.remaining() 440 + sslEngine.getSession().getPacketBufferSize() + 50); 441 carryOver.put(input); 442 } 443 } 444 445 @SuppressWarnings({ "PMD.CyclomaticComplexity", "PMD.NcssCount", 446 "PMD.AvoidInstantiatingObjectsInLoops", "PMD.ExcessiveMethodLength", 447 "PMD.NPathComplexity", "PMD.CognitiveComplexity" }) 448 private SSLEngineResult processInput(ByteBuffer input) 449 throws SSLException, InterruptedException, ExecutionException { 450 SSLEngineResult unwrapResult; 451 ManagedBuffer<ByteBuffer> unwrapped = downstreamPool.acquire(); 452 while (true) { 453 unwrapResult 454 = sslEngine.unwrap(input, unwrapped.backingBuffer()); 455 synchronized (inputProcessed) { 456 inputProcessed[0] = true; 457 inputProcessed.notifyAll(); 458 } 459 // Handle any handshaking procedures 460 switch (unwrapResult.getHandshakeStatus()) { 461 case NEED_TASK: 462 while (true) { 463 Runnable runnable = sslEngine.getDelegatedTask(); 464 if (runnable == null) { 465 break; 466 } 467 // Having this handled by the response thread is 468 // probably not really necessary, but as the delegated 469 // task usually includes sending upstream... 470 @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") 471 FutureTask<Boolean> task 472 = new FutureTask<>(runnable, true); 473 upstreamChannel().responsePipeline() 474 .executorService().submit(task); 475 task.get(); 476 } 477 continue; 478 479 case NEED_WRAP: 480 ManagedBuffer<ByteBuffer> feedback 481 = acquireUpstreamBuffer(); 482 synchronized (sslEngine) { 483 SSLEngineResult wrapResult = sslEngine.wrap( 484 ManagedBuffer.EMPTY_BYTE_BUFFER.backingBuffer(), 485 feedback.backingBuffer()); 486 // JDK11 sometimes returns NEED_WRAP (together 487 // with status is CLOSED) but does not produce 488 // anything when wrapping (all of which does 489 // not make sense). 490 if (feedback.position() == 0 491 && unwrapResult.getStatus() == Status.CLOSED) { 492 feedback.unlockBuffer(); 493 break; 494 } 495 upstreamChannel() 496 .respond(Output.fromSink(feedback, false)); 497 if (wrapResult 498 .getHandshakeStatus() == HandshakeStatus.FINISHED) { 499 fireAccepted(); 500 } 501 } 502 continue; 503 504 case FINISHED: 505 fireAccepted(); 506 break; 507 508 default: 509 break; 510 } 511 512 // Anything to forward downstream? 513 if (unwrapped.position() > 0) { 514 // forward unwrapped data 515 downPipeline.fire(Input.fromSink(unwrapped, 516 sslEngine.isInboundDone()), this); 517 unwrapped = null; 518 } 519 520 // If we have a buffer overflow or everything was okay 521 // and there's data left, we try again, else we quit. 522 if (unwrapResult.getStatus() != Status.BUFFER_OVERFLOW 523 && (unwrapResult.getStatus() != Status.OK 524 || !input.hasRemaining())) { 525 // Underflow or closed 526 if (unwrapped != null) { 527 unwrapped.unlockBuffer(); 528 } 529 break; 530 } 531 532 // Make sure we have an output buffer. 533 if (unwrapped == null) { 534 unwrapped = downstreamPool.acquire(); 535 } 536 } 537 return unwrapResult; 538 } 539 540 @SuppressWarnings("PMD.DataflowAnomalyAnalysis") 541 private void fireAccepted() { 542 List<SNIServerName> snis = Collections.emptyList(); 543 if (sslEngine.getSession() instanceof ExtendedSSLSession) { 544 snis = ((ExtendedSSLSession) sslEngine.getSession()) 545 .getRequestedServerNames(); 546 } 547 downPipeline.fire(new Accepted( 548 localAddress, remoteAddress, true, snis), this); 549 } 550 551 /** 552 * Send output upstream. 553 * 554 * @param event the event 555 * @throws SSLException the SSL exception 556 * @throws InterruptedException the interrupted exception 557 * @throws ExecutionException 558 */ 559 public void sendUpstream(Output<ByteBuffer> event) 560 throws SSLException, InterruptedException, ExecutionException { 561 ByteBuffer output = event.buffer().backingBuffer().duplicate(); 562 processOutput(output, event.isEndOfRecord()); 563 } 564 565 @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.NcssCount", 566 "PMD.CyclomaticComplexity", "PMD.NPathComplexity", 567 "PMD.CognitiveComplexity" }) 568 private void processOutput(ByteBuffer output, boolean eor) 569 throws InterruptedException, SSLException, ExecutionException { 570 ManagedBuffer<ByteBuffer> wrapped = acquireUpstreamBuffer(); 571 while (true) { 572 SSLEngineResult wrapResult; 573 // Cheap synchronization: no (relevant) input 574 inputProcessed[0] = false; 575 synchronized (sslEngine) { 576 wrapResult 577 = sslEngine.wrap(output, wrapped.backingBuffer()); 578 // Anything to be sent upstream? 579 if (wrapped.position() > 0) { 580 upstreamChannel().respond(Output.fromSink(wrapped, 581 sslEngine.isInboundDone() 582 || eor && !output.hasRemaining())); 583 wrapped = null; 584 } 585 } 586 switch (wrapResult.getHandshakeStatus()) { 587 case NEED_TASK: 588 while (true) { 589 Runnable runnable = sslEngine.getDelegatedTask(); 590 if (runnable == null) { 591 break; 592 } 593 runnable.run(); 594 } 595 continue; 596 597 case NEED_UNWRAP: 598 // Input required. Wait until 599 // input becomes available and retry. 600 synchronized (inputProcessed) { 601 while (!inputProcessed[0]) { 602 inputProcessed.wait(); 603 } 604 } 605 break; 606 607 default: 608 break; 609 } 610 611 // If we have a buffer overflow or everything was okay 612 // and there's data left, we try again, else we quit. 613 if (wrapResult.getStatus() != Status.BUFFER_OVERFLOW 614 && (wrapResult.getStatus() != Status.OK 615 || !output.hasRemaining())) { 616 // Underflow or closed 617 if (wrapped != null) { 618 wrapped.unlockBuffer(); 619 } 620 // Warn if data is discarded 621 if (output.hasRemaining()) { 622 logger.warning(() -> toString() 623 + ": Upstream data discarded, SSLEngine status: " 624 + wrapResult.getStatus()); 625 } 626 break; 627 } 628 629 // Was handshake (or partial content), get new buffer and try 630 // again 631 if (wrapped == null) { 632 wrapped = acquireUpstreamBuffer(); 633 } 634 } 635 } 636 637 /** 638 * Close the connection. 639 * 640 * @param event the event 641 * @throws InterruptedException the interrupted exception 642 * @throws SSLException the SSL exception 643 */ 644 public void close(Close event) 645 throws InterruptedException, SSLException { 646 sslEngine.closeOutbound(); 647 while (!sslEngine.isOutboundDone()) { 648 ManagedBuffer<ByteBuffer> feedback = acquireUpstreamBuffer(); 649 sslEngine.wrap(ManagedBuffer.EMPTY_BYTE_BUFFER 650 .backingBuffer(), feedback.backingBuffer()); 651 upstreamChannel().respond(Output.fromSink(feedback, false)); 652 } 653 upstreamChannel().respond(new Close()); 654 } 655 656 /** 657 * Handles the {@link HalfClosed} event. 658 * 659 * @throws SSLException the SSL exception 660 * @throws InterruptedException the interrupted exception 661 */ 662 @SuppressWarnings("PMD.GuardLogStatement") 663 public void upstreamHalfClosed() 664 throws SSLException, InterruptedException { 665 if (sslEngine.isInboundDone()) { 666 // Was properly closed on SSL level 667 return; 668 } 669 try { 670 sslEngine.closeInbound(); 671 sslEngine.closeOutbound(); 672 while (!sslEngine.isOutboundDone()) { 673 ManagedBuffer<ByteBuffer> feedback 674 = acquireUpstreamBuffer(); 675 SSLEngineResult result = sslEngine.wrap( 676 ManagedBuffer.EMPTY_BYTE_BUFFER.backingBuffer(), 677 feedback.backingBuffer()); 678 // This is required for/since JDK 11. It claims that 679 // outbound is not done, but doesn't produce any 680 // additional 681 // data. 682 if (result.getStatus() == Status.CLOSED 683 || feedback.position() == 0) { 684 feedback.unlockBuffer(); 685 break; 686 } 687 upstreamChannel() 688 .respond(Output.fromSink(feedback, false)); 689 } 690 } catch (SSLException e) { 691 // Several clients (notably chromium, see 692 // https://bugs.chromium.org/p/chromium/issues/detail?id=118366 693 // don't close the connection properly. So nobody is really 694 // interested in this message 695 logger.log(Level.FINEST, e.getMessage(), e); 696 } catch (InterruptedException e) { 697 logger.log(Level.WARNING, e.getMessage(), e); 698 } 699 } 700 701 /** 702 * Forwards the {@link Closed} event downstream. 703 * 704 * @throws SSLException the SSL exception 705 * @throws InterruptedException the interrupted exception 706 */ 707 public void upstreamClosed() 708 throws SSLException, InterruptedException { 709 downPipeline.fire(new Closed<Void>(), this); 710 } 711 712 private ManagedBuffer<ByteBuffer> acquireUpstreamBuffer() 713 throws InterruptedException { 714 ManagedBuffer<ByteBuffer> feedback 715 = upstreamChannel().byteBufferPool().acquire(); 716 int encSize 717 = sslEngine.getSession().getPacketBufferSize() + 50; 718 if (feedback.capacity() < encSize) { 719 feedback.replaceBackingBuffer(ByteBuffer.allocate( 720 encSize)); 721 } 722 return feedback; 723 } 724 725 /** 726 * Fire a {@link Purge} event downstream. 727 */ 728 public void purge() { 729 downPipeline.fire(new Purge(), this); 730 } 731 } 732}