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.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   * SslHttpChannelEndPoint.
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      // ssl
61      protected SSLSession _session;
62      
63      // TODO get rid of this
64      // StringBuilder h = new StringBuilder(500);
65      /*
66      class H 
67      {
68          H append(Object o)
69          {
70              System.err.print(o);
71              return this;
72          }
73      };
74      H h = new H();
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          // ssl
85          _engine=engine;
86          _session=engine.getSession();
87  
88          // TODO pool buffers and use only when needed.
89          _outNIOBuffer=(NIOBuffer)buffers.getBuffer(_session.getPacketBufferSize());
90          _outBuffer=_outNIOBuffer.getByteBuffer();
91          _inNIOBuffer=(NIOBuffer)buffers.getBuffer(_session.getPacketBufferSize());
92          _inBuffer=_inNIOBuffer.getByteBuffer();
93          
94          // h.append("CONSTRUCTED\n");
95      }
96  
97      // TODO get rid of these dumps
98      public void dump()
99      {
100         Log.info(""+_result);
101         // System.err.println(h.toString());
102         // System.err.println("--");
103     }
104     
105     /* ------------------------------------------------------------ */
106     /* (non-Javadoc)
107      * @see org.eclipse.io.nio.SelectChannelEndPoint#idleExpired()
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         // h.append("IDLE EXPIRED\n");
131         super.idleExpired();
132     }
133 
134     /* ------------------------------------------------------------ */
135     public void close() throws IOException
136     {
137         // TODO - this really should not be done in a loop here - but with async callbacks.
138 
139         // h.append("CLOSE\n");
140         _closing=true;
141         try
142         {   
143             int tries=0;
144             
145             while (_outNIOBuffer.length()>0)
146             {
147                 // TODO REMOVE loop check
148                 if (tries++>100)
149                     throw new IllegalStateException();
150                 flush();
151                 Thread.sleep(100); // TODO yuck
152             }
153 
154             _engine.closeOutbound();
155 
156             loop: while (isOpen() && !(_engine.isInboundDone() && _engine.isOutboundDone()))
157             {   
158                 // TODO REMOVE loop check
159                 if (tries++>100)
160                     throw new IllegalStateException();
161                 
162                 if (_outNIOBuffer.length()>0)
163                 {
164                     flush();
165                     Thread.sleep(100); // TODO yuck
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                                 // h.append("break loop\n");
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                     // TODO REMOVE loop check
273                     if (tries++>100)
274                         throw new IllegalStateException();
275 
276                     // h.append("Fill(Buffer)\n");
277                     
278                     if (_outNIOBuffer.length()>0)
279                         flush();
280 
281                     // h.append("status=").append(_engine.getHandshakeStatus()).append('\n');
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                                 // h.append("break loop\n");
294                                 break loop;
295                             }
296                             break;
297 
298                         case NEED_TASK:
299                         {
300                             Runnable task;
301                             while ((task=_engine.getDelegatedTask())!=null)
302                             {
303                                 // h.append("run task\n");
304                                 task.run();
305                             }
306                             if(initialStatus==HandshakeStatus.NOT_HANDSHAKING && 
307                                     HandshakeStatus.NEED_UNWRAP==_engine.getHandshakeStatus() && wraps==0)
308                             {
309                                 // This should be NEED_WRAP
310                                 // 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.
311                                 // 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.
312                                 // See http://jira.codehaus.org/browse/JETTY-567 for more details
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                                     // h.append("wrap ").append(_result).append('\n');
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             // TODO REMOVE loop check
393             if (tries++>100)
394                 throw new IllegalStateException();
395             
396             // h.append("Flush ").append(tries).append(' ').append(_outNIOBuffer.length()).append('\n');
397             
398             if (_outNIOBuffer.length()>0)
399                 flush();
400 
401             // h.append(_engine.getHandshakeStatus()).append('\n');
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                             // h.append("break").append('\n');
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                         // h.append("run task\n");
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                             // h.append("wrap=").append(_result).append('\n');
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             // h.append("flushed=").append(flushed).append(" of ").append(_outNIOBuffer.length()).append('\n');
507             if (flushed==0)
508             {
509                 Thread.yield();
510                 flushed=super.flush(_outNIOBuffer);
511                 // h.append("flushed2=").append(flushed).append(" of ").append(_outNIOBuffer.length()).append('\n');
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      * @return true if progress is made
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                 // h.append("fill=").append(filled).append('\n');
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         // h.append("inNIOBuffer=").append(_inNIOBuffer.length()).append('\n');
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             // h.append("unwrap=").append(_result).append('\n');
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                 // h.append("progress=").append(progress).append('\n');
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                         // h.append("wrap2=").append(_result).append('\n');
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                     // h.append("wrap1=").append(_result).append('\n');
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 }