Skip to content

Commit 12588d8

Browse files
committed
Basic support for binary messages over web sockets
1 parent 5ab1256 commit 12588d8

6 files changed

Lines changed: 142 additions & 37 deletions

File tree

examples/src/main/java/jooby/MyApp.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,13 +239,11 @@ public class MyApp extends Jooby {
239239
ws("/ws", (ws) -> {
240240
ws.onMessage(message -> {
241241
System.out.println(message.to(User.class));
242-
System.out.println(message.stringValue());
243-
ws.send(message.to(User.class), () -> {System.out.println("Sent!");});
242+
ws.send(message.to(User.class));
244243
});
245244

246245
ws.send("Hello web browser");
247-
})
248-
.consumes(MediaType.json);
246+
}).consumes(MediaType.json);
249247
}
250248

251249
public static void main(final String[] args) throws Exception {

examples/src/main/java/jooby/Users.java

Lines changed: 0 additions & 28 deletions
This file was deleted.

examples/src/main/resources/ws.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
<body>
33
<script type="text/javascript">
44
var ws = new WebSocket("ws://localhost:8090/ws");
5+
ws.binaryType = "blob";
56

67
ws.onopen = function() {
78
console.log("opened");
89
ws.send('{"firstName": "John", "lastName": "Doe"}');
910
};
1011

1112
ws.onmessage = function (evt) {
13+
console.log(evt);
1214
console.log("Message: " + evt.data);
1315
};
1416

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package jooby.internal;
2+
3+
import java.io.ByteArrayInputStream;
4+
import java.io.InputStream;
5+
import java.io.InputStreamReader;
6+
import java.io.Reader;
7+
import java.nio.ByteBuffer;
8+
import java.util.List;
9+
import java.util.Optional;
10+
import java.util.Set;
11+
import java.util.SortedSet;
12+
13+
import jooby.Response;
14+
import jooby.Route;
15+
import jooby.Variant;
16+
17+
import com.google.common.base.Charsets;
18+
import com.google.inject.TypeLiteral;
19+
20+
public class WebSocketBinaryMessage implements Variant {
21+
22+
private ByteBuffer buffer;
23+
24+
public WebSocketBinaryMessage(final ByteBuffer buffer) {
25+
this.buffer = buffer;
26+
}
27+
28+
@Override
29+
public boolean booleanValue() {
30+
throw typeError(boolean.class);
31+
}
32+
33+
@Override
34+
public byte byteValue() {
35+
throw typeError(byte.class);
36+
}
37+
38+
@Override
39+
public short shortValue() {
40+
throw typeError(short.class);
41+
}
42+
43+
@Override
44+
public int intValue() {
45+
throw typeError(int.class);
46+
}
47+
48+
@Override
49+
public long longValue() {
50+
throw typeError(long.class);
51+
}
52+
53+
@Override
54+
public String stringValue() {
55+
throw typeError(String.class);
56+
}
57+
58+
@Override
59+
public float floatValue() {
60+
throw typeError(float.class);
61+
}
62+
63+
@Override
64+
public double doubleValue() {
65+
throw typeError(double.class);
66+
}
67+
68+
@Override
69+
public <T extends Enum<T>> T enumValue(final Class<T> type) {
70+
throw typeError(type);
71+
}
72+
73+
@Override
74+
public <T> List<T> toList(final Class<T> type) {
75+
throw typeError(type);
76+
}
77+
78+
@Override
79+
public <T> Set<T> toSet(final Class<T> type) {
80+
throw typeError(type);
81+
}
82+
83+
@Override
84+
public <T extends Comparable<T>> SortedSet<T> toSortedSet(final Class<T> type) {
85+
throw typeError(type);
86+
}
87+
88+
@Override
89+
public <T> Optional<T> toOptional(final Class<T> type) {
90+
throw typeError(type);
91+
}
92+
93+
@SuppressWarnings("unchecked")
94+
@Override
95+
public <T> T to(final TypeLiteral<T> type) {
96+
Class<? super T> rawType = type.getRawType();
97+
if (rawType == byte[].class) {
98+
return (T) buffer.array();
99+
}
100+
if (rawType == ByteBuffer.class) {
101+
return (T) buffer;
102+
}
103+
if (rawType == InputStream.class) {
104+
return (T) new ByteArrayInputStream(buffer.array());
105+
}
106+
if (rawType == Reader.class) {
107+
return (T) new InputStreamReader(new ByteArrayInputStream(buffer.array()), Charsets.UTF_8);
108+
}
109+
throw typeError(rawType);
110+
}
111+
112+
private Route.Err typeError(final Class<?> type) {
113+
return new Route.Err(Response.Status.BAD_REQUEST, "Can't convert to "
114+
+ ByteBuffer.class.getName() + " to " + type);
115+
}
116+
}

jooby-core/src/main/java/jooby/internal/WebSocketImpl.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,15 @@ public void writeFailed(final Throwable cause) {
146146
new BodyWriterImpl(Charsets.UTF_8, stream, reader));
147147
} else {
148148
RemoteEndpoint remote = session.getRemote();
149-
// TODO: complete me!
150-
remote.sendString(data.toString(), callback);
149+
150+
if (byte[].class == data.getClass() || Byte[].class == data.getClass()) {
151+
remote.sendBytes(ByteBuffer.wrap((byte[]) data), callback);
152+
} else if (ByteBuffer.class.isInstance(data)) {
153+
remote.sendBytes((ByteBuffer) data, callback);
154+
} else {
155+
// TODO: complete me!
156+
remote.sendString(data.toString(), callback);
157+
}
151158
}
152159
}
153160

