001/*
002 * Copyright 2002-2015 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.socket.sockjs.support;
018
019import java.io.IOException;
020import javax.servlet.ServletContext;
021import javax.servlet.ServletException;
022import javax.servlet.http.HttpServletRequest;
023import javax.servlet.http.HttpServletResponse;
024
025import org.springframework.context.Lifecycle;
026import org.springframework.http.server.ServerHttpRequest;
027import org.springframework.http.server.ServerHttpResponse;
028import org.springframework.http.server.ServletServerHttpRequest;
029import org.springframework.http.server.ServletServerHttpResponse;
030import org.springframework.util.Assert;
031import org.springframework.web.HttpRequestHandler;
032import org.springframework.web.context.ServletContextAware;
033import org.springframework.web.cors.CorsConfiguration;
034import org.springframework.web.cors.CorsConfigurationSource;
035import org.springframework.web.servlet.HandlerMapping;
036import org.springframework.web.socket.WebSocketHandler;
037import org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator;
038import org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator;
039import org.springframework.web.socket.sockjs.SockJsException;
040import org.springframework.web.socket.sockjs.SockJsService;
041
042/**
043 * An {@link HttpRequestHandler} that allows mapping a {@link SockJsService} to requests
044 * in a Servlet container.
045 *
046 * @author Rossen Stoyanchev
047 * @author Sebastien Deleuze
048 * @since 4.0
049 */
050public class SockJsHttpRequestHandler
051                implements HttpRequestHandler, CorsConfigurationSource, Lifecycle, ServletContextAware {
052
053        // No logging: HTTP transports too verbose and we don't know enough to log anything of value
054
055        private final SockJsService sockJsService;
056
057        private final WebSocketHandler webSocketHandler;
058
059        private volatile boolean running = false;
060
061
062        /**
063         * Create a new SockJsHttpRequestHandler.
064         * @param sockJsService the SockJS service
065         * @param webSocketHandler the websocket handler
066         */
067        public SockJsHttpRequestHandler(SockJsService sockJsService, WebSocketHandler webSocketHandler) {
068                Assert.notNull(sockJsService, "SockJsService must not be null");
069                Assert.notNull(webSocketHandler, "WebSocketHandler must not be null");
070                this.sockJsService = sockJsService;
071                this.webSocketHandler =
072                                new ExceptionWebSocketHandlerDecorator(new LoggingWebSocketHandlerDecorator(webSocketHandler));
073        }
074
075
076        /**
077         * Return the {@link SockJsService}.
078         */
079        public SockJsService getSockJsService() {
080                return this.sockJsService;
081        }
082
083        /**
084         * Return the {@link WebSocketHandler}.
085         */
086        public WebSocketHandler getWebSocketHandler() {
087                return this.webSocketHandler;
088        }
089
090        @Override
091        public void setServletContext(ServletContext servletContext) {
092                if (this.sockJsService instanceof ServletContextAware) {
093                        ((ServletContextAware) this.sockJsService).setServletContext(servletContext);
094                }
095        }
096
097
098        @Override
099        public void start() {
100                if (!isRunning()) {
101                        this.running = true;
102                        if (this.sockJsService instanceof Lifecycle) {
103                                ((Lifecycle) this.sockJsService).start();
104                        }
105                }
106        }
107
108        @Override
109        public void stop() {
110                if (isRunning()) {
111                        this.running = false;
112                        if (this.sockJsService instanceof Lifecycle) {
113                                ((Lifecycle) this.sockJsService).stop();
114                        }
115                }
116        }
117
118        @Override
119        public boolean isRunning() {
120                return this.running;
121        }
122
123
124        @Override
125        public void handleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
126                        throws ServletException, IOException {
127
128                ServerHttpRequest request = new ServletServerHttpRequest(servletRequest);
129                ServerHttpResponse response = new ServletServerHttpResponse(servletResponse);
130
131                try {
132                        this.sockJsService.handleRequest(request, response, getSockJsPath(servletRequest), this.webSocketHandler);
133                }
134                catch (Throwable ex) {
135                        throw new SockJsException("Uncaught failure in SockJS request, uri=" + request.getURI(), ex);
136                }
137        }
138
139        private String getSockJsPath(HttpServletRequest servletRequest) {
140                String attribute = HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE;
141                String path = (String) servletRequest.getAttribute(attribute);
142                return (path.length() > 0 && path.charAt(0) != '/' ? "/" + path : path);
143        }
144
145        @Override
146        public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
147                if (this.sockJsService instanceof CorsConfigurationSource) {
148                        return ((CorsConfigurationSource) this.sockJsService).getCorsConfiguration(request);
149                }
150                return null;
151        }
152
153}