W3docs

Java HttpURLConnection

Make HTTP requests in Java with the legacy HttpURLConnection API.

Java HttpURLConnection

HttpURLConnection is the HTTP-aware subclass of URLConnection. When you openConnection() on an http: or https: URL, the object you get back is an HttpURLConnection — cast it to reach the HTTP-specific features: setting the request method, reading the status code, and getting the separate error stream. It is the JDK's original HTTP client, available since Java 1.1.

Modern note: for new code, prefer java.net.http.HttpClient (next chapter) — it is cleaner, supports HTTP/2, and handles async. HttpURLConnection is still everywhere in older codebases, so it is worth knowing well.

Casting and choosing a method

URL url = URI.create("http://example.com/api").toURL();
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");                       // GET is the default
conn.setRequestProperty("Content-Type", "application/json");
conn.setConnectTimeout(2000);
conn.setReadTimeout(2000);

setRequestMethod takes "GET", "POST", "PUT", "DELETE", etc. Add request headers with setRequestProperty.

Sending a request body

To send a body you must opt in with setDoOutput(true) (which also makes the method default to POST), then write to the output stream:

conn.setDoOutput(true);
try (OutputStream os = conn.getOutputStream()) {
    os.write(payload.getBytes(StandardCharsets.UTF_8));
}

Status code and the two streams

The defining HTTP feature is the status code, and the trap that follows from it:

int code = conn.getResponseCode();          // triggers the request
InputStream body = (code >= 200 && code < 400)
        ? conn.getInputStream()             // success body
        : conn.getErrorStream();            // error body (4xx/5xx)

getInputStream() throws IOException on a 4xx/5xx response. The error body lives on a different stream, getErrorStream(). Forgetting this is the classic HttpURLConnection bug: an error response blows up instead of letting you read the server's explanation. Always branch on getResponseCode() first. Finish with conn.disconnect().

A worked example: a POST round trip

This program runs a loopback server that echoes the request body back with a 201 Created, then performs a POST through HttpURLConnection: setting the method, writing a body, reading the status, and choosing the right stream.

java— editable, runs on the server

What to take from the run:

  • Casting the URLConnection to HttpURLConnection unlocked the HTTP layer: setRequestMethod("POST"), getResponseCode(), and getResponseMessage() exist only on the subclass. For an http: URL the object really is an HttpURLConnection, so the cast always succeeds.
  • Sending a body required setDoOutput(true) before writing to getOutputStream(). Without that call the connection stays read-only and writing fails — opting into output is the switch that turns a GET into a body-carrying request.
  • getResponseCode() returned 201 and is what actually fired the request to the server. The status code is the first thing to read, because the next decision — which stream to read — depends on it.
  • The example branched between getInputStream() and getErrorStream(). Here 201 is success so the input stream carried the echoed body, but on a 4xx/5xx response getInputStream() would have thrown and the server's message would only be reachable through getErrorStream(). That split is the API's sharpest edge.
  • The flow took five-plus configuration calls for one POST, and disconnect() to clean up. That verbosity — manual streams, the error-stream trap, no built-in async — is exactly why the next chapter's HttpClient exists.

Practice

Practice

A client does a PUT with 'HttpURLConnection'. When the server returns '400 Bad Request', the code crashes with an 'IOException' on 'conn.getInputStream()' instead of logging the server's error message. What is the correct fix?