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.HttpURLConnectionis 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.
What to take from the run:
- Casting the
URLConnectiontoHttpURLConnectionunlocked the HTTP layer:setRequestMethod("POST"),getResponseCode(), andgetResponseMessage()exist only on the subclass. For anhttp:URL the object really is anHttpURLConnection, so the cast always succeeds. - Sending a body required
setDoOutput(true)before writing togetOutputStream(). 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()returned201and 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()andgetErrorStream(). Here201is success so the input stream carried the echoed body, but on a 4xx/5xx responsegetInputStream()would have thrown and the server's message would only be reachable throughgetErrorStream(). 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'sHttpClientexists.
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?