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