001/* 002 * Copyright 2002-2016 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.transport.handler; 018 019import java.io.IOException; 020import java.util.Arrays; 021 022import org.springframework.http.HttpStatus; 023import org.springframework.http.MediaType; 024import org.springframework.http.server.ServerHttpRequest; 025import org.springframework.http.server.ServerHttpResponse; 026import org.springframework.util.Assert; 027import org.springframework.web.socket.WebSocketHandler; 028import org.springframework.web.socket.sockjs.SockJsException; 029import org.springframework.web.socket.sockjs.transport.SockJsSession; 030import org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession; 031 032/** 033 * Base class for HTTP transport handlers that receive messages via HTTP POST. 034 * 035 * @author Rossen Stoyanchev 036 * @since 4.0 037 */ 038public abstract class AbstractHttpReceivingTransportHandler extends AbstractTransportHandler { 039 040 @Override 041 public boolean checkSessionType(SockJsSession session) { 042 return (session instanceof AbstractHttpSockJsSession); 043 } 044 045 @Override 046 public final void handleRequest(ServerHttpRequest request, ServerHttpResponse response, 047 WebSocketHandler wsHandler, SockJsSession wsSession) throws SockJsException { 048 049 Assert.notNull(wsSession, "No session"); 050 AbstractHttpSockJsSession sockJsSession = (AbstractHttpSockJsSession) wsSession; 051 052 handleRequestInternal(request, response, wsHandler, sockJsSession); 053 } 054 055 protected void handleRequestInternal(ServerHttpRequest request, ServerHttpResponse response, 056 WebSocketHandler wsHandler, AbstractHttpSockJsSession sockJsSession) throws SockJsException { 057 058 String[] messages; 059 try { 060 messages = readMessages(request); 061 } 062 catch (IOException ex) { 063 logger.error("Failed to read message", ex); 064 if (ex.getClass().getName().contains("Mapping")) { 065 // e.g. Jackson's JsonMappingException, indicating an incomplete payload 066 handleReadError(response, "Payload expected.", sockJsSession.getId()); 067 } 068 else { 069 handleReadError(response, "Broken JSON encoding.", sockJsSession.getId()); 070 } 071 return; 072 } 073 catch (Throwable ex) { 074 logger.error("Failed to read message", ex); 075 handleReadError(response, "Failed to read message(s)", sockJsSession.getId()); 076 return; 077 } 078 if (messages == null) { 079 handleReadError(response, "Payload expected.", sockJsSession.getId()); 080 return; 081 } 082 if (logger.isTraceEnabled()) { 083 logger.trace("Received message(s): " + Arrays.asList(messages)); 084 } 085 response.setStatusCode(getResponseStatus()); 086 response.getHeaders().setContentType(new MediaType("text", "plain", UTF8_CHARSET)); 087 088 sockJsSession.delegateMessages(messages); 089 } 090 091 private void handleReadError(ServerHttpResponse response, String error, String sessionId) { 092 try { 093 response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); 094 response.getBody().write(error.getBytes(UTF8_CHARSET)); 095 } 096 catch (IOException ex) { 097 throw new SockJsException("Failed to send error: " + error, sessionId, ex); 098 } 099 } 100 101 102 protected abstract String[] readMessages(ServerHttpRequest request) throws IOException; 103 104 protected abstract HttpStatus getResponseStatus(); 105 106}