|
Specifying HTTP Response Headers |
Note: Chapter 7 of Core Servlets and JavaServer Pages
contains much more detailed and up to date information on this topic. The full
text is now available online
in PDF for free access.
A response from a Web server normally
consists of a status line, one or more response headers, a blank line, and the
document. Setting the HTTP response headers often goes hand in hand with setting
the status codes in the status line. For example, several of the "document
moved" status codes have an accompanying 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.
- The
setContentType method sets the Content-Type
header, and is used by the majority of servlets.
- The
setContentLength method sets the
Content-Length header, useful if the browser supports persistent
(keep-alive) HTTP connections.
- The
addCookie method sets a cookie (there is no corresponding
setCookie , since it is normal to have multiple
Set-Cookie lines).
- And, as discussed in the previous
section, the
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).
| For full details on HTTP headers, see the specifications at
http://www.w3.org/Protocols/.
Here is an example that lets you ask for a list of some large
prime numbers. Since this may take some time for very large numbers (e.g. 150
digits), the servlet immediately returns the results found so far, but then
keeps calculating, using a low-priority thread so that it won't degrade Web
server performance. If the calculations are not complete, it instructs the
browser to ask for a new page in a few seconds by sending it a
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.
3.1 PrimeNumbers.java (Download source code)Note: also uses ServletUtilities.java,
shown earlier, PrimeList.java
for creating a 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);
}
}
}
3.3 PrimeNumbers.html
<!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>
3.4 Front End
3.5 Intermediate Result
3.6 Final Result
|