Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java HTTP client project: a real local response and status-aware handling

Last updated: 29 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

An HTTP client needs separate rules for transport failure, HTTP status, response decoding and the application value carried by a successful body.

Download Java source kit

Java 11+. This is a complete program using JDK classes.

Test a response, not just a request builder

This project starts a loopback-only server on an operating-system selected port, sends a GET request with the Java HTTP client and checks both a success response and a missing route. It makes no request to an external domain.

The request has a timeout and the client has a connect timeout. Redirects are disabled. A response with status 404 is still an HTTP response; it is not the same as an exception caused by connection failure. Only a success body should be interpreted as a quote value under the application grammar.

The server returns a fixed UTF-8 body and content type. The sample reads that small body as a String. An untrusted service can send a much larger body, so a production client needs a response-size limit during consumption rather than after allocating the complete String.

Choose retry semantics before adding retries

Repeating a GET can be reasonable under the service contract, but retrying a state-changing request can duplicate an operation that already committed. Set an overall budget, classify failures and use the server’s idempotency contract rather than treating every timeout as evidence that nothing happened.

Working program

Java
import com.sun.net.httpserver.HttpServer;
import java.net.*;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.concurrent.*;
public class LocalQuoteHttpProject {
    public static void main(String[] args) throws Exception {
        HttpServer server=HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(),0),0);
        ExecutorService workers=Executors.newSingleThreadExecutor();server.setExecutor(workers);
        server.createContext("/quote",exchange->{
            byte[] body="quote=150".getBytes(StandardCharsets.UTF_8);
            exchange.getResponseHeaders().set("Content-Type","text/plain; charset=utf-8");
            exchange.sendResponseHeaders(200,body.length);
            try(java.io.OutputStream output=exchange.getResponseBody()){output.write(body);}
            finally{exchange.close();}
        });
        server.start();
        try{
            String host=server.getAddress().getAddress().getHostAddress();int port=server.getAddress().getPort();
            HttpClient client=HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(2)).followRedirects(HttpClient.Redirect.NEVER).build();
            URI quote=new URI("http",null,host,port,"/quote",null,null);
            HttpRequest request=HttpRequest.newBuilder(quote).timeout(Duration.ofSeconds(2)).GET().build();
            HttpResponse<String> response=client.send(request,HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
            System.out.println(response.statusCode()+":"+response.body());
            URI missing=new URI("http",null,host,port,"/missing",null,null);
            System.out.println(client.send(HttpRequest.newBuilder(missing).timeout(Duration.ofSeconds(2)).build(),HttpResponse.BodyHandlers.discarding()).statusCode());
        }finally{
            server.stop(0);workers.shutdownNow();
            if(!workers.awaitTermination(2,TimeUnit.SECONDS))throw new IllegalStateException("HTTP workers retained");
        }
    }
}

Output

Output
200:quote=150
404

Costs and boundaries

Request and response costs depend on the body sizes, peer behavior and network. This fixture tests local correctness and does not measure external latency or TLS behavior. For n body bytes, String body handling uses O(n) storage. Both the server and its owned worker pool are stopped in cleanup.

Common Mistakes

  • A 404 response is not a transport exception.
  • A request timeout does not prove a remote side effect was undone.
  • String body handling alone does not bound untrusted response size.

Read next

Client configuration, Transport framing.

java
http-project
Storage details