Skip to content

Commit e87837f

Browse files
committed
Decouple the handshake part ServerWebSocket API.
Motivation: The server WebSocket API can control handshake implicitly (e.g. sending a message) or explicitly (accept or any WebSocket interaction). This result in a more complex implementation than it should be for such API. Changes: Extract the handshake API of the ServerWebSocket API in a new ServerWebSocketHandshake API for which an handler can be set when WebSocket handshake needs to be controlled. This is a backport of Vert.x 5 server WebSocket handshake handler. The current API is maintained but deprecated.
1 parent fcfc661 commit e87837f

File tree

9 files changed

+386
-49
lines changed

9 files changed

+386
-49
lines changed

src/main/asciidoc/http.adoc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1907,14 +1907,20 @@ When a WebSocket connection is made to the server, the handler will be called, p
19071907
{@link examples.HTTPExamples#example51}
19081908
----
19091909

1910-
You can choose to reject the WebSocket by calling {@link io.vertx.core.http.ServerWebSocket#reject()}.
1910+
===== Server WebSocket handshake
1911+
1912+
By default, the server accepts any inbound WebSocket.
1913+
1914+
You can set a WebSocket handshake handler to control the outcome of a WebSocket handshake, i.e. accept or reject an incoming WebSocket.
1915+
1916+
You can choose to reject the WebSocket by calling {@link io.vertx.core.http.ServerWebSocketHandshake#accept()} or {@link io.vertx.core.http.ServerWebSocketHandshake#reject()}.
19111917

19121918
[source,$lang]
19131919
----
19141920
{@link examples.HTTPExamples#example52}
19151921
----
19161922

1917-
You can perform an asynchronous handshake by calling {@link io.vertx.core.http.ServerWebSocket#setHandshake} with a `Future`:
1923+
You can perform an asynchronous handshake:
19181924

19191925
[source,$lang]
19201926
----

src/main/java/examples/HTTPExamples.java

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,29 +1047,39 @@ public void example51(HttpServer server) {
10471047

10481048
public void example52(HttpServer server) {
10491049

1050-
server.webSocketHandler(webSocket -> {
1051-
if (webSocket.path().equals("/myapi")) {
1052-
webSocket.reject();
1053-
} else {
1050+
server
1051+
.webSocketHandshakeHandler(handshake -> {
1052+
if (handshake.path().equals("/myapi")) {
1053+
handshake.reject();
1054+
} else {
1055+
handshake.accept();
1056+
}
1057+
})
1058+
.webSocketHandler(webSocket -> {
10541059
// Do something
1055-
}
1056-
});
1060+
});
10571061
}
10581062

10591063
public void exampleAsynchronousHandshake(HttpServer server) {
1060-
server.webSocketHandler(webSocket -> {
1061-
Promise<Integer> promise = Promise.promise();
1062-
webSocket.setHandshake(promise.future());
1063-
authenticate(webSocket.headers(), ar -> {
1064-
if (ar.succeeded()) {
1065-
// Terminate the handshake with the status code 101 (Switching Protocol)
1066-
// Reject the handshake with 401 (Unauthorized)
1067-
promise.complete(ar.result() ? 101 : 401);
1068-
} else {
1069-
// Will send a 500 error
1070-
promise.fail(ar.cause());
1071-
}
1072-
});
1064+
server
1065+
.webSocketHandshakeHandler(handshake -> {
1066+
authenticate(handshake.headers(), ar -> {
1067+
if (ar.succeeded()) {
1068+
if (ar.result()) {
1069+
// Terminate the handshake with the status code 101 (Switching Protocol)
1070+
handshake.accept();
1071+
} else {
1072+
// Reject the handshake with 401 (Unauthorized)
1073+
handshake.reject(401);
1074+
}
1075+
} else {
1076+
// Will send a 500 error
1077+
handshake.reject(500);
1078+
}
1079+
});
1080+
})
1081+
.webSocketHandler(webSocket -> {
1082+
// Do something
10731083
});
10741084
}
10751085

src/main/java/io/vertx/core/http/HttpServer.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,18 @@ public interface HttpServer extends Measured {
119119
@Fluent
120120
HttpServer webSocketHandler(Handler<ServerWebSocket> handler);
121121

122+
/**
123+
* Set a handler for WebSocket handshake.
124+
*
125+
* <p>When an inbound HTTP request presents a WebSocket upgrade, this handler is called first. The handler
126+
* can chose to {@link ServerWebSocketHandshake#accept()} or {@link ServerWebSocketHandshake#reject()} the request.</p>
127+
*
128+
* <p>Setting no handler, implicitly accepts any HTTP request connection presenting an upgrade header and upgrades it
129+
* to a WebSocket.</p>
130+
*/
131+
@Fluent
132+
HttpServer webSocketHandshakeHandler(Handler<ServerWebSocketHandshake> handler);
133+
122134
/**
123135
* @return the WebSocket handler
124136
*/

src/main/java/io/vertx/core/http/ServerWebSocket.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ public interface ServerWebSocket extends WebSocketBase {
130130
* terminate the WebSocket handshake.
131131
*
132132
* @throws IllegalStateException when the WebSocket handshake is already set
133+
* @deprecated instead use {@link ServerWebSocketHandshake#accept()}
133134
*/
135+
@Deprecated
134136
void accept();
135137

136138
/**
@@ -143,12 +145,17 @@ public interface ServerWebSocket extends WebSocketBase {
143145
* You might use this method, if for example you only want to accept WebSockets with a particular path.
144146
*
145147
* @throws IllegalStateException when the WebSocket handshake is already set
148+
* @deprecated instead use {@link ServerWebSocketHandshake#reject()}
146149
*/
150+
@Deprecated
147151
void reject();
148152

149153
/**
150154
* Like {@link #reject()} but with a {@code status}.
155+
*
156+
* @deprecated instead use {@link ServerWebSocketHandshake#reject(int)}
151157
*/
158+
@Deprecated
152159
void reject(int status);
153160

154161
/**
@@ -172,12 +179,17 @@ public interface ServerWebSocket extends WebSocketBase {
172179
* @param future the future to complete with
173180
* @param handler the completion handler
174181
* @throws IllegalStateException when the WebSocket has already an asynchronous result
182+
* @deprecated instead use {@link ServerWebSocketHandshake}
175183
*/
184+
@Deprecated
176185
void setHandshake(Future<Integer> future, Handler<AsyncResult<Integer>> handler);
177186

178187
/**
179188
* Like {@link #setHandshake(Future, Handler)} but returns a {@code Future} of the asynchronous result
189+
*
190+
* @deprecated instead use {@link ServerWebSocketHandshake}
180191
*/
192+
@Deprecated
181193
Future<Integer> setHandshake(Future<Integer> future);
182194

183195
/**
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright (c) 2011-2024 Contributors to the Eclipse Foundation
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7+
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
8+
*
9+
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10+
*/
11+
package io.vertx.core.http;
12+
13+
import io.vertx.codegen.annotations.CacheReturn;
14+
import io.vertx.codegen.annotations.GenIgnore;
15+
import io.vertx.codegen.annotations.Nullable;
16+
import io.vertx.codegen.annotations.VertxGen;
17+
import io.vertx.core.Future;
18+
import io.vertx.core.MultiMap;
19+
import io.vertx.core.net.HostAndPort;
20+
import io.vertx.core.net.SocketAddress;
21+
22+
import javax.net.ssl.SSLPeerUnverifiedException;
23+
import javax.net.ssl.SSLSession;
24+
import java.security.cert.Certificate;
25+
import java.util.List;
26+
27+
/**
28+
* A server WebSocket handshake, allows to control acceptance or rejection of a WebSocket.
29+
*
30+
* @author <a href="mailto:[email protected]">Julien Viet</a>
31+
*/
32+
@VertxGen
33+
public interface ServerWebSocketHandshake {
34+
35+
/**
36+
* Returns the HTTP headers.
37+
*
38+
* @return the headers
39+
*/
40+
MultiMap headers();
41+
42+
/**
43+
* @return the WebSocket handshake scheme
44+
*/
45+
@Nullable
46+
String scheme();
47+
48+
/**
49+
* @return the WebSocket handshake authority
50+
*/
51+
@Nullable
52+
HostAndPort authority();
53+
54+
/*
55+
* @return the WebSocket handshake URI. This is a relative URI.
56+
*/
57+
String uri();
58+
59+
/**
60+
* @return the WebSocket handshake path.
61+
*/
62+
String path();
63+
64+
/**
65+
* @return the WebSocket handshake query string.
66+
*/
67+
@Nullable
68+
String query();
69+
70+
/**
71+
* Accept the WebSocket and terminate the WebSocket handshake.
72+
* <p/>
73+
* This method should be called from the WebSocket handler to explicitly accept the WebSocket and
74+
* terminate the WebSocket handshake.
75+
*
76+
* @throws IllegalStateException when the WebSocket handshake is already set
77+
*/
78+
Future<ServerWebSocket> accept();
79+
80+
/**
81+
* Reject the WebSocket.
82+
* <p>
83+
* Calling this method from the WebSocket handler when it is first passed to you gives you the opportunity to reject
84+
* the WebSocket, which will cause the WebSocket handshake to fail by returning
85+
* a {@literal 502} response code.
86+
* <p>
87+
* You might use this method, if for example you only want to accept WebSockets with a particular path.
88+
*
89+
* @throws IllegalStateException when the WebSocket handshake is already set
90+
*/
91+
default Future<Void> reject() {
92+
// SC_BAD_GATEWAY
93+
return reject(502);
94+
}
95+
96+
/**
97+
* Like {@link #reject()} but with a {@code status}.
98+
*/
99+
Future<Void> reject(int status);
100+
101+
/**
102+
* @return the remote address for this connection, possibly {@code null} (e.g a server bound on a domain socket).
103+
* If {@code useProxyProtocol} is set to {@code true}, the address returned will be of the actual connecting client.
104+
*/
105+
@CacheReturn
106+
SocketAddress remoteAddress();
107+
108+
/**
109+
* @return the local address for this connection, possibly {@code null} (e.g a server bound on a domain socket)
110+
* If {@code useProxyProtocol} is set to {@code true}, the address returned will be of the proxy.
111+
*/
112+
@CacheReturn
113+
SocketAddress localAddress();
114+
115+
/**
116+
* @return true if this {@link io.vertx.core.http.HttpConnection} is encrypted via SSL/TLS.
117+
*/
118+
boolean isSsl();
119+
120+
/**
121+
* @return SSLSession associated with the underlying socket. Returns null if connection is
122+
* not SSL.
123+
* @see javax.net.ssl.SSLSession
124+
*/
125+
@GenIgnore(GenIgnore.PERMITTED_TYPE)
126+
SSLSession sslSession();
127+
128+
/**
129+
* @return an ordered list of the peer certificates. Returns null if connection is
130+
* not SSL.
131+
* @throws javax.net.ssl.SSLPeerUnverifiedException SSL peer's identity has not been verified.
132+
* @see SSLSession#getPeerCertificates() ()
133+
* @see #sslSession()
134+
*/
135+
@GenIgnore()
136+
List<Certificate> peerCertificates() throws SSLPeerUnverifiedException;
137+
138+
}

0 commit comments

Comments
 (0)