View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2015 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.jsr356.server;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import javax.websocket.Extension;
26  import javax.websocket.Extension.Parameter;
27  import javax.websocket.server.ServerEndpointConfig;
28  import javax.websocket.server.ServerEndpointConfig.Configurator;
29  
30  import org.eclipse.jetty.util.StringUtil;
31  import org.eclipse.jetty.util.log.Log;
32  import org.eclipse.jetty.util.log.Logger;
33  import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
34  import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
35  import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
36  import org.eclipse.jetty.websocket.jsr356.JsrExtension;
37  import org.eclipse.jetty.websocket.jsr356.endpoints.EndpointInstance;
38  import org.eclipse.jetty.websocket.jsr356.server.pathmap.WebSocketPathSpec;
39  import org.eclipse.jetty.websocket.server.pathmap.PathSpec;
40  import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
41  import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
42  import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
43  
44  public class JsrCreator implements WebSocketCreator
45  {
46      public static final String PROP_REMOTE_ADDRESS = "javax.websocket.endpoint.remoteAddress";
47      public static final String PROP_LOCAL_ADDRESS = "javax.websocket.endpoint.localAddress";
48      private static final Logger LOG = Log.getLogger(JsrCreator.class);
49      private final WebSocketContainerScope containerScope;
50      private final ServerEndpointMetadata metadata;
51      private final ExtensionFactory extensionFactory;
52  
53      public JsrCreator(WebSocketContainerScope containerScope, ServerEndpointMetadata metadata, ExtensionFactory extensionFactory)
54      {
55          this.containerScope = containerScope;
56          this.metadata = metadata;
57          this.extensionFactory = extensionFactory;
58      }
59  
60      @Override
61      public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
62      {
63          JsrHandshakeRequest hsreq = new JsrHandshakeRequest(req);
64          JsrHandshakeResponse hsresp = new JsrHandshakeResponse(resp);
65  
66          // Get raw config, as defined when the endpoint was added to the container
67          ServerEndpointConfig config = metadata.getConfig();
68          
69          // Establish a copy of the config, so that the UserProperties are unique
70          // per upgrade request.
71          config = new BasicServerEndpointConfig(containerScope, config);
72          
73          // Bug 444617 - Expose localAddress and remoteAddress for jsr modify handshake to use
74          // This is being implemented as an optional set of userProperties so that
75          // it is not JSR api breaking.  A few users on #jetty and a few from cometd
76          // have asked for access to this information.
77          config.getUserProperties().put(PROP_LOCAL_ADDRESS,req.getLocalSocketAddress());
78          config.getUserProperties().put(PROP_REMOTE_ADDRESS,req.getRemoteSocketAddress());
79  
80          // Get Configurator from config object (not guaranteed to be unique per endpoint upgrade)
81          ServerEndpointConfig.Configurator configurator = config.getConfigurator();
82  
83          // modify handshake
84          configurator.modifyHandshake(config,hsreq,hsresp);
85  
86          // check origin
87          if (!configurator.checkOrigin(req.getOrigin()))
88          {
89              try
90              {
91                  resp.sendForbidden("Origin mismatch");
92              }
93              catch (IOException e)
94              {
95                  if (LOG.isDebugEnabled())
96                      LOG.debug("Unable to send error response",e);
97              }
98              return null;
99          }
100 
101         // deal with sub protocols
102         List<String> supported = config.getSubprotocols();
103         List<String> requested = req.getSubProtocols();
104         String subprotocol = configurator.getNegotiatedSubprotocol(supported,requested);
105         if (StringUtil.isNotBlank(subprotocol))
106         {
107             resp.setAcceptedSubProtocol(subprotocol);
108         }
109 
110         // deal with extensions
111         List<Extension> installedExts = new ArrayList<>();
112         for (String extName : extensionFactory.getAvailableExtensions().keySet())
113         {
114             installedExts.add(new JsrExtension(extName));
115         }
116         List<Extension> requestedExts = new ArrayList<>();
117         for (ExtensionConfig reqCfg : req.getExtensions())
118         {
119             requestedExts.add(new JsrExtension(reqCfg));
120         }
121         List<Extension> usedExts = configurator.getNegotiatedExtensions(installedExts,requestedExts);
122         List<ExtensionConfig> configs = new ArrayList<>();
123         if (usedExts != null)
124         {
125             for (Extension used : usedExts)
126             {
127                 ExtensionConfig ecfg = new ExtensionConfig(used.getName());
128                 for (Parameter param : used.getParameters())
129                 {
130                     ecfg.setParameter(param.getName(),param.getValue());
131                 }
132                 configs.add(ecfg);
133             }
134         }
135         resp.setExtensions(configs);
136 
137         // create endpoint class
138         try
139         {
140             Class<?> endpointClass = config.getEndpointClass();
141             Configurator configr = config.getConfigurator();
142             Object endpoint = configr.getEndpointInstance(endpointClass);
143             // Do not decorate here (let the Connection and Session start first)
144             // This will allow CDI to see Session for injection into Endpoint classes.
145             PathSpec pathSpec = hsreq.getRequestPathSpec();
146             if (pathSpec instanceof WebSocketPathSpec)
147             {
148                 // We have a PathParam path spec
149                 WebSocketPathSpec wspathSpec = (WebSocketPathSpec)pathSpec;
150                 String requestPath = req.getRequestPath();
151                 // Wrap the config with the path spec information
152                 config = new PathParamServerEndpointConfig(containerScope,config,wspathSpec,requestPath);
153             }
154             return new EndpointInstance(endpoint,config,metadata);
155         }
156         catch (InstantiationException e)
157         {
158             if (LOG.isDebugEnabled())
159                 LOG.debug("Unable to create websocket: " + config.getEndpointClass().getName(),e);
160             return null;
161         }
162     }
163 
164     @Override
165     public String toString()
166     {
167         return String.format("%s[metadata=%s]",this.getClass().getName(),metadata);
168     }
169 }