KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > coyote > http11 > Http11Processor


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.coyote.http11;
19
20 import java.io.IOException JavaDoc;
21 import java.io.InterruptedIOException JavaDoc;
22 import java.net.InetAddress JavaDoc;
23 import java.net.Socket JavaDoc;
24 import java.security.AccessController JavaDoc;
25 import java.security.PrivilegedAction JavaDoc;
26 import java.util.StringTokenizer JavaDoc;
27 import java.util.regex.Pattern JavaDoc;
28 import java.util.regex.PatternSyntaxException JavaDoc;
29
30 import org.apache.coyote.ActionCode;
31 import org.apache.coyote.ActionHook;
32 import org.apache.coyote.Adapter;
33 import org.apache.coyote.Request;
34 import org.apache.coyote.RequestInfo;
35 import org.apache.coyote.Response;
36 import org.apache.coyote.http11.filters.BufferedInputFilter;
37 import org.apache.coyote.http11.filters.ChunkedInputFilter;
38 import org.apache.coyote.http11.filters.ChunkedOutputFilter;
39 import org.apache.coyote.http11.filters.GzipOutputFilter;
40 import org.apache.coyote.http11.filters.IdentityInputFilter;
41 import org.apache.coyote.http11.filters.IdentityOutputFilter;
42 import org.apache.coyote.http11.filters.SavedRequestInputFilter;
43 import org.apache.coyote.http11.filters.VoidInputFilter;
44 import org.apache.coyote.http11.filters.VoidOutputFilter;
45 import org.apache.tomcat.util.buf.Ascii;
46 import org.apache.tomcat.util.buf.ByteChunk;
47 import org.apache.tomcat.util.buf.HexUtils;
48 import org.apache.tomcat.util.buf.MessageBytes;
49 import org.apache.tomcat.util.http.FastHttpDateFormat;
50 import org.apache.tomcat.util.http.MimeHeaders;
51 import org.apache.tomcat.util.net.JIoEndpoint;
52 import org.apache.tomcat.util.net.SSLSupport;
53 import org.apache.tomcat.util.res.StringManager;
54
55
56 /**
57  * Processes HTTP requests.
58  *
59  * @author Remy Maucherat
60  */

