View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.web.socket.sockjs.transport.handler;
18  
19  import java.io.IOException;
20  import java.io.UnsupportedEncodingException;
21  
22  import org.springframework.http.MediaType;
23  import org.springframework.http.server.ServerHttpRequest;
24  import org.springframework.http.server.ServerHttpResponse;
25  import org.springframework.util.MultiValueMap;
26  import org.springframework.util.StringUtils;
27  import org.springframework.web.socket.WebSocketHandler;
28  import org.springframework.web.socket.sockjs.SockJsException;
29  import org.springframework.web.socket.sockjs.frame.SockJsFrame;
30  import org.springframework.web.socket.sockjs.frame.SockJsFrameFormat;
31  import org.springframework.web.socket.sockjs.transport.SockJsSession;
32  import org.springframework.web.socket.sockjs.transport.SockJsSessionFactory;
33  import org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession;
34  import org.springframework.web.util.UriComponentsBuilder;
35  import org.springframework.web.util.UriUtils;
36  
37  /**
38   * Base class for HTTP transport handlers that push messages to connected clients.
39   *
40   * @author Rossen Stoyanchev
41   * @since 4.0
42   */
43  public abstract class AbstractHttpSendingTransportHandler extends AbstractTransportHandler
44  		implements SockJsSessionFactory {
45  
46  	@Override
47  	public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
48  			WebSocketHandler wsHandler, SockJsSession wsSession) throws SockJsException {
49  
50  		AbstractHttpSockJsSession sockJsSession = (AbstractHttpSockJsSession) wsSession;
51  
52  		String protocol = null;  // https://github.com/sockjs/sockjs-client/issues/130
53  		sockJsSession.setAcceptedProtocol(protocol);
54  
55  		// Set content type before writing
56  		response.getHeaders().setContentType(getContentType());
57  
58  		handleRequestInternal(request, response, sockJsSession);
59  	}
60  
61  	protected void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response,
62  			AbstractHttpSockJsSession sockJsSession) throws SockJsException {
63  
64  		if (sockJsSession.isNew()) {
65  			if (logger.isDebugEnabled()) {
66  				logger.debug(request.getMethod() + " " + request.getURI());
67  			}
68  			sockJsSession.handleInitialRequest(request, response, getFrameFormat(request));
69  		}
70  		else if (sockJsSession.isClosed()) {
71  			if (logger.isDebugEnabled()) {
72  				logger.debug("Connection already closed (but not removed yet) for " + sockJsSession);
73  			}
74  			SockJsFrame frame = SockJsFrame.closeFrameGoAway();
75  			try {
76  				response.getBody().write(frame.getContentBytes());
77  			}
78  			catch (IOException ex) {
79  				throw new SockJsException("Failed to send " + frame, sockJsSession.getId(), ex);
80  			}
81  		}
82  		else if (!sockJsSession.isActive()) {
83  			if (logger.isTraceEnabled()) {
84  				logger.trace("Starting " + getTransportType() + " async request.");
85  			}
86  			sockJsSession.handleSuccessiveRequest(request, response, getFrameFormat(request));
87  		}
88  		else {
89  			if (logger.isDebugEnabled()) {
90  				logger.debug("Another " + getTransportType() + " connection still open for " + sockJsSession);
91  			}
92  			String formattedFrame = getFrameFormat(request).format(SockJsFrame.closeFrameAnotherConnectionOpen());
93  			try {
94  				response.getBody().write(formattedFrame.getBytes(SockJsFrame.CHARSET));
95  			}
96  			catch (IOException ex) {
97  				throw new SockJsException("Failed to send " + formattedFrame, sockJsSession.getId(), ex);
98  			}
99  		}
100 	}
101 
102 
103 	protected abstract MediaType getContentType();
104 
105 	protected abstract SockJsFrameFormat getFrameFormat(ServerHttpRequest request);
106 
107 
108 	protected final String getCallbackParam(ServerHttpRequest request) {
109 		String query = request.getURI().getQuery();
110 		MultiValueMap<String, String> params = UriComponentsBuilder.newInstance().query(query).build().getQueryParams();
111 		String value = params.getFirst("c");
112 		try {
113 			return (!StringUtils.isEmpty(value) ? UriUtils.decode(value, "UTF-8") : null);
114 		}
115 		catch (UnsupportedEncodingException ex) {
116 			// should never happen
117 			throw new SockJsException("Unable to decode callback query parameter", null, ex);
118 		}
119 	}
120 
121 }