Skip to content

Commit 637b638

Browse files
committed
Starting point for reactive WebSocket support
Includes basic abstractions and an RxNetty support to start. Issue: SPR-14527
1 parent 8662b77 commit 637b638

20 files changed

+1380
-5
lines changed

build.gradle

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,11 @@ project("spring-web-reactive") {
802802
optional("org.freemarker:freemarker:${freemarkerVersion}")
803803
optional "org.apache.httpcomponents:httpclient:${httpclientVersion}"
804804
optional('org.webjars:webjars-locator:0.32')
805+
optional("io.reactivex:rxnetty-http:${rxnettyVersion}") {
806+
exclude group: 'io.reactivex', module: 'rxjava'
807+
}
808+
optional("io.reactivex:rxjava:${rxjavaVersion}")
809+
optional("io.reactivex:rxjava-reactive-streams:${rxjavaAdapterVersion}")
805810
testCompile("io.projectreactor.addons:reactor-test:${reactorCoreVersion}")
806811
testCompile("javax.validation:validation-api:${beanvalVersion}")
807812
testCompile("org.hibernate:hibernate-validator:${hibval5Version}")
@@ -810,12 +815,7 @@ project("spring-web-reactive") {
810815
testCompile("org.eclipse.jetty:jetty-server:${jettyVersion}")
811816
testCompile("org.eclipse.jetty:jetty-servlet:${jettyVersion}")
812817
testCompile("io.projectreactor.ipc:reactor-netty:${reactorNettyVersion}")
813-
testCompile("io.reactivex:rxnetty-http:${rxnettyVersion}") {
814-
exclude group: 'io.reactivex', module: 'rxjava'
815-
}
816-
testCompile("io.reactivex:rxjava:${rxjavaVersion}")
817818
testCompile "io.reactivex.rxjava2:rxjava:${rxjava2Version}"
818-
testCompile("io.reactivex:rxjava-reactive-streams:${rxjavaAdapterVersion}")
819819
testCompile("io.undertow:undertow-core:${undertowVersion}")
820820
testCompile("org.jboss.xnio:xnio-api:${xnioVersion}")
821821
testCompile("com.fasterxml:aalto-xml:1.0.0")
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
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.reactive.socket;
18+
19+
import org.springframework.util.Assert;
20+
import org.springframework.util.ObjectUtils;
21+
22+
/**
23+
* Representation of WebSocket "close" status codes and reasons. Status codes
24+
* in the 1xxx range are pre-defined by the protocol.
25+
*
26+
* <p>See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">
27+
* RFC 6455, Section 7.4.1 "Defined Status Codes"</a>.
28+
*
29+
* @author Rossen Stoyanchev
30+
* @since 5.0
31+
*/
32+
public final class CloseStatus {
33+
34+
/**
35+
* "1000 indicates a normal closure, meaning that the purpose for which the connection
36+
* was established has been fulfilled."
37+
*/
38+
public static final CloseStatus NORMAL = new CloseStatus(1000);
39+
40+
/**
41+
* "1001 indicates that an endpoint is "going away", such as a server going down or a
42+
* browser having navigated away from a page."
43+
*/
44+
public static final CloseStatus GOING_AWAY = new CloseStatus(1001);
45+
46+
/**
47+
* "1002 indicates that an endpoint is terminating the connection due to a protocol
48+
* error."
49+
*/
50+
public static final CloseStatus PROTOCOL_ERROR = new CloseStatus(1002);
51+
52+
/**
53+
* "1003 indicates that an endpoint is terminating the connection because it has
54+
* received a type of data it cannot accept (e.g., an endpoint that understands only
55+
* text data MAY send this if it receives a binary message)."
56+
*/
57+
public static final CloseStatus NOT_ACCEPTABLE = new CloseStatus(1003);
58+
59+
// 10004: Reserved.
60+
// The specific meaning might be defined in the future.
61+
62+
/**
63+
* "1005 is a reserved value and MUST NOT be set as a status code in a Close control
64+
* frame by an endpoint. It is designated for use in applications expecting a status
65+
* code to indicate that no status code was actually present."
66+
*/
67+
public static final CloseStatus NO_STATUS_CODE = new CloseStatus(1005);
68+
69+
/**
70+
* "1006 is a reserved value and MUST NOT be set as a status code in a Close control
71+
* frame by an endpoint. It is designated for use in applications expecting a status
72+
* code to indicate that the connection was closed abnormally, e.g., without sending
73+
* or receiving a Close control frame."
74+
*/
75+
public static final CloseStatus NO_CLOSE_FRAME = new CloseStatus(1006);
76+
77+
/**
78+
* "1007 indicates that an endpoint is terminating the connection because it has
79+
* received data within a message that was not consistent with the type of the message
80+
* (e.g., non-UTF-8 [RFC3629] data within a text message)."
81+
*/
82+
public static final CloseStatus BAD_DATA = new CloseStatus(1007);
83+
84+
/**
85+
* "1008 indicates that an endpoint is terminating the connection because it has
86+
* received a message that violates its policy. This is a generic status code that can
87+
* be returned when there is no other more suitable status code (e.g., 1003 or 1009)
88+
* or if there is a need to hide specific details about the policy."
89+
*/
90+
public static final CloseStatus POLICY_VIOLATION = new CloseStatus(1008);
91+
92+
/**
93+
* "1009 indicates that an endpoint is terminating the connection because it has
94+
* received a message that is too big for it to process."
95+
*/
96+
public static final CloseStatus TOO_BIG_TO_PROCESS = new CloseStatus(1009);
97+
98+
/**
99+
* "1010 indicates that an endpoint (client) is terminating the connection because it
100+
* has expected the server to negotiate one or more extension, but the server didn't
101+
* return them in the response message of the WebSocket handshake. The list of
102+
* extensions that are needed SHOULD appear in the /reason/ part of the Close frame.
103+
* Note that this status code is not used by the server, because it can fail the
104+
* WebSocket handshake instead."
105+
*/
106+
public static final CloseStatus REQUIRED_EXTENSION = new CloseStatus(1010);
107+
108+
/**
109+
* "1011 indicates that a server is terminating the connection because it encountered
110+
* an unexpected condition that prevented it from fulfilling the request."
111+
*/
112+
public static final CloseStatus SERVER_ERROR = new CloseStatus(1011);
113+
114+
/**
115+
* "1012 indicates that the service is restarted. A client may reconnect, and if it
116+
* chooses to do, should reconnect using a randomized delay of 5 - 30s."
117+
*/
118+
public static final CloseStatus SERVICE_RESTARTED = new CloseStatus(1012);
119+
120+
/**
121+
* "1013 indicates that the service is experiencing overload. A client should only
122+
* connect to a different IP (when there are multiple for the target) or reconnect to
123+
* the same IP upon user action."
124+
*/
125+
public static final CloseStatus SERVICE_OVERLOAD = new CloseStatus(1013);
126+
127+
/**
128+
* "1015 is a reserved value and MUST NOT be set as a status code in a Close control
129+
* frame by an endpoint. It is designated for use in applications expecting a status
130+
* code to indicate that the connection was closed due to a failure to perform a TLS
131+
* handshake (e.g., the server certificate can't be verified)."
132+
*/
133+
public static final CloseStatus TLS_HANDSHAKE_FAILURE = new CloseStatus(1015);
134+
135+
136+
private final int code;
137+
138+
private final String reason;
139+
140+
141+
/**
142+
* Create a new {@link CloseStatus} instance.
143+
* @param code the status code
144+
*/
145+
public CloseStatus(int code) {
146+
this(code, null);
147+
}
148+
149+
/**
150+
* Create a new {@link CloseStatus} instance.
151+
* @param code the status code
152+
* @param reason the reason
153+
*/
154+
public CloseStatus(int code, String reason) {
155+
Assert.isTrue((code >= 1000 && code < 5000), "Invalid status code");
156+
this.code = code;
157+
this.reason = reason;
158+
}
159+
160+
161+
/**
162+
* Return the status code.
163+
*/
164+
public int getCode() {
165+
return this.code;
166+
}
167+
168+
/**
169+
* Return the reason, or {@code null} if none.
170+
*/
171+
public String getReason() {
172+
return this.reason;
173+
}
174+
175+
/**
176+
* Create a new {@link CloseStatus} from this one with the specified reason.
177+
* @param reason the reason
178+
* @return a new {@link CloseStatus} instance
179+
*/
180+
public CloseStatus withReason(String reason) {
181+
Assert.hasText(reason, "Reason must not be empty");
182+
return new CloseStatus(this.code, reason);
183+
}
184+
185+
186+
public boolean equalsCode(CloseStatus other) {
187+
return (this.code == other.code);
188+
}
189+
190+
@Override
191+
public boolean equals(Object other) {
192+
if (this == other) {
193+
return true;
194+
}
195+
if (!(other instanceof CloseStatus)) {
196+
return false;
197+
}
198+
CloseStatus otherStatus = (CloseStatus) other;
199+
return (this.code == otherStatus.code &&
200+
ObjectUtils.nullSafeEquals(this.reason, otherStatus.reason));
201+
}
202+
203+
@Override
204+
public int hashCode() {
205+
return this.code * 29 + ObjectUtils.nullSafeHashCode(this.reason);
206+
}
207+
208+
@Override
209+
public String toString() {
210+
return "CloseStatus[code=" + this.code + ", reason=" + this.reason + "]";
211+
}
212+
213+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2002-2016 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+
package org.springframework.web.reactive.socket;
17+
18+
import java.util.Collections;
19+
import java.util.List;
20+
21+
import reactor.core.publisher.Mono;
22+
23+
/**
24+
* Handler for a WebSocket-style session interaction.
25+
*
26+
* @author Rossen Stoyanchev
27+
* @since 5.0
28+
*/
29+
public interface WebSocketHandler {
30+
31+
/**
32+
* Return the list of sub-protocols supported by this handler.
33+
* <p>By default an empty list is returned.
34+
*/
35+
default List<String> getSubProtocols() {
36+
return Collections.emptyList();
37+
}
38+
39+
/**
40+
* Handle the given WebSocket session.
41+
* @param session the session
42+
* @return signals completion for session handling
43+
*/
44+
Mono<Void> handle(WebSocketSession session);
45+
46+
}

0 commit comments

Comments
 (0)