# Io / Github / Biezhi / Java11 / Http

## Overview

This package contains Java 11-style HTTP client usage examples using the new `java.net.http` module introduced in JDK 11. It demonstrates how to perform synchronous and asynchronous HTTP requests (GET and POST), download and upload files, configure proxy and basic authentication settings, and enable HTTP/2 support. The package serves as a reference for leveraging the modern, non-blocking HTTP client API that replaced the older, cumbersome `java.net.URL`-based approach.

## Key Classes

### `Example`

**File:** [src/main/java/io/github/biezhi/java11/http/Example.java](src/main/java/io/github/biezhi/java11/http/Example.java)

The central class of this package, `Example` is a collection of static convenience methods that each demonstrate a different HTTP client capability available in Java 11's `HttpClient` API. Every method creates its own `HttpClient` instance (either via the convenience `newHttpClient()` factory or a `HttpClient.Builder`), constructs an `HttpRequest` using the `HttpRequest.Builder` fluent API, and then sends the request.

This class does not represent a reusable HTTP client for application use. It is a self-contained reference illustrating idiomatic patterns for the JDK 11 HTTP API — each method is a standalone, copy-paste-able example.

#### Methods

##### `syncGet(String uri)`

Performs a synchronous GET request to the given URI.

- **Parameter:** `uri` — a URL string (e.g. `"https://biezhi.me"`) that gets converted to a `URI` via `URI.create()`.
- **Returns:** `void` (results are printed to `System.out`).
- **Side effects:** Prints the HTTP status code and response body to standard output.
- **How it works:** Creates a default `HttpClient`, builds a `HttpRequest` with `.uri()` and `.build()`, then calls `client.send(request, HttpResponse.BodyHandlers.ofString())` which blocks until the full response body is received as a `String`.

##### `asyncGet(String uri)`

Performs an asynchronous GET request to the given URI using `CompletableFuture`.

- **Parameter:** `uri` — a URL string.
- **Returns:** `void`.
- **Side effects:** Prints the response body and status code. Blocks the calling thread with `.join()` to wait for completion.
- **How it works:** Uses `client.sendAsync(request, HttpResponse.BodyHandlers.ofString())` to obtain a `CompletableFuture<HttpResponse<String>>`. Attaches a `whenComplete` callback to handle either the response body (on success) or stack trace (on failure), then calls `.join()` to block until the future completes.

##### `asyncPost()`

Performs an asynchronous POST request with a JSON body.

- **Parameter:** none.
- **Returns:** `void`.
- **Side effects:** Posts a serialized `Foo` object (with `name` and `url` fields) to `https://httpbin.org/post` via Gson, and prints the server's echoed response.
- **How it works:** Serializes a `Foo` instance into JSON using Gson, builds a `HttpRequest` with `.header("Content-Type", "application/json")` and `.POST(HttpRequest.BodyPublishers.ofString(jsonBody))`, then sends it asynchronously with the same `sendAsync` + `whenComplete` + `join` pattern as `asyncGet`.

##### `downloadFile()`

Downloads a web page from a URL and saves it to a temporary file.

- **Parameter:** none (hardcoded to `https://labs.consol.de/`).
- **Returns:** `void`.
- **Side effects:** Creates a temp file in the OS temporary directory with prefix `consol-labs-home` and suffix `.html`, and writes the HTTP response body to it. Prints the temp file path.
- **How it works:** Uses `HttpResponse.BodyHandlers.ofFile(tempFile)` as the body handler, which writes the response body directly to disk instead of loading it into memory.

##### `uploadFile()`

Uploads a local file via HTTP POST.

- **Parameter:** none (hardcoded to `/tmp/files-to-upload.txt` POSTed to `http://localhost:8080/upload/`).
- **Returns:** `void`.
- **Side effects:** Reads the local file and streams it as the POST body. Prints the response status code.
- **How it works:** Uses `HttpRequest.BodyPublishers.ofFile(Paths.get("/tmp/files-to-upload.txt"))` as the body publisher, and `HttpResponse.BodyHandlers.discarding()` since the response body is not needed.

##### `proxy()`

Configures and uses an HTTP proxy for a request.

- **Parameter:** none (hardcoded proxy at `127.0.0.1:1080`).
- **Returns:** `void`.
- **Side effects:** Makes a GET request to `https://www.google.com` through the configured proxy.
- **How it works:** Builds the client with `HttpClient.newBuilder().proxy(ProxySelector.of(new InetSocketAddress("127.0.0.1", 1080))).build()` rather than using the convenience `newHttpClient()` factory. All subsequent requests on this client route through the proxy.

##### `basicAuth()`

Configures Basic Authentication on the HTTP client.

