001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *  http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    //
021    // This source code implements specifications defined by the Java
022    // Community Process. In order to remain compliant with the specification
023    // DO NOT add / change / or delete method signatures!
024    //
025    
026    package javax.servlet.http;
027    
028    import java.io.IOException;
029    import java.io.OutputStreamWriter;
030    import java.io.PrintWriter;
031    import java.io.UnsupportedEncodingException;
032    import java.lang.reflect.Method;
033    import java.text.MessageFormat;
034    import java.util.Enumeration;
035    import java.util.Locale;
036    import java.util.ResourceBundle;
037    import javax.servlet.GenericServlet;
038    import javax.servlet.ServletException;
039    import javax.servlet.ServletOutputStream;
040    import javax.servlet.ServletRequest;
041    import javax.servlet.ServletResponse;
042    
043    
044    /**
045     * Provides an abstract class to be subclassed to create
046     * an HTTP servlet suitable for a Web site. A subclass of
047     * <code>HttpServlet</code> must override at least
048     * one method, usually one of these:
049     *
050     * <ul>
051     * <li> <code>doGet</code>, if the servlet supports HTTP GET requests
052     * <li> <code>doPost</code>, for HTTP POST requests
053     * <li> <code>doPut</code>, for HTTP PUT requests
054     * <li> <code>doDelete</code>, for HTTP DELETE requests
055     * <li> <code>init</code> and <code>destroy</code>,
056     * to manage resources that are held for the life of the servlet
057     * <li> <code>getServletInfo</code>, which the servlet uses to
058     * provide information about itself
059     * </ul>
060     *
061     * <p>There's almost no reason to override the <code>service</code>
062     * method. <code>service</code> handles standard HTTP
063     * requests by dispatching them to the handler methods
064     * for each HTTP request type (the <code>do</code><i>XXX</i>
065     * methods listed above).
066     *
067     * <p>Likewise, there's almost no reason to override the
068     * <code>doOptions</code> and <code>doTrace</code> methods.
069     *
070     * <p>Servlets typically run on multithreaded servers,
071     * so be aware that a servlet must handle concurrent
072     * requests and be careful to synchronize access to shared resources.
073     * Shared resources include in-memory data such as
074     * instance or class variables and external objects
075     * such as files, database connections, and network
076     * connections.
077     * See the
078     * <a href="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">
079     * Java Tutorial on Multithreaded Programming</a> for more
080     * information on handling multiple threads in a Java program.
081     *
082     * @version $Rev: 467553 $ $Date: 2006-10-25 06:01:51 +0200 (Mi, 25. Okt 2006) $
083     */
084    public abstract class HttpServlet extends GenericServlet
085            implements java.io.Serializable {
086        private static final String METHOD_DELETE = "DELETE";
087        private static final String METHOD_HEAD = "HEAD";
088        private static final String METHOD_GET = "GET";
089        private static final String METHOD_OPTIONS = "OPTIONS";
090        private static final String METHOD_POST = "POST";
091        private static final String METHOD_PUT = "PUT";
092        private static final String METHOD_TRACE = "TRACE";
093    
094        private static final String HEADER_IFMODSINCE = "If-Modified-Since";
095        private static final String HEADER_LASTMOD = "Last-Modified";
096    
097        private static final String LSTRING_FILE =
098                "javax.servlet.http.LocalStrings";
099        private static ResourceBundle lStrings =
100                ResourceBundle.getBundle(LSTRING_FILE);
101    
102        /**
103         * Does nothing, because this is an abstract class.
104         */
105        public HttpServlet() {
106        }
107    
108        /**
109         * Called by the server (via the <code>service</code> method) to
110         * allow a servlet to handle a GET request.
111         *
112         * <p>Overriding this method to support a GET request also
113         * automatically supports an HTTP HEAD request. A HEAD
114         * request is a GET request that returns no body in the
115         * response, only the request header fields.
116         *
117         * <p>When overriding this method, read the request data,
118         * write the response headers, get the response's writer or
119         * output stream object, and finally, write the response data.
120         * It's best to include content type and encoding. When using
121         * a <code>PrintWriter</code> object to return the response,
122         * set the content type before accessing the
123         * <code>PrintWriter</code> object.
124         *
125         * <p>The servlet container must write the headers before
126         * committing the response, because in HTTP the headers must be sent
127         * before the response body.
128         *
129         * <p>Where possible, set the Content-Length header (with the
130         * {@link javax.servlet.ServletResponse#setContentLength} method),
131         * to allow the servlet container to use a persistent connection
132         * to return its response to the client, improving performance.
133         * The content length is automatically set if the entire response fits
134         * inside the response buffer.
135         *
136         * <p>When using HTTP 1.1 chunked encoding (which means that the response
137         * has a Transfer-Encoding header), do not set the Content-Length header.
138         *
139         * <p>The GET method should be safe, that is, without
140         * any side effects for which users are held responsible.
141         * For example, most form queries have no side effects.
142         * If a client request is intended to change stored data,
143         * the request should use some other HTTP method.
144         *
145         * <p>The GET method should also be idempotent, meaning
146         * that it can be safely repeated. Sometimes making a
147         * method safe also makes it idempotent. For example,
148         * repeating queries is both safe and idempotent, but
149         * buying a product online or modifying data is neither
150         * safe nor idempotent.
151         *
152         * <p>If the request is incorrectly formatted, <code>doGet</code>
153         * returns an HTTP "Bad Request" message.
154         *
155         *
156         * @param req an {@link HttpServletRequest} object that
157         * contains the request the client has made of the servlet
158         *
159         * @param resp an {@link HttpServletResponse} object that
160         * contains the response the servlet sends to the client
161         *
162         * @exception IOException if an input or output error is
163         * detected when the servlet handles the GET request
164         *
165         * @exception ServletException if the request for the GET
166         * could not be handled
167         *
168         * @see javax.servlet.ServletResponse#setContentType
169         */
170        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
171                throws ServletException, IOException {
172            String protocol = req.getProtocol();
173            String msg = lStrings.getString("http.method_get_not_supported");
174            if (protocol.endsWith("1.1")) {
175                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
176            } else {
177                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
178            }
179        }
180    
181        /**
182         * Returns the time the <code>HttpServletRequest</code>
183         * object was last modified,
184         * in milliseconds since midnight January 1, 1970 GMT.
185         * If the time is unknown, this method returns a negative
186         * number (the default).
187         *
188         * <p>Servlets that support HTTP GET requests and can quickly determine
189         * their last modification time should override this method.
190         * This makes browser and proxy caches work more effectively,
191         * reducing the load on server and network resources.
192         *
193         * @param req the <code>HttpServletRequest</code>
194         * object that is sent to the servlet
195         *
196         * @return a <code>long</code> integer specifying
197         * the time the <code>HttpServletRequest</code> object was
198         * last modified, in milliseconds since midnight, January 1,
199         * 1970 GMT, or -1 if the time is not known
200         */
201        protected long getLastModified(HttpServletRequest req) {
202            return -1;
203        }
204    
205        /**
206         * <p>Receives an HTTP HEAD request from the protected
207         * <code>service</code> method and handles the
208         * request.
209         * The client sends a HEAD request when it wants
210         * to see only the headers of a response, such as
211         * Content-Type or Content-Length. The HTTP HEAD
212         * method counts the output bytes in the response
213         * to set the Content-Length header accurately.
214         *
215         * <p>If you override this method, you can avoid computing
216         * the response body and just set the response headers
217         * directly to improve performance. Make sure that the
218         * <code>doHead</code> method you write is both safe
219         * and idempotent (that is, protects itself from being
220         * called multiple times for one HTTP HEAD request).
221         *
222         * <p>If the HTTP HEAD request is incorrectly formatted,
223         * <code>doHead</code> returns an HTTP "Bad Request"
224         * message.
225         *
226         * @param req the request object that is passed
227         * to the servlet
228         *
229         * @param resp the response object that the servlet
230         * uses to return the headers to the clien
231         *
232         * @exception IOException if an input or output error occurs
233         *
234         * @exception ServletException if the request for the HEAD
235         * could not be handled
236         */
237        protected void doHead(HttpServletRequest req, HttpServletResponse resp)
238                throws ServletException, IOException {
239            NoBodyResponse response = new NoBodyResponse(resp);
240    
241            doGet(req, response);
242            response.setContentLength();
243        }
244    
245        /**
246         * Called by the server (via the <code>service</code> method)
247         * to allow a servlet to handle a POST request.
248         *
249         * The HTTP POST method allows the client to send
250         * data of unlimited length to the Web server a single time
251         * and is useful when posting information such as
252         * credit card numbers.
253         *
254         * <p>When overriding this method, read the request data,
255         * write the response headers, get the response's writer or output
256         * stream object, and finally, write the response data. It's best
257         * to include content type and encoding. When using a
258         * <code>PrintWriter</code> object to return the response, set the
259         * content type before accessing the <code>PrintWriter</code> object.
260         *
261         * <p>The servlet container must write the headers before committing the
262         * response, because in HTTP the headers must be sent before the
263         * response body.
264         *
265         * <p>Where possible, set the Content-Length header (with the
266         * {@link javax.servlet.ServletResponse#setContentLength} method),
267         * to allow the servlet container to use a persistent connection
268         * to return its response to the client, improving performance.
269         * The content length is automatically set if the entire response fits
270         * inside the response buffer.
271         *
272         * <p>When using HTTP 1.1 chunked encoding (which means that the response
273         * has a Transfer-Encoding header), do not set the Content-Length header.
274         *
275         * <p>This method does not need to be either safe or idempotent.
276         * Operations requested through POST can have side effects for
277         * which the user can be held accountable, for example,
278         * updating stored data or buying items online.
279         *
280         * <p>If the HTTP POST request is incorrectly formatted,
281         * <code>doPost</code> returns an HTTP "Bad Request" message.
282         *
283         * @param req an {@link HttpServletRequest} object that
284         * contains the request the client has made of the servlet
285         *
286         * @param resp an {@link HttpServletResponse} object that
287         * contains the response the servlet sends to the client
288         *
289         * @exception IOException if an input or output error is
290         * detected when the servlet handles the request
291         *
292         * @exception ServletException if the request for the POST
293         * could not be handled
294         *
295         * @see javax.servlet.ServletOutputStream
296         * @see javax.servlet.ServletResponse#setContentType
297         */
298        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
299                throws ServletException, IOException {
300            String protocol = req.getProtocol();
301            String msg = lStrings.getString("http.method_post_not_supported");
302            if (protocol.endsWith("1.1")) {
303                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
304            } else {
305                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
306            }
307        }
308    
309        /**
310         * Called by the server (via the <code>service</code> method)
311         * to allow a servlet to handle a PUT request.
312         *
313         * The PUT operation allows a client to
314         * place a file on the server and is similar to
315         * sending a file by FTP.
316         *
317         * <p>When overriding this method, leave intact
318         * any content headers sent with the request (including
319         * Content-Length, Content-Type, Content-Transfer-Encoding,
320         * Content-Encoding, Content-Base, Content-Language, Content-Location,
321         * Content-MD5, and Content-Range). If your method cannot
322         * handle a content header, it must issue an error message
323         * (HTTP 501 - Not Implemented) and discard the request.
324         * For more information on HTTP 1.1, see RFC 2616
325         * <a href="http://www.ietf.org/rfc/rfc2616.txt"></a>.
326         *
327         * <p>This method does not need to be either safe or idempotent.
328         * Operations that <code>doPut</code> performs can have side
329         * effects for which the user can be held accountable. When using
330         * this method, it may be useful to save a copy of the
331         * affected URL in temporary storage.
332         *
333         * <p>If the HTTP PUT request is incorrectly formatted,
334         * <code>doPut</code> returns an HTTP "Bad Request" message.
335         *
336         * @param req the {@link HttpServletRequest} object that
337         * contains the request the client made of the servlet
338         *
339         * @param resp the {@link HttpServletResponse} object that
340         * contains the response the servlet returns to the client
341         *
342         * @exception IOException if an input or output error occurs
343         * while the servlet is handling the PUT request
344         *
345         * @exception ServletException if the request for the PUT
346         * cannot be handled
347         */
348        protected void doPut(HttpServletRequest req, HttpServletResponse resp)
349                throws ServletException, IOException {
350            String protocol = req.getProtocol();
351            String msg = lStrings.getString("http.method_put_not_supported");
352            if (protocol.endsWith("1.1")) {
353                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
354            } else {
355                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
356            }
357        }
358    
359        /**
360         * Called by the server (via the <code>service</code> method)
361         * to allow a servlet to handle a DELETE request.
362         *
363         * The DELETE operation allows a client to remove a document
364         * or Web page from the server.
365         *
366         * <p>This method does not need to be either safe
367         * or idempotent. Operations requested through
368         * DELETE can have side effects for which users
369         * can be held accountable. When using
370         * this method, it may be useful to save a copy of the
371         * affected URL in temporary storage.
372         *
373         * <p>If the HTTP DELETE request is incorrectly formatted,
374         * <code>doDelete</code> returns an HTTP "Bad Request"
375         * message.
376         *
377         * @param req the {@link HttpServletRequest} object that
378         * contains the request the client made of the servlet
379         *
380         * @param resp the {@link HttpServletResponse} object that
381         * contains the response the servlet returns to the client
382         *
383         * @exception IOException if an input or output error occurs
384         * while the servlet is handling the DELETE request
385         *
386         * @exception ServletException if the request for the
387         * DELETE cannot be handled
388         */
389        protected void doDelete(HttpServletRequest req,
390                HttpServletResponse resp)
391                throws ServletException, IOException {
392            String protocol = req.getProtocol();
393            String msg = lStrings.getString("http.method_delete_not_supported");
394            if (protocol.endsWith("1.1")) {
395                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
396            } else {
397                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
398            }
399        }
400    
401        private Method[] getAllDeclaredMethods(Class c) {
402            if (c.getName().equals("javax.servlet.http.HttpServlet"))
403                return null;
404    
405            int j = 0;
406            Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
407            Method[] thisMethods = c.getDeclaredMethods();
408    
409            if (parentMethods != null) {
410                Method[] allMethods =
411                        new Method[parentMethods.length + thisMethods.length];
412                for (int i = 0; i < parentMethods.length; i++) {
413                    allMethods[i] = parentMethods[i];
414                    j = i;
415                }
416                j++;
417                for (int i = j; i < thisMethods.length + j; i++) {
418                    allMethods[i] = thisMethods[i - j];
419                }
420                return allMethods;
421            }
422            return thisMethods;
423        }
424    
425        /**
426         * Called by the server (via the <code>service</code> method)
427         * to allow a servlet to handle a OPTIONS request.
428         *
429         * The OPTIONS request determines which HTTP methods
430         * the server supports and
431         * returns an appropriate header. For example, if a servlet
432         * overrides <code>doGet</code>, this method returns the
433         * following header:
434         *
435         * <p><code>Allow: GET, HEAD, TRACE, OPTIONS</code>
436         *
437         * <p>There's no need to override this method unless the
438         * servlet implements new HTTP methods, beyond those
439         * implemented by HTTP 1.1.
440         *
441         * @param req the {@link HttpServletRequest} object that
442         * contains the request the client made of the servlet
443         *
444         * @param resp the {@link HttpServletResponse} object that
445         * contains the response the servlet returns to the client
446         *
447         * @exception IOException if an input or output error occurs
448         * while the servlet is handling the OPTIONS request
449         *
450         * @exception ServletException if the request for the
451         * OPTIONS cannot be handled
452         */
453        protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
454                throws ServletException, IOException {
455            Method[] methods = getAllDeclaredMethods(this.getClass());
456    
457            boolean ALLOW_GET = false;
458            boolean ALLOW_HEAD = false;
459            boolean ALLOW_POST = false;
460            boolean ALLOW_PUT = false;
461            boolean ALLOW_DELETE = false;
462            boolean ALLOW_TRACE = true;
463            boolean ALLOW_OPTIONS = true;
464    
465            for (int i = 0; i < methods.length; i++) {
466                Method m = methods[i];
467    
468                if (m.getName().equals("doGet")) {
469                    ALLOW_GET = true;
470                    ALLOW_HEAD = true;
471                }
472                if (m.getName().equals("doPost"))
473                    ALLOW_POST = true;
474                if (m.getName().equals("doPut"))
475                    ALLOW_PUT = true;
476                if (m.getName().equals("doDelete"))
477                    ALLOW_DELETE = true;
478            }
479    
480            String allow = null;
481            if (ALLOW_GET)
482                if (allow == null) allow = METHOD_GET;
483            if (ALLOW_HEAD)
484                if (allow == null)
485                    allow = METHOD_HEAD;
486                else
487                    allow += ", " + METHOD_HEAD;
488            if (ALLOW_POST)
489                if (allow == null)
490                    allow = METHOD_POST;
491                else
492                    allow += ", " + METHOD_POST;
493            if (ALLOW_PUT)
494                if (allow == null)
495                    allow = METHOD_PUT;
496                else
497                    allow += ", " + METHOD_PUT;
498            if (ALLOW_DELETE)
499                if (allow == null)
500                    allow = METHOD_DELETE;
501                else
502                    allow += ", " + METHOD_DELETE;
503            if (ALLOW_TRACE)
504                if (allow == null)
505                    allow = METHOD_TRACE;
506                else
507                    allow += ", " + METHOD_TRACE;
508            if (ALLOW_OPTIONS)
509                if (allow == null)
510                    allow = METHOD_OPTIONS;
511                else
512                    allow += ", " + METHOD_OPTIONS;
513    
514            resp.setHeader("Allow", allow);
515        }
516    
517        /**
518         * Called by the server (via the <code>service</code> method)
519         * to allow a servlet to handle a TRACE request.
520         *
521         * A TRACE returns the headers sent with the TRACE
522         * request to the client, so that they can be used in
523         * debugging. There's no need to override this method.
524         *
525         * @param req the {@link HttpServletRequest} object that
526         * contains the request the client made of the servlet
527         *
528         * @param resp the {@link HttpServletResponse} object that
529         * contains the response the servlet returns to the client
530         *
531         * @exception IOException if an input or output error occurs
532         * while the servlet is handling the TRACE request
533         *
534         * @exception ServletException if the request for the
535         * TRACE cannot be handled
536         */
537        protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
538                throws ServletException, IOException {
539    
540            int responseLength;
541    
542            String CRLF = "\r\n";
543            String responseString = "TRACE " + req.getRequestURI() +
544                    " " + req.getProtocol();
545    
546            Enumeration reqHeaderEnum = req.getHeaderNames();
547    
548            while (reqHeaderEnum.hasMoreElements()) {
549                String headerName = (String) reqHeaderEnum.nextElement();
550                responseString += CRLF + headerName + ": " +
551                        req.getHeader(headerName);
552            }
553    
554            responseString += CRLF;
555    
556            responseLength = responseString.length();
557    
558            resp.setContentType("message/http");
559            resp.setContentLength(responseLength);
560            ServletOutputStream out = resp.getOutputStream();
561            out.print(responseString);
562            out.close();
563            return;
564        }
565    
566        /**
567         * Receives standard HTTP requests from the public
568         * <code>service</code> method and dispatches
569         * them to the <code>do</code><i>XXX</i> methods defined in
570         * this class. This method is an HTTP-specific version of the
571         * {@link javax.servlet.Servlet#service} method. There's no
572         * need to override this method.
573         *
574         * @param req the {@link HttpServletRequest} object that
575         * contains the request the client made of the servlet
576         *
577         * @param resp the {@link HttpServletResponse} object that
578         * contains the response the servlet returns to the client
579         *
580         * @exception IOException if an input or output error occurs
581         * while the servlet is handling the HTTP request
582         *
583         * @exception ServletException if the HTTP request
584         * cannot be handled
585         *
586         * @see javax.servlet.Servlet#service
587         */
588        protected void service(HttpServletRequest req, HttpServletResponse resp)
589                throws ServletException, IOException {
590            String method = req.getMethod();
591    
592            if (method.equals(METHOD_GET)) {
593                long lastModified = getLastModified(req);
594                if (lastModified == -1) {
595                    // servlet doesn't support if-modified-since, no reason
596                    // to go through further expensive logic
597                    doGet(req, resp);
598                } else {
599                    long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
600                    if (ifModifiedSince < (lastModified / 1000 * 1000)) {
601                        // If the servlet mod time is later, call doGet()
602                        // Round down to the nearest second for a proper compare
603                        // A ifModifiedSince of -1 will always be less
604                        maybeSetLastModified(resp, lastModified);
605                        doGet(req, resp);
606                    } else {
607                        resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
608                    }
609                }
610    
611            } else if (method.equals(METHOD_HEAD)) {
612                long lastModified = getLastModified(req);
613                maybeSetLastModified(resp, lastModified);
614                doHead(req, resp);
615    
616            } else if (method.equals(METHOD_POST)) {
617                doPost(req, resp);
618    
619            } else if (method.equals(METHOD_PUT)) {
620                doPut(req, resp);
621    
622            } else if (method.equals(METHOD_DELETE)) {
623                doDelete(req, resp);
624    
625            } else if (method.equals(METHOD_OPTIONS)) {
626                doOptions(req, resp);
627    
628            } else if (method.equals(METHOD_TRACE)) {
629                doTrace(req, resp);
630    
631            } else {
632                //
633                // Note that this means NO servlet supports whatever
634                // method was requested, anywhere on this server.
635                //
636    
637                String errMsg = lStrings.getString("http.method_not_implemented");
638                Object[] errArgs = new Object[1];
639                errArgs[0] = method;
640                errMsg = MessageFormat.format(errMsg, errArgs);
641    
642                resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
643            }
644        }
645    
646        /**
647         * Sets the Last-Modified entity header field, if it has not
648         * already been set and if the value is meaningful.  Called before
649         * doGet, to ensure that headers are set before response data is
650         * written.  A subclass might have set this header already, so we
651         * check.
652         */
653        private void maybeSetLastModified(HttpServletResponse resp, long lastModified) {
654            if (resp.containsHeader(HEADER_LASTMOD))
655                return;
656            if (lastModified >= 0)
657                resp.setDateHeader(HEADER_LASTMOD, lastModified);
658        }
659    
660        /**
661         * Dispatches client requests to the protected
662         * <code>service</code> method. There's no need to
663         * override this method.
664         *
665         * @param req the {@link HttpServletRequest} object that
666         * contains the request the client made of the servlet
667         *
668         * @param res the {@link HttpServletResponse} object that
669         * contains the response the servlet returns to the client
670         *
671         * @exception IOException if an input or output error occurs
672         * while the servlet is handling the HTTP request
673         *
674         * @exception ServletException if the HTTP request cannot
675         * be handled
676         *
677         * @see javax.servlet.Servlet#service
678         */
679        public void service(ServletRequest req, ServletResponse res)
680                throws ServletException, IOException {
681            HttpServletRequest request;
682            HttpServletResponse response;
683    
684            try {
685                request = (HttpServletRequest) req;
686                response = (HttpServletResponse) res;
687            } catch (ClassCastException e) {
688                throw new ServletException("non-HTTP request or response");
689            }
690            service(request, response);
691        }
692    }
693    
694    /**
695     * A response that includes no body, for use in (dumb) "HEAD" support.
696     * This just swallows that body, counting the bytes in order to set
697     * the content length appropriately.  All other methods delegate directly
698     * to the HTTP Servlet Response object used to construct this one.
699     */
700    class NoBodyResponse implements HttpServletResponse {
701        private HttpServletResponse resp;
702        private NoBodyOutputStream noBody;
703        private PrintWriter writer;
704        private boolean didSetContentLength;
705    
706        NoBodyResponse(HttpServletResponse r) {
707            resp = r;
708            noBody = new NoBodyOutputStream();
709        }
710    
711        void setContentLength() {
712            if (!didSetContentLength) {
713                resp.setContentLength(noBody.getContentLength());
714            }
715        }
716    
717        //
718        // SERVLET RESPONSE interface methods
719        //
720    
721        public void setContentLength(int len) {
722            resp.setContentLength(len);
723            didSetContentLength = true;
724        }
725    
726        public void setCharacterEncoding(String charset) {
727            resp.setCharacterEncoding(charset);
728        }
729    
730        public void setContentType(String type) {
731            resp.setContentType(type);
732        }
733    
734        public String getContentType() {
735            return resp.getContentType();
736        }
737    
738        public ServletOutputStream getOutputStream() throws IOException {
739            return noBody;
740        }
741    
742        public String getCharacterEncoding() {
743            return resp.getCharacterEncoding();
744        }
745    
746        public PrintWriter getWriter() throws UnsupportedEncodingException {
747            if (writer == null) {
748                OutputStreamWriter w;
749    
750                w = new OutputStreamWriter(noBody, getCharacterEncoding());
751                writer = new PrintWriter(w);
752            }
753            return writer;
754        }
755    
756        public void setBufferSize(int size) throws IllegalStateException {
757            resp.setBufferSize(size);
758        }
759    
760        public int getBufferSize() {
761            return resp.getBufferSize();
762        }
763    
764        public void reset() throws IllegalStateException {
765            resp.reset();
766        }
767    
768        public void resetBuffer() throws IllegalStateException {
769            resp.resetBuffer();
770        }
771    
772        public boolean isCommitted() {
773            return resp.isCommitted();
774        }
775    
776        public void flushBuffer() throws IOException {
777            resp.flushBuffer();
778        }
779    
780        public void setLocale(Locale loc) {
781            resp.setLocale(loc);
782        }
783    
784        public Locale getLocale() {
785            return resp.getLocale();
786        }
787    
788        //
789        // HTTP SERVLET RESPONSE interface methods
790        //
791    
792        public void addCookie(Cookie cookie) {
793            resp.addCookie(cookie);
794        }
795    
796        public boolean containsHeader(String name) {
797            return resp.containsHeader(name);
798        }
799    
800        /**
801         * @deprecated
802         */
803        public void setStatus(int sc, String sm) {
804            resp.setStatus(sc, sm);
805        }
806    
807        public void setStatus(int sc) {
808            resp.setStatus(sc);
809        }
810    
811        public void setHeader(String name, String value) {
812            resp.setHeader(name, value);
813        }
814    
815        public void setIntHeader(String name, int value) {
816            resp.setIntHeader(name, value);
817        }
818    
819        public void setDateHeader(String name, long date) {
820            resp.setDateHeader(name, date);
821        }
822    
823        public void sendError(int sc, String msg) throws IOException {
824            resp.sendError(sc, msg);
825        }
826    
827        public void sendError(int sc) throws IOException {
828            resp.sendError(sc);
829        }
830    
831        public void sendRedirect(String location) throws IOException {
832            resp.sendRedirect(location);
833        }
834    
835        public String encodeURL(String url) {
836            return resp.encodeURL(url);
837        }
838    
839        public String encodeRedirectURL(String url) {
840            return resp.encodeRedirectURL(url);
841        }
842    
843        public void addHeader(String name, String value) {
844            resp.addHeader(name, value);
845        }
846    
847        public void addDateHeader(String name, long value) {
848            resp.addDateHeader(name, value);
849        }
850    
851        public void addIntHeader(String name, int value) {
852            resp.addIntHeader(name, value);
853        }
854    
855        /**
856         * @deprecated As of Version 2.1, replaced by
857         * {@link HttpServletResponse#encodeURL}.
858         */
859        public String encodeUrl(String url) {
860            return this.encodeURL(url);
861        }
862    
863        /**
864         * @deprecated As of Version 2.1, replaced by
865         * {@link HttpServletResponse#encodeRedirectURL}.
866         */
867        public String encodeRedirectUrl(String url) {
868            return this.encodeRedirectURL(url);
869        }
870    }
871    
872    
873    /**
874     * Servlet output stream that gobbles up all its data.
875     */
876    class NoBodyOutputStream extends ServletOutputStream {
877        private static final String LSTRING_FILE =
878                "javax.servlet.http.LocalStrings";
879        private static ResourceBundle lStrings =
880                ResourceBundle.getBundle(LSTRING_FILE);
881    
882        private int contentLength = 0;
883    
884        NoBodyOutputStream() {
885        }
886    
887        int getContentLength() {
888            return contentLength;
889        }
890    
891        public void write(int b) {
892            contentLength++;
893        }
894    
895        public void write(byte buf[], int offset, int len)
896                throws IOException {
897            if (len >= 0) {
898                contentLength += len;
899            } else {
900                // XXX
901                // isn't this really an IllegalArgumentException?
902    
903                String msg = lStrings.getString("err.io.negativelength");
904                throw new IOException("negative length");
905            }
906        }
907    }