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