All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.websocket.server.JettyWebSocketServlet Maven / Gradle / Ivy

There is a newer version: 11.0.24
Show newest version
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.websocket.server;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.time.Duration;
import java.util.Set;

import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.websocket.core.Configuration;
import org.eclipse.jetty.websocket.core.WebSocketComponents;
import org.eclipse.jetty.websocket.core.server.FrameHandlerFactory;
import org.eclipse.jetty.websocket.core.server.ServerUpgradeRequest;
import org.eclipse.jetty.websocket.core.server.ServerUpgradeResponse;
import org.eclipse.jetty.websocket.core.server.WebSocketCreator;
import org.eclipse.jetty.websocket.core.server.WebSocketMappings;
import org.eclipse.jetty.websocket.core.server.WebSocketServerComponents;
import org.eclipse.jetty.websocket.server.internal.DelegatedServerUpgradeRequest;
import org.eclipse.jetty.websocket.server.internal.DelegatedServerUpgradeResponse;
import org.eclipse.jetty.websocket.server.internal.JettyServerFrameHandlerFactory;
import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Abstract Servlet used to bridge the Servlet API to the WebSocket API.
 * 

* To use this servlet, you will be required to register your websockets with the {@link WebSocketMappings} so that it can create your websockets under the * appropriate conditions. *

*

The most basic implementation would be as follows:

*
 * package my.example;
 *
 * import JettyWebSocketServlet;
 * import JettyWebSocketServletFactory;
 *
 * public class MyEchoServlet extends JettyWebSocketServlet
 * {
 *     @Override
 *     public void configure(JettyWebSocketServletFactory factory)
 *     {
 *       factory.setDefaultMaxFrameSize(4096);
 *       factory.addMapping(factory.parsePathSpec("/"), (req,res)->new EchoSocket());
 *     }
 * }
 * 
*

