An online markdown blog and knowledge repository.
Dev.to on the HTTP Request Lifecycle
Baeldung reviews how to Do a Simple HTTP Request
HTTP communications boil down to this template: <protocol>://<host><:optional port>/<path/to/resource><?query>. An example is |http|://|www.example.com||:5000||/mainpage||?query=param&query2=param2|
[Daniel Golent, Dev.to article, accessed 29-May-22]
TCP/IP connection features are invoked as part of HTTP request/responses.
Name Resolution Plays a Part!
An actual HTTP Request isn't sent until TCP/IP and DNS have completed their initial tasks.
All setups, requests, and teardown also have to traverse the network of Routers.
Note: Routes to/from might not always be the same.
File/resource caching plays a part!
HTTP is a higher-level ("Application Layer") protocol in the OSI stack, so it relies on other lower-level protocols to do their job, so that it can send requests and handle responses accordingly.
Simple HTTP Req/Res protocol is "connectionless" and "stateless".
There are other HTTP protocol actions that are different in later versions.
Applies to Spring 5 and Spring Boot 2.
Baeldung has a link to a page that explores updates to Java's HTTP API
Class that enables basic HTTP Requests.
Single library needed: java.net
Simple functionality only.
For more advanced, use other libraries.
This is step one and only creates the request.
Can be used for the following: GET, POST, HEAD, OPTIONS, PUT, DELETE, and TRACE.
URL url = new URL("http://website.ext");
HttpURLConnection connection = (HttpURLConnecion) url.openConnection();
connection.setRequestMethod(String: verb);
Request parameters are set by Boolean property doOutput.
Use a String to create an appropriate key:value pair to add a request parameter.
Map<String, String> parameters = new HashMap<>();
parameters.put("param1", "val");
connection.setDoOutput(true);
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
// parameterStringBuilder is a custom class see Baeldung.com for details
out.writeBytes(ParameterStringBuilder.getParamsString(parameters));
out.flush();
out.close();
Add headers to a request via method setRequestProperty()
Read the value of a header from a connection via method getHeaderField()
connection.setRequestProperty("Content-Type", "application/json");
String contentType = connection.getHeaderField("Content-Type");
Set connect and read Timeouts via HttpUrlConnection class using setConnectTimeout()
and setReadTimeout()
methods.
connection.setConnectTimeout(Integer: number);
connection.setReadTimeout(Integer: number);
Work with cookies using CookieManager and HttpCookie.
Read cookies from Response:
String cookiesHeader = connection.getHeaderField("Set-Cookie");
List<HttpCookie> cookies = HttpCookie.parse(cookiesHeader);
Add cookies to Cookies Store:
cookies.forEach(cookie -> cookieManager.getCookieStore().add(null, cookie));
Check for specific cookie by name
Optional<HttpCookie> usernameCookie = cookies.stream()
.findAny().filter(cookie -> cookie.getName()
.equals("username"));
if (usernameCookie == null) {
cookieManager.getCookieStore()
.add(null, new HttpCookie("username", String: username));
}
Add cookies to Request (after closing then re-opening the connection):
connection.disconnect();
connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Cookie",
StringUtils.join(cookieManager.getCookieStore().getCookies(),
";"));
Enable or Disable automatic redirect following for a specific connection.
Use setInstanceFollowRedirects(Boolean: param)
to true or false.
Default behavior is to follow automatic redirects (true).
// specified connection
connection.setInstanceFollowRedirects(false);
// all connections
HttpUrlConnection.setFollowRedirects(false);
When an HTTP 301 or 302 response is returned, create a new request to the new URL:
if (status == HttpURLConnection.HTTP_MOVED_TEMP ||
status == HttpURLConnection.HTTP_MOVED_PERM) {
String location = connection.getHeaderField("Location");
URL newUrl = new URL(location);
connection = (HttpURLConnection) newUrl.openConnection();
}
Parse the input stream of the connection instance to read the response.
int status = connection.getResponseCode();
// place the response into a string
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuffer content = new StringBuffer();
while ((inputLine = bufferedReader.readLine()) != null) {
content.append(inputLine);
}
bufferedReader.close();
// close the connection
connection.disconnect();
Requests do not always succeed, so the InputStream from HttpUrlConnection instance will not be useful.
Consume the stream provided by HttpUrlConnection.getErrorStream()
int status = connection.getResponseCode();
Reader streamReader = null;
if (status > 299) {
streamReader = new InputStreamReader(connection.getErrorStream());
} else {
streamReader = new InputStreamReader(connection.getInputStream());
}
It is not possible to get the full response info from HttpUrlConnection
instance.
Information on the response can be built using HttpUrlConnection
instance though.
public class FullResponseBuilder {
public static String getFullResponse(HttpURLConnection con) throws IOException {
StringBuilder fullResponseBuilder = new StringBuilder();
// read status and message
// read headers
// read response content
return fullResponseBuilder.toString();
}
}
// add the response status information
fullResponseBuilder.append(connection.getResponseCode())
.append(" ")
.append(connection.getResponseMessage())
.append("\n");
// get the headers using getHeaderFields and add to StringBuilder
connection.getHeaderFields().entrySet().stream()
.filter(entry -> entry.getKey() != null)
.forEach(entry -> {
fullResponseBuilder.append(entry.getKey()).append(": ");
List headerValues = entry.getValue();
Iterator item = headerValues.iterator();
if (item.hasNext()) {
fullResponseBuilder.append(item.next());
while (item.hasNext()) {
fullResponseBuilder.append(", ").append(item.next());
}
}
fullResponseBuilder.append("\n");
});
After executing this code, go back and execute the "read response content" code and append it.
This will enable making decisions on whether to use connection.getInputStream()
or connection.getErrorStream()
to retreive the request content.
Note: Every code sample is available at the baeldung.com website. Certain variable names were changed and comments were added so that I have a quick-reference available for myself.
Return to Parent Readme.md