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