* Only request that conforms to a "WebSocket: Upgrade" handshake request will trigger the {@link WebSocketMappings} handling of creating * WebSockets. All other requests are treated as normal servlet requests. The configuration defined by this servlet init parameters will * be used as the customizer for any mappings created by {@link JettyWebSocketServletFactory#addMapping(String, JettyWebSocketCreator)} during * {@link #configure(JettyWebSocketServletFactory)} calls. The request upgrade may be peformed by this servlet, or is may be performed by a * {@link WebSocketUpgradeFilter} instance that will share the same {@link WebSocketMappings} instance. If the filter is used, then the * filter configuraton is used as the default configuration prior to this servlets configuration being applied. *

*

* Configuration / Init-Parameters: *

*
*
idleTimeout
*
set the time in ms that a websocket may be idle before closing
*
maxTextMessageSize
*
set the size in UTF-8 bytes that a websocket may be accept as a Text Message before closing
*
maxBinaryMessageSize
*
set the size in bytes that a websocket may be accept as a Binary Message before closing
*
inputBufferSize
*
set the size in bytes of the buffer used to read raw bytes from the network layer
*
outputBufferSize
*
set the size in bytes of the buffer used to write bytes to the network layer
*
maxFrameSize
*
The maximum frame size sent or received.
*
autoFragment
*
If true, frames are automatically fragmented to respect the maximum frame size.
*
*/ public abstract class JettyWebSocketServlet extends HttpServlet { private static final Logger LOG = LoggerFactory.getLogger(JettyWebSocketServlet.class); private final CustomizedWebSocketServletFactory customizer = new CustomizedWebSocketServletFactory(); private WebSocketMappings mapping; private WebSocketComponents components; /** * Configure the JettyWebSocketServletFactory for this servlet instance by setting default * configuration (which may be overriden by annotations) and mapping {@link JettyWebSocketCreator}s. * This method assumes a single {@link FrameHandlerFactory} will be available as a bean on the * {@link ContextHandler}, which in practise will mostly the the Jetty WebSocket API factory. * * @param factory the JettyWebSocketServletFactory */ protected abstract void configure(JettyWebSocketServletFactory factory); /** * @return the instance of {@link FrameHandlerFactory} to be used to create the FrameHandler */ private FrameHandlerFactory getFactory() { JettyServerFrameHandlerFactory frameHandlerFactory = JettyServerFrameHandlerFactory.getFactory(getServletContext()); if (frameHandlerFactory == null) throw new IllegalStateException("JettyServerFrameHandlerFactory not found"); return frameHandlerFactory; } @Override public void init() throws ServletException { try { ServletContext servletContext = getServletContext(); components = WebSocketServerComponents.getWebSocketComponents(servletContext); mapping = new WebSocketMappings(components); String max = getInitParameter("idleTimeout"); if (max == null) { max = getInitParameter("maxIdleTime"); if (max != null) LOG.warn("'maxIdleTime' init param is deprecated, use 'idleTimeout' instead"); } if (max != null) customizer.setIdleTimeout(Duration.ofMillis(Long.parseLong(max))); max = getInitParameter("maxTextMessageSize"); if (max != null) customizer.setMaxTextMessageSize(Long.parseLong(max)); max = getInitParameter("maxBinaryMessageSize"); if (max != null) customizer.setMaxBinaryMessageSize(Long.parseLong(max)); max = getInitParameter("inputBufferSize"); if (max != null) customizer.setInputBufferSize(Integer.parseInt(max)); max = getInitParameter("outputBufferSize"); if (max != null) customizer.setOutputBufferSize(Integer.parseInt(max)); max = getInitParameter("maxFrameSize"); if (max == null) max = getInitParameter("maxAllowedFrameSize"); if (max != null) customizer.setMaxFrameSize(Long.parseLong(max)); String autoFragment = getInitParameter("autoFragment"); if (autoFragment != null) customizer.setAutoFragment(Boolean.parseBoolean(autoFragment)); configure(customizer); // Let user modify customizer prior after init params } catch (Throwable x) { throw new ServletException(x); } } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // provide a null default customizer the customizer will be on the negotiator in the mapping if (mapping.upgrade(req, resp, null)) return; // If we reach this point, it means we had an incoming request to upgrade // but it was either not a proper websocket upgrade, or it was possibly rejected // due to incoming request constraints (controlled by WebSocketCreator) if (resp.isCommitted()) return; // Handle normally super.service(req, resp); } private class CustomizedWebSocketServletFactory extends Configuration.ConfigurationCustomizer implements JettyWebSocketServletFactory { @Override public Set getAvailableExtensionNames() { return components.getExtensionRegistry().getAvailableExtensionNames(); } @Override public void addMapping(String pathSpec, JettyWebSocketCreator creator) { mapping.addMapping(WebSocketMappings.parsePathSpec(pathSpec), new WrappedJettyCreator(creator), getFactory(), this); } @Override public void register(Class endpointClass) { Constructor constructor; try { constructor = endpointClass.getDeclaredConstructor(); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } JettyWebSocketCreator creator = (req, resp) -> { try { return constructor.newInstance(); } catch (Throwable t) { t.printStackTrace(); return null; } }; addMapping("/", creator); } @Override public void setCreator(JettyWebSocketCreator creator) { addMapping("/", creator); } @Override public JettyWebSocketCreator getMapping(String pathSpec) { WebSocketCreator creator = mapping.getWebSocketCreator(WebSocketMappings.parsePathSpec(pathSpec)); if (creator instanceof WrappedJettyCreator) return ((WrappedJettyCreator)creator).getJettyWebSocketCreator(); return null; } @Override public boolean removeMapping(String pathSpec) { return mapping.removeMapping(WebSocketMappings.parsePathSpec(pathSpec)); } } private static class WrappedJettyCreator implements WebSocketCreator { private final JettyWebSocketCreator creator; private WrappedJettyCreator(JettyWebSocketCreator creator) { this.creator = creator; } private JettyWebSocketCreator getJettyWebSocketCreator() { return creator; } @Override public Object createWebSocket(ServerUpgradeRequest req, ServerUpgradeResponse resp) { return creator.createWebSocket(new DelegatedServerUpgradeRequest(req), new DelegatedServerUpgradeResponse(resp)); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy