View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.websocket.client.io;
20  
21  import java.net.InetSocketAddress;
22  import java.net.SocketAddress;
23  import java.net.URI;
24  import java.nio.channels.SocketChannel;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.Locale;
28  import java.util.Queue;
29  import java.util.concurrent.ConcurrentLinkedQueue;
30  
31  import org.eclipse.jetty.util.component.ContainerLifeCycle;
32  import org.eclipse.jetty.util.log.Log;
33  import org.eclipse.jetty.util.log.Logger;
34  import org.eclipse.jetty.websocket.api.WebSocketException;
35  import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
36  import org.eclipse.jetty.websocket.client.WebSocketClient;
37  import org.eclipse.jetty.websocket.common.WebSocketSession;
38  import org.eclipse.jetty.websocket.common.events.EventDriver;
39  
40  /**
41   * Internal Connection/Client Manager used to track active clients, their physical vs virtual connection information, and provide some means to create new
42   * physical or virtual connections.
43   */
44  public class ConnectionManager extends ContainerLifeCycle
45  {
46      private class PhysicalConnect extends ConnectPromise
47      {
48          private SocketAddress bindAddress;
49  
50          public PhysicalConnect(WebSocketClient client, EventDriver driver, ClientUpgradeRequest request)
51          {
52              super(client,driver,request);
53              this.bindAddress = client.getBindAddress();
54          }
55  
56          @Override
57          public void run()
58          {
59              try
60              {
61                  SocketChannel channel = SocketChannel.open();
62                  if (bindAddress != null)
63                  {
64                      channel.bind(bindAddress);
65                  }
66  
67                  URI wsUri = getRequest().getRequestURI();
68  
69                  channel.socket().setTcpNoDelay(true); // disable nagle
70                  channel.configureBlocking(false); // async always
71  
72                  InetSocketAddress address = toSocketAddress(wsUri);
73  
74                  channel.connect(address);
75                  getSelector().connect(channel,this);
76              }
77              catch (Throwable t)
78              {
79                  failed(t);
80              }
81          }
82      }
83  
84      private class VirtualConnect extends ConnectPromise
85      {
86          public VirtualConnect(WebSocketClient client, EventDriver driver, ClientUpgradeRequest request)
87          {
88              super(client,driver,request);
89          }
90  
91          @Override
92          public void run()
93          {
94              failed(new WebSocketException("MUX Not yet supported"));
95          }
96      }
97  
98      private static final Logger LOG = Log.getLogger(ConnectionManager.class);
99  
100     public static InetSocketAddress toSocketAddress(URI uri)
101     {
102         if (!uri.isAbsolute())
103         {
104             throw new IllegalArgumentException("Cannot get InetSocketAddress of non-absolute URIs");
105         }
106 
107         int port = uri.getPort();
108         String scheme = uri.getScheme().toLowerCase(Locale.ENGLISH);
109         if ("ws".equals(scheme))
110         {
111             if (port == (-1))
112             {
113                 port = 80;
114             }
115         }
116         else if ("wss".equals(scheme))
117         {
118             if (port == (-1))
119             {
120                 port = 443;
121             }
122         }
123         else
124         {
125             throw new IllegalArgumentException("Only support ws:// and wss:// URIs");
126         }
127 
128         return new InetSocketAddress(uri.getHost(),port);
129     }
130 
131     private final Queue<WebSocketSession> sessions = new ConcurrentLinkedQueue<>();
132     private final WebSocketClient client;
133     private WebSocketClientSelectorManager selector;
134 
135     public ConnectionManager(WebSocketClient client)
136     {
137         this.client = client;
138     }
139 
140     public void addSession(WebSocketSession session)
141     {
142         sessions.add(session);
143     }
144 
145     private void closeAllConnections()
146     {
147         for (WebSocketSession session : sessions)
148         {
149             if (session.getConnection() != null)
150             {
151                 try
152                 {
153                     session.getConnection().close();
154                 }
155                 catch (Throwable t)
156                 {
157                     LOG.debug("During Close All Connections",t);
158                 }
159             }
160         }
161     }
162 
163     public ConnectPromise connect(WebSocketClient client, EventDriver driver, ClientUpgradeRequest request)
164     {
165         URI toUri = request.getRequestURI();
166         String hostname = toUri.getHost();
167 
168         if (isVirtualConnectionPossibleTo(hostname))
169         {
170             return new VirtualConnect(client,driver,request);
171         }
172 
173         return new PhysicalConnect(client,driver,request);
174     }
175 
176     @Override
177     protected void doStart() throws Exception
178     {
179         selector = newWebSocketClientSelectorManager(client);
180         selector.setSslContextFactory(client.getSslContextFactory());
181         selector.setConnectTimeout(client.getConnectTimeout());
182         addBean(selector);
183 
184         super.doStart();
185     }
186 
187     @Override
188     protected void doStop() throws Exception
189     {
190         closeAllConnections();
191         sessions.clear();
192         super.doStop();
193         removeBean(selector);
194     }
195 
196     public WebSocketClientSelectorManager getSelector()
197     {
198         return selector;
199     }
200 
201     public Collection<WebSocketSession> getSessions()
202     {
203         return Collections.unmodifiableCollection(sessions);
204     }
205 
206     private boolean isVirtualConnectionPossibleTo(String hostname)
207     {
208         // TODO Auto-generated method stub
209         return false;
210     }
211 
212     /**
213      * Factory method for new WebSocketClientSelectorManager (used by other projects like cometd)
214      * 
215      * @param client
216      *            the client used to create the WebSocketClientSelectorManager
217      * @return the new WebSocketClientSelectorManager
218      */
219     protected WebSocketClientSelectorManager newWebSocketClientSelectorManager(WebSocketClient client)
220     {
221         return new WebSocketClientSelectorManager(client);
222     }
223 
224     public void removeSession(WebSocketSession session)
225     {
226         sessions.remove(session);
227     }
228 }