Headers, charsets, and HttpURLConnection
◈ 5 cardsThe six named header getters and their sentinels, arbitrary headers by name and by index, deriving the charset from Content-Type, and the two HttpURLConnection calls that turn a response status into a choice of stream.
The named getters
What an HTTP response means — the anatomy of its status line, the five families of status code, what a particular code obliges a client to do — is module 5, and this lesson does not pre-empt it. Here the response is an object with fields on it: a set of named header values plus a body. URLConnection wraps the six most-wanted fields in typed getters — and the value each returns when the header is absent is as important as the value itself, because none of them throws:
getContentType()—String, ornull. Typicallytext/html; charset=UTF-8.getContentLength()—int, or-1. Also-1for a body larger thanInteger.MAX_VALUE, which is whatgetContentLengthLong()exists to fix.getContentEncoding()—String, ornull. This is the compression (gzip), not the charset. The name is a permanent trap.getDate(),getExpiration(),getLastModified()—longmilliseconds since the epoch, or0.
So -1, null and 0 are three different ways of saying "the server did not tell you". Testing getContentLength() > 0 and testing getContentLength() != -1 are not the same test.
Any header at all
The named getters are thin wrappers. getHeaderField(String name) returns any header by name — case-insensitively, without the colon — or null. There is also an indexed pair: getHeaderFieldKey(int n) and getHeaderField(int n). In HTTP, field 0 is the status line, and its key is null; the real headers start at 1. So the loop that dumps a whole header block starts at 1 and stops when the value comes back null.
The charset lives inside Content-Type
There is no getCharset(). The encoding is a parameter of the media type:
Content-Type: text/html; charset=UTF-8
You find charset=, take what follows, and hand that to your Reader. When there is no parameter you must choose a fallback: RFC 2616 once made ISO-8859-1 the default for text/*, RFC 7231 removed that rule, and modern practice is UTF-8. Whatever you pick, pick it explicitly — decoding with the platform default is how a page reads correctly on your machine and arrives full of replacement characters on the marker's.
HttpURLConnection
For an http or https URL the object you already hold is an HttpURLConnection; cast it and five more capabilities appear.
setRequestMethod(String)— one of the seven case-sensitive stringsGET,POST,HEAD,PUT,DELETE,OPTIONS,TRACE. Anything else throwsProtocolException(anIOException). The default isGET.getResponseCode()/getResponseMessage()— the numeric status and the phrase beside it. Both connect if the connection is not open yet, so they also end the configuration window. Treat the number as an opaque integer for now; module 5 is where it acquires meaning.getErrorStream()— the body of a response whose code is 400 or above, ornullwhen there is not one. The rule you need in this module is purely mechanical: below 400 the body is ongetInputStream(); at 400 and abovegetInputStream()throws, and the body — if the server sent one — is ongetErrorStream(). That boundary is a documented property of the class, not a fact about HTTP, and you can write correct code against it without being able to name a single status code.setInstanceFollowRedirects(boolean)— per connection; the staticsetFollowRedirects(boolean)changes the default for every instance created afterwards. Redirects are followed by default, which is convenient and is also how a client silently ends up somewhere it was not asked to go.disconnect()— releases a keep-alive socket you are finished with. Closing a stream does not do it.
Worked example — headers in, charset out
Asking the module's reference URL for its headers, then deciding how to decode the body:
getResponseCode() -> 200
getContentType() -> text/html; charset=UTF-8
getContentLength() -> 4312
getContentEncoding() -> null
getLastModified() -> 0
-> 200 is below 400, so the body is on getInputStream()
-> "charset=" found at offset 10 of the content type -> UTF-8
-> 4312 bytes expected, no compression, no modification date given
Every line there is a sentinel decision. The null and the 0 are not failures and not real values — they are the server declining to say, and code that treats them as data invents facts about a page it never received. Only getContentType() had anything to parse, and the eight characters charset= are the whole of the parsing.
Now point the same code at a page that does not exist. getResponseCode() returns some number at or above 400, getInputStream() throws instead of handing back a body, and whatever the server sent to explain itself is waiting on getErrorStream(). Notice what the branch did not need: you never had to know which number came back. The code is a value you report; the 400 boundary is the thing you branch on. Module 5 supplies the meaning that turns that report into a useful message.