2525import java .util .List ;
2626import java .util .concurrent .Executor ;
2727import java .util .concurrent .atomic .AtomicReference ;
28+ import java .util .function .ToIntFunction ;
2829import javax .net .ssl .SSLEngine ;
2930import javax .net .ssl .SSLEngineResult ;
3031import javax .net .ssl .SSLEngineResult .HandshakeStatus ;
3132import javax .net .ssl .SSLEngineResult .Status ;
3233import javax .net .ssl .SSLException ;
3334import javax .net .ssl .SSLHandshakeException ;
35+ import javax .net .ssl .SSLSession ;
3436
3537import org .eclipse .jetty .io .AbstractConnection ;
3638import org .eclipse .jetty .io .AbstractEndPoint ;
@@ -308,10 +310,37 @@ private boolean isHandshakeComplete()
308310 return state == HandshakeState .SUCCEEDED || state == HandshakeState .FAILED ;
309311 }
310312
313+ private int getApplicationBufferSize ()
314+ {
315+ return getBufferSize (SSLSession ::getApplicationBufferSize );
316+ }
317+
318+ private int getPacketBufferSize ()
319+ {
320+ return getBufferSize (SSLSession ::getPacketBufferSize );
321+ }
322+
323+ private int getBufferSize (ToIntFunction <SSLSession > bufferSizeFn )
324+ {
325+ SSLSession hsSession = _sslEngine .getHandshakeSession ();
326+ SSLSession session = _sslEngine .getSession ();
327+ int size = bufferSizeFn .applyAsInt (session );
328+ if (hsSession == null || hsSession == session )
329+ return size ;
330+ int hsSize = bufferSizeFn .applyAsInt (hsSession );
331+ return Math .max (hsSize , size );
332+ }
333+
311334 private void acquireEncryptedInput ()
312335 {
313336 if (_encryptedInput == null )
314- _encryptedInput = _bufferPool .acquire (_sslEngine .getSession ().getPacketBufferSize (), _encryptedDirectBuffers );
337+ _encryptedInput = _bufferPool .acquire (getPacketBufferSize (), _encryptedDirectBuffers );
338+ }
339+
340+ private void acquireEncryptedOutput ()
341+ {
342+ if (_encryptedOutput == null )
343+ _encryptedOutput = _bufferPool .acquire (getPacketBufferSize (), _encryptedDirectBuffers );
315344 }
316345
317346 @ Override
@@ -409,6 +438,24 @@ public String toConnectionString()
409438 connection instanceof AbstractConnection ? ((AbstractConnection )connection ).toConnectionString () : connection );
410439 }
411440
441+ private void releaseEncryptedInputBuffer ()
442+ {
443+ if (_encryptedInput != null && !_encryptedInput .hasRemaining ())
444+ {
445+ _bufferPool .release (_encryptedInput );
446+ _encryptedInput = null ;
447+ }
448+ }
449+
450+ protected void releaseDecryptedInputBuffer ()
451+ {
452+ if (_decryptedInput != null && !_decryptedInput .hasRemaining ())
453+ {
454+ _bufferPool .release (_decryptedInput );
455+ _decryptedInput = null ;
456+ }
457+ }
458+
412459 private void releaseEncryptedOutputBuffer ()
413460 {
414461 if (!Thread .holdsLock (_decryptedEndPoint ))
@@ -544,9 +591,12 @@ public void setConnection(Connection connection)
544591 {
545592 if (connection instanceof AbstractConnection )
546593 {
547- AbstractConnection a = (AbstractConnection )connection ;
548- if (a .getInputBufferSize () < _sslEngine .getSession ().getApplicationBufferSize ())
549- a .setInputBufferSize (_sslEngine .getSession ().getApplicationBufferSize ());
594+ // This is an optimization to avoid that upper layer connections use small
595+ // buffers and we need to copy decrypted data rather than decrypting in place.
596+ AbstractConnection c = (AbstractConnection )connection ;
597+ int appBufferSize = getApplicationBufferSize ();
598+ if (c .getInputBufferSize () < appBufferSize )
599+ c .setInputBufferSize (appBufferSize );
550600 }
551601 super .setConnection (connection );
552602 }
@@ -613,12 +663,13 @@ public int fill(ByteBuffer buffer) throws IOException
613663
614664 // can we use the passed buffer if it is big enough
615665 ByteBuffer appIn ;
666+ int appBufferSize = getApplicationBufferSize ();
616667 if (_decryptedInput == null )
617668 {
618- if (BufferUtil .space (buffer ) > _sslEngine . getSession (). getApplicationBufferSize () )
669+ if (BufferUtil .space (buffer ) > appBufferSize )
619670 appIn = buffer ;
620671 else
621- appIn = _decryptedInput = _bufferPool .acquire (_sslEngine . getSession (). getApplicationBufferSize () , _decryptedDirectBuffers );
672+ appIn = _decryptedInput = _bufferPool .acquire (appBufferSize , _decryptedDirectBuffers );
622673 }
623674 else
624675 {
@@ -698,8 +749,21 @@ public int fill(ByteBuffer buffer) throws IOException
698749 }
699750 return filled = netFilled ;
700751
752+ case BUFFER_OVERFLOW :
753+ // It's possible that SSLSession.applicationBufferSize has been expanded
754+ // by the SSLEngine implementation. Unwrapping a large encrypted buffer
755+ // causes BUFFER_OVERFLOW because the (old) applicationBufferSize is
756+ // too small. Release the decrypted input buffer so it will be re-acquired
757+ // with the larger capacity.
758+ // See also system property "jsse.SSLEngine.acceptLargeFragments".
759+ if (BufferUtil .isEmpty (_decryptedInput ) && appBufferSize < getApplicationBufferSize ())
760+ {
761+ releaseDecryptedInputBuffer ();
762+ continue ;
763+ }
764+ throw new IllegalStateException ("Unexpected unwrap result " + unwrap );
765+
701766 case OK :
702- {
703767 if (unwrapResult .getHandshakeStatus () == HandshakeStatus .FINISHED )
704768 handshakeSucceeded ();
705769
@@ -717,7 +781,6 @@ public int fill(ByteBuffer buffer) throws IOException
717781 }
718782
719783 break ;
720- }
721784
722785 default :
723786 throw new IllegalStateException ("Unexpected unwrap result " + unwrap );
@@ -737,17 +800,8 @@ public int fill(ByteBuffer buffer) throws IOException
737800 }
738801 finally
739802 {
740- if (_encryptedInput != null && !_encryptedInput .hasRemaining ())
741- {
742- _bufferPool .release (_encryptedInput );
743- _encryptedInput = null ;
744- }
745-
746- if (_decryptedInput != null && !_decryptedInput .hasRemaining ())
747- {
748- _bufferPool .release (_decryptedInput );
749- _decryptedInput = null ;
750- }
803+ releaseEncryptedInputBuffer ();
804+ releaseDecryptedInputBuffer ();
751805
752806 if (_flushState == FlushState .WAIT_FOR_FILL )
753807 {
@@ -974,16 +1028,17 @@ public boolean flush(ByteBuffer... appOuts) throws IOException
9741028 throw new IllegalStateException ("Unexpected HandshakeStatus " + status );
9751029 }
9761030
977- if ( _encryptedOutput == null )
978- _encryptedOutput = _bufferPool . acquire ( _sslEngine . getSession (). getPacketBufferSize (), _encryptedDirectBuffers );
1031+ int packetBufferSize = getPacketBufferSize ();
1032+ acquireEncryptedOutput ( );
9791033
9801034 if (_handshake .compareAndSet (HandshakeState .INITIAL , HandshakeState .HANDSHAKE ))
9811035 {
9821036 if (LOG .isDebugEnabled ())
9831037 LOG .debug ("flush starting handshake {}" , SslConnection .this );
9841038 }
9851039
986- // We call sslEngine.wrap to try to take bytes from appOut buffers and encrypt them into the _netOut buffer
1040+ // We call sslEngine.wrap to try to take bytes from appOuts
1041+ // buffers and encrypt them into the _encryptedOutput buffer.
9871042 BufferUtil .compact (_encryptedOutput );
9881043 int pos = BufferUtil .flipToFill (_encryptedOutput );
9891044 SSLEngineResult wrapResult ;
@@ -1032,7 +1087,18 @@ public boolean flush(ByteBuffer... appOuts) throws IOException
10321087 case BUFFER_OVERFLOW :
10331088 if (!flushed )
10341089 return result = false ;
1035- continue ;
1090+ // It's possible that SSLSession.packetBufferSize has been expanded
1091+ // by the SSLEngine implementation. Wrapping a large application buffer
1092+ // causes BUFFER_OVERFLOW because the (old) packetBufferSize is
1093+ // too small. Release the encrypted output buffer so that it will
1094+ // be re-acquired with the larger capacity.
1095+ // See also system property "jsse.SSLEngine.acceptLargeFragments".
1096+ if (packetBufferSize < getPacketBufferSize ())
1097+ {
1098+ releaseEncryptedOutputBuffer ();
1099+ continue ;
1100+ }
1101+ throw new IllegalStateException ("Unexpected wrap result " + wrap );
10361102
10371103 case OK :
10381104 if (wrapResult .getHandshakeStatus () == HandshakeStatus .FINISHED )
0 commit comments