Skip to content

Commit 49e10aa

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

File tree

6 files changed

+129
-26
lines changed

6 files changed

+129
-26
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: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/**
22
* Copyright (C) 2015 Red Hat, Inc.
3-
*
3+
* <p>
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
7+
* <p>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,17 +20,9 @@
2020
import com.fasterxml.jackson.databind.ObjectMapper;
2121
import io.fabric8.mockwebserver.Context;
2222
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;
23+
import io.fabric8.mockwebserver.dsl.*;
24+
25+
import java.util.*;
3426

3527
public class InlineWebSocketSessionBuilder<T> implements WebSocketSessionBuilder<T>, EventDoneable<T> {
3628

@@ -153,7 +145,8 @@ public Emitable<EventDoneable<T>> immediately() {
153145
private List<WebSocketMessage> toWebSocketMessages(Object... messages) {
154146
List<WebSocketMessage> response = new ArrayList<>();
155147
for (Object msg : messages) {
156-
response.add(toWebSocketMessage(msg));
148+
WebSocketMessage webSocketMessage = toWebSocketMessage(msg);
149+
response.add(webSocketMessage);
157150
}
158151
return response;
159152
}
@@ -171,7 +164,10 @@ private WebSocketMessage toWebSocketMessage(Object content, Boolean toBeRemoved)
171164
}
172165

173166
private WebSocketMessage toWebSocketMessage(Long delay, Object content, Boolean toBeRemoved) {
174-
if (content instanceof String) {
167+
if (content instanceof WebsocketCloseReason) {
168+
WebsocketCloseReason closeReason = (WebsocketCloseReason) content;
169+
return new WebSocketMessage(delay, closeReason.getReason(), toBeRemoved, closeReason.getCode());
170+
} else if (content instanceof String) {
175171
return new WebSocketMessage(delay, (String) content, toBeRemoved);
176172
} else if (content instanceof WebSocketMessage) {
177173
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: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.fabric8.mockwebserver.internal;
2+
3+
public class WebsocketCloseReason {
4+
5+
private final int code;
6+
private final String reason;
7+
8+
public WebsocketCloseReason(int code, String reason) {
9+
super();
10+
this.code = code;
11+
this.reason = reason;
12+
}
13+
14+
public int getCode() {
15+
return code;
16+
}
17+
18+
public String getReason() {
19+
return reason;
20+
}
21+
22+
}

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)