1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.http.ssl;
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.nio.NIOBuffer;
30 import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
31 import org.eclipse.jetty.io.nio.SelectorManager;
32 import org.eclipse.jetty.util.log.Log;
33 import org.eclipse.jetty.util.log.Logger;
34
35
36
37
38
39
40
41
42
43
44
45 public class SslSelectChannelEndPoint extends SelectChannelEndPoint
46 {
47 static Logger __log = Log.getLogger("org.eclipse.jetty.http.ssl");
48
49 private static final ByteBuffer[] __NO_BUFFERS={};
50
51 private final Buffers _buffers;
52
53 private final SSLEngine _engine;
54 private final SSLSession _session;
55 private final ByteBuffer _inBuffer;
56 private final NIOBuffer _inNIOBuffer;
57 private final ByteBuffer _outBuffer;
58 private final NIOBuffer _outNIOBuffer;
59
60 private final NIOBuffer[] _reuseBuffer=new NIOBuffer[2];
61 private final ByteBuffer[] _gather=new ByteBuffer[2];
62
63 private boolean _closing=false;
64 private SSLEngineResult _result;
65 private String _last;
66
67 private final boolean _debug = __log.isDebugEnabled();
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 public SslSelectChannelEndPoint(Buffers buffers,SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, SSLEngine engine)
86 throws IOException
87 {
88 super(channel,selectSet,key);
89 _buffers=buffers;
90
91
92 _engine=engine;
93 _session=engine.getSession();
94
95
96 _outNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize());
97 _outBuffer=_outNIOBuffer.getByteBuffer();
98 _inNIOBuffer=(NIOBuffer)_buffers.getBuffer(_session.getPacketBufferSize());
99 _inBuffer=_inNIOBuffer.getByteBuffer();
100
101
102 if (_debug) __log.debug(_session+" channel="+channel);
103 }
104
105
106 public void dump()
107 {
108 Log.info(""+_result);
109
110
111 }
112
113
114
115
116
117 protected void idleExpired()
118 {
119 try
120 {
121 getSelectManager().dispatch(new Runnable()
122 {
123 public void run()
124 {
125 doIdleExpired();
126 }
127 });
128 }
129 catch(Exception e)
130 {
131 Log.ignore(e);
132 }
133 }
134
135
136 protected void doIdleExpired()
137 {
138
139 super.idleExpired();
140 }
141
142
143 public void close() throws IOException
144 {
145
146
147
148 _closing=true;
149 try
150 {
151 int tries=0;
152
153 while (_outNIOBuffer.length()>0)
154 {
155
156 if (tries++>100)
157 throw new IllegalStateException();
158 flush();
159 Thread.sleep(100);
160 }
161
162 _engine.closeOutbound();
163
164 loop: while (isOpen() && !(_engine.isInboundDone() && _engine.isOutboundDone()))
165 {
166
167 if (tries++>100)
168 throw new IllegalStateException();
169
170 if (_outNIOBuffer.length()>0)
171 {
172 flush();
173 Thread.sleep(100);
174 }
175
176 if (_debug) __log.debug(_session+" closing "+_engine.getHandshakeStatus());
177 switch(_engine.getHandshakeStatus())
178 {
179 case FINISHED:
180 case NOT_HANDSHAKING:
181 break loop;
182
183 case NEED_UNWRAP:
184 Buffer buffer =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
185 try
186 {
187 ByteBuffer bbuffer = ((NIOBuffer)buffer).getByteBuffer();
188 if (!unwrap(bbuffer) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
189 {
190
191 break loop;
192 }
193 }
194 catch(SSLException e)
195 {
196 Log.ignore(e);
197 }
198 finally
199 {
200 _buffers.returnBuffer(buffer);
201 }
202 break;
203
204 case NEED_TASK:
205 {
206 Runnable task;
207 while ((task=_engine.getDelegatedTask())!=null)
208 {
209 task.run();
210 }
211 break;
212 }
213
214 case NEED_WRAP:
215 {
216 if (_outNIOBuffer.length()>0)
217 flush();
218
219 try
220 {
221 _outNIOBuffer.compact();
222 int put=_outNIOBuffer.putIndex();
223 _outBuffer.position(put);
224 _result=null;
225 _last="close wrap";
226 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
227 if (_debug) __log.debug(_session+" close wrap "+_result);
228 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
229 }
230 finally
231 {
232 _outBuffer.position(0);
233 }
234
235 flush();
236
237 break;
238 }
239 }
240 }
241 }
242 catch(IOException e)
243 {
244 Log.ignore(e);
245 }
246 catch (InterruptedException e)
247 {
248 Log.ignore(e);
249 }
250 finally
251 {
252 super.close();
253
254 if (_inNIOBuffer!=null)
255 _buffers.returnBuffer(_inNIOBuffer);
256 if (_outNIOBuffer!=null)
257 _buffers.returnBuffer(_outNIOBuffer);
258 if (_reuseBuffer[0]!=null)
259 _buffers.returnBuffer(_reuseBuffer[0]);
260 if (_reuseBuffer[1]!=null)
261 _buffers.returnBuffer(_reuseBuffer[1]);
262 }
263 }
264
265
266
267
268 public int fill(Buffer buffer) throws IOException
269 {
270 final ByteBuffer bbuf=extractInputBuffer(buffer);
271 int size=buffer.length();
272 HandshakeStatus initialStatus = _engine.getHandshakeStatus();
273
274 synchronized (bbuf)
275 {
276 try
277 {
278 unwrap(bbuf);
279
280 int tries=0, wraps=0;
281 loop: while (true)
282 {
283
284 if (tries++>100)
285 throw new IllegalStateException();
286
287
288
289 if (_outNIOBuffer.length()>0)
290 flush();
291
292
293 switch(_engine.getHandshakeStatus())
294 {
295 case FINISHED:
296 case NOT_HANDSHAKING:
297 if (_closing)
298 return -1;
299 break loop;
300
301 case NEED_UNWRAP:
302 if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
303 {
304
305 break loop;
306 }
307 break;
308
309 case NEED_TASK:
310 {
311 Runnable task;
312 while ((task=_engine.getDelegatedTask())!=null)
313 {
314
315 task.run();
316 }
317 if(initialStatus==HandshakeStatus.NOT_HANDSHAKING &&
318 HandshakeStatus.NEED_UNWRAP==_engine.getHandshakeStatus() && wraps==0)
319 {
320
321
322
323
324 if (_debug) __log.warn(_session+" JETTY-567");
325 return -1;
326 }
327 break;
328 }
329
330 case NEED_WRAP:
331 {
332 wraps++;
333 synchronized(_outBuffer)
334 {
335 try
336 {
337 _outNIOBuffer.compact();
338 int put=_outNIOBuffer.putIndex();
339 _outBuffer.position();
340 _result=null;
341 _last="fill wrap";
342 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
343 if (_debug) __log.debug(_session+" fill wrap "+_result);
344 switch(_result.getStatus())
345 {
346 case BUFFER_OVERFLOW:
347 case BUFFER_UNDERFLOW:
348 Log.warn("wrap {}",_result);
349 case CLOSED:
350 _closing=true;
351 }
352
353
354 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
355 }
356 finally
357 {
358 _outBuffer.position(0);
359 }
360 }
361
362 flush();
363
364 break;
365 }
366 }
367 }
368 }
369 catch(SSLException e)
370 {
371 Log.warn(e.toString());
372 Log.debug(e);
373 throw e;
374 }
375 finally
376 {
377 buffer.setPutIndex(bbuf.position());
378 bbuf.position(0);
379 }
380 }
381 return buffer.length()-size;
382
383 }
384
385
386 public int flush(Buffer buffer) throws IOException
387 {
388 return flush(buffer,null,null);
389 }
390
391
392
393
394
395 public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
396 {
397 int consumed=0;
398 int available=header.length();
399 if (buffer!=null)
400 available+=buffer.length();
401
402 int tries=0;
403 loop: while (true)
404 {
405
406 if (tries++>100)
407 throw new IllegalStateException();
408
409
410
411 if (_outNIOBuffer.length()>0)
412 flush();
413
414
415
416 switch(_engine.getHandshakeStatus())
417 {
418 case FINISHED:
419 case NOT_HANDSHAKING:
420
421 if (_closing || available==0)
422 {
423 if (consumed==0)
424 consumed= -1;
425 break loop;
426 }
427
428 int c=(header!=null && buffer!=null)?wrap(header,buffer):wrap(header);
429 if (c>0)
430 {
431 consumed+=c;
432 available-=c;
433 }
434 else if (c<0)
435 {
436 if (consumed==0)
437 consumed=-1;
438 break loop;
439 }
440
441 break;
442
443 case NEED_UNWRAP:
444 Buffer buf =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
445 try
446 {
447 ByteBuffer bbuf = ((NIOBuffer)buf).getByteBuffer();
448 if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
449 {
450
451 break loop;
452 }
453 }
454 finally
455 {
456 _buffers.returnBuffer(buf);
457 }
458
459 break;
460
461 case NEED_TASK:
462 {
463 Runnable task;
464 while ((task=_engine.getDelegatedTask())!=null)
465 {
466
467 task.run();
468 }
469 break;
470 }
471
472 case NEED_WRAP:
473 {
474 synchronized(_outBuffer)
475 {
476 try
477 {
478 _outNIOBuffer.compact();
479 int put=_outNIOBuffer.putIndex();
480 _outBuffer.position();
481 _result=null;
482 _last="flush wrap";
483 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
484 if (_debug) __log.debug(_session+" flush wrap "+_result);
485 switch(_result.getStatus())
486 {
487 case BUFFER_OVERFLOW:
488 case BUFFER_UNDERFLOW:
489 Log.warn("unwrap {}",_result);
490 case CLOSED:
491 _closing=true;
492 }
493
494 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
495 }
496 finally
497 {
498 _outBuffer.position(0);
499 }
500 }
501
502 flush();
503
504 break;
505 }
506 }
507 }
508
509 return consumed;
510 }
511
512
513 public void flush() throws IOException
514 {
515 while (_outNIOBuffer.length()>0)
516 {
517 int flushed=super.flush(_outNIOBuffer);
518 if (_debug) __log.debug(_session+" flushed "+flushed);
519
520
521 if (flushed==0)
522 {
523 Thread.yield();
524
525 flushed=super.flush(_outNIOBuffer);
526
527 }
528 }
529 }
530
531
532 private ByteBuffer extractInputBuffer(Buffer buffer)
533 {
534 assert buffer instanceof NIOBuffer;
535 NIOBuffer nbuf=(NIOBuffer)buffer;
536 ByteBuffer bbuf=nbuf.getByteBuffer();
537 bbuf.position(buffer.putIndex());
538 return bbuf;
539 }
540
541
542
543
544
545 private boolean unwrap(ByteBuffer buffer) throws IOException
546 {
547 if (_inNIOBuffer.hasContent())
548 _inNIOBuffer.compact();
549 else
550 _inNIOBuffer.clear();
551
552 int total_filled=0;
553 while (_inNIOBuffer.space()>0 && super.isOpen())
554 {
555 try
556 {
557 int filled=super.fill(_inNIOBuffer);
558 if (_debug) __log.debug(_session+" unwrap filled "+filled);
559
560 if (filled<=0)
561 break;
562 total_filled+=filled;
563 }
564 catch(IOException e)
565 {
566 if (_inNIOBuffer.length()==0)
567 throw e;
568 break;
569 }
570 }
571
572
573
574 if (_inNIOBuffer.length()==0)
575 {
576 if(!isOpen())
577 throw new org.eclipse.jetty.io.EofException();
578 return false;
579 }
580
581 try
582 {
583 _inBuffer.position(_inNIOBuffer.getIndex());
584 _inBuffer.limit(_inNIOBuffer.putIndex());
585 _result=null;
586 _last="unwrap";
587 _result=_engine.unwrap(_inBuffer,buffer);
588 if (_debug) __log.debug(_session+" unwrap unwrap "+_result);
589
590 _inNIOBuffer.skip(_result.bytesConsumed());
591 }
592 finally
593 {
594 _inBuffer.position(0);
595 _inBuffer.limit(_inBuffer.capacity());
596 }
597
598 switch(_result.getStatus())
599 {
600 case BUFFER_OVERFLOW:
601 case BUFFER_UNDERFLOW:
602 if (Log.isDebugEnabled()) Log.debug("unwrap {}",_result);
603 return (total_filled > 0);
604
605 case CLOSED:
606 _closing=true;
607 case OK:
608 return total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
609 default:
610 Log.warn("unwrap "+_result);
611 throw new IOException(_result.toString());
612 }
613 }
614
615
616
617 private ByteBuffer extractOutputBuffer(Buffer buffer,int n)
618 {
619 if (buffer.buffer() instanceof NIOBuffer)
620 {
621 NIOBuffer nBuf=(NIOBuffer)buffer.buffer();
622 return nBuf.getByteBuffer();
623 }
624 else
625 {
626 if (_reuseBuffer[n]==null)
627 _reuseBuffer[n] = (NIOBuffer)_buffers.getBuffer(_session.getApplicationBufferSize());
628 NIOBuffer buf = _reuseBuffer[n];
629 buf.clear();
630 buf.put(buffer);
631 return buf.getByteBuffer();
632 }
633 }
634
635
636 private int wrap(final Buffer header, final Buffer buffer) throws IOException
637 {
638 _gather[0]=extractOutputBuffer(header,0);
639 synchronized(_gather[0])
640 {
641 _gather[0].position(header.getIndex());
642 _gather[0].limit(header.putIndex());
643
644 _gather[1]=extractOutputBuffer(buffer,1);
645
646 synchronized(_gather[1])
647 {
648 _gather[1].position(buffer.getIndex());
649 _gather[1].limit(buffer.putIndex());
650
651 synchronized(_outBuffer)
652 {
653 int consumed=0;
654 try
655 {
656 _outNIOBuffer.clear();
657 _outBuffer.position(0);
658 _outBuffer.limit(_outBuffer.capacity());
659
660 _result=null;
661 _last="wrap wrap";
662 _result=_engine.wrap(_gather,_outBuffer);
663 if (_debug) __log.debug(_session+" wrap wrap "+_result);
664
665 _outNIOBuffer.setGetIndex(0);
666 _outNIOBuffer.setPutIndex(_result.bytesProduced());
667 consumed=_result.bytesConsumed();
668 }
669 finally
670 {
671 _outBuffer.position(0);
672
673 if (consumed>0)
674 {
675 int len=consumed<header.length()?consumed:header.length();
676 header.skip(len);
677 consumed-=len;
678 _gather[0].position(0);
679 _gather[0].limit(_gather[0].capacity());
680 }
681 if (consumed>0)
682 {
683 int len=consumed<buffer.length()?consumed:buffer.length();
684 buffer.skip(len);
685 consumed-=len;
686 _gather[1].position(0);
687 _gather[1].limit(_gather[1].capacity());
688 }
689 assert consumed==0;
690 }
691 }
692 }
693 }
694
695
696 switch(_result.getStatus())
697 {
698 case BUFFER_OVERFLOW:
699 case BUFFER_UNDERFLOW:
700 Log.warn("unwrap {}",_result);
701
702 case OK:
703 return _result.bytesConsumed();
704 case CLOSED:
705 _closing=true;
706 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
707
708 default:
709 Log.warn("wrap "+_result);
710 throw new IOException(_result.toString());
711 }
712 }
713
714
715 private int wrap(final Buffer header) throws IOException
716 {
717 _gather[0]=extractOutputBuffer(header,0);
718 synchronized(_gather[0])
719 {
720 _gather[0].position(header.getIndex());
721 _gather[0].limit(header.putIndex());
722
723 int consumed=0;
724 synchronized(_outBuffer)
725 {
726 try
727 {
728 _outNIOBuffer.clear();
729 _outBuffer.position(0);
730 _outBuffer.limit(_outBuffer.capacity());
731 _result=null;
732 _last="wrap wrap";
733 _result=_engine.wrap(_gather[0],_outBuffer);
734 if (_debug) __log.debug(_session+" wrap wrap "+_result);
735
736 _outNIOBuffer.setGetIndex(0);
737 _outNIOBuffer.setPutIndex(_result.bytesProduced());
738 consumed=_result.bytesConsumed();
739 }
740 finally
741 {
742 _outBuffer.position(0);
743
744 if (consumed>0)
745 {
746 int len=consumed<header.length()?consumed:header.length();
747 header.skip(len);
748 consumed-=len;
749 _gather[0].position(0);
750 _gather[0].limit(_gather[0].capacity());
751 }
752 assert consumed==0;
753 }
754 }
755 }
756 switch(_result.getStatus())
757 {
758 case BUFFER_OVERFLOW:
759 case BUFFER_UNDERFLOW:
760 Log.warn("unwrap {}",_result);
761
762 case OK:
763 return _result.bytesConsumed();
764 case CLOSED:
765 _closing=true;
766 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
767
768 default:
769 Log.warn("wrap "+_result);
770 throw new IOException(_result.toString());
771 }
772 }
773
774
775 public boolean isBufferingInput()
776 {
777 return _inNIOBuffer.hasContent();
778 }
779
780
781 public boolean isBufferingOutput()
782 {
783 return _outNIOBuffer.hasContent();
784 }
785
786
787 public boolean isBufferred()
788 {
789 return true;
790 }
791
792
793 public SSLEngine getSSLEngine()
794 {
795 return _engine;
796 }
797
798
799 public String toString()
800 {
801 return super.toString()+","+_engine.getHandshakeStatus()+", in/out="+_inNIOBuffer.length()+"/"+_outNIOBuffer.length()+" last "+_last+" "+_result;
802 }
803 }