HTTP Client

HTTP client

MagicUtils HTTP client wraps java.net.http.HttpClient with config defaults, JSON mapping, retries, and a small multipart helper.

Dependency

kotlin
dependencies {    implementation("dev.ua.theroer:magicutils-http-client:1.22.0")}

Configuration

The module stores settings in http-client.{ext} (JSONC by default on Fabric).

Key sections:

  • timeouts: connect + request timeouts
  • retry: backoff settings and retry status codes
  • logging: request/response logging toggles
  • defaults: base URL, headers, HTTP version

Basic usage

java
Platform platform = new BukkitPlatformProvider(this);ConfigManager configManager = new ConfigManager(platform);MagicHttpClient client = MagicHttpClient.builder(platform, configManager)        .baseUrl("https://api.example.com/")        .header("Authorization", "Bearer token")        .build();HttpResponse<String> response = client.get("status");

Runtime profiles

When you already have a MagicRuntime, you can declare named HTTP/WebSocket profiles that rebuild automatically on config reload:

java
MagicHttpClientProfile<ApiConfig> monitoring = MagicHttpClientProfile        .builder(runtime, "http.monitoring", ApiConfig.class)        .sections("monitoring")        .baseUrl(config -> config.monitoring.baseUrl)        .bearerAuth(config -> config.monitoring.token)        .build();MagicHttpClient client = monitoring.require();MagicHttpClient sameClient = runtime.requireNamedComponent("http.monitoring", MagicHttpClient.class);
java
MagicWebSocketClientProfile<ApiConfig> gateway = MagicWebSocketClientProfile        .builder(runtime, "ws.gateway", ApiConfig.class)        .sections("gateway")        .baseUrl(config -> config.gateway.baseUrl)        .bearerAuth(config -> config.gateway.token)        .subprotocols(config -> config.gateway.subprotocols)        .build();

This removes the usual close old client -> build new client -> swap references boilerplate from service reload paths.

Profile builder options

Every builder setter takes a Function<C, ...> of your config type, so the value is recomputed each time the profile rebuilds on reload:

MethodPurpose
sections(String...)Config sections the profile binds to.
baseUrl(cfg -> ...)Base URL.
userAgent(cfg -> ...)User-Agent header.
header(name, value) / header(name, cfg -> ...)A static or config-derived header.
headers(cfg -> Map)A map of headers.
bearerAuth(cfg -> token)Adds an Authorization: Bearer header.
connectTimeout(cfg -> Duration)Connection timeout.
requestTimeout(cfg -> Duration)Per-request timeout.
followRedirects(cfg -> Boolean)Redirect policy.
version(cfg -> HttpClient.Version)HTTP version.
logger(cfg -> PlatformLogger)Logger for diagnostics.
configure(cfg, builder -> ...) / configure(builder -> ...)Escape hatch onto the underlying MagicHttpClient.Builder.

Profile instance methods

Once built, the profile is a small handle around the live client:

  • require() — the current client, or throw if not built yet.
  • current()Optional<MagicHttpClient> without throwing.
  • refresh() — force a rebuild from the current config.
  • binding() — the underlying MagicRuntimeConfigBinding.
  • name() — the runtime component name.
  • close() — release the client (the runtime also closes it on shutdown).

Smart methods

Smart methods switch to async automatically on blocking-sensitive threads and return a CompletableFuture:

java
client.getSmart("status").thenAccept(response -> {    // ...});

JSON to POJO

java
public record StatusResponse(String status) {}StatusResponse response = client.getJson("status", StatusResponse.class);

JSON POST

java
public record CreateRequest(String name) {}client.postJson("items", new CreateRequest("Test"));

Multipart upload

java
MultipartBody body = MultipartBody.builder()        .text("title", "Hello")        .file("file", Path.of("logo.png"))        .build();client.postMultipart("upload", body);

Download to file

Stream a response body straight to disk instead of into memory:

java
HttpResponse<Path> response = client.downloadToFile("dist/mod.jar", Path.of("mod.jar"));

Like the other convenience methods it has downloadToFileAsync(...) and downloadToFileSmart(...) variants.

WebSocket client

MagicWebSocketClient wraps java.net.http.WebSocket with the same builder pattern and config integration as the HTTP client.

Basic usage

java
MagicWebSocketClient wsClient = MagicWebSocketClient.builder(platform, configManager)        .baseUrl("wss://api.example.com/ws")        .header("Authorization", "Bearer token")        .subprotocols(List.of("v1"))        .build();CompletableFuture<WebSocket> ws = wsClient.connectAsync("/events", myListener);

Builder options

The builder supports the same options as MagicHttpClient.Builder:

  • baseUrl(...) — base URL prepended to connect paths
  • header(...) / headers(...) — default headers
  • userAgent(...) — User-Agent header
  • connectTimeout(...) — connection timeout
  • followRedirects(...) — redirect policy
  • subprotocols(...) — WebSocket subprotocol list
  • logger(...) — platform logger for connection diagnostics
  • config(...) / logging(...) — shared HttpClientConfig settings
  • mapper(...) — custom Jackson ObjectMapper

Connect methods

MethodDescription
connect(path, listener)Synchronous connect (blocks).
connectAsync(path, listener)Returns CompletableFuture<WebSocket>.
connectSmart(path, listener)Async on blocking-sensitive threads, sync otherwise.

Runtime profiles

Use MagicWebSocketClientProfile for config-aware WebSocket clients that rebuild on config reload:

java
MagicWebSocketClientProfile<ApiConfig> gateway = MagicWebSocketClientProfile        .builder(runtime, "ws.gateway", ApiConfig.class)        .sections("gateway")        .baseUrl(config -> config.gateway.wsUrl)        .bearerAuth(config -> config.gateway.token)        .subprotocols(config -> config.gateway.subprotocols)        .build();MagicWebSocketClient client = gateway.require();

Cleanup

Call wsClient.close() to release resources. When using runtime profiles, the profile handles cleanup automatically on config reload and runtime shutdown.

Async method variants

Every convenience method on MagicHttpClient has three variants:

SuffixBehaviour
(none)Synchronous. Throws on blocking-sensitive threads.
...Async(...)Returns CompletableFuture. Always non-blocking.
...Smart(...)Sync when safe, async when on a blocking-sensitive thread.

Available methods: get, getJson, post, postJson, postMultipart, downloadToFile, send.

Notes

  • Retries apply to the convenience methods (get, post, postJson, etc.).
  • For raw send(...), retries are not automatic.
  • JSON mapping uses Jackson; invalid JSON throws IllegalStateException.
  • Synchronous methods throw IllegalStateException on blocking-sensitive threads. Use ...Async() or ...Smart() in handlers.