View Javadoc

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