The HTTP clients we commonly use—such as RestTemplate, fetch, and axios—let us send requests with very simple, high-level calls. Because of this convenience, we rarely get to see what actually happens underneath. By looking at a lower-level API like HttpURLConnection, we can clearly understand how an HTTP request is constructed and transmitted.
1. Example of a Low-Level HTTP Request
Below is a simple example that sends JSON data with a POST request and reads the response as a string.
public static String sendJsonPost(String targetUrl, String jsonBody) throws IOException {
URL url = new URL(targetUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
conn.setRequestProperty("Accept", "application/json");
conn.setDoOutput(true);
try (OutputStream out = conn.getOutputStream()) {
byte[] data = jsonBody.getBytes(StandardCharsets.UTF_8);
out.write(data);
}
StringBuilder responseText = new StringBuilder();
try (BufferedReader reader =
new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
responseText.append(line);
}
}
return responseText.toString();
}
A single line of code in a tool like RestTemplate internally performs the same sequence of steps. Seeing a manual implementation like this makes it easier to understand how HTTP communication actually works.
2. Why Strings (JSON) Must Be Converted to Bytes
Although HTTP often appears text-based, every request and response is ultimately transmitted as a byte stream. A string cannot be written directly to the network; it must first be converted into a byte array.
byte[] data = jsonBody.getBytes(StandardCharsets.UTF_8);
out.write(data);
This process essentially means:
- Convert the JSON string into a UTF-8 encoded byte array.
- Write that byte array into the HTTP request body.
High-level libraries like fetch and RestTemplate handle this automatically, so we rarely think about this conversion explicitly.
3. Reading the Response Is Also Byte-Based
BufferedReader reader =
new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
The server's response also arrives over the network as a stream of bytes. It must be decoded back into characters using the appropriate charset—UTF-8 in this example—before being assembled into a string.
4. Why We Hardly Ever See Code Like This
Most modern development environments already provide high-level HTTP clients.
- Browser:
fetch,axios - Spring:
RestTemplate,WebClient - Android/Java:
OkHttp
These tools manage the entire process—stream creation, byte conversion, error handling, and encoding—so developers rarely encounter the lower-level details. Because of this abstraction, the actual structure of an HTTP request often remains hidden.
Summary
- HTTP communication is fundamentally the exchange of byte streams.
- High-level libraries conceal this fact by providing very simple interfaces.
- Examining low-level APIs reveals how requests and responses actually work beneath the surface.
Understanding these lower-level mechanisms can be helpful not only for learning, but also for diagnosing unexpected network issues that might occur in real environments.