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 }