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