Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/timboudreau/blather
Async Java websocket client and test harness
https://github.com/timboudreau/blather
async java netty websocket websocket-client
Last synced: 4 months ago
JSON representation
Async Java websocket client and test harness
- Host: GitHub
- URL: https://github.com/timboudreau/blather
- Owner: timboudreau
- Created: 2017-10-02T07:04:39.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2023-05-15T03:59:24.000Z (almost 2 years ago)
- Last Synced: 2023-12-05T08:41:56.586Z (about 1 year ago)
- Topics: async, java, netty, websocket, websocket-client
- Language: Java
- Size: 80.1 KB
- Stars: 4
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
Blather - A Netty Based Websocket Client and Test Harness
=========================================================Blather is a Netty-based Java websocket client, with a test harness that makes
it trivial to write JUnit tests for websocket communication.It emphasizes simplicity and functional interfaces. Code being worth a thousand
words, here is a trivial client that just connects, sends "hello" and then
"goodbye " with whatever response it got appended:```java
public class WebsocketTest {
public static void main(String[] args) {
Blather.create().client("ws://foo.example/websocket").sendOnConnect("hello")
.onMessage(String.class, WebsocketTest::onMessage);
}
public static Object onMessage(int msgIndex, String inboundMessage, ChannelControl ctrl) {
return "goodbye " + inboundMessage;
}
}
```Note the use of JDK 8 member references to separate the logic of connecting from the
code that has the conversation.To use, add the Maven repository as [described here](https://timboudreau.com/builds/) and
```xml
blather
com.mastfrog
2.0.1-dev```
Manipulating The Connection
---------------------------The `ChannelControl` interface passed into all callbacks allows you to:
* Asynchronously send messages to the server, rather than sending them as the return
value
* Close the connection (politely, sending a close frame)
* Replace the current handler with another one - so if your connection has various
modes - perhaps its own handshaking phase followed by other communication - you can
simply hand off message handling to a different handler by calling `ChannelControl.nextCallback()`.Data Marshalling
----------------Blather uses [Jackson](https://github.com/FasterXML/jackson) for data marshalling for non-string
types (note that Jackson can speak more than JSON - for example, [Bson4Jackson](https://github.com/michel-kraemer/bson4jackson)).
So, you'll note that the message handler method above simply returned `Object` - you have
flexibility here - you can return* A `String` or any `CharSequence`, which will be marshalled to a plain text websocket frame
* A Netty `WebSocketFrame`, which will be sent as-is
* Any other object, which will be marshalled using Jackson (you can supply and configure the
`ObjectMapper` using oen of the other factory methods on `Blather`.
* Directly get the Netty `Channel` object to do as you wish withException Handling
------------------One of the main use cases is writing unit- or functional-tests, so out of the box exception
handling is dealt with in a particular way:* In a test, simply call `WebsocketClientRequest.await()`, and any exception thrown will cause
the connection to be closed, and the exception will be rethrown in the main thread (any
subsequent exceptions will show up as suppressed exceptions in the stack trace of the first)* For other environments (where you don't have a thread blocked waiting for request completion -
this is an asynchronous library after all), provide a `WebsocketErrorHandler` to
`WebsocketClientRequest.withErrorHandler()` and that can decide whether to close the
connection or not.Test Harness
============There is a `test-jar` Maven artifact which includes a test harness for writing tests which
automatically start a server. While designed with [Acteur](https://github.com/timboudreau/acteur) in
mind, the `Server` and `ServerControl` interfaces are
[trivial to implement](https://timboudreau.com/builds/job/mastfrog-parent/lastSuccessfulBuild/artifact/acteur-modules/acteur-parent/acteur-util/target/apidocs/com/mastfrog/acteur/util/Server.html).Here is a test which starts a small Acteur server, makes a websocket connection, does some
stuff and closes it:```java
@RunWith(GuiceRunner.class)
@TestWith(TestApplication.Module.class)
public class HarnessTest {
int count = 0;
@Test(timeout=20000)
public void test(WebsocketHostClient client) throws Throwable {
client.request("/ws")
.log()
.addHeader(Headers.stringHeader("X-Foo"), "bar")
.addUrlQueryPair("foo", "bar")
.onMessage(String.class, this::withFrame)
.await(Duration.ofSeconds(20));
}public Object withFrame(int msgIndex, String data, ChannelControl ctrl) {
if (count++ == 4) {
ctrl.close();
fail("Handler should have been replaced");
return null;
} else if (count == 3) {
ctrl.nextCallback(this::bypass, String.class);
}
return "Hello: " + data;
}public Object bypass(int msgIndex, String data, ChannelControl ctrl) {
System.out.println("BYPASS GOT " + data);
ctrl.close();
return null;
}
}
```The test harness also has the ability, with Acteur (or anything that wants to
inject an `ErrorInterceptor` and call it on errors) to catch server-side exceptions
and rethrow those at the end of a test, so that server side errors are not opaque
to the test, and they simply show up as test failures with a stack trace.