View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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.io.IOException;
22  import java.net.InetSocketAddress;
23  import java.net.SocketAddress;
24  import java.net.URI;
25  import java.nio.channels.SocketChannel;
26  import java.util.Locale;
27  
28  import org.eclipse.jetty.util.component.ContainerLifeCycle;
29  import org.eclipse.jetty.util.log.Log;
30  import org.eclipse.jetty.util.log.Logger;
31  import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
32  import org.eclipse.jetty.websocket.client.WebSocketClient;
33  import org.eclipse.jetty.websocket.common.events.EventDriver;
34  
35  /**
36   * Internal Connection/Client Manager used to track active clients, their physical vs virtual connection information, and provide some means to create new
37   * physical or virtual connections.
38   */
39  public class ConnectionManager extends ContainerLifeCycle
40  {
41      private class PhysicalConnect extends ConnectPromise
42      {
43          private SocketAddress bindAddress;
44  
45          public PhysicalConnect(WebSocketClient client, EventDriver driver, ClientUpgradeRequest request)
46          {
47              super(client,driver,request);
48              this.bindAddress = client.getBindAddress();
49          }
50  
51          @Override
52          public void run()
53          {
54              SocketChannel channel = null;
55              try
56              {
57                  channel = SocketChannel.open();
58                  if (bindAddress != null)
59                  {
60                      channel.bind(bindAddress);
61                  }
62  
63                  URI wsUri = getRequest().getRequestURI();
64  
65                  channel.socket().setTcpNoDelay(true); // disable nagle
66                  channel.configureBlocking(false); // async always
67  
68                  InetSocketAddress address = toSocketAddress(wsUri);
69  
70                  if (channel.connect(address))
71                  {
72                      getSelector().accept(channel, this);
73                  }
74                  else
75                  {
76                      getSelector().connect(channel, this);
77                  }
78              }
79              catch (Throwable t)
80              {
81                  // close the socket channel
82                  if (channel != null)
83                  {
84                      try
85                      {
86                          channel.close();
87                      }
88                      catch (IOException ignore)
89                      {
90                          LOG.ignore(ignore);
91                      }
92                  }
93                  
94                  // notify the future
95                  failed(t);
96              }
97          }
98      }
99  
100     private static final Logger LOG = Log.getLogger(ConnectionManager.class);
101 
102     public static InetSocketAddress toSocketAddress(URI uri)
103     {
104         if (!uri.isAbsolute())
105         {
106             throw new IllegalArgumentException("Cannot get InetSocketAddress of non-absolute URIs");
107         }
108 
109         int port = uri.getPort();
110         String scheme = uri.getScheme().toLowerCase(Locale.ENGLISH);
111         if ("ws".equals(scheme))
112         {
113             if (port == (-1))
114             {
115                 port = 80;
116             }
117         }
118         else if ("wss".equals(scheme))
119         {
120             if (port == (-1))
121             {
122                 port = 443;
123             }
124         }
125         else
126         {
127             throw new IllegalArgumentException("Only support ws:// and wss:// URIs");
128         }
129 
130         return new InetSocketAddress(uri.getHost(),port);
131     }
132 
133     private final WebSocketClient client;
134     private WebSocketClientSelectorManager selector;
135 
136     public ConnectionManager(WebSocketClient client)
137     {
138         this.client = client;
139     }
140 
141     public ConnectPromise connect(WebSocketClient client, EventDriver driver, ClientUpgradeRequest request)
142     {
143         return new PhysicalConnect(client,driver,request);
144     }
145 
146     @Override
147     protected void doStart() throws Exception
148     {
149         selector = newWebSocketClientSelectorManager(client);
150         selector.setSslContextFactory(client.getSslContextFactory());
151         selector.setConnectTimeout(client.getConnectTimeout());
152         addBean(selector);
153 
154         super.doStart();
155     }
156 
157     @Override
158     protected void doStop() throws Exception
159     {
160         super.doStop();
161         removeBean(selector);
162     }
163 
164     public WebSocketClientSelectorManager getSelector()
165     {
166         return selector;
167     }
168 
169     /**
170      * Factory method for new WebSocketClientSelectorManager (used by other projects like cometd)
171      * 
172      * @param client
173      *            the client used to create the WebSocketClientSelectorManager
174      * @return the new WebSocketClientSelectorManager
175      */
176     protected WebSocketClientSelectorManager newWebSocketClientSelectorManager(WebSocketClient client)
177     {
178         return new WebSocketClientSelectorManager(client);
179     }
180 }