1   /**
2    * 
3    */
4   package org.mortbay.jetty.client;
5   
6   import java.io.IOException;
7   import java.nio.channels.SelectionKey;
8   import java.nio.channels.SocketChannel;
9   
10  import javax.net.ssl.SSLContext;
11  import javax.net.ssl.SSLEngine;
12  
13  import org.mortbay.component.AbstractLifeCycle;
14  import org.mortbay.io.Buffer;
15  import org.mortbay.io.Buffers;
16  import org.mortbay.io.Connection;
17  import org.mortbay.io.nio.NIOBuffer;
18  import org.mortbay.io.nio.SelectChannelEndPoint;
19  import org.mortbay.io.nio.SelectorManager;
20  import org.mortbay.jetty.AbstractBuffers;
21  import org.mortbay.jetty.HttpMethods;
22  import org.mortbay.jetty.HttpVersions;
23  import org.mortbay.jetty.security.SslHttpChannelEndPoint;
24  import org.mortbay.log.Log;
25  
26  class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector, Runnable
27  {
28      private final HttpClient _httpClient;
29      private SSLContext _sslContext;
30      private Buffers _sslBuffers;
31  
32      SelectorManager _selectorManager=new Manager();
33  
34      /**
35       * @param httpClient
36       */
37      SelectConnector(HttpClient httpClient)
38      {
39          _httpClient = httpClient;
40      }
41  
42      protected void doStart() throws Exception
43      {
44          _selectorManager.start();
45          _httpClient._threadPool.dispatch(this);
46      }
47  
48      protected void doStop() throws Exception
49      {
50          _selectorManager.stop();
51      }
52  
53      public void startConnection( HttpDestination destination )
54          throws IOException
55      {
56          SocketChannel channel = SocketChannel.open();
57          channel.connect( destination.isProxied() ? destination.getProxy() : destination.getAddress() );
58          channel.configureBlocking( false );
59          channel.socket().setSoTimeout( _httpClient._soTimeout );
60          _selectorManager.register( channel, destination );
61      }
62  
63      public void run()
64      {
65          while (_httpClient.isRunning())
66          {
67              try
68              {
69                  _selectorManager.doSelect(0);
70              }
71              catch (Exception e)
72              {
73                  e.printStackTrace();
74              }
75          }
76      }
77  
78      class Manager extends SelectorManager
79      {
80          protected SocketChannel acceptChannel(SelectionKey key) throws IOException
81          {
82              throw new IllegalStateException();
83          }
84  
85          public boolean dispatch(Runnable task)
86          {
87              return SelectConnector.this._httpClient._threadPool.dispatch(task);
88          }
89  
90          protected void endPointOpened(SelectChannelEndPoint endpoint)
91          {
92          }
93  
94          protected void endPointClosed(SelectChannelEndPoint endpoint)
95          {
96          }
97  
98          protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint)
99          {
100             return new HttpConnection(_httpClient,endpoint,SelectConnector.this._httpClient.getHeaderBufferSize(),SelectConnector.this._httpClient.getRequestBufferSize());
101         }
102 
103         protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException
104         {
105             // key should have destination at this point (will be replaced by endpoint after this call)
106             HttpDestination dest=(HttpDestination)key.attachment();
107             
108 
109             SelectChannelEndPoint ep=null;
110             
111             if (dest.isSecure())
112             {
113                 if (dest.isProxied())
114                 {
115                     String connect = HttpMethods.CONNECT+" "+dest.getAddress()+HttpVersions.HTTP_1_0+"\r\n\r\n";
116                     // TODO need to send this over channel unencrypted and setup endpoint to ignore the 200 OK response.
117                    
118                     throw new IllegalStateException("Not Implemented");
119                 }
120 
121                 SSLEngine engine=newSslEngine();
122                 ep = new SslHttpChannelEndPoint(_sslBuffers,channel,selectSet,key,engine);
123             }
124             else
125             {
126                 ep=new SelectChannelEndPoint(channel,selectSet,key);
127             }
128             
129             HttpConnection connection=(HttpConnection)ep.getConnection();
130             connection.setDestination(dest);
131             dest.onNewConnection(connection);
132             return ep;
133         }
134 
135         private synchronized SSLEngine newSslEngine() throws IOException
136         {
137             if (_sslContext==null)
138             {
139                 _sslContext = SelectConnector.this._httpClient.getLooseSSLContext();
140             }
141                 
142             SSLEngine sslEngine = _sslContext.createSSLEngine();
143             sslEngine.setUseClientMode(true);
144             sslEngine.beginHandshake();
145                 
146             if (_sslBuffers==null)
147             {
148                 AbstractBuffers buffers = new AbstractBuffers()
149                 {
150                     protected Buffer newBuffer( int size )
151                     {
152                         return new NIOBuffer( size, NIOBuffer.INDIRECT );
153                     }        
154                 }; 
155             
156                 buffers.setRequestBufferSize( sslEngine.getSession().getPacketBufferSize());
157                 buffers.setResponseBufferSize(sslEngine.getSession().getApplicationBufferSize());
158                 
159                 try
160                 {
161                     buffers.start();
162                 }
163                 catch(Exception e)
164                 {       
165                     throw new IllegalStateException(e);
166                 }
167                 _sslBuffers=buffers;
168             }
169             
170             return sslEngine;
171         }
172 
173         /* ------------------------------------------------------------ */
174         /* (non-Javadoc)
175          * @see org.mortbay.io.nio.SelectorManager#connectionFailed(java.nio.channels.SocketChannel, java.lang.Throwable, java.lang.Object)
176          */
177         protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
178         {
179             if (attachment instanceof HttpDestination)
180                 ((HttpDestination)attachment).onConnectionFailed(ex);
181             else
182                 Log.warn(ex);
183         }
184        
185     }
186 
187 }