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
34
35
36
37
38
39
40
41 public class SslSelectChannelEndPoint extends SelectChannelEndPoint
42 {
43 private static final ByteBuffer[] __NO_BUFFERS={};
44
45 private Buffers _buffers;
46
47 private SSLEngine _engine;
48 private ByteBuffer _inBuffer;
49 private NIOBuffer _inNIOBuffer;
50 private ByteBuffer _outBuffer;
51 private NIOBuffer _outNIOBuffer;
52
53 private NIOBuffer[] _reuseBuffer=new NIOBuffer[2];
54 private ByteBuffer[] _gather=new ByteBuffer[2];
55
56 private boolean _closing=false;
57 private SSLEngineResult _result;
58 private String _last;
59
60
61 protected SSLSession _session;
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 SSLException, 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 _selectSet.getManager().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 ByteBuffer bbuf=extractInputBuffer(buffer);
261 int size=buffer.length();
262 HandshakeStatus initialStatus = _engine.getHandshakeStatus();
263 synchronized (bbuf)
264 {
265 try
266 {
267 unwrap(bbuf);
268
269 int tries=0, wraps=0;
270 loop: while (true)
271 {
272
273 if (tries++>100)
274 throw new IllegalStateException();
275
276
277
278 if (_outNIOBuffer.length()>0)
279 flush();
280
281
282 switch(_engine.getHandshakeStatus())
283 {
284 case FINISHED:
285 case NOT_HANDSHAKING:
286 if (_closing)
287 return -1;
288 break loop;
289
290 case NEED_UNWRAP:
291 if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
292 {
293
294 break loop;
295 }
296 break;
297
298 case NEED_TASK:
299 {
300 Runnable task;
301 while ((task=_engine.getDelegatedTask())!=null)
302 {
303
304 task.run();
305 }
306 if(initialStatus==HandshakeStatus.NOT_HANDSHAKING &&
307 HandshakeStatus.NEED_UNWRAP==_engine.getHandshakeStatus() && wraps==0)
308 {
309
310
311
312
313 return -1;
314 }
315 break;
316 }
317
318 case NEED_WRAP:
319 {
320 wraps++;
321 synchronized(_outBuffer)
322 {
323 try
324 {
325 _outNIOBuffer.compact();
326 int put=_outNIOBuffer.putIndex();
327 _outBuffer.position();
328 _result=null;
329 _last="fill wrap";
330 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
331 switch(_result.getStatus())
332 {
333 case BUFFER_OVERFLOW:
334 case BUFFER_UNDERFLOW:
335 Log.warn("wrap {}",_result);
336 case CLOSED:
337 _closing=true;
338 }
339
340
341 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
342 }
343 finally
344 {
345 _outBuffer.position(0);
346 }
347 }
348
349 flush();
350
351 break;
352 }
353 }
354 }
355 }
356 catch(SSLException e)
357 {
358 Log.warn(e.toString());
359 Log.debug(e);
360 throw e;
361 }
362 finally
363 {
364 buffer.setPutIndex(bbuf.position());
365 bbuf.position(0);
366 }
367 }
368 return buffer.length()-size;
369
370 }
371
372
373 public int flush(Buffer buffer) throws IOException
374 {
375 return flush(buffer,null,null);
376 }
377
378
379
380
381
382 public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
383 {
384 int consumed=0;
385 int available=header.length();
386 if (buffer!=null)
387 available+=buffer.length();
388
389 int tries=0;
390 loop: while (true)
391 {
392
393 if (tries++>100)
394 throw new IllegalStateException();
395
396
397
398 if (_outNIOBuffer.length()>0)
399 flush();
400
401
402
403 switch(_engine.getHandshakeStatus())
404 {
405 case FINISHED:
406 case NOT_HANDSHAKING:
407
408 if (_closing || available==0)
409 {
410 if (consumed==0)
411 consumed= -1;
412 break loop;
413 }
414
415 int c=(header!=null && buffer!=null)?wrap(header,buffer):wrap(header);
416 if (c>0)
417 {
418 consumed+=c;
419 available-=c;
420 }
421 else if (c<0)
422 {
423 if (consumed==0)
424 consumed=-1;
425 break loop;
426 }
427
428 break;
429
430 case NEED_UNWRAP:
431 Buffer buf =_buffers.getBuffer(_engine.getSession().getApplicationBufferSize());
432 try
433 {
434 ByteBuffer bbuf = ((NIOBuffer)buf).getByteBuffer();
435 if (!unwrap(bbuf) && _engine.getHandshakeStatus()==HandshakeStatus.NEED_UNWRAP)
436 {
437
438 break loop;
439 }
440 }
441 finally
442 {
443 _buffers.returnBuffer(buf);
444 }
445
446 break;
447
448 case NEED_TASK:
449 {
450 Runnable task;
451 while ((task=_engine.getDelegatedTask())!=null)
452 {
453
454 task.run();
455 }
456 break;
457 }
458
459 case NEED_WRAP:
460 {
461 synchronized(_outBuffer)
462 {
463 try
464 {
465 _outNIOBuffer.compact();
466 int put=_outNIOBuffer.putIndex();
467 _outBuffer.position();
468 _result=null;
469 _last="flush wrap";
470 _result=_engine.wrap(__NO_BUFFERS,_outBuffer);
471 switch(_result.getStatus())
472 {
473 case BUFFER_OVERFLOW:
474 case BUFFER_UNDERFLOW:
475 Log.warn("unwrap {}",_result);
476 case CLOSED:
477 _closing=true;
478 }
479
480 _outNIOBuffer.setPutIndex(put+_result.bytesProduced());
481 }
482 finally
483 {
484 _outBuffer.position(0);
485 }
486 }
487
488 flush();
489
490 break;
491 }
492 }
493 }
494
495 return consumed;
496 }
497
498
499
500 public void flush() throws IOException
501 {
502 while (_outNIOBuffer.length()>0)
503 {
504 int flushed=super.flush(_outNIOBuffer);
505
506
507 if (flushed==0)
508 {
509 Thread.yield();
510 flushed=super.flush(_outNIOBuffer);
511
512 }
513 }
514 }
515
516
517 private ByteBuffer extractInputBuffer(Buffer buffer)
518 {
519 assert buffer instanceof NIOBuffer;
520 NIOBuffer nbuf=(NIOBuffer)buffer;
521 ByteBuffer bbuf=nbuf.getByteBuffer();
522 bbuf.position(buffer.putIndex());
523 return bbuf;
524 }
525
526
527
528
529
530 private boolean unwrap(ByteBuffer buffer) throws IOException
531 {
532 if (_inNIOBuffer.hasContent())
533 _inNIOBuffer.compact();
534 else
535 _inNIOBuffer.clear();
536
537 int total_filled=0;
538 while (_inNIOBuffer.space()>0 && super.isOpen())
539 {
540 try
541 {
542 int filled=super.fill(_inNIOBuffer);
543
544 if (filled<=0)
545 break;
546 total_filled+=filled;
547 }
548 catch(IOException e)
549 {
550 if (_inNIOBuffer.length()==0)
551 throw e;
552 break;
553 }
554 }
555
556
557
558 if (_inNIOBuffer.length()==0)
559 {
560 if(!isOpen())
561 throw new org.eclipse.jetty.io.EofException();
562 return false;
563 }
564
565 try
566 {
567 _inBuffer.position(_inNIOBuffer.getIndex());
568 _inBuffer.limit(_inNIOBuffer.putIndex());
569 _result=null;
570 _last="unwrap";
571 _result=_engine.unwrap(_inBuffer,buffer);
572
573 _inNIOBuffer.skip(_result.bytesConsumed());
574 }
575 finally
576 {
577 _inBuffer.position(0);
578 _inBuffer.limit(_inBuffer.capacity());
579 }
580
581
582 switch(_result.getStatus())
583 {
584 case BUFFER_OVERFLOW:
585 case BUFFER_UNDERFLOW:
586 if (Log.isDebugEnabled()) Log.debug("unwrap {}",_result);
587 return (total_filled > 0);
588
589 case CLOSED:
590 _closing=true;
591 case OK:
592 boolean progress=total_filled>0 ||_result.bytesConsumed()>0 || _result.bytesProduced()>0;
593
594 return progress;
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 NIOBuffer nBuf=null;
606
607 if (buffer.buffer() instanceof NIOBuffer)
608 {
609 nBuf=(NIOBuffer)buffer.buffer();
610 return nBuf.getByteBuffer();
611 }
612 else
613 {
614 if (_reuseBuffer[n]==null)
615 _reuseBuffer[n] = (NIOBuffer)_buffers.getBuffer(_session.getApplicationBufferSize());
616 NIOBuffer buf = _reuseBuffer[n];
617 buf.clear();
618 buf.put(buffer);
619 return buf.getByteBuffer();
620 }
621 }
622
623
624 private int wrap(Buffer header, Buffer buffer) throws IOException
625 {
626 _gather[0]=extractOutputBuffer(header,0);
627 synchronized(_gather[0])
628 {
629 _gather[0].position(header.getIndex());
630 _gather[0].limit(header.putIndex());
631
632 _gather[1]=extractOutputBuffer(buffer,1);
633
634 synchronized(_gather[1])
635 {
636 _gather[1].position(buffer.getIndex());
637 _gather[1].limit(buffer.putIndex());
638
639 synchronized(_outBuffer)
640 {
641 int consumed=0;
642 try
643 {
644 _outNIOBuffer.clear();
645 _outBuffer.position(0);
646 _outBuffer.limit(_outBuffer.capacity());
647
648 _result=null;
649 _last="wrap wrap";
650 _result=_engine.wrap(_gather,_outBuffer);
651
652 _outNIOBuffer.setGetIndex(0);
653 _outNIOBuffer.setPutIndex(_result.bytesProduced());
654 consumed=_result.bytesConsumed();
655 }
656 finally
657 {
658 _outBuffer.position(0);
659
660 if (consumed>0 && header!=null)
661 {
662 int len=consumed<header.length()?consumed:header.length();
663 header.skip(len);
664 consumed-=len;
665 _gather[0].position(0);
666 _gather[0].limit(_gather[0].capacity());
667 }
668 if (consumed>0 && buffer!=null)
669 {
670 int len=consumed<buffer.length()?consumed:buffer.length();
671 buffer.skip(len);
672 consumed-=len;
673 _gather[1].position(0);
674 _gather[1].limit(_gather[1].capacity());
675 }
676 assert consumed==0;
677 }
678 }
679 }
680 }
681
682
683 switch(_result.getStatus())
684 {
685 case BUFFER_OVERFLOW:
686 case BUFFER_UNDERFLOW:
687 Log.warn("unwrap {}",_result);
688
689 case OK:
690 return _result.bytesConsumed();
691 case CLOSED:
692 _closing=true;
693 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
694
695 default:
696 Log.warn("wrap "+_result);
697 throw new IOException(_result.toString());
698 }
699 }
700
701
702 private int wrap(Buffer header) throws IOException
703 {
704 _gather[0]=extractOutputBuffer(header,0);
705 synchronized(_gather[0])
706 {
707 _gather[0].position(header.getIndex());
708 _gather[0].limit(header.putIndex());
709
710 int consumed=0;
711 synchronized(_outBuffer)
712 {
713 try
714 {
715 _outNIOBuffer.clear();
716 _outBuffer.position(0);
717 _outBuffer.limit(_outBuffer.capacity());
718 _result=null;
719 _last="wrap wrap";
720 _result=_engine.wrap(_gather[0],_outBuffer);
721
722 _outNIOBuffer.setGetIndex(0);
723 _outNIOBuffer.setPutIndex(_result.bytesProduced());
724 consumed=_result.bytesConsumed();
725 }
726 finally
727 {
728 _outBuffer.position(0);
729
730 if (consumed>0 && header!=null)
731 {
732 int len=consumed<header.length()?consumed:header.length();
733 header.skip(len);
734 consumed-=len;
735 _gather[0].position(0);
736 _gather[0].limit(_gather[0].capacity());
737 }
738 assert consumed==0;
739 }
740 }
741 }
742 switch(_result.getStatus())
743 {
744 case BUFFER_OVERFLOW:
745 case BUFFER_UNDERFLOW:
746 Log.warn("unwrap {}",_result);
747
748 case OK:
749 return _result.bytesConsumed();
750 case CLOSED:
751 _closing=true;
752 return _result.bytesConsumed()>0?_result.bytesConsumed():-1;
753
754 default:
755 Log.warn("wrap "+_result);
756 throw new IOException(_result.toString());
757 }
758 }
759
760
761 public boolean isBufferingInput()
762 {
763 return _inNIOBuffer.hasContent();
764 }
765
766
767 public boolean isBufferingOutput()
768 {
769 return _outNIOBuffer.hasContent();
770 }
771
772
773 public boolean isBufferred()
774 {
775 return true;
776 }
777
778
779 public SSLEngine getSSLEngine()
780 {
781 return _engine;
782 }
783
784
785 public String toString()
786 {
787 return super.toString()+","+_engine.getHandshakeStatus()+", in/out="+_inNIOBuffer.length()+"/"+_outNIOBuffer.length()+" last "+_last+" "+_result;
788 }
789 }