Fix lost responses and close handling; publish to Gitea as 0.2.0
Gradle Build / build (pull_request) Failing after 6s
Gradle Build / build (pull_request) Failing after 6s
- Register pending requests before sending so fast responses aren't lost - Fail pending requests with SessionClosedException when the connection closes or errors, and add onClose(), isOpen() and a request overload with a timeout - Serialize text sends: java.net.http.WebSocket allows only one outstanding send, so concurrent requests could throw - Reassemble text messages delivered in parts before parsing - Complete error responses with SessionErrorException carrying the payload - Answer requests for unknown methods with an error response - Fix SessionResult.error() producing success responses - Fix close() using invalid close code 0; send 1000 - Publish as dev.selimaj:session-java to the Gitea package registry; make Jackson an api dependency and drop unused Tyrus/Jakarta deps - Replace the empty test with 9 tests against an in-process server Fixes #1 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -17,59 +17,69 @@ It ensures compile-time guarantees for message structure, reduces runtime errors
|
||||
|
||||
- Fully typed WebSocket sessions
|
||||
- Type-safe request/response mechanism
|
||||
- Optional typed notifications (Todo)
|
||||
- Typed notifications
|
||||
- Lightweight, minimal runtime overhead
|
||||
|
||||
## Installation
|
||||
|
||||
#### Add the repository
|
||||
`session-java` is published to the Gitea package registry at `git.selimaj.dev`.
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
maven { url 'https://jitpack.io' }
|
||||
maven { url 'https://git.selimaj.dev/api/packages/selimaj-dev/maven' }
|
||||
}
|
||||
```
|
||||
|
||||
#### Add the dependency
|
||||
```groovy
|
||||
dependencies {
|
||||
implementation 'com.github.selimaj-dev:session-java:0.1.3'
|
||||
implementation 'dev.selimaj:session-java:0.2.0'
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
Versions up to 0.1.6 were published through JitPack as `com.github.selimaj-dev:session-java`.
|
||||
|
||||
### **Basic Example (client)**
|
||||
## Usage
|
||||
|
||||
```java
|
||||
Session session = Session.connect("ws://localhost:8080/");
|
||||
static final Method<String, String, String> DATA =
|
||||
new Method<>("data", String.class, String.class, String.class);
|
||||
|
||||
TextNode response = session.request(Methods.Data, TextNode.valueOf("Hello from client")).get();
|
||||
Session session = Session.connect("ws://localhost:8080/", 10, TimeUnit.SECONDS);
|
||||
|
||||
System.out.println(response);
|
||||
// Handle requests and notifications from the server.
|
||||
session.onRequest(DATA, (id, data) -> SessionResult.ok("echo: " + data));
|
||||
session.onNotification(DATA, data -> System.out.println("notified: " + data));
|
||||
|
||||
// Find out when the connection ends, from either side.
|
||||
session.onClose(() -> System.out.println("disconnected"));
|
||||
|
||||
// Send a request, with or without a deadline.
|
||||
String reply = session.request(DATA, "Hello", Duration.ofSeconds(10)).get();
|
||||
|
||||
session.close();
|
||||
```
|
||||
|
||||
### **Basic Example (server)**
|
||||
### Semantics
|
||||
|
||||
#### ⚠️ Server functionality not done yet this is only for future examples
|
||||
- A request completes exceptionally with:
|
||||
- `SessionErrorException` when the peer answers with an error response (`getError()` has the payload)
|
||||
- `SessionClosedException` when the session closes before the response arrives
|
||||
- `TimeoutException` when the timeout passes, if one was given
|
||||
- `onClose` handlers run exactly once, whether the session was closed locally, by the peer, or by a network failure. A handler registered after close runs immediately.
|
||||
- Sends are serialized, so the session is safe to use from multiple threads.
|
||||
- A request for an unknown method is answered with an error response instead of no reply.
|
||||
|
||||
```java
|
||||
SessionServer server = SessionServer.bind("ws://localhost:8080/");
|
||||
## Publishing
|
||||
|
||||
server.onClient((session, addr) -> {
|
||||
session.onRequest(Methods.Data, (id, data) -> {
|
||||
return SessionResult.ok(TextNode.valueOf("Response from server"));
|
||||
});
|
||||
});
|
||||
```sh
|
||||
GITEA_TOKEN=<token with package:write> ./gradlew publish
|
||||
```
|
||||
|
||||
## Protocol
|
||||
|
||||
Every message is a JSON text frame with a `type` tag.
|
||||
|
||||
#### Request
|
||||
|
||||
The request `id` is separated from the peer, and will increment only on it's requests.
|
||||
The `id` is chosen by the sender and increments per peer.
|
||||
|
||||
```json
|
||||
{ "type": "request", "id": 1, "method": "data", "data": "Hello from client" }
|
||||
@@ -77,16 +87,22 @@ The request `id` is separated from the peer, and will increment only on it's req
|
||||
|
||||
#### Response
|
||||
|
||||
The response `id` **must** remain the same as the request.
|
||||
A response **must** carry the id of the request it answers.
|
||||
|
||||
```json
|
||||
{ "type": "response", "id": 1, "result": "Hello from server" }
|
||||
```
|
||||
|
||||
#### Notifications
|
||||
|
||||
A notification is a method that doesn't need validation or output, it simply notifies a peer for a specific information
|
||||
#### Error response
|
||||
|
||||
```json
|
||||
{ "type": "notification", "result": "Hello from server" }
|
||||
{ "type": "errorresponse", "id": 1, "error": "Invalid data" }
|
||||
```
|
||||
|
||||
#### Notification
|
||||
|
||||
A notification is fire-and-forget and gets no response.
|
||||
|
||||
```json
|
||||
{ "type": "notification", "method": "data", "data": "Hello from server" }
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user