--- layout: default title: KissRequests AI Skill library: kiss-requests skill_version: 0.1.0 release_version: 0.1.0 maven: io.github.arthurhoch:kiss-requests:0.1.0 java: "17+" format: markdown --- # KissRequests AI Skill v0.1.0 This Markdown file is a versioned AI skill for using **KissRequests** in other Java projects. It is intentionally self-contained so an AI assistant can load this one document, add the Maven dependency, write consumer code, and avoid inventing APIs. Use this skill for release **0.1.0**. If the repository source is on a later -SNAPSHOT, consumer documentation should still use 0.1.0 unless the user explicitly asks for a snapshot build. ## Library Summary Tiny zero-dependency Java 17+ HTTP client facade over `java.net.http.HttpClient` with prepared calls, retries, file upload/download, streaming, multipart, and curl rendering. ## Maven Dependency ~~~xml io.github.arthurhoch kiss-requests 0.1.0 ~~~ ## AI Usage Rules - Target Java 17 or newer. - Prefer the public package rooted at io.github.arthurhoch.kissrequests. - Do not invent convenience APIs. Use only the public members listed in this file or in generated Javadocs for the same release. - Keep examples small and explicit, matching the KISS philosophy. - Do not add extra frameworks unless the consuming project already uses them. - Keep older skill files in place when a new release is documented. ## Quick Example ~~~java Http http = Http.create(); HttpResult result = http.request(HttpMethod.GET, "https://example.com") .execute(); System.out.println(result.statusCode()); System.out.println(result.body()); ~~~ ## How To Use The Library - `Http.create()` creates a reusable client with default timeout, retry, concurrency, and executor settings. - `Http.builder()` customizes shared client behavior. Build one instance and reuse it for a target integration. - Every request factory returns an immutable `HttpCall`. Nothing is sent until `execute()` is called. - Use `toCurl()` or `toCurlBase64()` for diagnostics before executing or when reporting an error. Treat curl output as sensitive when headers or bodies contain secrets. - Use string method constants from `HttpMethod`; custom methods are still accepted as nonblank strings. - Do not import `io.github.arthurhoch.kissrequests.internal.*` in consumer projects. Those classes are implementation details, even though some are public for package architecture. ## Behavioral Contract For v0.1.0 - `HttpMethod` is a constants class, not an enum. Constants are `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, and `OPTIONS`. - `RetryPolicy.defaults()` performs one attempt. `RetryPolicy.of(...)` enables conservative retries for status codes 429, 500, 502, 503, and 504 on idempotent methods by default. - `HttpResult` is used by text, upload, and multipart calls. `HttpDownloadResult` stores the target path and byte count. `HttpStreamResult` exposes an `InputStream`; the caller owns closing it. - `HttpException` carries method, URL, curl command, attempts, total duration, response status, headers, truncated response body, and root cause. Use `report()` for human diagnostics. - `upload` streams one file as the request body. `download` writes the response body to disk. `multipart` builds a `multipart/form-data` request from text fields and file fields. - Headers maps passed to the API may be `null`; null headers are treated as empty. Method and URL must be non-null and nonblank. ## Practical Examples ### Reusable client with retries ~~~java Http http = Http.builder() .connectTimeout(Duration.ofSeconds(5)) .requestTimeout(Duration.ofSeconds(20)) .retryPolicy(RetryPolicy.of(3, Duration.ofMillis(250))) .maxConcurrentRequests(32) .build(); ~~~ ### File download ~~~java Path target = Path.of("response.bin"); HttpDownloadResult download = http .download(HttpMethod.GET, "https://example.com/file", Map.of(), target) .execute(); System.out.println(download.bytesWritten()); ~~~ ## Public API Specification The following index is generated from compiled public classes with javap -public. It includes public constructors, constants, enum methods, record accessors, inherited Object overrides when public, and public nested classes. When an internal package appears here, treat it as implementation detail unless the project documentation explicitly says otherwise. Consumer code should prefer the public surface described above. ~~~text public final class io.github.arthurhoch.kissrequests.Http { public static io.github.arthurhoch.kissrequests.Http create(); public static io.github.arthurhoch.kissrequests.Http$Builder builder(); public io.github.arthurhoch.kissrequests.HttpCall request(java.lang.String, java.lang.String); public io.github.arthurhoch.kissrequests.HttpCall request(java.lang.String, java.lang.String, java.util.Map); public io.github.arthurhoch.kissrequests.HttpCall request(java.lang.String, java.lang.String, java.util.Map, java.lang.String); public io.github.arthurhoch.kissrequests.HttpCall upload(java.lang.String, java.lang.String, java.util.Map, java.nio.file.Path); public io.github.arthurhoch.kissrequests.HttpCall download(java.lang.String, java.lang.String, java.util.Map, java.nio.file.Path); public io.github.arthurhoch.kissrequests.HttpCall stream(java.lang.String, java.lang.String, java.util.Map, java.lang.String); public io.github.arthurhoch.kissrequests.HttpCall multipart(java.lang.String, java.lang.String, java.util.Map, java.util.Map, java.util.Map); } public final class io.github.arthurhoch.kissrequests.Http$Builder { public io.github.arthurhoch.kissrequests.Http$Builder(); public io.github.arthurhoch.kissrequests.Http$Builder connectTimeout(java.time.Duration); public io.github.arthurhoch.kissrequests.Http$Builder requestTimeout(java.time.Duration); public io.github.arthurhoch.kissrequests.Http$Builder retryPolicy(io.github.arthurhoch.kissrequests.RetryPolicy); public io.github.arthurhoch.kissrequests.Http$Builder maxConcurrentRequests(int); public io.github.arthurhoch.kissrequests.Http$Builder executor(java.util.concurrent.Executor); public io.github.arthurhoch.kissrequests.Http build(); } public final class io.github.arthurhoch.kissrequests.HttpAttempt extends java.lang.Record { public io.github.arthurhoch.kissrequests.HttpAttempt(int, int, java.time.Duration, java.lang.String); public final java.lang.String toString(); public final int hashCode(); public final boolean equals(java.lang.Object); public int attemptNumber(); public int statusCode(); public java.time.Duration duration(); public java.lang.String failureMessage(); } public final class io.github.arthurhoch.kissrequests.HttpCall { public T execute(); public java.lang.String toCurl(); public java.lang.String toCurlBase64(); public java.lang.String method(); public java.lang.String url(); public java.util.Map headers(); public java.lang.String body(); public java.nio.file.Path file(); public java.nio.file.Path targetPath(); public java.util.Map fields(); public java.util.Map fileFields(); public io.github.arthurhoch.kissrequests.internal.CallType callType(); } public final class io.github.arthurhoch.kissrequests.HttpConfig extends java.lang.Record { public io.github.arthurhoch.kissrequests.HttpConfig(java.time.Duration, java.time.Duration, io.github.arthurhoch.kissrequests.RetryPolicy, int, java.util.concurrent.Executor); public static io.github.arthurhoch.kissrequests.HttpConfig defaults(); public final java.lang.String toString(); public final int hashCode(); public final boolean equals(java.lang.Object); public java.time.Duration connectTimeout(); public java.time.Duration requestTimeout(); public io.github.arthurhoch.kissrequests.RetryPolicy retryPolicy(); public int maxConcurrentRequests(); public java.util.concurrent.Executor executor(); } public final class io.github.arthurhoch.kissrequests.HttpDownloadResult extends java.lang.Record { public io.github.arthurhoch.kissrequests.HttpDownloadResult(int, java.util.Map>, java.nio.file.Path, long, java.time.Duration, java.util.List, java.lang.String, java.lang.String); public final java.lang.String toString(); public final int hashCode(); public final boolean equals(java.lang.Object); public int statusCode(); public java.util.Map> headers(); public java.nio.file.Path file(); public long bytesWritten(); public java.time.Duration duration(); public java.util.List attempts(); public java.lang.String method(); public java.lang.String url(); } public class io.github.arthurhoch.kissrequests.HttpException extends java.lang.RuntimeException { public io.github.arthurhoch.kissrequests.HttpException(java.lang.String, java.lang.String, java.lang.String, java.util.List, java.time.Duration, int, java.util.Map>, java.lang.String, java.lang.Throwable); public java.lang.String method(); public java.lang.String url(); public java.lang.String curl(); public java.util.List attempts(); public java.time.Duration totalDuration(); public int statusCode(); public java.util.Map> responseHeaders(); public java.lang.String responseBody(); public synchronized java.lang.Throwable getCause(); public java.lang.Throwable rootCause(); public java.lang.String report(); public static java.lang.String truncateBody(java.lang.String); } public final class io.github.arthurhoch.kissrequests.HttpMethod { public static final java.lang.String GET; public static final java.lang.String POST; public static final java.lang.String PUT; public static final java.lang.String DELETE; public static final java.lang.String PATCH; public static final java.lang.String HEAD; public static final java.lang.String OPTIONS; } public final class io.github.arthurhoch.kissrequests.HttpResult extends java.lang.Record { public io.github.arthurhoch.kissrequests.HttpResult(int, java.util.Map>, java.lang.String, java.time.Duration, java.util.List, java.lang.String, java.lang.String); public final java.lang.String toString(); public final int hashCode(); public final boolean equals(java.lang.Object); public int statusCode(); public java.util.Map> headers(); public java.lang.String body(); public java.time.Duration duration(); public java.util.List attempts(); public java.lang.String method(); public java.lang.String url(); } public final class io.github.arthurhoch.kissrequests.HttpStreamResult extends java.lang.Record { public io.github.arthurhoch.kissrequests.HttpStreamResult(int, java.util.Map>, java.io.InputStream, java.time.Duration, java.util.List, java.lang.String, java.lang.String); public final java.lang.String toString(); public final int hashCode(); public final boolean equals(java.lang.Object); public int statusCode(); public java.util.Map> headers(); public java.io.InputStream inputStream(); public java.time.Duration duration(); public java.util.List attempts(); public java.lang.String method(); public java.lang.String url(); } public final class io.github.arthurhoch.kissrequests.RetryPolicy { public static io.github.arthurhoch.kissrequests.RetryPolicy defaults(); public static io.github.arthurhoch.kissrequests.RetryPolicy of(int); public static io.github.arthurhoch.kissrequests.RetryPolicy of(int, java.time.Duration); public static io.github.arthurhoch.kissrequests.RetryPolicy of(int, java.time.Duration, java.util.Set, java.util.Set); public int maxAttempts(); public java.time.Duration initialBackoff(); public java.util.Set retryOnStatusCodes(); public java.util.Set retryOnMethods(); public boolean shouldRetryOnStatus(int); public boolean shouldRetryMethod(java.lang.String); } public final class io.github.arthurhoch.kissrequests.internal.CallType extends java.lang.Enum { public static final io.github.arthurhoch.kissrequests.internal.CallType TEXT; public static final io.github.arthurhoch.kissrequests.internal.CallType UPLOAD; public static final io.github.arthurhoch.kissrequests.internal.CallType DOWNLOAD; public static final io.github.arthurhoch.kissrequests.internal.CallType STREAM; public static final io.github.arthurhoch.kissrequests.internal.CallType MULTIPART; public static io.github.arthurhoch.kissrequests.internal.CallType[] values(); public static io.github.arthurhoch.kissrequests.internal.CallType valueOf(java.lang.String); } public final class io.github.arthurhoch.kissrequests.internal.ConcurrencyLimiter { public io.github.arthurhoch.kissrequests.internal.ConcurrencyLimiter(int); public io.github.arthurhoch.kissrequests.internal.ConcurrencyLimiter$Token acquire() throws java.lang.InterruptedException; } public final class io.github.arthurhoch.kissrequests.internal.ConcurrencyLimiter$Token { public void release(); } public final class io.github.arthurhoch.kissrequests.internal.CurlRenderer { public static java.lang.String render(io.github.arthurhoch.kissrequests.HttpCall); } public final class io.github.arthurhoch.kissrequests.internal.HttpExecutionEngine { public io.github.arthurhoch.kissrequests.internal.HttpExecutionEngine(java.net.http.HttpClient, io.github.arthurhoch.kissrequests.HttpConfig); public java.lang.Object execute(io.github.arthurhoch.kissrequests.HttpCall); } public final class io.github.arthurhoch.kissrequests.internal.MultipartBodyBuilder { public static java.lang.String generateBoundary(); public static java.net.http.HttpRequest$BodyPublisher buildBody(java.util.Map, java.util.Map, java.lang.String) throws java.io.IOException; public static long computeContentLength(java.util.Map, java.util.Map, java.lang.String); } ~~~ ## Local Verification Commands Run these commands in the KissRequests repository when changing examples, docs, or release skill files: ~~~bash mvn -B clean verify mvn -B javadoc:javadoc ~~~ ## Release Skill Maintenance For a future release such as 0.2.0, create a new file at docs/skills/v0.2.0.md instead of editing or deleting this historical file. Update docs/skills/index.md with a view link and a raw download link for the new version.