61 public class Http11Processor implements ActionHook {
62
63
64     /**
65      * Logger.
66      */

67     protected static org.apache.commons.logging.Log log
68         = org.apache.commons.logging.LogFactory.getLog(Http11Processor.class);
69
70     /**
71      * The string manager for this package.
72      */

73     protected static StringManager sm =
74         StringManager.getManager(Constants.Package);
75
76
77     // ------------------------------------------------------------ Constructor
78

79
80     public Http11Processor(int headerBufferSize, JIoEndpoint endpoint) {
81
82         this.endpoint = endpoint;
83         
84         request = new Request();
85         inputBuffer = new InternalInputBuffer(request, headerBufferSize);
86         request.setInputBuffer(inputBuffer);
87
88         response = new Response();
89         response.setHook(this);
90         outputBuffer = new InternalOutputBuffer(response, headerBufferSize);
91         response.setOutputBuffer(outputBuffer);
92         request.setResponse(response);
93
94         initializeFilters();
95
96         // Cause loading of HexUtils
97
int foo = HexUtils.DEC[0];
98
99     }
100
101
102     // ----------------------------------------------------- Instance Variables
103

104
105     /**
106      * Associated adapter.
107      */

108     protected Adapter adapter = null;
109
110
111     /**
112      * Request object.
113      */

114     protected Request request = null;
115
116
117     /**
118      * Response object.
119      */

120     protected Response response = null;
121
122
123     /**
124      * Input.
125      */

126     protected InternalInputBuffer inputBuffer = null;
127
128
129     /**
130      * Output.
131      */

132     protected InternalOutputBuffer outputBuffer = null;
133
134
135     /**
136      * State flag.
137      */

138     protected boolean started = false;
139
140
141     /**
142      * Error flag.
143      */

144     protected boolean error = false;
145
146
147     /**
148      * Keep-alive.
149      */

150     protected boolean keepAlive = true;
151
152
153     /**
154      * HTTP/1.1 flag.
155      */

156     protected boolean http11 = true;
157
158
159     /**
160      * HTTP/0.9 flag.
161      */

162     protected boolean http09 = false;
163
164
165     /**
166      * Content delimitator for the request (if false, the connection will
167      * be closed at the end of the request).
168      */

169     protected boolean contentDelimitation = true;
170
171
172     /**
173      * Is there an expectation ?
174      */

175     protected boolean expectation = false;
176
177
178     /**
179      * List of restricted user agents.
180      */

181     protected Pattern JavaDoc[] restrictedUserAgents = null;
182
183
184     /**
185      * Maximum number of Keep-Alive requests to honor.
186      */

187     protected int maxKeepAliveRequests = -1;
188
189
190     /**
191      * SSL information.
192      */

193     protected SSLSupport sslSupport;
194
195
196     /**
197      * Socket associated with the current connection.
198      */

199     protected Socket JavaDoc socket;
200
201
202     /**
203      * Remote Address associated with the current connection.
204      */

205     protected String JavaDoc remoteAddr = null;
206
207
208     /**
209      * Remote Host associated with the current connection.
210      */

211     protected String JavaDoc remoteHost = null;
212
213
214     /**
215      * Local Host associated with the current connection.
216      */

217     protected String JavaDoc localName = null;
218
219
220
221     /**
222      * Local port to which the socket is connected
223      */

224     protected int localPort = -1;
225
226
227     /**
228      * Remote port to which the socket is connected
229      */

230     protected int remotePort = -1;
231
232
233     /**
234      * The local Host address.
235      */

236     protected String JavaDoc localAddr = null;
237
238
239     /**
240      * Maximum timeout on uploads. 5 minutes as in Apache HTTPD server.
241      */

242     protected int timeout = 300000;
243
244
245     /**
246      * Flag to disable setting a different time-out on uploads.
247      */

248     protected boolean disableUploadTimeout = false;
249
250
251     /**
252      * Allowed compression level.
253      */

254     protected int compressionLevel = 0;
255
256
257     /**
258      * Minimum contentsize to make compression.
259      */

260     protected int compressionMinSize = 2048;
261
262
263     /**
264      * Socket buffering.
265      */

266     protected int socketBuffer = -1;
267
268
269     /**
270      * Max saved post size.
271      */

272     protected int maxSavePostSize = 4 * 1024;
273
274
275     /**
276      * List of user agents to not use gzip with
277      */

278     protected Pattern JavaDoc noCompressionUserAgents[] = null;
279
280     /**
281      * List of MIMES which could be gzipped
282      */

283     protected String JavaDoc[] compressableMimeTypes =
284     { "text/html", "text/xml", "text/plain" };
285
286
287     /**
288      * Host name (used to avoid useless B2C conversion on the host name).
289      */

290     protected char[] hostNameC = new char[0];
291
292
293     /**
294      * Associated endpoint.
295      */

296     protected JIoEndpoint endpoint;
297
298
299     /**
300      * Allow a customized the server header for the tin-foil hat folks.
301      */

302     protected String JavaDoc server = null;
303
304
305     // ------------------------------------------------------------- Properties
306

307
308     /**
309      * Return compression level.
310      */

311     public String JavaDoc getCompression() {
312         switch (compressionLevel) {
313         case 0:
314             return "off";
315         case 1:
316             return "on";
317         case 2:
318             return "force";
319         }
320         return "off";
321     }
322
323
324     /**
325      * Set compression level.
326      */

327     public void setCompression(String JavaDoc compression) {
328         if (compression.equals("on")) {
329             this.compressionLevel = 1;
330         } else if (compression.equals("force")) {
331             this.compressionLevel = 2;
332         } else if (compression.equals("off")) {
333             this.compressionLevel = 0;
334         } else {
335             try {
336                 // Try to parse compression as an int, which would give the
337
// minimum compression size
338
compressionMinSize = Integer.parseInt(compression);
339                 this.compressionLevel = 1;
340             } catch (Exception JavaDoc e) {
341                 this.compressionLevel = 0;
342             }
343         }
344     }
345
346     /**
347      * Set Minimum size to trigger compression.
348      */

349     public void setCompressionMinSize(int compressionMinSize) {
350         this.compressionMinSize = compressionMinSize;
351     }
352
353
354     /**
355      * Add user-agent for which gzip compression didn't works
356      * The user agent String given will be exactly matched
357      * to the user-agent header submitted by the client.
358      *
359      * @param userAgent user-agent string
360      */

361     public void addNoCompressionUserAgent(String JavaDoc userAgent) {
362         try {
363             Pattern JavaDoc nRule = Pattern.compile(userAgent);
364             noCompressionUserAgents =
365                 addREArray(noCompressionUserAgents, nRule);
366         } catch (PatternSyntaxException JavaDoc pse) {
367             log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
368         }
369     }
370
371
372     /**
373      * Set no compression user agent list (this method is best when used with
374      * a large number of connectors, where it would be better to have all of
375      * them referenced a single array).
376      */

377     public void setNoCompressionUserAgents(Pattern JavaDoc[] noCompressionUserAgents) {
378         this.noCompressionUserAgents = noCompressionUserAgents;
379     }
380
381
382     /**
383      * Set no compression user agent list.
384      * List contains users agents separated by ',' :
385      *
386      * ie: "gorilla,desesplorer,tigrus"
387      */

388     public void setNoCompressionUserAgents(String JavaDoc noCompressionUserAgents) {
389         if (noCompressionUserAgents != null) {
390             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(noCompressionUserAgents, ",");
391
392             while (st.hasMoreTokens()) {
393                 addNoCompressionUserAgent(st.nextToken().trim());
394             }
395         }
396     }
397
398     /**
399      * Add a mime-type which will be compressable
400      * The mime-type String will be exactly matched
401      * in the response mime-type header .
402      *
403      * @param mimeType mime-type string
404      */

405     public void addCompressableMimeType(String JavaDoc mimeType) {
406         compressableMimeTypes =
407             addStringArray(compressableMimeTypes, mimeType);
408     }
409
410
411     /**
412      * Set compressable mime-type list (this method is best when used with
413      * a large number of connectors, where it would be better to have all of
414      * them referenced a single array).
415      */

416     public void setCompressableMimeTypes(String JavaDoc[] compressableMimeTypes) {
417         this.compressableMimeTypes = compressableMimeTypes;
418     }
419
420
421     /**
422      * Set compressable mime-type list
423      * List contains users agents separated by ',' :
424      *
425      * ie: "text/html,text/xml,text/plain"
426      */

427     public void setCompressableMimeTypes(String JavaDoc compressableMimeTypes) {
428         if (compressableMimeTypes != null) {
429             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(compressableMimeTypes, ",");
430
431             while (st.hasMoreTokens()) {
432                 addCompressableMimeType(st.nextToken().trim());
433             }
434         }
435     }
436
437
438     /**
439      * Return the list of restricted user agents.
440      */

441     public String JavaDoc[] findCompressableMimeTypes() {
442         return (compressableMimeTypes);
443     }
444
445
446
447     // --------------------------------------------------------- Public Methods
448

449
450     /**
451      * Add input or output filter.
452      *
453      * @param className class name of the filter
454      */

455     protected void addFilter(String JavaDoc className) {
456         try {
457             Class JavaDoc clazz = Class.forName(className);
458             Object JavaDoc obj = clazz.newInstance();
459             if (obj instanceof InputFilter) {
460                 inputBuffer.addFilter((InputFilter) obj);
461             } else if (obj instanceof OutputFilter) {
462                 outputBuffer.addFilter((OutputFilter) obj);
463             } else {
464                 log.warn(sm.getString("http11processor.filter.unknown", className));
465             }
466         } catch (Exception JavaDoc e) {
467             log.error(sm.getString("http11processor.filter.error", className), e);
468         }
469     }
470
471
472     /**
473      * General use method
474      *
475      * @param sArray the StringArray
476      * @param value string
477      */

478     private String JavaDoc[] addStringArray(String JavaDoc sArray[], String JavaDoc value) {
479         String JavaDoc[] result = null;
480         if (sArray == null) {
481             result = new String JavaDoc[1];
482             result[0] = value;
483         }
484         else {
485             result = new String JavaDoc[sArray.length + 1];
486             for (int i = 0; i < sArray.length; i++)
487                 result[i] = sArray[i];
488             result[sArray.length] = value;
489         }
490         return result;
491     }
492
493
494     /**
495      * General use method
496      *
497      * @param rArray the REArray
498      * @param value Obj
499      */

500     private Pattern JavaDoc[] addREArray(Pattern JavaDoc rArray[], Pattern JavaDoc value) {
501         Pattern JavaDoc[] result = null;
502         if (rArray == null) {
503             result = new Pattern JavaDoc[1];
504             result[0] = value;
505         }
506         else {
507             result = new Pattern JavaDoc[rArray.length + 1];
508             for (int i = 0; i < rArray.length; i++)
509                 result[i] = rArray[i];
510             result[rArray.length] = value;
511         }
512         return result;
513     }
514
515
516     /**
517      * General use method
518      *
519      * @param sArray the StringArray
520      * @param value string
521      */

522     private boolean inStringArray(String JavaDoc sArray[], String JavaDoc value) {
523         for (int i = 0; i < sArray.length; i++) {
524             if (sArray[i].equals(value)) {
525                 return true;
526             }
527         }
528         return false;
529     }
530
531
532     /**
533      * Checks if any entry in the string array starts with the specified value
534      *
535      * @param sArray the StringArray
536      * @param value string
537      */

538     private boolean startsWithStringArray(String JavaDoc sArray[], String JavaDoc value) {
539         if (value == null)
540            return false;
541         for (int i = 0; i < sArray.length; i++) {
542             if (value.startsWith(sArray[i])) {
543                 return true;
544             }
545         }
546         return false;
547     }
548
549
550     /**
551      * Add restricted user-agent (which will downgrade the connector
552      * to HTTP/1.0 mode). The user agent String given will be matched
553      * via regexp to the user-agent header submitted by the client.
554      *
555      * @param userAgent user-agent string
556      */

557     public void addRestrictedUserAgent(String JavaDoc userAgent) {
558         try {
559             Pattern JavaDoc nRule = Pattern.compile(userAgent);
560             restrictedUserAgents = addREArray(restrictedUserAgents, nRule);
561         } catch (PatternSyntaxException JavaDoc pse) {
562             log.error(sm.getString("http11processor.regexp.error", userAgent), pse);
563         }
564     }
565
566
567     /**
568      * Set restricted user agent list (this method is best when used with
569      * a large number of connectors, where it would be better to have all of
570      * them referenced a single array).
571      */

572     public void setRestrictedUserAgents(Pattern JavaDoc[] restrictedUserAgents) {
573         this.restrictedUserAgents = restrictedUserAgents;
574     }
575
576
577     /**
578      * Set restricted user agent list (which will downgrade the connector
579      * to HTTP/1.0 mode). List contains users agents separated by ',' :
580      *
581      * ie: "gorilla,desesplorer,tigrus"
582      */

583     public void setRestrictedUserAgents(String JavaDoc restrictedUserAgents) {
584         if (restrictedUserAgents != null) {
585             StringTokenizer JavaDoc st =
586                 new StringTokenizer JavaDoc(restrictedUserAgents, ",");
587             while (st.hasMoreTokens()) {
588                 addRestrictedUserAgent(st.nextToken().trim());
589             }
590         }
591     }
592
593
594     /**
595      * Return the list of restricted user agents.
596      */

597     public String JavaDoc[] findRestrictedUserAgents() {
598         String JavaDoc[] sarr = new String JavaDoc [restrictedUserAgents.length];
599
600         for (int i = 0; i < restrictedUserAgents.length; i++)
601             sarr[i] = restrictedUserAgents[i].toString();
602
603         return (sarr);
604     }
605
606
607     /**
608      * Set the maximum number of Keep-Alive requests to honor.
609      * This is to safeguard from DoS attacks. Setting to a negative
610      * value disables the check.
611      */

612     public void setMaxKeepAliveRequests(int mkar) {
613         maxKeepAliveRequests = mkar;
614     }
615
616
617     /**
618      * Return the number of Keep-Alive requests that we will honor.
619      */

620     public int getMaxKeepAliveRequests() {
621         return maxKeepAliveRequests;
622     }
623
624
625     /**
626      * Set the maximum size of a POST which will be buffered in SSL mode.
627      */

628     public void setMaxSavePostSize(int msps) {
629         maxSavePostSize = msps;
630     }
631
632
633     /**
634      * Return the maximum size of a POST which will be buffered in SSL mode.
635      */

636     public int getMaxSavePostSize() {
637         return maxSavePostSize;
638     }
639
640
641     /**
642      * Set the SSL information for this HTTP connection.
643      */

644     public void setSSLSupport(SSLSupport sslSupport) {
645         this.sslSupport = sslSupport;
646     }
647
648
649     /**
650      * Set the flag to control upload time-outs.
651      */

652     public void setDisableUploadTimeout(boolean isDisabled) {
653         disableUploadTimeout = isDisabled;
654     }
655
656     /**
657      * Get the flag that controls upload time-outs.
658      */

659     public boolean getDisableUploadTimeout() {
660         return disableUploadTimeout;
661     }
662
663     /**
664      * Set the socket buffer flag.
665      */

666     public void setSocketBuffer(int socketBuffer) {
667         this.socketBuffer = socketBuffer;
668         outputBuffer.setSocketBuffer(socketBuffer);
669     }
670
671     /**
672      * Get the socket buffer flag.
673      */

674     public int getSocketBuffer() {
675         return socketBuffer;
676     }
677
678     /**
679      * Set the upload timeout.
680      */

681     public void setTimeout( int timeouts ) {
682         timeout = timeouts ;
683     }
684
685     /**
686      * Get the upload timeout.
687      */

688     public int getTimeout() {
689         return timeout;
690     }
691
692
693     /**
694      * Set the server header name.
695      */

696     public void setServer( String JavaDoc server ) {
697         if (server==null || server.equals("")) {
698             this.server = null;
699         } else {
700             this.server = server;
701         }
702     }
703
704     /**
705      * Get the server header name.
706      */

707     public String JavaDoc getServer() {
708         return server;
709     }
710
711
712     /** Get the request associated with this processor.
713      *
714      * @return The request
715      */

716     public Request getRequest() {
717         return request;
718     }
719
720     /**
721      * Process pipelined HTTP requests using the specified input and output
722      * streams.
723      *
724      * @param input stream from which the HTTP requests will be read
725      * @param output stream which will be used to output the HTTP
726      * responses
727      * @throws IOException error during an I/O operation
728      */

729     public void process(Socket JavaDoc socket)
730         throws IOException JavaDoc {
731         RequestInfo rp = request.getRequestProcessor();
732         rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
733
734         // Set the remote address
735
remoteAddr = null;
736         remoteHost = null;
737         localAddr = null;
738         localName = null;
739         remotePort = -1;
740         localPort = -1;
741
742         // Setting up the I/O
743
this.socket = socket;
744         inputBuffer.setInputStream(socket.getInputStream());
745         outputBuffer.setOutputStream(socket.getOutputStream());
746
747         // Error flag
748
error = false;
749         keepAlive = true;
750
751         int keepAliveLeft = maxKeepAliveRequests;
752         int soTimeout = socket.getSoTimeout();
753         int oldSoTimeout = soTimeout;
754
755         int threadRatio = (endpoint.getCurrentThreadsBusy() * 100)
756                 / endpoint.getMaxThreads();
757         if (threadRatio > 75) {
758             keepAliveLeft = 1;
759         }
760         
761         if (soTimeout != oldSoTimeout) {
762             try {
763                 socket.setSoTimeout(soTimeout);
764             } catch (Throwable JavaDoc t) {
765                 log.debug(sm.getString("http11processor.socket.timeout"), t);
766                 error = true;
767             }
768         }
769
770         boolean keptAlive = false;
771
772         while (started && !error && keepAlive) {
773
774             // Parsing the request header
775
try {
776                 if( !disableUploadTimeout && keptAlive && soTimeout > 0 ) {
777                     socket.setSoTimeout(soTimeout);
778                 }
779                 inputBuffer.parseRequestLine();
780                 request.setStartTime(System.currentTimeMillis());
781                 keptAlive = true;
782                 if (!disableUploadTimeout) {
783                     socket.setSoTimeout(timeout);
784                 }
785                 inputBuffer.parseHeaders();
786             } catch (IOException JavaDoc e) {
787                 error = true;
788                 break;
789             } catch (Throwable JavaDoc t) {
790                 if (log.isDebugEnabled()) {
791                     log.debug(sm.getString("http11processor.header.parse"), t);
792                 }
793                 // 400 - Bad Request
794
response.setStatus(400);
795                 error = true;
796             }
797
798             // Setting up filters, and parse some request headers
799
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
800             try {
801                 prepareRequest();
802             } catch (Throwable JavaDoc t) {
803                 if (log.isDebugEnabled()) {
804                     log.debug(sm.getString("http11processor.request.prepare"), t);
805                 }
806                 // 400 - Internal Server Error
807
response.setStatus(400);
808                 error = true;
809             }
810
811             if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0)
812                 keepAlive = false;
813
814             // Process the request in the adapter
815
if (!error) {
816                 try {
817                     rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
818                     adapter.service(request, response);
819                     // Handle when the response was committed before a serious
820
// error occurred. Throwing a ServletException should both
821
// set the status to 500 and set the errorException.
822
// If we fail here, then the response is likely already
823
// committed, so we can't try and set headers.
824
if(keepAlive && !error) { // Avoid checking twice.
825
error = response.getErrorException() != null ||
826                                 statusDropsConnection(response.getStatus());
827                     }
828
829                 } catch (InterruptedIOException JavaDoc e) {
830                     error = true;
831                 } catch (Throwable JavaDoc t) {
832                     log.error(sm.getString("http11processor.request.process"), t);
833                     // 500 - Internal Server Error
834
response.setStatus(500);
835                     error = true;
836                 }
837             }
838
839             // Finish the handling of the request
840
try {
841                 rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
842                 inputBuffer.endRequest();
843             } catch (IOException JavaDoc e) {
844                 error = true;
845             } catch (Throwable JavaDoc t) {
846                 log.error(sm.getString("http11processor.request.finish"), t);
847                 // 500 - Internal Server Error
848
response.setStatus(500);
849                 error = true;
850             }
851             try {
852                 rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
853                 outputBuffer.endRequest();
854             } catch (IOException JavaDoc e) {
855                 error = true;
856             } catch (Throwable JavaDoc t) {
857                 log.error(sm.getString("http11processor.response.finish"), t);
858                 error = true;
859             }
860
861             // If there was an error, make sure the request is counted as
862
// and error, and update the statistics counter
863
if (error) {
864                 response.setStatus(500);
865             }
866             request.updateCounters();
867
868             rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
869
870             // Don't reset the param - we'll see it as ended. Next request
871
// will reset it
872
// thrA.setParam(null);
873
// Next request
874
inputBuffer.nextRequest();
875             outputBuffer.nextRequest();
876
877         }
878
879         rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
880
881         // Recycle
882
inputBuffer.recycle();
883         outputBuffer.recycle();
884
885         // Recycle ssl info
886
sslSupport = null;
887     }
888
889
890     // ----------------------------------------------------- ActionHook Methods
891

