Skip to content

Commit d5a16c9

Browse files
committed
fabric8io#71 Add closing message to websocket
1 parent 482fbb1 commit d5a16c9

File tree

6 files changed

+138
-22
lines changed

6 files changed

+138
-22
lines changed

readme.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,16 @@ To support mock of web sockets this wrapper allows you to either specify a ``req
110110
.waitFor(1500).andEmit("root - DELETED")
111111
.done()
112112
.once()
113+
114+
#### Closing Web Socket messages ####
115+
116+
server.expect().withPath("/api/v1/users/watch")
117+
.andUpgradeToWebSocket()
118+
.open()
119+
.waitFor(1000).andEmit("root - CREATED")
120+
.waitFor(1500).andEmit(new WebsocketCloseReason(1000, "Bye bye"))
121+
.done()
122+
.once()
113123

114124
### CRUD Mocking ###
115125

src/main/java/io/fabric8/mockwebserver/internal/InlineWebSocketSessionBuilder.java

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,15 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
1716
package io.fabric8.mockwebserver.internal;
1817

1918
import com.fasterxml.jackson.core.JsonProcessingException;
2019
import com.fasterxml.jackson.databind.ObjectMapper;
2120
import io.fabric8.mockwebserver.Context;
2221
import io.fabric8.mockwebserver.MockServerException;
23-
import io.fabric8.mockwebserver.dsl.Emitable;
24-
import io.fabric8.mockwebserver.dsl.EventDoneable;
25-
import io.fabric8.mockwebserver.dsl.Function;
26-
import io.fabric8.mockwebserver.dsl.TimesOrOnceable;
27-
import io.fabric8.mockwebserver.dsl.WebSocketSessionBuilder;
28-
29-
import java.util.ArrayDeque;
30-
import java.util.ArrayList;
31-
import java.util.Collections;
32-
import java.util.List;
33-
import java.util.Queue;
22+
import io.fabric8.mockwebserver.dsl.*;
23+
24+
import java.util.*;
3425

3526
public class InlineWebSocketSessionBuilder<T> implements WebSocketSessionBuilder<T>, EventDoneable<T> {
3627

@@ -171,7 +162,10 @@ private WebSocketMessage toWebSocketMessage(Object content, Boolean toBeRemoved)
171162
}
172163

