[{"data":1,"prerenderedAt":752},["ShallowReactive",2],{"docs-\u002Fmodules\u002Fhttp-client":3},{"id":4,"title":5,"body":6,"description":742,"extension":743,"meta":744,"navTitle":745,"navigation":746,"path":747,"rawbody":748,"seo":749,"stem":750,"__hash__":751},"docs\u002Fmodules\u002Fhttp-client.md","HTTP Client",{"type":7,"value":8,"toc":717},"minimark",[9,14,23,28,38,42,49,52,80,84,92,96,103,109,115,122,127,134,289,293,296,341,345,352,358,362,368,372,378,382,388,392,395,401,412,416,426,429,435,439,444,513,517,563,566,573,579,583,590,594,601,648,674,678],[10,11,13],"h1",{"id":12},"http-client","HTTP client",[15,16,17,18,22],"p",{},"MagicUtils HTTP client wraps ",[19,20,21],"code",{},"java.net.http.HttpClient"," with config defaults,\nJSON mapping, retries, and a small multipart helper.",[24,25,27],"h2",{"id":26},"dependency","Dependency",[29,30,36],"pre",{"className":31,"code":33,"language":34,"meta":35},[32],"language-kotlin","dependencies {\n    implementation(\"dev.ua.theroer:magicutils-http-client:1.27.1\")\n}\n","kotlin","",[19,37,33],{"__ignoreMap":35},[24,39,41],{"id":40},"configuration","Configuration",[15,43,44,45,48],{},"The module stores settings in ",[19,46,47],{},"http-client.{ext}"," (JSONC by default on Fabric).",[15,50,51],{},"Key sections:",[53,54,55,62,68,74],"ul",{},[56,57,58,61],"li",{},[19,59,60],{},"timeouts",": connect + request timeouts",[56,63,64,67],{},[19,65,66],{},"retry",": backoff settings and retry status codes",[56,69,70,73],{},[19,71,72],{},"logging",": request\u002Fresponse logging toggles",[56,75,76,79],{},[19,77,78],{},"defaults",": base URL, headers, HTTP version",[24,81,83],{"id":82},"basic-usage","Basic usage",[29,85,90],{"className":86,"code":88,"language":89,"meta":35},[87],"language-java","Platform platform = new BukkitPlatformProvider(this);\nConfigManager configManager = new ConfigManager(platform);\n\nMagicHttpClient client = MagicHttpClient.builder(platform, configManager)\n        .baseUrl(\"https:\u002F\u002Fapi.example.com\u002F\")\n        .header(\"Authorization\", \"Bearer token\")\n        .build();\n\nHttpResponse\u003CString> response = client.get(\"status\");\n","java",[19,91,88],{"__ignoreMap":35},[24,93,95],{"id":94},"runtime-profiles","Runtime profiles",[15,97,98,99,102],{},"When you already have a ",[19,100,101],{},"MagicRuntime",", you can declare named HTTP\u002FWebSocket\nprofiles that rebuild automatically on config reload:",[29,104,107],{"className":105,"code":106,"language":89,"meta":35},[87],"MagicHttpClientProfile\u003CApiConfig> monitoring = MagicHttpClientProfile\n        .builder(runtime, \"http.monitoring\", ApiConfig.class)\n        .sections(\"monitoring\")\n        .baseUrl(config -> config.monitoring.baseUrl)\n        .bearerAuth(config -> config.monitoring.token)\n        .build();\n\nMagicHttpClient client = monitoring.require();\nMagicHttpClient sameClient = runtime.requireNamedComponent(\"http.monitoring\", MagicHttpClient.class);\n",[19,108,106],{"__ignoreMap":35},[29,110,113],{"className":111,"code":112,"language":89,"meta":35},[87],"MagicWebSocketClientProfile\u003CApiConfig> gateway = MagicWebSocketClientProfile\n        .builder(runtime, \"ws.gateway\", ApiConfig.class)\n        .sections(\"gateway\")\n        .baseUrl(config -> config.gateway.baseUrl)\n        .bearerAuth(config -> config.gateway.token)\n        .subprotocols(config -> config.gateway.subprotocols)\n        .build();\n",[19,114,112],{"__ignoreMap":35},[15,116,117,118,121],{},"This removes the usual ",[19,119,120],{},"close old client -> build new client -> swap references","\nboilerplate from service reload paths.",[123,124,126],"h3",{"id":125},"profile-builder-options","Profile builder options",[15,128,129,130,133],{},"Every builder setter takes a ",[19,131,132],{},"Function\u003CC, ...>"," of your config type, so the value\nis recomputed each time the profile rebuilds on reload:",[135,136,137,150],"table",{},[138,139,140],"thead",{},[141,142,143,147],"tr",{},[144,145,146],"th",{},"Method",[144,148,149],{},"Purpose",[151,152,153,164,174,184,198,208,222,232,242,252,262,272],"tbody",{},[141,154,155,161],{},[156,157,158],"td",{},[19,159,160],{},"sections(String...)",[156,162,163],{},"Config sections the profile binds to.",[141,165,166,171],{},[156,167,168],{},[19,169,170],{},"baseUrl(cfg -> ...)",[156,172,173],{},"Base URL.",[141,175,176,181],{},[156,177,178],{},[19,179,180],{},"userAgent(cfg -> ...)",[156,182,183],{},"User-Agent header.",[141,185,186,195],{},[156,187,188,191,192],{},[19,189,190],{},"header(name, value)"," \u002F ",[19,193,194],{},"header(name, cfg -> ...)",[156,196,197],{},"A static or config-derived header.",[141,199,200,205],{},[156,201,202],{},[19,203,204],{},"headers(cfg -> Map)",[156,206,207],{},"A map of headers.",[141,209,210,215],{},[156,211,212],{},[19,213,214],{},"bearerAuth(cfg -> token)",[156,216,217,218,221],{},"Adds an ",[19,219,220],{},"Authorization: Bearer"," header.",[141,223,224,229],{},[156,225,226],{},[19,227,228],{},"connectTimeout(cfg -> Duration)",[156,230,231],{},"Connection timeout.",[141,233,234,239],{},[156,235,236],{},[19,237,238],{},"requestTimeout(cfg -> Duration)",[156,240,241],{},"Per-request timeout.",[141,243,244,249],{},[156,245,246],{},[19,247,248],{},"followRedirects(cfg -> Boolean)",[156,250,251],{},"Redirect policy.",[141,253,254,259],{},[156,255,256],{},[19,257,258],{},"version(cfg -> HttpClient.Version)",[156,260,261],{},"HTTP version.",[141,263,264,269],{},[156,265,266],{},[19,267,268],{},"logger(cfg -> PlatformLogger)",[156,270,271],{},"Logger for diagnostics.",[141,273,274,282],{},[156,275,276,191,279],{},[19,277,278],{},"configure(cfg, builder -> ...)",[19,280,281],{},"configure(builder -> ...)",[156,283,284,285,288],{},"Escape hatch onto the underlying ",[19,286,287],{},"MagicHttpClient.Builder",".",[123,290,292],{"id":291},"profile-instance-methods","Profile instance methods",[15,294,295],{},"Once built, the profile is a small handle around the live client:",[53,297,298,304,314,320,329,335],{},[56,299,300,303],{},[19,301,302],{},"require()"," — the current client, or throw if not built yet.",[56,305,306,309,310,313],{},[19,307,308],{},"current()"," — ",[19,311,312],{},"Optional\u003CMagicHttpClient>"," without throwing.",[56,315,316,319],{},[19,317,318],{},"refresh()"," — force a rebuild from the current config.",[56,321,322,325,326,288],{},[19,323,324],{},"binding()"," — the underlying ",[19,327,328],{},"MagicRuntimeConfigBinding",[56,330,331,334],{},[19,332,333],{},"name()"," — the runtime component name.",[56,336,337,340],{},[19,338,339],{},"close()"," — release the client (the runtime also closes it on shutdown).",[24,342,344],{"id":343},"smart-methods","Smart methods",[15,346,347,348,351],{},"Smart methods switch to async automatically on blocking-sensitive threads and\nreturn a ",[19,349,350],{},"CompletableFuture",":",[29,353,356],{"className":354,"code":355,"language":89,"meta":35},[87],"client.getSmart(\"status\").thenAccept(response -> {\n    \u002F\u002F ...\n});\n",[19,357,355],{"__ignoreMap":35},[24,359,361],{"id":360},"json-to-pojo","JSON to POJO",[29,363,366],{"className":364,"code":365,"language":89,"meta":35},[87],"public record StatusResponse(String status) {}\n\nStatusResponse response = client.getJson(\"status\", StatusResponse.class);\n",[19,367,365],{"__ignoreMap":35},[24,369,371],{"id":370},"json-post","JSON POST",[29,373,376],{"className":374,"code":375,"language":89,"meta":35},[87],"public record CreateRequest(String name) {}\n\nclient.postJson(\"items\", new CreateRequest(\"Test\"));\n",[19,377,375],{"__ignoreMap":35},[24,379,381],{"id":380},"multipart-upload","Multipart upload",[29,383,386],{"className":384,"code":385,"language":89,"meta":35},[87],"MultipartBody body = MultipartBody.builder()\n        .text(\"title\", \"Hello\")\n        .file(\"file\", Path.of(\"logo.png\"))\n        .build();\n\nclient.postMultipart(\"upload\", body);\n",[19,387,385],{"__ignoreMap":35},[24,389,391],{"id":390},"download-to-file","Download to file",[15,393,394],{},"Stream a response body straight to disk instead of into memory:",[29,396,399],{"className":397,"code":398,"language":89,"meta":35},[87],"HttpResponse\u003CPath> response = client.downloadToFile(\"dist\u002Fmod.jar\", Path.of(\"mod.jar\"));\n",[19,400,398],{"__ignoreMap":35},[15,402,403,404,407,408,411],{},"Like the other convenience methods it has ",[19,405,406],{},"downloadToFileAsync(...)"," and\n",[19,409,410],{},"downloadToFileSmart(...)"," variants.",[24,413,415],{"id":414},"websocket-client","WebSocket client",[15,417,418,421,422,425],{},[19,419,420],{},"MagicWebSocketClient"," wraps ",[19,423,424],{},"java.net.http.WebSocket"," with the same builder\npattern and config integration as the HTTP client.",[123,427,83],{"id":428},"basic-usage-1",[29,430,433],{"className":431,"code":432,"language":89,"meta":35},[87],"MagicWebSocketClient wsClient = MagicWebSocketClient.builder(platform, configManager)\n        .baseUrl(\"wss:\u002F\u002Fapi.example.com\u002Fws\")\n        .header(\"Authorization\", \"Bearer token\")\n        .subprotocols(List.of(\"v1\"))\n        .build();\n\nCompletableFuture\u003CWebSocket> ws = wsClient.connectAsync(\"\u002Fevents\", myListener);\n",[19,434,432],{"__ignoreMap":35},[123,436,438],{"id":437},"builder-options","Builder options",[15,440,441,442,351],{},"The builder supports the same options as ",[19,443,287],{},[53,445,446,452,461,467,473,479,485,491,504],{},[56,447,448,451],{},[19,449,450],{},"baseUrl(...)"," — base URL prepended to connect paths",[56,453,454,191,457,460],{},[19,455,456],{},"header(...)",[19,458,459],{},"headers(...)"," — default headers",[56,462,463,466],{},[19,464,465],{},"userAgent(...)"," — User-Agent header",[56,468,469,472],{},[19,470,471],{},"connectTimeout(...)"," — connection timeout",[56,474,475,478],{},[19,476,477],{},"followRedirects(...)"," — redirect policy",[56,480,481,484],{},[19,482,483],{},"subprotocols(...)"," — WebSocket subprotocol list",[56,486,487,490],{},[19,488,489],{},"logger(...)"," — platform logger for connection diagnostics",[56,492,493,191,496,499,500,503],{},[19,494,495],{},"config(...)",[19,497,498],{},"logging(...)"," — shared ",[19,501,502],{},"HttpClientConfig"," settings",[56,505,506,509,510],{},[19,507,508],{},"mapper(...)"," — custom Jackson ",[19,511,512],{},"ObjectMapper",[123,514,516],{"id":515},"connect-methods","Connect methods",[135,518,519,528],{},[138,520,521],{},[141,522,523,525],{},[144,524,146],{},[144,526,527],{},"Description",[151,529,530,540,553],{},[141,531,532,537],{},[156,533,534],{},[19,535,536],{},"connect(path, listener)",[156,538,539],{},"Synchronous connect (blocks).",[141,541,542,547],{},[156,543,544],{},[19,545,546],{},"connectAsync(path, listener)",[156,548,549,550,288],{},"Returns ",[19,551,552],{},"CompletableFuture\u003CWebSocket>",[141,554,555,560],{},[156,556,557],{},[19,558,559],{},"connectSmart(path, listener)",[156,561,562],{},"Async on blocking-sensitive threads, sync otherwise.",[123,564,95],{"id":565},"runtime-profiles-1",[15,567,568,569,572],{},"Use ",[19,570,571],{},"MagicWebSocketClientProfile"," for config-aware WebSocket clients that\nrebuild on config reload:",[29,574,577],{"className":575,"code":576,"language":89,"meta":35},[87],"MagicWebSocketClientProfile\u003CApiConfig> gateway = MagicWebSocketClientProfile\n        .builder(runtime, \"ws.gateway\", ApiConfig.class)\n        .sections(\"gateway\")\n        .baseUrl(config -> config.gateway.wsUrl)\n        .bearerAuth(config -> config.gateway.token)\n        .subprotocols(config -> config.gateway.subprotocols)\n        .build();\n\nMagicWebSocketClient client = gateway.require();\n",[19,578,576],{"__ignoreMap":35},[123,580,582],{"id":581},"cleanup","Cleanup",[15,584,585,586,589],{},"Call ",[19,587,588],{},"wsClient.close()"," to release resources. When using runtime profiles, the\nprofile handles cleanup automatically on config reload and runtime shutdown.",[24,591,593],{"id":592},"async-method-variants","Async method variants",[15,595,596,597,600],{},"Every convenience method on ",[19,598,599],{},"MagicHttpClient"," has three variants:",[135,602,603,613],{},[138,604,605],{},[141,606,607,610],{},[144,608,609],{},"Suffix",[144,611,612],{},"Behaviour",[151,614,615,626,638],{},[141,616,617,623],{},[156,618,619],{},[620,621,622],"em",{},"(none)",[156,624,625],{},"Synchronous. Throws on blocking-sensitive threads.",[141,627,628,633],{},[156,629,630],{},[19,631,632],{},"...Async(...)",[156,634,549,635,637],{},[19,636,350],{},". Always non-blocking.",[141,639,640,645],{},[156,641,642],{},[19,643,644],{},"...Smart(...)",[156,646,647],{},"Sync when safe, async when on a blocking-sensitive thread.",[15,649,650,651,654,655,654,658,654,661,654,664,667,668,654,671,288],{},"Available methods: ",[19,652,653],{},"get",", ",[19,656,657],{},"getJson",[19,659,660],{},"post",[19,662,663],{},"postJson",[19,665,666],{},"postMultipart",",\n",[19,669,670],{},"downloadToFile",[19,672,673],{},"send",[24,675,677],{"id":676},"notes","Notes",[53,679,680,690,697,703],{},[56,681,682,683,654,685,654,687,689],{},"Retries apply to the convenience methods (",[19,684,653],{},[19,686,660],{},[19,688,663],{},", etc.).",[56,691,692,693,696],{},"For raw ",[19,694,695],{},"send(...)",", retries are not automatic.",[56,698,699,700,288],{},"JSON mapping uses Jackson; invalid JSON throws ",[19,701,702],{},"IllegalStateException",[56,704,705,706,708,709,712,713,716],{},"Synchronous methods throw ",[19,707,702],{}," on blocking-sensitive\nthreads. Use ",[19,710,711],{},"...Async()"," or ",[19,714,715],{},"...Smart()"," in handlers.",{"title":35,"searchDepth":718,"depth":718,"links":719},3,[720,722,723,724,728,729,730,731,732,733,740,741],{"id":26,"depth":721,"text":27},2,{"id":40,"depth":721,"text":41},{"id":82,"depth":721,"text":83},{"id":94,"depth":721,"text":95,"children":725},[726,727],{"id":125,"depth":718,"text":126},{"id":291,"depth":718,"text":292},{"id":343,"depth":721,"text":344},{"id":360,"depth":721,"text":361},{"id":370,"depth":721,"text":371},{"id":380,"depth":721,"text":381},{"id":390,"depth":721,"text":391},{"id":414,"depth":721,"text":415,"children":734},[735,736,737,738,739],{"id":428,"depth":718,"text":83},{"id":437,"depth":718,"text":438},{"id":515,"depth":718,"text":516},{"id":565,"depth":718,"text":95},{"id":581,"depth":718,"text":582},{"id":592,"depth":721,"text":593},{"id":676,"depth":721,"text":677},"Runtime-aware HTTP and WebSocket clients in MagicUtils with JSON, retries, multipart, and named request profiles.","md",{},null,true,"\u002Fmodules\u002Fhttp-client","---\ntitle: HTTP Client\ndescription: Runtime-aware HTTP and WebSocket clients in MagicUtils with JSON, retries, multipart, and named request profiles.\n---\n\n# HTTP client\n\nMagicUtils HTTP client wraps `java.net.http.HttpClient` with config defaults,\nJSON mapping, retries, and a small multipart helper.\n\n## Dependency\n\n```kotlin\ndependencies {\n    implementation(\"dev.ua.theroer:magicutils-http-client:1.27.1\")\n}\n```\n\n## Configuration\n\nThe module stores settings in `http-client.{ext}` (JSONC by default on Fabric).\n\nKey sections:\n\n- `timeouts`: connect + request timeouts\n- `retry`: backoff settings and retry status codes\n- `logging`: request\u002Fresponse logging toggles\n- `defaults`: base URL, headers, HTTP version\n\n## Basic usage\n\n```java\nPlatform platform = new BukkitPlatformProvider(this);\nConfigManager configManager = new ConfigManager(platform);\n\nMagicHttpClient client = MagicHttpClient.builder(platform, configManager)\n        .baseUrl(\"https:\u002F\u002Fapi.example.com\u002F\")\n        .header(\"Authorization\", \"Bearer token\")\n        .build();\n\nHttpResponse\u003CString> response = client.get(\"status\");\n```\n\n## Runtime profiles\n\nWhen you already have a `MagicRuntime`, you can declare named HTTP\u002FWebSocket\nprofiles that rebuild automatically on config reload:\n\n```java\nMagicHttpClientProfile\u003CApiConfig> monitoring = MagicHttpClientProfile\n        .builder(runtime, \"http.monitoring\", ApiConfig.class)\n        .sections(\"monitoring\")\n        .baseUrl(config -> config.monitoring.baseUrl)\n        .bearerAuth(config -> config.monitoring.token)\n        .build();\n\nMagicHttpClient client = monitoring.require();\nMagicHttpClient sameClient = runtime.requireNamedComponent(\"http.monitoring\", MagicHttpClient.class);\n```\n\n```java\nMagicWebSocketClientProfile\u003CApiConfig> gateway = MagicWebSocketClientProfile\n        .builder(runtime, \"ws.gateway\", ApiConfig.class)\n        .sections(\"gateway\")\n        .baseUrl(config -> config.gateway.baseUrl)\n        .bearerAuth(config -> config.gateway.token)\n        .subprotocols(config -> config.gateway.subprotocols)\n        .build();\n```\n\nThis removes the usual `close old client -> build new client -> swap references`\nboilerplate from service reload paths.\n\n### Profile builder options\n\nEvery builder setter takes a `Function\u003CC, ...>` of your config type, so the value\nis recomputed each time the profile rebuilds on reload:\n\n| Method | Purpose |\n| --- | --- |\n| `sections(String...)` | Config sections the profile binds to. |\n| `baseUrl(cfg -> ...)` | Base URL. |\n| `userAgent(cfg -> ...)` | User-Agent header. |\n| `header(name, value)` \u002F `header(name, cfg -> ...)` | A static or config-derived header. |\n| `headers(cfg -> Map)` | A map of headers. |\n| `bearerAuth(cfg -> token)` | Adds an `Authorization: Bearer` header. |\n| `connectTimeout(cfg -> Duration)` | Connection timeout. |\n| `requestTimeout(cfg -> Duration)` | Per-request timeout. |\n| `followRedirects(cfg -> Boolean)` | Redirect policy. |\n| `version(cfg -> HttpClient.Version)` | HTTP version. |\n| `logger(cfg -> PlatformLogger)` | Logger for diagnostics. |\n| `configure(cfg, builder -> ...)` \u002F `configure(builder -> ...)` | Escape hatch onto the underlying `MagicHttpClient.Builder`. |\n\n### Profile instance methods\n\nOnce built, the profile is a small handle around the live client:\n\n- `require()` — the current client, or throw if not built yet.\n- `current()` — `Optional\u003CMagicHttpClient>` without throwing.\n- `refresh()` — force a rebuild from the current config.\n- `binding()` — the underlying `MagicRuntimeConfigBinding`.\n- `name()` — the runtime component name.\n- `close()` — release the client (the runtime also closes it on shutdown).\n\n## Smart methods\n\nSmart methods switch to async automatically on blocking-sensitive threads and\nreturn a `CompletableFuture`:\n\n```java\nclient.getSmart(\"status\").thenAccept(response -> {\n    \u002F\u002F ...\n});\n```\n\n## JSON to POJO\n\n```java\npublic record StatusResponse(String status) {}\n\nStatusResponse response = client.getJson(\"status\", StatusResponse.class);\n```\n\n## JSON POST\n\n```java\npublic record CreateRequest(String name) {}\n\nclient.postJson(\"items\", new CreateRequest(\"Test\"));\n```\n\n## Multipart upload\n\n```java\nMultipartBody body = MultipartBody.builder()\n        .text(\"title\", \"Hello\")\n        .file(\"file\", Path.of(\"logo.png\"))\n        .build();\n\nclient.postMultipart(\"upload\", body);\n```\n\n## Download to file\n\nStream a response body straight to disk instead of into memory:\n\n```java\nHttpResponse\u003CPath> response = client.downloadToFile(\"dist\u002Fmod.jar\", Path.of(\"mod.jar\"));\n```\n\nLike the other convenience methods it has `downloadToFileAsync(...)` and\n`downloadToFileSmart(...)` variants.\n\n## WebSocket client\n\n`MagicWebSocketClient` wraps `java.net.http.WebSocket` with the same builder\npattern and config integration as the HTTP client.\n\n### Basic usage\n\n```java\nMagicWebSocketClient wsClient = MagicWebSocketClient.builder(platform, configManager)\n        .baseUrl(\"wss:\u002F\u002Fapi.example.com\u002Fws\")\n        .header(\"Authorization\", \"Bearer token\")\n        .subprotocols(List.of(\"v1\"))\n        .build();\n\nCompletableFuture\u003CWebSocket> ws = wsClient.connectAsync(\"\u002Fevents\", myListener);\n```\n\n### Builder options\n\nThe builder supports the same options as `MagicHttpClient.Builder`:\n\n- `baseUrl(...)` — base URL prepended to connect paths\n- `header(...)` \u002F `headers(...)` — default headers\n- `userAgent(...)` — User-Agent header\n- `connectTimeout(...)` — connection timeout\n- `followRedirects(...)` — redirect policy\n- `subprotocols(...)` — WebSocket subprotocol list\n- `logger(...)` — platform logger for connection diagnostics\n- `config(...)` \u002F `logging(...)` — shared `HttpClientConfig` settings\n- `mapper(...)` — custom Jackson `ObjectMapper`\n\n### Connect methods\n\n| Method | Description |\n| --- | --- |\n| `connect(path, listener)` | Synchronous connect (blocks). |\n| `connectAsync(path, listener)` | Returns `CompletableFuture\u003CWebSocket>`. |\n| `connectSmart(path, listener)` | Async on blocking-sensitive threads, sync otherwise. |\n\n### Runtime profiles\n\nUse `MagicWebSocketClientProfile` for config-aware WebSocket clients that\nrebuild on config reload:\n\n```java\nMagicWebSocketClientProfile\u003CApiConfig> gateway = MagicWebSocketClientProfile\n        .builder(runtime, \"ws.gateway\", ApiConfig.class)\n        .sections(\"gateway\")\n        .baseUrl(config -> config.gateway.wsUrl)\n        .bearerAuth(config -> config.gateway.token)\n        .subprotocols(config -> config.gateway.subprotocols)\n        .build();\n\nMagicWebSocketClient client = gateway.require();\n```\n\n### Cleanup\n\nCall `wsClient.close()` to release resources. When using runtime profiles, the\nprofile handles cleanup automatically on config reload and runtime shutdown.\n\n## Async method variants\n\nEvery convenience method on `MagicHttpClient` has three variants:\n\n| Suffix | Behaviour |\n| --- | --- |\n| _(none)_ | Synchronous. Throws on blocking-sensitive threads. |\n| `...Async(...)` | Returns `CompletableFuture`. Always non-blocking. |\n| `...Smart(...)` | Sync when safe, async when on a blocking-sensitive thread. |\n\nAvailable methods: `get`, `getJson`, `post`, `postJson`, `postMultipart`,\n`downloadToFile`, `send`.\n\n## Notes\n\n- Retries apply to the convenience methods (`get`, `post`, `postJson`, etc.).\n- For raw `send(...)`, retries are not automatic.\n- JSON mapping uses Jackson; invalid JSON throws `IllegalStateException`.\n- Synchronous methods throw `IllegalStateException` on blocking-sensitive\n  threads. Use `...Async()` or `...Smart()` in handlers.\n",{"title":5,"description":742},"modules\u002Fhttp-client","j61GVKcJ8JOJ4vd4X2xtWlwL7ftp9F8H2TbcJwRbOkw",1783944487820]