892
893     /**
894      * Send an action to the connector.
895      *
896      * @param actionCode Type of the action
897      * @param param Action parameter
898      */

899     public void action(ActionCode actionCode, Object JavaDoc param) {
900
901         if (actionCode == ActionCode.ACTION_COMMIT) {
902             // Commit current response
903

904             if (response.isCommitted())
905                 return;
906
907             // Validate and write response headers
908
prepareResponse();
909             try {
910                 outputBuffer.commit();
911             } catch (IOException JavaDoc e) {
912                 // Set error flag
913
error = true;
914             }
915
916         } else if (actionCode == ActionCode.ACTION_ACK) {
917
918             // Acknowlege request
919

920             // Send a 100 status back if it makes sense (response not committed
921
// yet, and client specified an expectation for 100-continue)
922

923             if ((response.isCommitted()) || !expectation)
924                 return;
925
926             inputBuffer.setSwallowInput(true);
927             try {
928                 outputBuffer.sendAck();
929             } catch (IOException JavaDoc e) {
930                 // Set error flag
931
error = true;
932             }
933
934         } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
935
936             try {
937                 outputBuffer.flush();
938             } catch (IOException JavaDoc e) {
939                 // Set error flag
940
error = true;
941                 response.setErrorException(e);
942             }
943
944         } else if (actionCode == ActionCode.ACTION_CLOSE) {
945             // Close
946

947             // End the processing of the current request, and stop any further
948
// transactions with the client
949

950             try {
951                 outputBuffer.endRequest();
952  &