- **Parameter:** none (hardcoded credentials: username `"username"`, password `"password"`).
- **Returns:** `void`.
- **Side effects:** Attaches an `Authenticator` to the client that supplies credentials on 401 challenges.
- **How it works:** Uses `HttpClient.newBuilder().authenticator(new Authenticator() { ... })` to register a callback that returns a `PasswordAuthentication` whenever the server requests authentication. This is applied transparently to all requests sent by the client.

##### `http2()`

Makes an HTTP/2 request with normal redirect handling.

- **Parameter:** none (hardcoded to `https://http2.akamai.com/demo`).
- **Returns:** `void`.
- **Side effects:** Prints the response body and status code of an HTTP/2 page.
- **How it works:** Configures the client with `.version(HttpClient.Version.HTTP_2)` to prefer HTTP/2, and `.followRedirects(HttpClient.Redirect.NORMAL)` to follow redirects once (but not across protocol changes). Sends an async GET request to the Akamai HTTP/2 demo endpoint.

##### `getURIs(List<URI> uris)`

Sends multiple HTTP GET requests in parallel.

- **Parameter:** `uris` — a `List<URI>` where each URI represents a target endpoint.
- **Returns:** `void`.
- **Side effects:** Blocks until all requests complete (using `CompletableFuture.allOf().join()`). Discards response bodies.
- **How it works:** Maps each `URI` to an `HttpRequest` using the builder, then streams those requests into `client.sendAsync(...)` calls. `CompletableFuture.allOf(...).join()` waits for all futures to complete before returning. This demonstrates concurrent, non-blocking batch requests without needing an external parallelism library.

##### `main(String[] args)`

The entry point. Currently commented out except for `asyncPost()`, which is the only invoked example.

#### Design notes

- Every method uses `HttpClient.newHttpClient()` or a builder, meaning **no connection pooling is shared** between calls. In production code you would typically create one `HttpClient` instance and reuse it across requests.
- The package uses **blocking `.join()`** on `CompletableFuture` results, so while the HTTP call itself is async, the calling thread still waits for the result. This keeps the examples simple but is not a true fire-and-forget pattern.
- Gson is used for JSON serialization in `asyncPost()`, making `com.google.gson.Gson` a runtime dependency.

---

### `Foo`

**File:** [src/main/java/io/github/biezhi/java11/http/Foo.java](src/main/java/io/github/biezhi/java11/http/Foo.java)

A simple plain-old-Java-object (POJO) with two public fields: `name` (String) and `url` (String). It has no methods, constructors, or annotations. Its sole purpose is to serve as a serializable body for the `asyncPost()` example — the `Gson` library converts it into a JSON string via `gson.toJson(foo)`, which is then sent as the POST request body to `https://httpbin.org/post`.

The class is intentionally minimal: no getters, no `toString`, no `equals`/`hashCode` — it exists purely as a test fixture for demonstrating body serialization.

---

## How It Works

### Request lifecycle

Each method in `Example` follows the same general pattern, which maps directly to the JDK 11 `java.net.http` API design:

```mermaid
flowchart TD
    A["Create HttpClient
(newHttpClient() or Builder)"] --> B["Build HttpRequest
via HttpRequest.Builder"]
    B --> C["Set body publisher
(GET/POST/BodyPublishers)"]
    B --> D["Set headers
(.header())"]
    C --> E["Send request
(send or sendAsync)"]
    D --> E
    E --> F["Specify body handler
(ofString / ofFile / discarding)"]
    F --> G["Handle response
(statusCode + body)"]
```

1. **Client creation** — Either `HttpClient.newHttpClient()` for defaults, or `HttpClient.newBuilder()` for customizing proxy, authenticator, version, etc.
2. **Request building** — `HttpRequest.newBuilder().uri(uri).method("GET", BodyPublishers.noBody()).build()` (or `.POST(...)` for POST).
3. **Sending** — `client.send()` for synchronous blocking, `client.sendAsync()` for non-blocking with `CompletableFuture`.
4. **Body handling** — The `HttpResponse.BodyHandler` determines how the response body is materialized: `ofString()` into memory, `ofFile(path)` to disk, or `discarding()` to ignore it.
5. **Completion** — For async calls, `whenComplete()` handles success/failure, and `join()` blocks until done.

### Request/response flow (GET example)

Tracing `syncGet` end-to-end:

1. The caller passes a URI string like `"https://biezhi.me"`.
2. `HttpClient.newHttpClient()` creates a client with default settings (connection pool enabled, HTTP/1.1 or HTTP/2 based on server support, follow redirects automatically).
3. `HttpRequest.newBuilder().uri(URI.create(uri)).build()` creates a GET request (GET is the default method).
4. `client.send(request, HttpResponse.BodyHandlers.ofString())` sends the request on the calling thread and blocks until the full response (headers + body) arrives.
5. The `HttpResponse<String>` is returned with `statusCode()` (e.g. 200) and `body()` (the HTML/text content).
6. These are printed to stdout.