173164
private WebSocketMessage toWebSocketMessage(Long delay, Object content, Boolean toBeRemoved) {
174-
if (content instanceof String) {
165+
if (content instanceof WebsocketCloseReason) {
166+
WebsocketCloseReason closeReason = (WebsocketCloseReason) content;
167+
return new WebSocketMessage(delay, closeReason.getReason(), toBeRemoved, closeReason.getCode());
168+
} else if (content instanceof String) {
175169
return new WebSocketMessage(delay, (String) content, toBeRemoved);
176170
} else if (content instanceof WebSocketMessage) {
177171
return (WebSocketMessage) content;

src/main/java/io/fabric8/mockwebserver/internal/WebSocketMessage.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,40 +23,50 @@ public class WebSocketMessage {
2323
private final byte[] body;
2424
private final boolean toBeRemoved;
2525
private final boolean binary;
26+
private final Integer closingReason;
2627

2728
public WebSocketMessage(String body) {
2829
this(0L, body, true);
2930
}
3031

3132
public WebSocketMessage(byte[] body) {
32-
this(0L, body, true, true);
33+
this(0L, body, true, true, null);
3334
}
3435

3536
public WebSocketMessage(String body, boolean toBeRemoved) {
36-
this(0L, body.getBytes(StandardCharsets.UTF_8), toBeRemoved, false);
37+
this(0L, body.getBytes(StandardCharsets.UTF_8), toBeRemoved, false, null);
3738
}
3839

3940
public WebSocketMessage(byte[] body, boolean toBeRemoved) {
40-
this(0L, body, toBeRemoved, true);
41+
this(0L, body, toBeRemoved, true, null);
4142
}
4243

4344
public WebSocketMessage(Long delay, String body, boolean toBeRemoved) {
44-
this(delay, body.getBytes(StandardCharsets.UTF_8), toBeRemoved, false);
45+
this(delay, body.getBytes(StandardCharsets.UTF_8), toBeRemoved, false, null);
4546
}
4647

4748
public WebSocketMessage(Long delay, byte[] body, boolean toBeRemoved) {
48-
this(delay, body, toBeRemoved, true);
49+
this(delay, body, toBeRemoved, true, null);
4950
}
51+
52+
public WebSocketMessage(Long delay, String body, boolean toBeRemoved, Integer closingReason) {
53+
this(delay, body.getBytes(StandardCharsets.UTF_8), toBeRemoved, false, closingReason);
54+
}
5055

51-
public WebSocketMessage(Long delay, String body, boolean toBeRemoved, boolean binary) {
52-
this(delay, body.getBytes(StandardCharsets.UTF_8), toBeRemoved, binary);
56+
public WebSocketMessage(Long delay, byte[] body, boolean toBeRemoved, Integer closingReason) {
57+
this(delay, body, toBeRemoved, true, closingReason);
58+
}
59+
60+
public WebSocketMessage(Long delay, String body, boolean toBeRemoved, boolean binary, Integer closingReason) {
61+
this(delay, body.getBytes(StandardCharsets.UTF_8), toBeRemoved, binary, closingReason);
5362
}
5463

55-
public WebSocketMessage(Long delay, byte[] body, boolean toBeRemoved, boolean binary) {
64+
public WebSocketMessage(Long delay, byte[] body, boolean toBeRemoved, boolean binary, Integer closingReason) {
5665
this.delay = delay;
5766
this.body = body;
5867
this.toBeRemoved = toBeRemoved;
5968
this.binary = binary;
69+
this.closingReason = closingReason;
6070
}
6171

6272
public Long getDelay() {
@@ -78,4 +88,8 @@ public byte[] getBytes() {
7888
public boolean isBinary() {
7989
return binary;
8090
}
91+
92+
public Integer getClosingReason() {
93+
return closingReason;
94+
}
8195
}

src/main/java/io/fabric8/mockwebserver/internal/WebSocketSession.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import okhttp3.mockwebserver.RecordedRequest;
2525
import okio.ByteString;
2626

27+
import java.nio.charset.StandardCharsets;
2728
import java.util.ArrayList;
2829
import java.util.Collection;
2930
import java.util.HashMap;
@@ -161,7 +162,13 @@ private void send(final WebSocket ws, final WebSocketMessage message) {
161162
pendingMessages.add(id);
162163
executor.schedule(() -> {
163164
if (ws != null) {
164-
if (message.isBinary()) {
165+
if (message.getClosingReason() != null) {
166+
if (message.isBinary()) {
167+
ws.close(message.getClosingReason(), new String(message.getBytes(), StandardCharsets.UTF_8));
168+
} else {
169+
ws.close(message.getClosingReason(), message.getBody());
170+
}
171+
} else if (message.isBinary()) {
165172
ws.send(ByteString.of(message.getBytes()));
166173
} else {
167174
ws.send(message.getBody());
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Copyright (C) 2015 Red Hat, Inc.
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 io.fabric8.mockwebserver.internal;
17+
18+
public class WebsocketCloseReason {
19+
20+
private final int code;
21+
private final String reason;
22+
23+
public WebsocketCloseReason(int code, String reason) {
24+
super();
25+
this.code = code;
26+
this.reason = reason;
27+
}
28+
29+
public int getCode() {
30+
return code;
31+
}
32+
33+
public String getReason() {
34+
return reason;
35+
}
36+
37+
}

src/test/groovy/io/fabric8/mockwebserver/DefaultMockServerWebSocketTest.groovy

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import java.util.concurrent.TimeUnit
2828
import java.util.stream.Collectors
2929
import java.util.stream.IntStream
3030

31+
import io.fabric8.mockwebserver.internal.WebsocketCloseReason
32+
3133
class DefaultMockServerWebSocketTest extends Specification {
3234

3335
DefaultMockServer server
@@ -119,4 +121,56 @@ class DefaultMockServerWebSocketTest extends Specification {
119121
cleanup:
120122
wss.forEach(ws -> ws.close(1000, "Test finished"))
121123
}
124+
125+
// https://github.com/fabric8io/mockwebserver/pull/66#issuecomment-944289335
126+
def "andUpgradeToWebSocket, with closing events, should close session"() {
127+
given:
128+
server.expect()
129+
.withPath("/websocket")
130+
.andUpgradeToWebSocket().open().waitFor(5L).andEmit("A text message").waitFor(10L).andEmit(new WebsocketCloseReason(1000, "Everything is ok")).done().always()
131+
def future = new CompletableFuture<List<String>>()
132+
when:
133+
def ws = client.newWebSocket(new Request.Builder().url(server.url("/websocket")).build(), new WebSocketListener() {
134+
List<String> messages = new ArrayList<>()
135+
@Override
136+
void onMessage(WebSocket webSocket, String text) {
137+
messages.add(text)
138+
}
139+
@Override
140+
void onClosing(WebSocket webSocket, int code, String reason) {
141+
messages.add("Session closed with code " + code + " and reason " + reason)
142+
future.complete(messages)
143+
}
144+
})
145+
then:
146+
def result = future.get(50L, TimeUnit.MILLISECONDS);
147+
assert result.size() == 2
148+
assert result.get(0) == "A text message"
149+
assert result.get(1) == "Session closed with code 1000 and reason Everything is ok"
150+
}
151+
152+
def "andUpgradeToWebSocket, with closing events, should close session, later message skipped"() {
153+
given:
154+
server.expect()
155+
.withPath("/websocket")
156+
.andUpgradeToWebSocket().open().waitFor(10L).andEmit("A text message").waitFor(5L).andEmit(new WebsocketCloseReason(1000, "Everything is ok")).done().always()
157+
def future = new CompletableFuture<List<String>>()
158+
when:
159+
def ws = client.newWebSocket(new Request.Builder().url(server.url("/websocket")).build(), new WebSocketListener() {
160+
List<String> messages = new ArrayList<>()
161+
@Override
162+
void onMessage(WebSocket webSocket, String text) {
163+
messages.add(text)
164+
}
165+
@Override
166+
void onClosing(WebSocket webSocket, int code, String reason) {
167+
messages.add("Session closed with code " + code + " and reason " + reason)
168+
future.complete(messages)
169+
}
170+
})
171+
then:
172+
def result = future.get(50L, TimeUnit.MILLISECONDS);
173+
assert result.size() == 1
174+
assert result.get(0) == "Session closed with code 1000 and reason Everything is ok"
175+
}
122176
}

0 commit comments

Comments
 (0)