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