001/* 002 * Copyright 2002-2013 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; 018 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025 026import org.springframework.http.HttpHeaders; 027import org.springframework.util.CollectionUtils; 028 029/** 030 * An {@link org.springframework.http.HttpHeaders} variant that adds support for 031 * the HTTP headers defined by the WebSocket specification RFC 6455. 032 * 033 * @author Rossen Stoyanchev 034 * @since 4.0 035 */ 036public class WebSocketHttpHeaders extends HttpHeaders { 037 038 public static final String SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept"; 039 040 public static final String SEC_WEBSOCKET_EXTENSIONS = "Sec-WebSocket-Extensions"; 041 042 public static final String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key"; 043 044 public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol"; 045 046 public static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version"; 047 048 private static final long serialVersionUID = -6644521016187828916L; 049 050 051 private final HttpHeaders headers; 052 053 054 /** 055 * Create a new instance. 056 */ 057 public WebSocketHttpHeaders() { 058 this(new HttpHeaders(), false); 059 } 060 061 /** 062 * Create an instance that wraps the given pre-existing HttpHeaders and also 063 * propagate all changes to it. 064 * @param headers the HTTP headers to wrap 065 */ 066 public WebSocketHttpHeaders(HttpHeaders headers) { 067 this(headers, false); 068 } 069 070 /** 071 * Private constructor that can create read-only {@code WebSocketHttpHeader} instances. 072 */ 073 private WebSocketHttpHeaders(HttpHeaders headers, boolean readOnly) { 074 this.headers = readOnly ? HttpHeaders.readOnlyHttpHeaders(headers) : headers; 075 } 076 077 /** 078 * Returns {@code WebSocketHttpHeaders} object that can only be read, not written to. 079 */ 080 public static WebSocketHttpHeaders readOnlyWebSocketHttpHeaders(WebSocketHttpHeaders headers) { 081 return new WebSocketHttpHeaders(headers, true); 082 } 083 084 085 /** 086 * Sets the (new) value of the {@code Sec-WebSocket-Accept} header. 087 * @param secWebSocketAccept the value of the header 088 */ 089 public void setSecWebSocketAccept(String secWebSocketAccept) { 090 set(SEC_WEBSOCKET_ACCEPT, secWebSocketAccept); 091 } 092 093 /** 094 * Returns the value of the {@code Sec-WebSocket-Accept} header. 095 * @return the value of the header 096 */ 097 public String getSecWebSocketAccept() { 098 return getFirst(SEC_WEBSOCKET_ACCEPT); 099 } 100 101 /** 102 * Returns the value of the {@code Sec-WebSocket-Extensions} header. 103 * @return the value of the header 104 */ 105 public List<WebSocketExtension> getSecWebSocketExtensions() { 106 List<String> values = get(SEC_WEBSOCKET_EXTENSIONS); 107 if (CollectionUtils.isEmpty(values)) { 108 return Collections.emptyList(); 109 } 110 else { 111 List<WebSocketExtension> result = new ArrayList<WebSocketExtension>(values.size()); 112 for (String value : values) { 113 result.addAll(WebSocketExtension.parseExtensions(value)); 114 } 115 return result; 116 } 117 } 118 119 /** 120 * Sets the (new) value(s) of the {@code Sec-WebSocket-Extensions} header. 121 * @param extensions the values for the header 122 */ 123 public void setSecWebSocketExtensions(List<WebSocketExtension> extensions) { 124 List<String> result = new ArrayList<String>(extensions.size()); 125 for (WebSocketExtension extension : extensions) { 126 result.add(extension.toString()); 127 } 128 set(SEC_WEBSOCKET_EXTENSIONS, toCommaDelimitedString(result)); 129 } 130 131 /** 132 * Sets the (new) value of the {@code Sec-WebSocket-Key} header. 133 * @param secWebSocketKey the value of the header 134 */ 135 public void setSecWebSocketKey(String secWebSocketKey) { 136 set(SEC_WEBSOCKET_KEY, secWebSocketKey); 137 } 138 139 /** 140 * Returns the value of the {@code Sec-WebSocket-Key} header. 141 * @return the value of the header 142 */ 143 public String getSecWebSocketKey() { 144 return getFirst(SEC_WEBSOCKET_KEY); 145 } 146 147 /** 148 * Sets the (new) value of the {@code Sec-WebSocket-Protocol} header. 149 * @param secWebSocketProtocol the value of the header 150 */ 151 public void setSecWebSocketProtocol(String secWebSocketProtocol) { 152 if (secWebSocketProtocol != null) { 153 set(SEC_WEBSOCKET_PROTOCOL, secWebSocketProtocol); 154 } 155 } 156 157 /** 158 * Sets the (new) value of the {@code Sec-WebSocket-Protocol} header. 159 * @param secWebSocketProtocols the value of the header 160 */ 161 public void setSecWebSocketProtocol(List<String> secWebSocketProtocols) { 162 set(SEC_WEBSOCKET_PROTOCOL, toCommaDelimitedString(secWebSocketProtocols)); 163 } 164 165 /** 166 * Returns the value of the {@code Sec-WebSocket-Key} header. 167 * @return the value of the header 168 */ 169 public List<String> getSecWebSocketProtocol() { 170 List<String> values = get(SEC_WEBSOCKET_PROTOCOL); 171 if (CollectionUtils.isEmpty(values)) { 172 return Collections.emptyList(); 173 } 174 else if (values.size() == 1) { 175 return getValuesAsList(SEC_WEBSOCKET_PROTOCOL); 176 } 177 else { 178 return values; 179 } 180 } 181 182 /** 183 * Sets the (new) value of the {@code Sec-WebSocket-Version} header. 184 * @param secWebSocketVersion the value of the header 185 */ 186 public void setSecWebSocketVersion(String secWebSocketVersion) { 187 set(SEC_WEBSOCKET_VERSION, secWebSocketVersion); 188 } 189 190 /** 191 * Returns the value of the {@code Sec-WebSocket-Version} header. 192 * @return the value of the header 193 */ 194 public String getSecWebSocketVersion() { 195 return getFirst(SEC_WEBSOCKET_VERSION); 196 } 197 198 199 // Single string methods 200 201 /** 202 * Return the first header value for the given header name, if any. 203 * @param headerName the header name 204 * @return the first header value; or {@code null} 205 */ 206 @Override 207 public String getFirst(String headerName) { 208 return this.headers.getFirst(headerName); 209 } 210 211 /** 212 * Add the given, single header value under the given name. 213 * @param headerName the header name 214 * @param headerValue the header value 215 * @throws UnsupportedOperationException if adding headers is not supported 216 * @see #put(String, List) 217 * @see #set(String, String) 218 */ 219 @Override 220 public void add(String headerName, String headerValue) { 221 this.headers.add(headerName, headerValue); 222 } 223 224 /** 225 * Set the given, single header value under the given name. 226 * @param headerName the header name 227 * @param headerValue the header value 228 * @throws UnsupportedOperationException if adding headers is not supported 229 * @see #put(String, List) 230 * @see #add(String, String) 231 */ 232 @Override 233 public void set(String headerName, String headerValue) { 234 this.headers.set(headerName, headerValue); 235 } 236 237 @Override 238 public void setAll(Map<String, String> values) { 239 this.headers.setAll(values); 240 } 241 242 @Override 243 public Map<String, String> toSingleValueMap() { 244 return this.headers.toSingleValueMap(); 245 } 246 247 // Map implementation 248 249 @Override 250 public int size() { 251 return this.headers.size(); 252 } 253 254 @Override 255 public boolean isEmpty() { 256 return this.headers.isEmpty(); 257 } 258 259 @Override 260 public boolean containsKey(Object key) { 261 return this.headers.containsKey(key); 262 } 263 264 @Override 265 public boolean containsValue(Object value) { 266 return this.headers.containsValue(value); 267 } 268 269 @Override 270 public List<String> get(Object key) { 271 return this.headers.get(key); 272 } 273 274 @Override 275 public List<String> put(String key, List<String> value) { 276 return this.headers.put(key, value); 277 } 278 279 @Override 280 public List<String> remove(Object key) { 281 return this.headers.remove(key); 282 } 283 284 @Override 285 public void putAll(Map<? extends String, ? extends List<String>> m) { 286 this.headers.putAll(m); 287 } 288 289 @Override 290 public void clear() { 291 this.headers.clear(); 292 } 293 294 @Override 295 public Set<String> keySet() { 296 return this.headers.keySet(); 297 } 298 299 @Override 300 public Collection<List<String>> values() { 301 return this.headers.values(); 302 } 303 304 @Override 305 public Set<Entry<String, List<String>>> entrySet() { 306 return this.headers.entrySet(); 307 } 308 309 310 @Override 311 public boolean equals(Object other) { 312 if (this == other) { 313 return true; 314 } 315 if (!(other instanceof WebSocketHttpHeaders)) { 316 return false; 317 } 318 WebSocketHttpHeaders otherHeaders = (WebSocketHttpHeaders) other; 319 return this.headers.equals(otherHeaders.headers); 320 } 321 322 @Override 323 public int hashCode() { 324 return this.headers.hashCode(); 325 } 326 327 @Override 328 public String toString() { 329 return this.headers.toString(); 330 } 331 332}