View Javadoc

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