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.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   * SslHttpChannelEndPoint.
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      // 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 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             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         // 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         final ByteBuffer bbuf=extractInputBuffer(buffer);
261         int size=buffer.length();
262         HandshakeStatus initialStatus = _engine.getHandshakeStatus();
263         //noinspection SynchronizationOnLocalVariableOrMethodParameter
264         synchronized (bbuf)
265         {
266             try
267             {
268                 unwrap(bbuf);
269 
270                 int tries=0, wraps=0;
271                 loop: while (true)
272                 {
273                     // TODO REMOVE loop check
274                     if (tries++>100)
275                         throw new IllegalStateException();
276 
277                     // h.append("Fill(Buffer)\n");
278                     
279                     if (_outNIOBuffer.length()>0)
280                         flush();
281 
282                     // h.append("status=").append(_engine.getHandshakeStatus()).append('\n');
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                                 // h.append("break loop\n");
295                                 break loop;
296                             }
297                             break;
298 
299                         case NEED_TASK:
300                         {
301                             Runnable task;
302                             while ((task=_engine.getDelegatedTask())!=null)
303                             {
304                                 // h.append("run task\n");
305                                 task.run();
306                             }
307                             if(initialStatus==HandshakeStatus.NOT_HANDSHAKING && 
308                                     HandshakeStatus.NEED_UNWRAP==_engine.getHandshakeStatus() && wraps==0)
309                             {
310                                 // This should be NEED_WRAP
311                                 // 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.
312                                 // 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.
313                                 // See http://jira.codehaus.org/browse/JETTY-567 for more details
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                                     // h.append("wrap ").append(_result).append('\n');
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             // TODO REMOVE loop check
394             if (tries++>100)
395                 throw new IllegalStateException();
396             
397             // h.append("Flush ").append(tries).append(' ').append(_outNIOBuffer.length()).append('\n');
398             
399             if (_outNIOBuffer.length()>0)
400                 flush();
401 
402             // h.append(_engine.getHandshakeStatus()).append('\n');
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                             // h.append("break").append('\n');
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                         // h.append("run task\n");
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                             // h.append("wrap=").append(_result).append('\n');
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             // h.append("flushed=").append(flushed).append(" of ").append(_outNIOBuffer.length()).append('\n');
508             if (flushed==0)
509             {
510                 Thread.yield();
511                 //noinspection UnusedAssignment
512                 flushed=super.flush(_outNIOBuffer);
513                 // h.append("flushed2=").append(flushed).append(" of ").append(_outNIOBuffer.length()).append('\n');
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      * @return true if progress is made
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                 // h.append("fill=").append(filled).append('\n');
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         // h.append("inNIOBuffer=").append(_inNIOBuffer.length()).append('\n');
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             // h.append("unwrap=").append(_result).append('\n');
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                         // h.append("wrap2=").append(_result).append('\n');
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                     // h.append("wrap1=").append(_result).append('\n');
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 }