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.util.StringUtil;
32  import org.eclipse.jetty.util.log.Log;
33  import org.eclipse.jetty.util.log.Logger;
34  import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
35  import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
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      public static final String PROP_LOCALES = "javax.websocket.upgrade.locales";
49      private static final Logger LOG = Log.getLogger(JsrCreator.class);
50      private final ServerEndpointMetadata metadata;
51      private final ExtensionFactory extensionFactory;
52  
53      public JsrCreator(ServerEndpointMetadata metadata, ExtensionFactory extensionFactory)
54      {
55          this.metadata = metadata;
56          this.extensionFactory = extensionFactory;
57      }
58  
59      @Override
60      public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
61      {
62          JsrHandshakeRequest jsrHandshakeRequest = new JsrHandshakeRequest(req);
63          JsrHandshakeResponse jsrHandshakeResponse = new JsrHandshakeResponse(resp);
64  
65          // Get raw config, as defined when the endpoint was added to the container
66          ServerEndpointConfig config = metadata.getConfig();
67  
68          // Establish a copy of the config, so that the UserProperties are unique
69          // per upgrade request.
70          config = new BasicServerEndpointConfig(config);
71  
72          // Bug 444617 - Expose localAddress and remoteAddress for jsr modify handshake to use
73          // This is being implemented as an optional set of userProperties so that
74          // it is not JSR api breaking.  A few users on #jetty and a few from cometd
75          // have asked for access to this information.
76          Map<String, Object> userProperties = config.getUserProperties();
77          userProperties.put(PROP_LOCAL_ADDRESS,req.getLocalSocketAddress());
78          userProperties.put(PROP_REMOTE_ADDRESS,req.getRemoteSocketAddress());
79          userProperties.put(PROP_LOCALES,Collections.list(req.getLocales()));
80  
81          // Get Configurator from config object (not guaranteed to be unique per endpoint upgrade)
82          ServerEndpointConfig.Configurator configurator = config.getConfigurator();
83  
84          // [JSR] Step 1: check origin
85          if (!configurator.checkOrigin(req.getOrigin()))
86          {
87              try
88              {
89                  resp.sendForbidden("Origin mismatch");
90              }
91              catch (IOException e)
92              {
93                  if (LOG.isDebugEnabled())
94                      LOG.debug("Unable to send error response",e);
95              }
96              return null;
97          }
98  
99          // [JSR] Step 2: deal with sub protocols
100         List<String> supported = config.getSubprotocols();
101         List<String> requested = req.getSubProtocols();
102         String subprotocol = configurator.getNegotiatedSubprotocol(supported,requested);
103         if (StringUtil.isNotBlank(subprotocol))
104         {
105             resp.setAcceptedSubProtocol(subprotocol);
106         }
107 
108         // [JSR] Step 3: deal with extensions
109         List<Extension> installedExtensions = new ArrayList<>();
110         for (String extName : extensionFactory.getAvailableExtensions().keySet())
111         {
112             installedExtensions.add(new JsrExtension(extName));
113         }
114         List<Extension> requestedExts = new ArrayList<>();
115         for (ExtensionConfig reqCfg : req.getExtensions())
116         {
117             requestedExts.add(new JsrExtension(reqCfg));
118         }
119         List<Extension> usedExtensions = configurator.getNegotiatedExtensions(installedExtensions,requestedExts);
120         List<ExtensionConfig> configs = new ArrayList<>();
121         if (usedExtensions != null)
122         {
123             for (Extension used : usedExtensions)
124             {
125                 ExtensionConfig ecfg = new ExtensionConfig(used.getName());
126                 for (Parameter param : used.getParameters())
127                 {
128                     ecfg.setParameter(param.getName(),param.getValue());
129                 }
130                 configs.add(ecfg);
131             }
132         }
133         resp.setExtensions(configs);
134 
135         // [JSR] Step 4: build out new ServerEndpointConfig
136         PathSpec pathSpec = jsrHandshakeRequest.getRequestPathSpec();
137         if (pathSpec instanceof WebSocketPathSpec)
138         {
139             // We have a PathParam path spec
140             WebSocketPathSpec wspathSpec = (WebSocketPathSpec)pathSpec;
141             String requestPath = req.getRequestPath();
142             // Wrap the config with the path spec information
143             config = new PathParamServerEndpointConfig(config,wspathSpec,requestPath);
144         }
145 
146         // [JSR] Step 5: Call modifyHandshake
147         configurator.modifyHandshake(config,jsrHandshakeRequest,jsrHandshakeResponse);
148 
149         try
150         {
151             // [JSR] Step 6: create endpoint class
152             Class<?> endpointClass = config.getEndpointClass();
153             Object endpoint = config.getConfigurator().getEndpointInstance(endpointClass);
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 }