Specifying HTTP Response Headers |
---|
Location
header, and a 401 (Unauthorized
)
code must include an accompanying WWW-Authenticate
header.
However, specifying headers can play a useful role even when no unusual status code is set. Response headers can be used to specify cookies, to supply the modification date (for caching), to instruct the browser to reload the page after a designated interval, to say how long the file is so that persistent HTTP connections can be used, and many other tasks.
The most general way to specify headers is by the setHeader
method of HttpServletResponse
, which takes two strings: the header name
and the header value. Like setting the status codes, this
must be done before any document content is sent.
There are also two specialized methods to set headers that contain
dates (setDateHeader
) and integers (setIntHeader
). The first saves you
the trouble of translating a Java date in milliseconds since the epoch
(as returned by System.currentTimeMillis
or the getTime
method applied
to a Date
object) into a GMT time string. The second spares you
the minor inconvenience of converting an int
to a String
.
Rather than setting a header outright, you can add a new header, in case a header
with that name already exists. Use addHeader
,
addDateHeader
, and addIntHeader
for this. If it really
matters to you whether a specific header has already been set, use
containsHeader
to check.
Finally, HttpServletResponse
also supplies a number of convenience methods
for specifying common headers.
setContentType
method sets the
Content-Type
header, and is used by the majority of servlets.
setContentLength
method sets the Content-Length
header,
useful if the browser supports persistent (keep-alive) HTTP connections.
addCookie
method sets a cookie (there is no corresponding setCookie
,
since it is normal to have multiple Set-Cookie
lines).
sendRedirect
method sets the Location
header
as well as setting the status code to 302.
Header | Interpretation/Purpose |
---|---|
Allow | What request methods (GET , POST , etc.) does the server support?
|
Content-Encoding | What method was used to encode the document?
You need to decode it to get the type specified by the
Content-Type header. Using gzip to compress the document can dramatically
reduce download times for HTML files, but it is only supported by Netscape
on Unix and IE 4 and 5 on Windows. On the other hand, gzipping HTML files can
dramatically reduce download times, and Java's GZIPOutputStream makes it easy.
So you should explicitly check if the browser supports this by
looking at the Accept-Encoding header (i.e. via
request.getHeader("Accept-Encoding") ).
That way, you can return gzipped pages to browser that know how to unzip them,
but still return regular pages to other browsers.
|
Content-Length | How many bytes are being sent?
This information is only needed if the browser is using a persistent (keep-alive)
HTTP connection. If you want your servlet to take advantage of this when the
browser supports it, your servlet should write the document into a
ByteArrayOutputStream , look up its size when done, put that into the
Content-Length field, then send the
content via byteArrayStream.writeTo(response.getOutputStream()) .
|
Content-Type | What is the MIME type of the following document? Default for servlets is
text/plain , but they usually explicitly specify text/html .
Setting this header is so common that there is a special method in
HttpServletResponse for it: setContentType .
|
Date | What is current time (in GMT)? Use the setDateHeader method to specify
this header. That saves you the trouble of formatting the date string properly.
|
Expires | At what time should content be considered out of date and thus no longer cached? |
Last-Modified | When was document last changed? Client can supply a date
via an If-Modified-Since request header. This is treated as a conditional
GET , with document only being returned if the Last-Modified date is later
than the specified date. Otherwise a 304 (Not Modified) status line
is returned. Again, use the setDateHeader method to specify
this header.
|
Location | Where should client go to get document? This is usually
set indirectly, along with a 302 status code, via the sendRedirect
method of HttpServletResponse .
|
Refresh | How soon should browser ask for an updated page (in seconds)?
Instead of just reloading current page, you can specify a specific page to load
via setHeader("Refresh", "5; URL=http://host/path") . Note that this is commonly set
via <META HTTP-EQUIV="Refresh" CONTENT="5; URL=http://host/path"> in
the HEAD section of the HTML page, rather than as an explicit header from the server.
This is because automatic reloading or forwarding is something often desired by HTML
authors who do not have CGI or servlet access. But for servlets, setting the header
directly is easier and clearer. Note that this header means "reload this page
or go to the specified URL in N seconds." It does not mean "reload this page or
go to the specified URL every N seconds." So you have to send a Refresh header
each time, and sending a 204 (No Content) status code stops the browser from
reloading further, regardless of whether you explicitly send the Refresh header
or use <META HTTP-EQUIV="Refresh" ...> . Note that this header
is not officially part of HTTP 1.1, but is an extension supported by both Netscape
and Internet Explorer.
|
Server | What server am I? Servlets don't usually set this; the Web server itself does. |
Set-Cookie | Specifies cookie associated with page. Servlets should not
use response.setHeader("Set-Cookie", ...) , but instead use the special-purpose
addCookie method of HttpServletResponse . See separate section on
handling cookies.
|
WWW-Authenticate | What authorization type and realm should client supply
in their Authorization header? This header is required in responses that
have a 401 (Unauthorized) status line. E.g.
response.setHeader("WWW-Authenticate", "BASIC realm=\"executives\"") .
Note that servlets do not usually handle this themselves, but instead let
password-protected Web pages be handled by the Web server's specialized
mechanisms (e.g. .htaccess).
|
Refresh
header.
If you want to try it out yourself, start with
the HTML front-end.
Note that, in addition to illustrating the value of HTTP response headers,
this example shows two other valuable servlet capabilities. First, it shows
that servlets can handle multiple simultaneous connections, each in their own
thread. In this case it maintains a Vector
of previous requests for
prime calculations, matching the current request to previous ones by looking
at the number of primes (length of list) and number of digits (length of each
prime), and synchronizing all access to this list. Secondly, it shows how easy
it is for servlets to maintain state between requests, something that is
cumbersome to implement in traditional CGI and many CGI alternatives. This
lets the browser access the ongoing calculations when reloading the page,
plus permits the servlet to keep a list of the N most recently requested
results, returning them immediately if a new request specifies the same
parameters as a recent request. We'll discuss persistent state even further
in a later section.
Vector
of prime numbers in a background thread, and
Primes.java for generating large random
numbers of type BigInteger
and checking if they are prime.
package hall; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import java.util.*; public class PrimeNumbers extends HttpServlet { private static Vector primeListVector = new Vector(); private static int maxPrimeLists = 30; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int numPrimes = ServletUtilities.getIntParameter(request, "numPrimes", 50); int numDigits = ServletUtilities.getIntParameter(request, "numDigits", 120); PrimeList primeList = findPrimeList(primeListVector, numPrimes, numDigits); if (primeList == null) { primeList = new PrimeList(numPrimes, numDigits, true); synchronized(primeListVector) { if (primeListVector.size() >= maxPrimeLists) primeListVector.removeElementAt(0); primeListVector.addElement(primeList); } } Vector currentPrimes = primeList.getPrimes(); int numCurrentPrimes = currentPrimes.size(); int numPrimesRemaining = (numPrimes - numCurrentPrimes); boolean isLastResult = (numPrimesRemaining == 0); if (!isLastResult) { response.setHeader("Refresh", "5"); } response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "Some " + numDigits + "-Digit Prime Numbers"; out.println(ServletUtilities.headWithTitle(title) + "<BODY BGCOLOR=\"#FDF5E6\">\n" + "<H2 ALIGN=CENTER>" + title + "</H2>\n" + "<H3>Primes found with " + numDigits + " or more digits: " + numCurrentPrimes + ".</H3>"); if (isLastResult) out.println("<B>Done searching.</B>"); else out.println("<B>Still looking for " + numPrimesRemaining + " more<BLINK>...</BLINK></B>"); out.println("<OL>"); for(int i=0; i<numCurrentPrimes; i++) { out.println(" <LI>" + currentPrimes.elementAt(i)); } out.println("</OL>"); out.println("</BODY></HTML>"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } // See if there is an existing ongoing or completed calculation with // the same number of primes and length of prime. If so, return // those results instead of starting a new background thread. Keep // this list small so that the Web server doesn't use too much memory. // Synchronize access to the list since there may be multiple simultaneous // requests. private PrimeList findPrimeList(Vector primeListVector, int numPrimes, int numDigits) { synchronized(primeListVector) { for(int i=0; i<primeListVector.size(); i++) { PrimeList primes = (PrimeList)primeListVector.elementAt(i); if ((numPrimes == primes.numPrimes()) && (numDigits == primes.numDigits())) return(primes); } return(null); } } }
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>Finding Large Prime Numbers</TITLE> </HEAD> <BODY BGCOLOR="#FDF5E6"> <H2 ALIGN="CENTER">Finding Large Prime Numbers</H2> <BR><BR> <CENTER> <FORM ACTION="/servlet/hall.PrimeNumbers"> <B>Number of primes to calculate:</B> <INPUT TYPE="TEXT" NAME="numPrimes" VALUE=25 SIZE=4><BR> <B>Number of digits:</B> <INPUT TYPE="TEXT" NAME="numDigits" VALUE=150 SIZE=3><BR> <INPUT TYPE="SUBMIT" VALUE="Start Calculating"> </FORM> </CENTER> </BODY> </HTML>