Sinobu

Concept

Sinobu provides a concise and powerful API for making HTTP(S) requests and handling WebSocket connections, built on top of Java's standard HttpClient. It simplifies common tasks like handling responses, content negotiation, and asynchronous processing using Signal.

💡 Fluent API

Simple static methods in I enable common HTTP GET requests and WebSocket connections without boilerplate.

🔧 Standard Integration

Built on java.net.http.HttpRequest.Builder, allowing fine-grained control over headers, methods, and request bodies.

🔄 Automatic Content Handling

Response bodies are automatically converted to suitable types (String, JSON, XML, beans, etc.), with gzip/deflate decompression handled transparently.

⚙️ Reactive Streams

Both HTTP and WebSocket messages are streamed asynchronously via Signal, promoting non-blocking and reactive design.

🔌 WebSocket Support

Provides a simple API for establishing WebSocket connections and handling incoming/outgoing messages with ease.

Request and Response

Making HTTP requests and processing responses is streamlined using I#http methods. You can make simple GET requests with just a URL or use Java's java.net.http.HttpRequest.Builder for full control over the request details (method, headers, body, etc.). Responses are delivered asynchronously as a Signal.

// Simple GET request, response as String
 I.http("https://example.com/data", String.class).to(html -> {
     System.out.println("Fetched HTML: " + html.substring(0, 100) + "...");
 });

 // POST request with custom headers, response mapped to a User object
 HttpRequest.Builder request = HttpRequest.newBuilder(URI.create("https://api.example.com/users"))
         .POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"John\"}"))
         .header("Content-Type", "application/json")
         .header("Authorization", "Bearer your_token");

 I.http(request, User.class).to(user -> {
     System.out.println("Created user: " + user.getName());
 });

 // Synchronous execution (blocks until response or error)
 try {
     String result = I.http("https://example.com", String.class).waitForTerminate().to().exact();
     System.out.println("Synchronous result: " + result);
 } catch (Exception e) {
     System.err.println("Request failed: " + e);
 }

Errors during the request (network issues, HTTP status codes >= 400) are propagated through the I#signalError(Throwable) channel.

Supported Type

The I#http methods automatically convert the response body to the specified Java type. This simplifies handling different content types.

Supported types include:

  • String: The response body is read as a UTF-8 string.
  • InputStream: Provides direct access to the (potentially decompressed) response body stream. You are responsible for closing this stream.
  • HttpResponse: Provides the full `HttpResponse ` object, giving access to status code, headers, and the body stream.
  • XML: Parses the response body as XML/HTML into an XML object.
  • JSON: Parses the response body as JSON into a JSON object.
  • Any JSON-mappable Bean/Record: Parses the JSON response body and maps it to an instance of the specified class using Sinobu's object mapping capabilities.
  • Automatic Decompression: Sinobu automatically inspects the Content-Encoding response header. If the content is compressed using gzip or deflate, it will be decompressed transparently before being passed to the type converter or returned as an InputStream.

WebSocket Support

Sinobu provides a simple way to establish WebSocket connections using I#http(String, Consumer, HttpClient...). Communication is handled reactively using Signal for incoming messages and a WebSocket object for sending messages.

Disposable connection = I.http("wss://echo.websocket.org", ws -> {
     // Connection opened callback - send a message
     System.out.println("WebSocket Opened!");
     ws.sendText("Hello WebSocket!", true);

     // You can send more messages later using the 'ws' object
     // ws.sendText("Another message", true);

     // Request more messages from the server (default is 1)
     // ws.request(5); // Request up to 5 more messages

 }).to(message -> { // onNext - received message
     System.out.println("Received: " + message);
     // ws.sendText("Got it: " + message, true); // Example: Echo back
 }, error -> { // onError - connection error
     System.err.println("WebSocket Error: " + error);
 }, () -> { // onComplete - connection closed
     System.out.println("WebSocket Closed");
 });

 // To close the connection later:
 // connection.dispose();

The Consumer<WebSocket> open lambda is executed once the connection is successfully established. The WebSocket instance provided allows you to send messages (sendText, sendBinary, sendPing, etc.) and manage the connection state (request, sendClose).

Incoming messages are received through the Signal returned by I.http.

Automatic Decompression: Similar to HTTP responses, Sinobu automatically handles WebSocket messages compressed with the standard permessage-deflate extension (commonly used for gzip/deflate over WebSockets), ensuring you receive decompressed text messages in the Signal.

Custom HttpClient

While I#http methods use a default, shared HttpClient instance internally, you can provide your own configured HttpClient instance(s) as optional trailing arguments to any of the I.http methods. This allows customization of timeouts, proxies, SSL contexts, authenticators, cookie handlers, etc., using the standard Java HttpClient.Builder API.

HttpClient customClient = HttpClient.newBuilder()
      .connectTimeout(Duration.ofSeconds(10))
      .followRedirects(HttpClient.Redirect.NORMAL)
      // .proxy(...)
      // .sslContext(...)
      // .authenticator(...)
      // .cookieHandler(...)
      .build();

 // Use the custom client for the request
 I.http("https://example.com", String.class, customClient).to(response -> { ... });

 // Use it for WebSocket too
 I.http("wss://example.com/ws", ws -> { ... }, customClient).to(message -> { ... });

If multiple clients are passed, the first non-null one is used. If none are provided or all are null, the default client (I.client) is used.

Template EngineReactivity