View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.io.nio;
15  
16  import java.io.IOException;
17  import java.nio.ByteBuffer;
18  import java.nio.channels.SelectionKey;
19  import java.nio.channels.SocketChannel;
20  
21  import javax.net.ssl.SSLEngine;
22  import javax.net.ssl.SSLEngineResult;
23  import javax.net.ssl.SSLException;
24  import javax.net.ssl.SSLSession;
25  import javax.net.ssl.SSLEngineResult.HandshakeStatus;
26  
27  import org.eclipse.jetty.io.Buffer;
28  import org.eclipse.jetty.io.Buffers;
29  import org.eclipse.jetty.io.EofException;
30  import org.eclipse.jetty.util.log.Log;
31  import org.eclipse.jetty.util.log.Logger;
32  
33  /* ------------------------------------------------------------ */
34  /**
35   * SslSelectChannelEndPoint
36   * <p>
37   * A SelectChannelEndPoint that uses an {@link SSLEngine} to handle an
38   * SSL connection.
39   * <p>
40   * There is a named logger "org.eclipse.jetty.http.ssl"
41   * </p>
42   */
43  public class SslSelectChannelEndPoint extends SelectChannelEndPoint
44  {
45      static Logger __log = Log.getLogger("org.eclipse.jetty.http.ssl");
46      
47      private static final ByteBuffer[] __NO_BUFFERS={};
48  
49      private final Buffers _buffers;
50      
51      private final SSLEngine _engine;
52      private final SSLSession _session;
53      private volatile NIOBuffer _inNIOBuffer;
54      private volatile NIOBuffer _outNIOBuffer;
55  
56      private final ByteBuffer[] _gather=new ByteBuffer[2];
57  
58      private boolean _closing=false;
59      private SSLEngineResult _result;
60  
61      private boolean _handshook=false;
62      private boolean _allowRenegotiate=false;
63  
64      private final boolean _debug = __log.isDebugEnabled(); // snapshot debug status for optimizer 
65  
66      /* ------------------------------------------------------------ */
67      public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine)
68              throws IOException
69      {
70          super(channel,selectSet,key);
71          _buffers=buffers;
72          
73          // ssl
74          _engine=engine;
75          _session=engine.getSession();
76  
77          if (_debug) __log.debug(_session+" channel="+channel);
78      }
79      
80      /* ------------------------------------------------------------ */
81      private void needOutBuffer()
82      {
83          if (_outNIOBuffer==null)
84          {
85              _outNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize());
86          }
87      }
88      
89      /* ------------------------------------------------------------ */
90      private void needInBuffer()
91      {
92          if (_inNIOBuffer==null)
93          {
94              _inNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize());
95          }
96      }
97      
98      /* ------------------------------------------------------------ */
99      private void freeOutBuffer()
100     {
101         if (_outNIOBuffer.length()==0)
102         {
103             _buffers.returnBuffer(_outNIOBuffer);
104             _outNIOBuffer=null;
105         }
106     }
107     
108     /* ------------------------------------------------------------ */
109     private void freeInBuffer()
110     {
111         if (_inNIOBuffer.length()==0)
112         {
113             _buffers.returnBuffer(_inNIOBuffer);
114             _inNIOBuffer=null;
115         }
116     }
117 
118     /* ------------------------------------------------------------ */
119     /**
120      * @return True if SSL re-negotiation is allowed (default false)
121      */
122     public boolean isAllowRenegotiate()
123     {
124         return _allowRenegotiate;
125     }
126 
127     /* ------------------------------------------------------------ */
128     /**
129      * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
130      * a vulnerability in SSL/TLS with re-negotiation.  If your JVM
131      * does not have CVE-2009-3555 fixed, then re-negotiation should 
132      * not be allowed.
133      * @param allowRenegotiate true if re-negotiation is allowed (default false)
134      */
135     public void setAllowRenegotiate(boolean allowRenegotiate)
136     {
137         _allowRenegotiate = allowRenegotiate;
138     }
139 
140     /* ------------------------------------------------------------ */
141     // TODO get rid of these dumps
142     public void dump()
143     {
144         Log.info(""+_result);
145     }
146 
147     /* ------------------------------------------------------------ */
148     @Override
149     public void close() throws IOException
150     {
151         // TODO - this really should not be done in a loop here - but with async callbacks.
152 
153         _closing=true;
154         long end=System.currentTimeMillis()+((SocketChannel)_channel).socket().getSoTimeout();
155         try
156         {   
157             if (isBufferingOutput())
158             {
159                 flush();
160                 while (isOpen() && isBufferingOutput() && System.currentTimeMillis()<end)
161                 {
162                     Thread.sleep(100); // TODO non blocking
163                     flush();
164                 }
165             }
166 
167             _engine.closeOutbound();
168 
169             loop: while (isOpen() && !(_engine.isInboundDone() && _engine.isOutboundDone()) && System.currentTimeMillis()<end)
170             {   
171                 if (isBufferingOutput())
172                 {
173                     flush();
174                     while (isOpen() && isBufferingOutput() && System.currentTimeMillis()<end)
175                     {
176                         Thread.sleep(100); // TODO non blocking
177                         flush();
178                     }
179                 }
180 
181                 if (_debug) __log.debug(_session+" closing "+_engine.getHandshakeStatus());
182                 switch(_engine.getHandshakeStatus())
183                 {
184                     case FINISHED:
185                     case NOT_HANDSHAKING:
186                         _handshook=true;
187                         break loop;
188                         
189                     case NEED_UNWRAP:
190                         Buffer buffer =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
191                         try
192                         {
193                             ByteBuffer bbuffer = ((NIOBuffer)buffer).getByteBuffer();
194                             if (!unwrap(bbuffer) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
195                             {
196                                 break loop;
197                             }
198                         }
199                         catch(SSLException e)
200                         {
201                             Log.ignore(e);
202                         }
203                         finally
204                         {
205                             _buffers.returnBuffer(buffer);
206                         }
207                         break;
208                         
209                     case NEED_TASK:
210                     {
211                         Runnable task;
212                         while ((task=_engine.getDelegatedTask())!=null)
213                         {
214                             task.run();
215                         }
216                         break;
217                     }
218                         
219                     case NEED_WRAP:
220                     {
221                         needOutBuffer();
222                         ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
223                         try
224                         {   
225                             _outNIOBuffer.compact();
226                             int put=_outNIOBuffer.putIndex();
227                             out_buffer.position(put);
228                             _result=null;
229                             _result=_engine.wrap(__NO_BUFFERS,out_buffer);
230                             if (_debug) __log.debug(_session+" close wrap "+_result);
231                             _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
232                         }
233                         finally
234                         {
235                             out_buffer.position(0);
236                             freeOutBuffer();
237                         }
238                         
239                         break;
240                     }
241                 }
242             }
243         }
244         catch(IOException e)
245         {
246             Log.ignore(e);
247         }
248         catch (InterruptedException e)
249         {
250             Log.ignore(e);
251         }
252         finally
253         {
254             super.close();
255             if (_inNIOBuffer!=null)
256                 _buffers.returnBuffer(_inNIOBuffer);
257             if (_outNIOBuffer!=null)
258                 _buffers.returnBuffer(_outNIOBuffer);
259         }   
260     }
261 
262 
263     
264     /* ------------------------------------------------------------ */
265     /** Fill the buffer with unencrypted bytes.
266      * Called by a {@link org.eclipse.jetty.http.Parser} instance when more data is 
267      * needed to continue parsing a request or a response.
268      */
269     @Override
270     public int fill(Buffer buffer) throws IOException
271     {
272         // This end point only works on NIO buffer type (director
273         // or indirect), so extract the NIO buffer that is wrapped
274         // by the passed jetty Buffer.
275         final ByteBuffer bbuf=extractInputBuffer(buffer);
276         
277         // remember the original size of the unencrypted buffer
278         int size=buffer.length();
279         
280         HandshakeStatus initialStatus = _engine.getHandshakeStatus();
281         //noinspection SynchronizationOnLocalVariableOrMethodParameter
282         synchronized (bbuf)
283         {
284             try
285             {
286                 // Call the SSLEngine unwrap method to process data in
287                 // the inBuffer.  If there is no data in the inBuffer, then
288                 // super.fill is called to read encrypted bytes.
289                 unwrap(bbuf);
290 
291                 // Loop through the SSL engine state machine
292                 
293                 int wraps=0;
294                 loop: while (true)
295                 {
296                     // If we have encrypted data in output buffer
297                     if (isBufferingOutput())
298                     {
299                         // we must flush it, as the other end might be
300                         // waiting for that outgoing data before sending 
301                         // more incoming data
302                         flush();
303                         
304                         // If we were unable to flush all the data, then 
305                         // we should break the loop and wait for the call
306                         // back to handle when the SelectSet detects that
307                         // the channel is writable again.
308                         if (isBufferingOutput())
309                             break loop;
310                     }
311 
312                     // handle the current hand share status
313                     switch(_engine.getHandshakeStatus())
314                     {
315                         case FINISHED:
316                         case NOT_HANDSHAKING:
317                             // If we are closing, then unwrap must have CLOSED result,
318                             // so return -1 to signal upwards
319                             if (_closing)
320                                 return -1;
321                             
322                             // otherwise we break loop with the data we have unwrapped.
323                             break loop;
324 
325                         case NEED_UNWRAP:
326                             checkRenegotiate();
327                             // Need more data to be unwrapped so try another call to unwrap
328                             if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
329                             {
330                                 // If the unwrap call did not make any progress and we are still in
331                                 // NEED_UNWRAP, then we should break the loop and wait for more data to
332                                 // arrive.
333                                 break loop;
334                             }
335                             // progress was made so continue the loop.
336                             break;
337 
338                         case NEED_TASK:
339                         {
340                             // A task needs to be run, so run it!
341                             
342                             Runnable task;
343                             while ((task=_engine.getDelegatedTask())!=null)
344                             {
345                                 task.run();
346                             }
347                             
348                             // Detect SUN JVM Bug!!!
349                             if(initialStatus==HandshakeStatus.NOT_HANDSHAKING && 
350                                _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP && wraps==0)
351                             {
352                                 // This should be NEED_WRAP
353                                 // The fix simply detects the signature of the bug and then close the connection (fail-fast) so that ff3 will delegate to using SSL instead of TLS.
354                                 // This is a jvm bug on java1.6 where the SSLEngine expects more data from the initial handshake when the client(ff3-tls) already had given it.
355                                 // See http://jira.codehaus.org/browse/JETTY-567 for more details
356                                 if (_debug) __log.warn(_session+" JETTY-567");
357                                 return -1;
358                             }
359                             break;
360                         }
361 
362                         case NEED_WRAP:
363                         {
364                             checkRenegotiate();
365                             // The SSL needs to send some handshake data to the other side,
366                             // so let fill become a flush for a little bit.
367                             wraps++;
368                             needOutBuffer();
369                             ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
370                             synchronized(out_buffer)
371                             {
372                                 try
373                                 {
374                                     // call wrap with empty application buffers, so it can
375                                     // generate required handshake messages into _outNIOBuffer
376                                     _outNIOBuffer.compact();
377                                     int put=_outNIOBuffer.putIndex();
378                                     out_buffer.position();
379                                     _result=null;
380                                     _result=_engine.wrap(__NO_BUFFERS,out_buffer);
381                                     if (_debug) __log.debug(_session+" fill wrap "+_result);
382                                     switch(_result.getStatus())
383                                     {
384                                         case BUFFER_OVERFLOW:
385                                         case BUFFER_UNDERFLOW:
386                                             Log.warn("wrap {}",_result);
387                                         case CLOSED:
388                                             _closing=true;
389                                     }
390                                     
391                                     _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
392                                 }
393                                 finally
394                                 {
395                                     out_buffer.position(0);
396                                 }
397                             }
398 
399                             // flush the encrypted outNIOBuffer
400                             flush();
401                             freeOutBuffer();
402                             
403                             break;
404                         }
405                     }
406                 }
407             }
408             catch(SSLException e)
409             {
410                 Log.warn(e.toString());
411                 Log.debug(e);
412                 throw e;
413             }
414             finally
415             {
416                 // reset the Buffers
417                 buffer.setPutIndex(bbuf.position());
418                 bbuf.position(0);
419             }
420 
421             // return the number of unencrypted bytes filled.
422             int filled=buffer.length()-size; 
423             if (filled>0)
424                 _handshook=true;
425             return filled; 
426         }
427     }
428 
429     /* ------------------------------------------------------------ */
430     @Override
431     public int flush(Buffer buffer) throws IOException
432     {
433         return flush(buffer,null,null);
434     }
435 
436 
437     /* ------------------------------------------------------------ */
438     /*     
439      */
440     @Override
441     public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
442     {   
443         int consumed=0;
444         int available=header.length();
445         if (buffer!=null)
446             available+=buffer.length();
447         
448         needOutBuffer();
449         ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
450         loop: while (true)
451         {   
452             if (_outNIOBuffer.length()>0)
453             {
454                 flush();
455                 if (isBufferingOutput())
456                     break loop;
457             }
458 
459             switch(_engine.getHandshakeStatus())
460             {
461                 case FINISHED:
462                 case NOT_HANDSHAKING:
463                     if (_closing || available==0)
464                     {
465                         if (consumed==0)
466                             consumed= -1;
467                         break loop;
468                     }
469                         
470                     int c;
471                     if (header!=null && header.length()>0)
472                     {
473                         if (buffer!=null && buffer.length()>0)
474                             c=wrap(header,buffer);
475                         else
476                             c=wrap(header);
477                     }
478                     else 
479                         c=wrap(buffer);
480                     
481                     
482                     if (c>0)
483                     {
484                         _handshook=true;
485                         consumed+=c;
486                         available-=c;
487                     }
488                     else if (c<0)
489                     {
490                         if (consumed==0)
491                             consumed=-1;
492                         break loop;
493                     }
494                     
495                     break;
496 
497                 case NEED_UNWRAP:
498                     checkRenegotiate();
499                     Buffer buf =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
500                     try
501                     {
502                         ByteBuffer bbuf = ((NIOBuffer)buf).getByteBuffer();
503                         if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
504                         {
505                             break loop;
506                         }
507                     }
508                     finally
509                     {
510                         _buffers.returnBuffer(buf);
511                     }
512                     
513                     break;
514 
515                 case NEED_TASK:
516                 {
517                     Runnable task;
518                     while ((task=_engine.getDelegatedTask())!=null)
519                     {
520                         task.run();
521                     }
522                     break;
523                 }
524 
525                 case NEED_WRAP:
526                 {
527                     checkRenegotiate();
528                     synchronized(out_buffer)
529                     {
530                         try
531                         {
532                             _outNIOBuffer.compact();
533                             int put=_outNIOBuffer.putIndex();
534                             out_buffer.position();
535                             _result=null;
536                             _result=_engine.wrap(__NO_BUFFERS,out_buffer);
537                             if (_debug) __log.debug(_session+" flush wrap "+_result);
538                             switch(_result.getStatus())
539                             {
540                                 case BUFFER_OVERFLOW:
541                                 case BUFFER_UNDERFLOW:
542                                     Log.warn("unwrap {}",_result);
543                                 case CLOSED:
544                                     _closing=true;
545                             }
546                             _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
547                         }
548                         finally
549                         {
550                             out_buffer.position(0);
551                         }
552                     }
553 
554                     flush();
555                     if (isBufferingOutput())
556                         break loop;
557 
558                     break;
559                 }
560             }
561         }
562 
563         freeOutBuffer();
564         return consumed;
565     }
566     
567     /* ------------------------------------------------------------ */
568     @Override
569     public void flush() throws IOException
570     {
571         if (_outNIOBuffer==null)
572             return;
573         
574         int len=_outNIOBuffer.length();
575         if (isBufferingOutput())
576         {
577             int flushed=super.flush(_outNIOBuffer);
578             if (_debug) __log.debug(_session+" Flushed "+flushed+"/"+len);
579             if (isBufferingOutput())
580             {
581                 // Try again after yield.... cheaper than a reschedule.
582                 Thread.yield();
583                 flushed=super.flush(_outNIOBuffer);
584                 if (_debug) __log.debug(_session+" flushed "+flushed+"/"+len);
585             }
586         }
587     }
588     
589     /* ------------------------------------------------------------ */
590     private void checkRenegotiate() throws IOException
591     {
592         if (_handshook && !_allowRenegotiate && _channel!=null && _channel.isOpen())
593         {
594             Log.warn("SSL renegotiate denied: "+_channel);
595             close();
596         }   
597     }
598 
599     /* ------------------------------------------------------------ */
600     private ByteBuffer extractInputBuffer(Buffer buffer)
601     {
602         assert buffer instanceof NIOBuffer;
603         NIOBuffer nbuf=(NIOBuffer)buffer;
604         ByteBuffer bbuf=nbuf.getByteBuffer();
605         bbuf.position(buffer.putIndex());
606         return bbuf;
607     }
608 
609     /* ------------------------------------------------------------ */
610     /**
611      * @return true if progress is made
612      */
613     private boolean unwrap(ByteBuffer buffer) throws IOException
614     {
615         needInBuffer();
616         ByteBuffer in_buffer=_inNIOBuffer.getByteBuffer();
617         
618         if (_inNIOBuffer.hasContent())
619             _inNIOBuffer.compact();
620         else 
621             _inNIOBuffer.clear();
622 
623         int total_filled=0;
624         
625         // loop filling as much encrypted data as we can into the buffer
626         while (_inNIOBuffer.space()>0 && super.isOpen())
627         {
628             try
629             {
630                 int filled=super.fill(_inNIOBuffer);
631                 if (_debug) __log.debug(_session+" unwrap filled "+filled);
632                 // break the loop if no progress is made (we have read everything
633                 // there is to read).
634                 if (filled<=0)
635                     break;
636                 total_filled+=filled;
637             }
638             catch(IOException e)
639             {
640                 if (_inNIOBuffer.length()==0)
641                 {
642                     if (_outNIOBuffer!=null)
643                     {
644                         _outNIOBuffer.clear();
645                         freeOutBuffer();
646                     }
647                     throw e;
648                 }
649                 break;
650             }
651         }
652         
653         // If we have no progress and no data
654         if (total_filled==0 && _inNIOBuffer.length()==0)
655         {
656             if(!isOpen())
657             {
658                 if (_outNIOBuffer!=null)
659                     _outNIOBuffer.clear();
660                 throw new EofException();
661             }
662             return false;
663         }
664 
665         // We have some in data, so try to unwrap it.
666         try
667         {
668             // inBuffer is the NIO buffer inside the _inNIOBuffer,
669             // so update its position and limit from the inNIOBuffer.
670             in_buffer.position(_inNIOBuffer.getIndex());
671             in_buffer.limit(_inNIOBuffer.putIndex());
672             _result=null;
673             
674             // Do the unwrap
675             _result=_engine.unwrap(in_buffer,buffer);
676             if (_debug) __log.debug(_session+" unwrap unwrap "+_result);
677             
678             // skip the bytes consumed
679             _inNIOBuffer.skip(_result.bytesConsumed());
680         }
681         finally
682         {
683             // reset the buffer so it can be managed by the _inNIOBuffer again.
684             in_buffer.position(0);
685             in_buffer.limit(in_buffer.capacity());
686             freeInBuffer();
687         }
688 
689         // handle the unwrap results
690         switch(_result.getStatus())
691         {
692             case BUFFER_OVERFLOW:
693                 throw new IllegalStateException(_result.toString()+" "+buffer.position()+" "+buffer.limit());
694                 
695             case BUFFER_UNDERFLOW:
696                 // Not enough data, 
697                 // If we are closed, we will never get more, so EOF
698                 // else return and we will be tried again
699                 // later when more data arriving causes another dispatch.
700                 if (Log.isDebugEnabled()) Log.debug("unwrap {}",_result);
701                 if(!isOpen())
702                 {
703                     _inNIOBuffer.clear();
704                     if (_outNIOBuffer!=null)
705                         _outNIOBuffer.clear();
706                     throw new EofException();
707                 }
708                 return (total_filled > 0);
709                 
710             case CLOSED:
711                 _closing=true;
712                 // return true is some bytes somewhere were moved about.
713                 return total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
714             case OK:
715                 // return true is some bytes somewhere were moved about.
716                 return total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
717             default:
718                 Log.warn("unwrap "+_result);
719                 throw new IOException(_result.toString());
720         }
721     }
722 
723     
724     /* ------------------------------------------------------------ */
725     private ByteBuffer extractOutputBuffer(Buffer buffer)
726     {
727         if (buffer.buffer() instanceof NIOBuffer)
728             return ((NIOBuffer)buffer.buffer()).getByteBuffer();
729         
730         return ByteBuffer.wrap(buffer.array());
731     }
732 
733     /* ------------------------------------------------------------ */
734     private int wrap(final Buffer header, final Buffer buffer) throws IOException
735     {
736         _gather[0]=extractOutputBuffer(header);
737         
738         synchronized(_gather[0])
739         {
740             _gather[0].position(header.getIndex());
741             _gather[0].limit(header.putIndex());
742 
743             _gather[1]=extractOutputBuffer(buffer);
744 
745             synchronized(_gather[1])
746             {
747                 _gather[1].position(buffer.getIndex());
748                 _gather[1].limit(buffer.putIndex());
749 
750                 needOutBuffer();
751                 ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
752                 synchronized(out_buffer)
753                 {
754                     int consumed=0;
755                     try
756                     {
757                         _outNIOBuffer.clear();
758                         out_buffer.position(0);
759                         out_buffer.limit(out_buffer.capacity());
760 
761                         _result=null;
762                         _result=_engine.wrap(_gather,out_buffer);
763                         if (_debug) __log.debug(_session+" wrap wrap "+_result);
764                         _outNIOBuffer.setGetIndex(0);
765                         _outNIOBuffer.setPutIndex(_result.bytesProduced());
766                         consumed=_result.bytesConsumed();
767                     }
768                     finally
769                     {
770                         out_buffer.position(0);
771 
772                         if (consumed>0)
773                         {
774                             int len=consumed<header.length()?consumed:header.length();
775                             header.skip(len);
776                             consumed-=len;
777                             _gather[0].position(0);
778                             _gather[0].limit(_gather[0].capacity());
779                         }
780                         if (consumed>0)
781                         {
782                             int len=consumed<buffer.length()?consumed:buffer.length();
783                             buffer.skip(len);
784                             consumed-=len;
785                             _gather[1].position(0);
786                             _gather[1].limit(_gather[1].capacity());
787                         }
788                         assert consumed==0;
789                         
790                         freeOutBuffer();
791                     }
792                 }
793             }
794         }
795         
796 
797         switch(_result.getStatus())
798         {
799             case BUFFER_OVERFLOW:
800             case BUFFER_UNDERFLOW:
801                 Log.warn("unwrap {}",_result);
802                 
803             case OK:
804                 return _result.bytesConsumed();
805             case CLOSED:
806                 _closing=true;
807                 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
808 
809             default:
810                 Log.warn("wrap "+_result);
811             throw new IOException(_result.toString());
812         }
813     }
814 
815     /* ------------------------------------------------------------ */
816     private int wrap(final Buffer buffer) throws IOException
817     {
818         _gather[0]=extractOutputBuffer(buffer);
819         synchronized(_gather[0])
820         {
821             ByteBuffer bb;
822             
823             _gather[0].position(buffer.getIndex());
824             _gather[0].limit(buffer.putIndex());
825 
826             int consumed=0;
827             needOutBuffer();
828             ByteBuffer out_buffer=_outNIOBuffer.getByteBuffer();
829             synchronized(out_buffer)
830             {
831                 try
832                 {
833                     _outNIOBuffer.clear();
834                     out_buffer.position(0);
835                     out_buffer.limit(out_buffer.capacity());
836                     _result=null;
837                     _result=_engine.wrap(_gather[0],out_buffer);
838                     if (_debug) __log.debug(_session+" wrap wrap "+_result);
839                     _outNIOBuffer.setGetIndex(0);
840                     _outNIOBuffer.setPutIndex(_result.bytesProduced());
841                     consumed=_result.bytesConsumed();
842                 }
843                 finally
844                 {
845                     out_buffer.position(0);
846 
847                     if (consumed>0)
848                     {
849                         int len=consumed<buffer.length()?consumed:buffer.length();
850                         buffer.skip(len);
851                         consumed-=len;
852                         _gather[0].position(0);
853                         _gather[0].limit(_gather[0].capacity());
854                     }
855                     assert consumed==0;
856                     
857                     freeOutBuffer();
858                 }
859             }
860         }
861         switch(_result.getStatus())
862         {
863             case BUFFER_OVERFLOW:
864             case BUFFER_UNDERFLOW:
865                 Log.warn("unwrap {}",_result);
866                 
867             case OK:
868                 return _result.bytesConsumed();
869             case CLOSED:
870                 _closing=true;
871                 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
872 
873             default:
874                 Log.warn("wrap "+_result);
875             throw new IOException(_result.toString());
876         }
877     }
878 
879     /* ------------------------------------------------------------ */
880     @Override
881     public boolean isBufferingInput()
882     {
883         final Buffer in = _inNIOBuffer;
884         return in==null?false:_inNIOBuffer.hasContent();
885     }
886 
887     /* ------------------------------------------------------------ */
888     @Override
889     public boolean isBufferingOutput()
890     {
891         final NIOBuffer b=_outNIOBuffer;
892         return b==null?false:b.hasContent();
893     }
894 
895     /* ------------------------------------------------------------ */
896     @Override
897     public boolean isBufferred()
898     {
899         return true;
900     }
901 
902     /* ------------------------------------------------------------ */
903     public SSLEngine getSSLEngine()
904     {
905         return _engine;
906     }
907     
908     /* ------------------------------------------------------------ */
909     @Override
910     public String toString()
911     {
912         final NIOBuffer i=_inNIOBuffer;
913         final NIOBuffer o=_outNIOBuffer;
914         return super.toString()+","+_engine.getHandshakeStatus()+", in/out="+
915         (i==null?0:_inNIOBuffer.length())+"/"+(o==null?0:o.length())+" "+_result;
916     }
917 }