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