001/* 002 * Copyright 2002-2020 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 */ 016package org.springframework.web.reactive.socket; 017 018import java.nio.charset.Charset; 019import java.nio.charset.StandardCharsets; 020 021import org.springframework.core.io.buffer.DataBuffer; 022import org.springframework.core.io.buffer.DataBufferUtils; 023import org.springframework.lang.Nullable; 024import org.springframework.util.Assert; 025import org.springframework.util.ObjectUtils; 026 027/** 028 * Representation of a WebSocket message. 029 * 030 * <p>See static factory methods in {@link WebSocketSession} for creating messages with 031 * the {@link org.springframework.core.io.buffer.DataBufferFactory} for the session. 032 * 033 * @author Rossen Stoyanchev 034 * @since 5.0 035 */ 036public class WebSocketMessage { 037 038 private final Type type; 039 040 private final DataBuffer payload; 041 042 043 /** 044 * Constructor for a WebSocketMessage. 045 * <p>See static factory methods in {@link WebSocketSession} or alternatively 046 * use {@link WebSocketSession#bufferFactory()} to create the payload and 047 * then invoke this constructor. 048 */ 049 public WebSocketMessage(Type type, DataBuffer payload) { 050 Assert.notNull(type, "'type' must not be null"); 051 Assert.notNull(payload, "'payload' must not be null"); 052 this.type = type; 053 this.payload = payload; 054 } 055 056 057 /** 058 * Return the message type (text, binary, etc). 059 */ 060 public Type getType() { 061 return this.type; 062 } 063 064 /** 065 * Return the message payload. 066 */ 067 public DataBuffer getPayload() { 068 return this.payload; 069 } 070 071 /** 072 * A variant of {@link #getPayloadAsText(Charset)} that uses {@code UTF-8} 073 * for decoding the raw content to text. 074 */ 075 public String getPayloadAsText() { 076 return getPayloadAsText(StandardCharsets.UTF_8); 077 } 078 079 /** 080 * A shortcut for decoding the raw content of the message to text with the 081 * given character encoding. This is useful for text WebSocket messages, or 082 * otherwise when the payload is expected to contain text. 083 * @param charset the character encoding 084 * @since 5.0.5 085 */ 086 public String getPayloadAsText(Charset charset) { 087 return this.payload.toString(charset); 088 } 089 090 /** 091 * Retain the data buffer for the message payload, which is useful on 092 * runtimes (e.g. Netty) with pooled buffers. A shortcut for: 093 * <pre> 094 * DataBuffer payload = message.getPayload(); 095 * DataBufferUtils.retain(payload); 096 * </pre> 097 * @see DataBufferUtils#retain(DataBuffer) 098 */ 099 public WebSocketMessage retain() { 100 DataBufferUtils.retain(this.payload); 101 return this; 102 } 103 104 /** 105 * Release the payload {@code DataBuffer} which is useful on runtimes 106 * (e.g. Netty) with pooled buffers such as Netty. A shortcut for: 107 * <pre> 108 * DataBuffer payload = message.getPayload(); 109 * DataBufferUtils.release(payload); 110 * </pre> 111 * @see DataBufferUtils#release(DataBuffer) 112 */ 113 public void release() { 114 DataBufferUtils.release(this.payload); 115 } 116 117 118 @Override 119 public boolean equals(@Nullable Object other) { 120 if (this == other) { 121 return true; 122 } 123 if (!(other instanceof WebSocketMessage)) { 124 return false; 125 } 126 WebSocketMessage otherMessage = (WebSocketMessage) other; 127 return (this.type.equals(otherMessage.type) && 128 ObjectUtils.nullSafeEquals(this.payload, otherMessage.payload)); 129 } 130 131 @Override 132 public int hashCode() { 133 return this.type.hashCode() * 29 + this.payload.hashCode(); 134 } 135 136 @Override 137 public String toString() { 138 return "WebSocket " + this.type.name() + " message (" + this.payload.readableByteCount() + " bytes)"; 139 } 140 141 142 /** 143 * WebSocket message types. 144 */ 145 public enum Type { 146 /** 147 * Text WebSocket message. 148 */ 149 TEXT, 150 /** 151 * Binary WebSocket message. 152 */ 153 BINARY, 154 /** 155 * WebSocket ping. 156 */ 157 PING, 158 /** 159 * WebSocket pong. 160 */ 161 PONG 162 } 163 164}