1 //
2 // ========================================================================
3 // Copyright (c) 1995-2013 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.servlet;
20
21 import java.io.IOException;
22 import java.util.Iterator;
23 import java.util.ServiceLoader;
24
25 import javax.servlet.ServletException;
26 import javax.servlet.http.HttpServlet;
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
29
30 import org.eclipse.jetty.websocket.api.WebSocketBehavior;
31 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
32 import org.eclipse.jetty.websocket.api.annotations.WebSocket;
33
34 /**
35 * Abstract Servlet used to bridge the Servlet API to the WebSocket API.
36 * <p>
37 * To use this servlet, you will be required to register your websockets with the {@link WebSocketServerFactory} so that it can create your websockets under the
38 * appropriate conditions.
39 * <p>
40 * The most basic implementation would be as follows.
41 *
42 * <pre>
43 * package my.example;
44 *
45 * import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
46 * import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
47 *
48 * public class MyEchoServlet extends WebSocketServlet
49 * {
50 * @Override
51 * public void configure(WebSocketServletFactory factory)
52 * {
53 * // set a 10 second idle timeout
54 * factory.getPolicy().setIdleTimeout(10000);
55 * // register my socket
56 * factory.register(MyEchoSocket.class);
57 * }
58 * }
59 * </pre>
60 *
61 * Note: that only request that conforms to a "WebSocket: Upgrade" handshake request will trigger the {@link WebSocketServerFactory} handling of creating
62 * WebSockets.<br>
63 * All other requests are treated as normal servlet requests.
64 *
65 * <p>
66 * <b>Configuration / Init-Parameters:</b><br>
67 * Note: If you use the {@link WebSocket @WebSocket} annotation, these configuration settings can be specified on a per WebSocket basis, vs a per Servlet
68 * basis.
69 *
70 * <dl>
71 * <dt>maxIdleTime</dt>
72 * <dd>set the time in ms that a websocket may be idle before closing<br>
73 *
74 * <dt>maxMessagesSize</dt>
75 * <dd>set the size in bytes that a websocket may be accept before closing<br>
76 *
77 * <dt>inputBufferSize</dt>
78 * <dd>set the size in bytes of the buffer used to read raw bytes from the network layer<br>
79 * </dl>
80 */
81 @SuppressWarnings("serial")
82 public abstract class WebSocketServlet extends HttpServlet
83 {
84 private WebSocketServletFactory factory;
85
86 public abstract void configure(WebSocketServletFactory factory);
87
88 @Override
89 public void destroy()
90 {
91 factory.cleanup();
92 }
93
94 /**
95 * @see javax.servlet.GenericServlet#init()
96 */
97 @Override
98 public void init() throws ServletException
99 {
100 try
101 {
102 WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
103
104 String max = getInitParameter("maxIdleTime");
105 if (max != null)
106 {
107 policy.setIdleTimeout(Long.parseLong(max));
108 }
109
110 max = getInitParameter("maxMessageSize");
111 if (max != null)
112 {
113 policy.setMaxMessageSize(Long.parseLong(max));
114 }
115
116 max = getInitParameter("inputBufferSize");
117 if (max != null)
118 {
119 policy.setInputBufferSize(Integer.parseInt(max));
120 }
121
122 WebSocketServletFactory baseFactory;
123 Iterator<WebSocketServletFactory> factories = ServiceLoader.load(WebSocketServletFactory.class).iterator();
124
125 if (factories.hasNext())
126 {
127 baseFactory = factories.next();
128 }
129 else
130 {
131 // Load the default class if ServiceLoader mechanism isn't valid in this environment. (such as OSGi)
132 ClassLoader loader = Thread.currentThread().getContextClassLoader();
133 @SuppressWarnings("unchecked")
134 Class<WebSocketServletFactory> wssf = (Class<WebSocketServletFactory>)loader
135 .loadClass("org.eclipse.jetty.websocket.server.WebSocketServerFactory");
136 baseFactory = wssf.newInstance();
137 }
138
139 factory = baseFactory.createFactory(policy);
140
141 configure(factory);
142
143 factory.init();
144 }
145 catch (Exception x)
146 {
147 throw new ServletException(x);
148 }
149 }
150
151 /**
152 * @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
153 */
154 @Override
155 protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
156 {
157 if (factory.isUpgradeRequest(request,response))
158 {
159 // We have an upgrade request
160 if (factory.acceptWebSocket(request,response))
161 {
162 // We have a socket instance created
163 return;
164 }
165 // If we reach this point, it means we had an incoming request to upgrade
166 // but it was either not a proper websocket upgrade, or it was possibly rejected
167 // due to incoming request constraints (controlled by WebSocketCreator)
168 if (response.isCommitted())
169 {
170 // not much we can do at this point.
171 return;
172 }
173 }
174
175 // All other processing
176 super.service(request,response);
177 }
178 }