### Concurrent batch requests (`getURIs`)

The `getURIs` method demonstrates a pattern for fire-and-forget parallelism:

1. Each `URI` in the input list is converted to an `HttpRequest`.
2. Each request is dispatched via `client.sendAsync(..., BodyHandlers.ofString())`, returning a `CompletableFuture<HttpResponse<String>>`.
3. `CompletableFuture.allOf(...)` creates a single composite future that completes when **all** individual request futures complete.
4. `.join()` blocks the calling thread until every request is done.

Note: the response bodies from `getURIs` are discarded (the futures are awaited but never inspected). In production code you would collect the results with `.thenApply()` or `.thenAccept()` on each future.

---

## Data Model

The only data class in this package is `Foo`:

| Field | Type   | Purpose                                          |
|-------|--------|--------------------------------------------------|
| `name` | String | A name identifier (used as test data in POST body) |
| `url`  | String | A URL string (used as test data in POST body)     |

`Foo` is serialized to JSON by Gson:
```json
{"name":"王爵nice","url":"https://github.com/biezhi"}
```
and sent as the request body of `asyncPost()`.

---

## Dependencies and Integration

### External dependencies

| Dependency | Used in | Purpose |
|------------|---------|---------|
| `com.google.gson.Gson` | `Example.asyncPost()` | JSON serialization of `Foo` objects into POST bodies |

### JDK built-in types used

| Type | Role |
|------|------|
| `java.net.http.HttpClient` | Core HTTP client; handles connection management, redirects, proxy, and protocol version |
| `java.net.http.HttpRequest` | Immutable HTTP request representation; built via fluent builder |
| `java.net.http.HttpResponse<T>` | Immutable response; contains status code, headers, and typed body |
| `java.net.http.HttpClient.Builder` | Configurable client builder (proxy, authenticator, version, redirect policy) |
| `java.net.http.HttpRequest.BodyPublishers` | Factory for request body sources (string, file, no body, etc.) |
| `java.net.http.HttpResponse.BodyHandlers` | Factory for response body handlers (string, file, discarding, etc.) |
| `java.util.concurrent.CompletableFuture` | Async request completion and parallel orchestration |
| `java.net.Authenticator` / `java.net.PasswordAuthentication` | Basic HTTP authentication callback |
| `java.net.ProxySelector` / `java.net.InetSocketAddress` | Proxy configuration |

### Cross-module relationships

This package is a self-contained examples package within the broader `io.github.biezhi.java11` module hierarchy. It does not import or depend on other packages in the project — all logic is self-contained within `Example.java` and `Foo.java`.

---

## Notes for Developers

### `HttpClient` reuse

Every method creates a new `HttpClient` instance. In real applications, `HttpClient` is **thread-safe and designed to be reused**. Creating one shared instance and calling `send`/`sendAsync` repeatedly is the correct pattern for production code. The per-call instantiation here is intentional for example clarity.

### Async calls still block

All async examples (`asyncGet`, `asyncPost`, `http2`, `getURIs`) call `.join()` on the `CompletableFuture`. This means the calling thread is blocked until the HTTP round-trip completes. For truly non-blocking code, you would return the `CompletableFuture` to the caller and let them decide when (or whether) to wait.

### Default redirect behavior

`HttpClient.newHttpClient()` follows redirects automatically using `HttpClient.Redirect.ALWAYS`. When you need more control (e.g. redirect only on same protocol), use `HttpClient.Builder.followRedirects(HttpClient.Redirect.NORMAL)` as shown in `http2()`.

### Body handlers matter

The choice of `BodyHandler` has memory implications:
- `ofString()` loads the entire body into a Java `String` — fine for small responses but risky for large files.
- `ofFile(path)` streams directly to disk — use for large downloads.
- `discarding()` ignores the body entirely — use for requests where you only need the status code.

### Authentication is per-client

The `Authenticator` is configured on the `HttpClient.Builder`, not per-request. Once registered, **all** requests from that client will use the supplied credentials when challenged by a server returning 401.

### HTTP/2 support

The HTTP/2 example explicitly sets `.version(HttpClient.Version.HTTP_2)`. By default, `HttpClient` negotiates the best available protocol (HTTP/1.1 or HTTP/2) via ALPN. Explicit version setting is useful for testing or when you have a strict protocol requirement.

### Gson dependency

`asyncPost()` depends on Gson for JSON serialization. If you are using this package as a template, you can replace Gson with any JSON library (Jackson, Moshi, etc.) or manually construct the JSON string.

### File upload paths

The `uploadFile()` example uses a hardcoded absolute path (`/tmp/files-to-upload.txt`). In practice you would resolve this path relative to the application's working directory or pass it as a parameter.