jooby-core/src/main/java/jooby/internal/jetty/JettyWebSocketHandler.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package jooby.internal.jetty;
22

3+
import java.nio.ByteBuffer;
34
import java.util.NoSuchElementException;
45

56
import jooby.Response;
@@ -8,6 +9,7 @@
89
import jooby.Variant;
910
import jooby.WebSocket;
1011
import jooby.internal.VariantImpl;
12+
import jooby.internal.WebSocketBinaryMessage;
1113
import jooby.internal.WebSocketImpl;
1214

1315
import org.eclipse.jetty.websocket.api.Session;
@@ -30,14 +32,22 @@ public class JettyWebSocketHandler implements WebSocketListener {
3032

3133
private Logger log = LoggerFactory.getLogger(WebSocket.class);
3234

33-
public JettyWebSocketHandler(final Injector injector, final Config config, final WebSocketImpl socket) {
35+
public JettyWebSocketHandler(final Injector injector, final Config config,
36+
final WebSocketImpl socket) {
3437
this.injector = injector;
3538
this.socket = socket;
3639
this.closeOnErr = config.getBoolean("jetty.ws.closeOnError");
3740
}
3841

3942
@Override
40-
public void onWebSocketBinary(final byte[] payload, final int offset, final int len) {
43+
public void onWebSocketBinary(final byte[] array, final int offset, final int len) {
44+
try {
45+
// for Web Socket, charset is always UTF-8
46+
Variant variant = new WebSocketBinaryMessage(ByteBuffer.wrap(array, offset, len));
47+
socket.fireMessage(variant);
48+
} catch (Exception ex) {
49+
onWebSocketError(ex);
50+
}
4151
}
4252

4353
@Override
@@ -93,7 +103,7 @@ public void onWebSocketError(final Throwable cause) {
93103
closeStatus = WebSocket.BAD_DATA;
94104
}
95105
}
96-
socket.close(closeStatus);
106+
socket.close(closeStatus.code(), closeStatus.reason() + " " + cause.getMessage());
97107
} else {
98108
log.error("execution resulted in serious error", cause);
99109
}

0 commit comments

Comments
 (0)