View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.server;
15  
16  import java.io.IOException;
17  import java.io.InputStream;
18  import java.io.PrintWriter;
19  
20  import javax.servlet.ServletInputStream;
21  import javax.servlet.ServletOutputStream;
22  import javax.servlet.http.HttpServletResponse;
23  
24  import org.eclipse.jetty.continuation.ContinuationThrowable;
25  import org.eclipse.jetty.http.AbstractGenerator;
26  import org.eclipse.jetty.http.EncodedHttpURI;
27  import org.eclipse.jetty.http.Generator;
28  import org.eclipse.jetty.http.HttpBuffers;
29  import org.eclipse.jetty.http.HttpContent;
30  import org.eclipse.jetty.http.HttpException;
31  import org.eclipse.jetty.http.HttpFields;
32  import org.eclipse.jetty.http.HttpGenerator;
33  import org.eclipse.jetty.http.HttpHeaderValues;
34  import org.eclipse.jetty.http.HttpHeaders;
35  import org.eclipse.jetty.http.HttpMethods;
36  import org.eclipse.jetty.http.HttpParser;
37  import org.eclipse.jetty.http.HttpStatus;
38  import org.eclipse.jetty.http.HttpURI;
39  import org.eclipse.jetty.http.HttpVersions;
40  import org.eclipse.jetty.http.MimeTypes;
41  import org.eclipse.jetty.http.Parser;
42  import org.eclipse.jetty.io.Buffer;
43  import org.eclipse.jetty.io.Connection;
44  import org.eclipse.jetty.io.EndPoint;
45  import org.eclipse.jetty.io.EofException;
46  import org.eclipse.jetty.io.RuntimeIOException;
47  import org.eclipse.jetty.io.BufferCache.CachedBuffer;
48  import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
49  import org.eclipse.jetty.util.QuotedStringTokenizer;
50  import org.eclipse.jetty.util.StringUtil;
51  import org.eclipse.jetty.util.URIUtil;
52  import org.eclipse.jetty.util.log.Log;
53  import org.eclipse.jetty.util.resource.Resource;
54  import org.eclipse.jetty.util.thread.Timeout;
55  
56  /**
57   * <p>A HttpConnection represents the connection of a HTTP client to the server
58   * and is created by an instance of a {@link Connector}. It's prime function is 
59   * to associate {@link Request} and {@link Response} instances with a {@link EndPoint}.
60   * </p>
61   * <p>
62   * A connection is also the prime mechanism used by jetty to recycle objects without
63   * pooling.  The {@link Request},  {@link Response}, {@link HttpParser}, {@link HttpGenerator}
64   * and {@link HttpFields} instances are all recycled for the duraction of
65   * a connection. Where appropriate, allocated buffers are also kept associated
66   * with the connection via the parser and/or generator.
67   * </p>
68   * 
69   */
70  public class HttpConnection implements Connection
71  {
72      private static final int UNKNOWN = -2;
73      private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<HttpConnection>();
74  
75      private final long _timeStamp=System.currentTimeMillis();
76      private int _requests;
77      private volatile boolean _handling;
78  
79      protected final Connector _connector;
80      protected final EndPoint _endp;
81      protected final Server _server;
82      protected final HttpURI _uri;
83  
84      protected final Parser _parser;
85      protected final HttpFields _requestFields;
86      protected final Request _request;
87      protected ServletInputStream _in;
88  
89      protected final Generator _generator;
90      protected final HttpFields _responseFields;
91      protected final Response _response;
92      protected Output _out;
93      protected OutputWriter _writer;
94      protected PrintWriter _printWriter;
95  
96      int _include;
97      
98      private Object _associatedObject; // associated object
99  
100     private int _version = UNKNOWN;
101     
102     private boolean _expect = false;
103     private boolean _expect100Continue = false;
104     private boolean _expect102Processing = false;
105     private boolean _head = false;
106     private boolean _host = false;
107     private boolean  _delayedHandling=false;
108 
109     /* ------------------------------------------------------------ */
110     public static HttpConnection getCurrentConnection()
111     {
112         return __currentConnection.get();
113     }
114     
115     /* ------------------------------------------------------------ */
116     protected static void setCurrentConnection(HttpConnection connection)
117     {
118         __currentConnection.set(connection);
119     }
120 
121     /* ------------------------------------------------------------ */
122     /** Constructor
123      * 
124      */
125     public HttpConnection(Connector connector, EndPoint endpoint, Server server)
126     {
127         _uri = StringUtil.__UTF8.equals(URIUtil.__CHARSET)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
128         _connector = connector;
129         _endp = endpoint;
130         HttpBuffers ab = (HttpBuffers)_connector;
131         _parser = new HttpParser(ab.getRequestBuffers(), endpoint, new RequestHandler());
132         _requestFields = new HttpFields();
133         _responseFields = new HttpFields();
134         _request = new Request(this);
135         _response = new Response(this);
136         _generator = new HttpGenerator(ab.getResponseBuffers(), _endp);
137         _generator.setSendServerVersion(server.getSendServerVersion());
138         _server = server;
139     }
140 
141     /* ------------------------------------------------------------ */
142     protected HttpConnection(Connector connector, EndPoint endpoint, Server server,
143             Parser parser, Generator generator, Request request)
144     {
145         _uri = URIUtil.__CHARSET.equals(StringUtil.__UTF8)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
146         _connector = connector;
147         _endp = endpoint;
148         _parser = parser;
149         _requestFields = new HttpFields();
150         _responseFields = new HttpFields();
151         _request = request;
152         _response = new Response(this);
153         _generator = generator;
154         _generator.setSendServerVersion(server.getSendServerVersion());
155         _server = server;
156     }
157     
158     /* ------------------------------------------------------------ */
159     /**
160      * @return the parser used by this connection
161      */        
162     public Parser getParser()
163     {
164         return _parser;
165     }
166     
167     /* ------------------------------------------------------------ */
168     /**
169      * @return the number of requests handled by this connection
170      */
171     public int getRequests()
172     {
173         return _requests;
174     }
175 
176     /* ------------------------------------------------------------ */
177     /**
178      * @return The time this connection was established.
179      */
180     public long getTimeStamp()
181     {
182         return _timeStamp;
183     }
184     
185     /* ------------------------------------------------------------ */
186     /**
187      * @return Returns the associatedObject.
188      */
189     public Object getAssociatedObject()
190     {
191         return _associatedObject;
192     }
193 
194     /* ------------------------------------------------------------ */
195     /**
196      * @param associatedObject The associatedObject to set.
197      */
198     public void setAssociatedObject(Object associatedObject)
199     {
200         _associatedObject = associatedObject;
201     }
202 
203     /* ------------------------------------------------------------ */
204     /**
205      * @return Returns the connector.
206      */
207     public Connector getConnector()
208     {
209         return _connector;
210     }
211 
212     /* ------------------------------------------------------------ */
213     /**
214      * @return Returns the requestFields.
215      */
216     public HttpFields getRequestFields()
217     {
218         return _requestFields;
219     }
220 
221     /* ------------------------------------------------------------ */
222     /**
223      * @return Returns the responseFields.
224      */
225     public HttpFields getResponseFields()
226     {
227         return _responseFields;
228     }
229 
230     /* ------------------------------------------------------------ */
231     /**
232      * @return The result of calling {@link #getConnector}.{@link Connector#isConfidential(Request) isCondidential}(request), or false
233      *  if there is no connector.
234      */
235     public boolean isConfidential(Request request)
236     {
237         if (_connector!=null)
238             return _connector.isConfidential(request);
239         return false;
240     }
241     
242     /* ------------------------------------------------------------ */
243     /**
244      * Find out if the request is INTEGRAL security.
245      * @param request
246      * @return <code>true</code> if there is a {@link #getConnector() connector} and it considers <code>request</code>
247      *         to be {@link Connector#isIntegral(Request) integral}
248      */
249     public boolean isIntegral(Request request)
250     {
251         if (_connector!=null)
252             return _connector.isIntegral(request);
253         return false;
254     }
255 
256     /* ------------------------------------------------------------ */
257     /**
258      * @return The {@link EndPoint} for this connection.
259      */
260     public EndPoint getEndPoint()
261     {
262         return _endp;
263     }
264 
265     /* ------------------------------------------------------------ */
266     /**
267      * @return <code>false</code> (this method is not yet implemented)
268      */
269     public boolean getResolveNames()
270     {
271         return _connector.getResolveNames();
272     }
273 
274     /* ------------------------------------------------------------ */
275     /**
276      * @return Returns the request.
277      */
278     public Request getRequest()
279     {
280         return _request;
281     }
282 
283     /* ------------------------------------------------------------ */
284     /**
285      * @return Returns the response.
286      */
287     public Response getResponse()
288     {
289         return _response;
290     }
291 
292     /* ------------------------------------------------------------ */
293     /**
294      * Get the inputStream from the connection.
295      * <p>
296      * If the associated response has the Expect header set to 100 Continue,
297      * then accessing the input stream indicates that the handler/servlet
298      * is ready for the request body and thus a 100 Continue response is sent.
299      * 
300      * @return The input stream for this connection. 
301      * The stream will be created if it does not already exist.
302      */
303     public ServletInputStream getInputStream() throws IOException
304     {
305         // If the client is expecting 100 CONTINUE, then send it now.
306         if (_expect100Continue)
307         {
308             // is content missing?
309             if (((HttpParser)_parser).getHeaderBuffer()==null || ((HttpParser)_parser).getHeaderBuffer().length()<2)
310             {
311                 if (_generator.isCommitted())
312                     throw new IllegalStateException("Committed before 100 Continues");
313                 
314                 ((HttpGenerator)_generator).send1xx(HttpStatus.CONTINUE_100);
315             }
316             _expect100Continue=false;
317         }
318 
319         if (_in == null) 
320             _in = new HttpInput(((HttpParser)_parser),_connector.getMaxIdleTime());
321         return _in;
322     }
323 
324     /* ------------------------------------------------------------ */
325     /**
326      * @return The output stream for this connection. The stream will be created if it does not already exist.
327      */
328     public ServletOutputStream getOutputStream()
329     {
330         if (_out == null) 
331             _out = new Output();
332         return _out;
333     }
334 
335     /* ------------------------------------------------------------ */
336     /**
337      * @return A {@link PrintWriter} wrapping the {@link #getOutputStream output stream}. The writer is created if it
338      *    does not already exist.
339      */
340     public PrintWriter getPrintWriter(String encoding)
341     {
342         getOutputStream();
343         if (_writer==null)
344         {
345             _writer=new OutputWriter();
346             _printWriter=new PrintWriter(_writer)
347             {
348                 /* ------------------------------------------------------------ */
349                 /* 
350                  * @see java.io.PrintWriter#close()
351                  */
352                 public void close() 
353                 {
354                     try
355                     {
356                         out.close();
357                     }
358                     catch(IOException e)
359                     {
360                         Log.debug(e);
361                         setError();
362                     }
363                 }
364                 
365             };
366         }
367         _writer.setCharacterEncoding(encoding);
368         return _printWriter;
369     }
370     
371     /* ------------------------------------------------------------ */
372     public boolean isResponseCommitted()
373     {
374         return _generator.isCommitted();
375     }
376 
377     /* ------------------------------------------------------------ */
378     public void handle() throws IOException
379     {
380         // Loop while more in buffer
381         boolean more_in_buffer =true; // assume true until proven otherwise
382         boolean progress=true;
383         
384         try
385         {               
386             assert getCurrentConnection()==null;
387             assert _handling==false;
388             _handling=true;
389             setCurrentConnection(this);
390 
391             while (more_in_buffer)
392             {
393                 try
394                 {
395                     if (_request._async.isAsync())
396                     {
397                         Log.debug("async request",_request);
398                         if (!_request._async.isComplete())
399                             handleRequest();
400                         else if (!_parser.isComplete()) 
401                         {
402                             long parsed=_parser.parseAvailable();
403                             progress|=parsed>0;
404                         }
405                         
406                         if (_generator.isCommitted() && !_generator.isComplete())
407                             progress|=_generator.flushBuffer()>0;
408                         if (_endp.isBufferingOutput())
409                             _endp.flush();
410                     }
411                     else
412                     {
413                         // If we are not ended then parse available
414                         if (!_parser.isComplete()) 
415                             progress|=_parser.parseAvailable()>0;
416 
417                         // Do we have more generating to do?
418                         // Loop here because some writes may take multiple steps and
419                         // we need to flush them all before potentially blocking in the
420                         // next loop.
421                         while (_generator.isCommitted() && !_generator.isComplete())
422                         {
423                             long written=_generator.flushBuffer();
424                             if (written<=0)
425                                 break;
426                             progress=true;
427                             if (_endp.isBufferingOutput())
428                                 _endp.flush();
429                         }
430 
431                         // Flush buffers
432                         if (_endp.isBufferingOutput())
433                         {
434                             _endp.flush();
435                             if (!_endp.isBufferingOutput())
436                                 progress=true;
437                         }
438 
439                         if (!progress) 
440                             return;
441                         progress=false;
442                     }
443                 }
444                 catch (HttpException e)
445                 {
446                     if (Log.isDebugEnabled())
447                     {
448                         Log.debug("uri="+_uri);
449                         Log.debug("fields="+_requestFields);
450                         Log.debug(e);
451                     }
452                     _generator.sendError(e.getStatus(), e.getReason(), null, true);
453 
454                     _parser.reset(true);
455                     _endp.close();
456                     throw e;
457                 }
458                 finally
459                 {
460                     more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();  
461 
462                     if (_parser.isComplete() && _generator.isComplete() && !_endp.isBufferingOutput())
463                     {  
464                         if (!_generator.isPersistent())
465                         {
466                             _parser.reset(true);
467                             more_in_buffer=false;
468                         }
469 
470                         reset(!more_in_buffer);
471                         progress=true;
472                     }
473 
474                     if (_request.isAsyncStarted())
475                     {
476                         Log.debug("return with suspended request");
477                         more_in_buffer=false;
478                     }
479                     else if (_generator.isCommitted() && !_generator.isComplete() && _endp instanceof SelectChannelEndPoint) // TODO remove SelectChannel dependency
480                         ((SelectChannelEndPoint)_endp).setWritable(false);
481                 }
482             }
483         }
484         finally
485         {
486             setCurrentConnection(null);
487             _handling=false;
488         }
489     }
490     
491     /* ------------------------------------------------------------ */
492     public void scheduleTimeout(Timeout.Task task, long timeoutMs)
493     {
494         throw new UnsupportedOperationException();
495     }
496     
497     /* ------------------------------------------------------------ */
498     public void cancelTimeout(Timeout.Task task)
499     {
500         throw new UnsupportedOperationException();
501     }
502     
503     /* ------------------------------------------------------------ */
504     public void reset(boolean returnBuffers)
505     {
506         _parser.reset(returnBuffers); // TODO maybe only release when low on resources
507         _requestFields.clear();
508         _request.recycle();
509         
510         _generator.reset(returnBuffers); // TODO maybe only release when low on resources
511         _responseFields.clear();
512         _response.recycle();
513         
514         _uri.clear(); 
515     }
516     
517     /* ------------------------------------------------------------ */
518     protected void handleRequest() throws IOException
519     {
520         boolean error = false;
521 
522         String threadName=null;
523         try
524         {
525             if (Log.isDebugEnabled())
526             {
527                 threadName=Thread.currentThread().getName();
528                 Thread.currentThread().setName(threadName+" - "+_uri);
529             }
530             
531             
532             // Loop here to handle async request redispatches.  
533             // The loop is controlled by the call to async.unhandle in the
534             // finally block below.  If call is from a non-blocking connector,
535             // then the unhandle will return false only if an async dispatch has
536             // already happened when unhandle is called.   For a blocking connector,
537             // the wait for the asynchronous dispatch or timeout actually happens
538             // within the call to unhandle().
539 
540             final Server server=_server;
541             boolean handling=_request._async.handling() && server!=null && server.isRunning();
542             while (handling)
543             {
544                 _request.setHandled(false);
545 
546                 String info=null;
547                 try
548                 {
549                     _uri.getPort();
550                     info=URIUtil.canonicalPath(_uri.getDecodedPath());
551                     if (info==null)
552                         throw new HttpException(400);
553                     _request.setPathInfo(info);
554 
555                     if (_out!=null)
556                         _out.reopen();
557 
558                     if (_request._async.isInitial())
559                     {
560                         _request.setDispatcherType(DispatcherType.REQUEST);
561                         _connector.customize(_endp, _request);
562                         server.handle(this);
563                     }
564                     else 
565                     {
566                         _request.setDispatcherType(DispatcherType.ASYNC);
567                         server.handleAsync(this);
568                     }
569                 }
570                 catch (ContinuationThrowable e)
571                 {
572                     Log.debug(e);
573                 }
574                 catch (EofException e)
575                 {
576                     Log.debug(e);
577                     error=true;
578                 }
579                 catch (RuntimeIOException e)
580                 {
581                     Log.debug(e);
582                     error=true;
583                 }
584                 catch (HttpException e)
585                 {
586                     Log.debug(e);
587                     _request.setHandled(true);
588                     _response.sendError(e.getStatus(), e.getReason());
589                     error=true;
590                 }
591                 catch (Throwable e)
592                 {
593                     if (e instanceof ThreadDeath)
594                         throw (ThreadDeath)e;
595                         
596                     error=true;
597                     if (info==null)
598                     {
599                         Log.warn(_uri+": "+e);
600                         _request.setHandled(true);
601                         _generator.sendError(400, null, null, true);
602                     }
603                     else
604                     {
605                         Log.warn(""+_uri,e);
606                         _request.setHandled(true);
607                         _generator.sendError(500, null, null, true);
608                     }
609                 }
610                 finally
611                 {   
612                     handling = !_request._async.unhandle() && server.isRunning() && _server!=null;
613                 }
614             }
615         }
616         finally
617         {
618             if (threadName!=null)
619                 Thread.currentThread().setName(threadName);
620 
621             if (_request._async.isUncompleted())
622             {   
623                 _request._async.doComplete();
624                 
625                 if (_expect100Continue)
626                 {
627                     // Continue not sent so don't parse any content 
628                     _expect100Continue = false;
629                     if (_parser instanceof HttpParser)
630                         ((HttpParser)_parser).setState(HttpParser.STATE_END);
631                 }
632 
633                 if(_endp.isOpen())
634                 {
635                     if (_generator.isPersistent())
636                         _connector.persist(_endp);
637 
638                     if (error) 
639                         _endp.close();
640                     else
641                     {
642                         if (!_response.isCommitted() && !_request.isHandled())
643                             _response.sendError(HttpServletResponse.SC_NOT_FOUND);
644                         _response.complete();
645                     }
646                 }
647                 else
648                 {
649                     _response.complete(); 
650                 }
651 
652                 _request.setHandled(true);
653             } 
654         }
655     }
656 
657     /* ------------------------------------------------------------ */
658     public void commitResponse(boolean last) throws IOException
659     {
660         if (!_generator.isCommitted())
661         {
662             _generator.setResponse(_response.getStatus(), _response.getReason());
663             try
664             {
665                 _generator.completeHeader(_responseFields, last);
666             }
667             catch(IOException io)
668             {
669                 throw io;
670             }
671             catch(RuntimeException e)
672             {
673                 Log.warn("header full: "+e);
674                 Log.debug(e);
675                 
676                 _response.reset();
677                 _generator.reset(true);
678                 _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null);
679                 _generator.completeHeader(_responseFields,HttpGenerator.LAST);
680                 throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500);
681             }
682             
683         }
684         if (last) 
685             _generator.complete();
686     }
687 
688     /* ------------------------------------------------------------ */
689     public void completeResponse() throws IOException
690     {
691         if (!_generator.isCommitted())
692         {
693             _generator.setResponse(_response.getStatus(), _response.getReason());
694             try
695             {
696                 _generator.completeHeader(_responseFields, HttpGenerator.LAST);
697             }
698             catch(IOException io)
699             {
700                 throw io;
701             }
702             catch(RuntimeException e)
703             {
704                 Log.warn("header full: "+e);
705                 Log.debug(e);
706                 
707                 _response.reset();
708                 _generator.reset(true);
709                 _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null);
710                 _generator.completeHeader(_responseFields,HttpGenerator.LAST);
711                 throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500);
712             }
713         }
714 
715         _generator.complete();
716     }
717 
718     /* ------------------------------------------------------------ */
719     public void flushResponse() throws IOException
720     {
721         try
722         {
723             commitResponse(HttpGenerator.MORE);
724             _generator.flushBuffer();
725         }
726         catch(IOException e)
727         {
728             throw (e instanceof EofException) ? e:new EofException(e);
729         }
730     }
731 
732     /* ------------------------------------------------------------ */
733     public Generator getGenerator()
734     {
735         return _generator;
736     }
737 
738     /* ------------------------------------------------------------ */
739     public boolean isIncluding()
740     {
741         return _include>0;
742     }
743 
744     /* ------------------------------------------------------------ */
745     public void include()
746     {
747         _include++;
748     }
749 
750     /* ------------------------------------------------------------ */
751     public void included()
752     {
753         _include--;
754         if (_out!=null)
755             _out.reopen();
756     }
757 
758     /* ------------------------------------------------------------ */
759     public boolean isIdle()
760     {
761         return _generator.isIdle() && (_parser.isIdle() || _delayedHandling);
762     }
763     
764     /* ------------------------------------------------------------ */
765     /**
766      * @see org.eclipse.jetty.io.Connection#isSuspended()
767      */
768     public boolean isSuspended()
769     {
770         return _request.getAsyncContinuation().isSuspended();
771     }
772 
773     /* ------------------------------------------------------------ */
774     public boolean isExpecting100Continues()
775     {
776         return _expect100Continue;
777     }
778 
779     /* ------------------------------------------------------------ */
780     public boolean isExpecting102Processing()
781     {
782         return _expect102Processing;
783     }
784 
785     /* ------------------------------------------------------------ */
786     /* ------------------------------------------------------------ */
787     /* ------------------------------------------------------------ */
788     private class RequestHandler extends HttpParser.EventHandler
789     {
790         private String _charset;
791         
792         /*
793          * 
794          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#startRequest(org.eclipse.io.Buffer,
795          *      org.eclipse.io.Buffer, org.eclipse.io.Buffer)
796          */
797         public void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException
798         {
799             _host = false;
800             _expect = false;
801             _expect100Continue=false;
802             _expect102Processing=false;
803             _delayedHandling=false;
804             _charset=null;
805 
806             if(_request.getTimeStamp()==0)
807                 _request.setTimeStamp(System.currentTimeMillis());
808             _request.setMethod(method.toString());
809 
810             try
811             {
812                 _uri.parse(uri.array(), uri.getIndex(), uri.length());
813                 _request.setUri(_uri);
814 
815                 if (version==null)
816                 {
817                     _request.setProtocol(HttpVersions.HTTP_0_9);
818                     _version=HttpVersions.HTTP_0_9_ORDINAL;
819                 }
820                 else
821                 {
822                     version= HttpVersions.CACHE.get(version);
823                     _version = HttpVersions.CACHE.getOrdinal(version);
824                     if (_version <= 0) _version = HttpVersions.HTTP_1_0_ORDINAL;
825                     _request.setProtocol(version.toString());
826                 }
827 
828                 _head = method == HttpMethods.HEAD_BUFFER; // depends on method being decached.
829             }
830             catch (Exception e)
831             {
832                 Log.warn(method+" "+uri+" "+version+": "+e);
833                 Log.debug(e);
834                 throw new HttpException(HttpStatus.BAD_REQUEST_400,null,e);
835             }
836         }
837 
838         /*
839          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#parsedHeaderValue(org.eclipse.io.Buffer)
840          */
841         public void parsedHeader(Buffer name, Buffer value)
842         {
843             int ho = HttpHeaders.CACHE.getOrdinal(name);
844             switch (ho)
845             {
846                 case HttpHeaders.HOST_ORDINAL:
847                     // TODO check if host matched a host in the URI.
848                     _host = true;
849                     break;
850                     
851                 case HttpHeaders.EXPECT_ORDINAL:
852                     value = HttpHeaderValues.CACHE.lookup(value);
853                     switch(HttpHeaderValues.CACHE.getOrdinal(value))
854                     {
855                         case HttpHeaderValues.CONTINUE_ORDINAL:
856                             _expect100Continue=_generator instanceof HttpGenerator;
857                             break;
858 
859                         case HttpHeaderValues.PROCESSING_ORDINAL:
860                             _expect102Processing=_generator instanceof HttpGenerator;
861                             break;
862                             
863                         default:
864                             String[] values = value.toString().split(",");
865                             for  (int i=0;values!=null && i<values.length;i++)
866                             {
867                                 CachedBuffer cb=HttpHeaderValues.CACHE.get(values[i].trim());
868                                 if (cb==null)
869                                     _expect=true;
870                                 else
871                                 {
872                                     switch(cb.getOrdinal())
873                                     {
874                                         case HttpHeaderValues.CONTINUE_ORDINAL:
875                                             _expect100Continue=_generator instanceof HttpGenerator;
876                                             break;
877                                         case HttpHeaderValues.PROCESSING_ORDINAL:
878                                             _expect102Processing=_generator instanceof HttpGenerator;
879                                             break;
880                                         default:
881                                             _expect=true;
882                                     }
883                                 }
884                             }
885                     }
886                     break;
887                     
888                 case HttpHeaders.ACCEPT_ENCODING_ORDINAL:
889                 case HttpHeaders.USER_AGENT_ORDINAL:
890                     value = HttpHeaderValues.CACHE.lookup(value);
891                     break;
892                     
893                 case HttpHeaders.CONTENT_TYPE_ORDINAL:
894                     value = MimeTypes.CACHE.lookup(value);
895                     _charset=MimeTypes.getCharsetFromContentType(value);
896                     break;
897 
898                 case HttpHeaders.CONNECTION_ORDINAL:
899                     //looks rather clumsy, but the idea is to optimize for a single valued header
900                     switch(HttpHeaderValues.CACHE.getOrdinal(value))
901                     {
902                         case -1:
903                         { 
904                             String[] values = value.toString().split(",");
905                             for  (int i=0;values!=null && i<values.length;i++)
906                             {
907                                 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim());
908 
909                                 if (cb!=null)
910                                 {
911                                     switch(cb.getOrdinal())
912                                     {
913                                         case HttpHeaderValues.CLOSE_ORDINAL:
914                                             _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
915                                             _generator.setPersistent(false);
916                                             break;
917 
918                                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
919                                             if (_version==HttpVersions.HTTP_1_0_ORDINAL)
920                                                 _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
921                                             break;
922                                     }
923                                 }
924                             }
925                             break;
926                         }
927                         case HttpHeaderValues.CLOSE_ORDINAL:
928                             _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
929                             _generator.setPersistent(false);
930                             break;
931 
932                         case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
933                             if (_version==HttpVersions.HTTP_1_0_ORDINAL)
934                                 _responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
935                             break;
936                     } 
937             }
938 
939             _requestFields.add(name, value);
940         }
941 
942         /*
943          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#headerComplete()
944          */
945         public void headerComplete() throws IOException
946         {
947             _requests++;
948             _generator.setVersion(_version);
949             switch (_version)
950             {
951                 case HttpVersions.HTTP_0_9_ORDINAL:
952                     break;
953                 case HttpVersions.HTTP_1_0_ORDINAL:
954                     _generator.setHead(_head);
955                     break;
956                 case HttpVersions.HTTP_1_1_ORDINAL:
957                     _generator.setHead(_head);
958                     
959                     if (_server.getSendDateHeader())
960                         _responseFields.put(HttpHeaders.DATE_BUFFER, _request.getTimeStampBuffer(),_request.getTimeStamp());
961                     
962                     if (!_host)
963                     {
964                         _generator.setResponse(HttpStatus.BAD_REQUEST_400, null);
965                         _responseFields.put(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.CLOSE_BUFFER);
966                         _generator.completeHeader(_responseFields, true);
967                         _generator.complete();
968                         return;
969                     }
970 
971                     if (_expect)
972                     {
973                         _generator.sendError(HttpStatus.EXPECTATION_FAILED_417, null, null, true);
974                         return;
975                     }
976                     
977                     break;
978                 default:
979             }
980 
981             if(_charset!=null)
982                 _request.setCharacterEncodingUnchecked(_charset);
983             
984             // Either handle now or wait for first content
985             if ((((HttpParser)_parser).getContentLength()<=0 && !((HttpParser)_parser).isChunking())||_expect100Continue) 
986                 handleRequest();
987             else
988                 _delayedHandling=true;
989         }
990 
991         /* ------------------------------------------------------------ */
992         /*
993          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#content(int, org.eclipse.io.Buffer)
994          */
995         public void content(Buffer ref) throws IOException
996         {
997             if (_delayedHandling)
998             {
999                 _delayedHandling=false;
1000                 handleRequest();
1001             }
1002         }
1003 
1004         /* ------------------------------------------------------------ */
1005         /*
1006          * (non-Javadoc)
1007          * 
1008          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#messageComplete(int)
1009          */
1010         public void messageComplete(long contentLength) throws IOException
1011         {
1012             if (_delayedHandling)
1013             {
1014                 _delayedHandling=false;
1015                 handleRequest();
1016             }
1017         }
1018 
1019         /* ------------------------------------------------------------ */
1020         /*
1021          * (non-Javadoc)
1022          * 
1023          * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#startResponse(org.eclipse.io.Buffer, int,
1024          *      org.eclipse.io.Buffer)
1025          */
1026         public void startResponse(Buffer version, int status, Buffer reason)
1027         {
1028             Log.debug("Bad request!: "+version+" "+status+" "+reason);
1029         }
1030     }
1031 
1032     
1033     /* ------------------------------------------------------------ */
1034     /* ------------------------------------------------------------ */
1035     /* ------------------------------------------------------------ */
1036     public class Output extends HttpOutput 
1037     {
1038         Output()
1039         {
1040             super((AbstractGenerator)HttpConnection.this._generator,
1041                   _connector.isLowResources()?_connector.getLowResourceMaxIdleTime():_connector.getMaxIdleTime());
1042         }
1043         
1044         /* ------------------------------------------------------------ */
1045         /*
1046          * @see java.io.OutputStream#close()
1047          */
1048         public void close() throws IOException
1049         {
1050             if (_closed)
1051                 return;
1052             
1053             if (!isIncluding() && !super._generator.isCommitted())
1054                 commitResponse(HttpGenerator.LAST);
1055             else
1056                 flushResponse();
1057             
1058             super.close();
1059         }
1060 
1061         
1062         /* ------------------------------------------------------------ */
1063         /*
1064          * @see java.io.OutputStream#flush()
1065          */
1066         public void flush() throws IOException
1067         {
1068             if (!super._generator.isCommitted())
1069                 commitResponse(HttpGenerator.MORE);
1070             super.flush();
1071         }
1072 
1073         /* ------------------------------------------------------------ */
1074         /* 
1075          * @see javax.servlet.ServletOutputStream#print(java.lang.String)
1076          */
1077         public void print(String s) throws IOException
1078         {
1079             if (_closed)
1080                 throw new IOException("Closed");
1081             PrintWriter writer=getPrintWriter(null);
1082             writer.print(s);
1083         }
1084 
1085         /* ------------------------------------------------------------ */
1086         public void sendResponse(Buffer response) throws IOException
1087         {
1088             ((HttpGenerator)super._generator).sendResponse(response);
1089         }
1090         
1091         /* ------------------------------------------------------------ */
1092         public void sendContent(Object content) throws IOException
1093         {
1094             Resource resource=null;
1095             
1096             if (_closed)
1097                 throw new IOException("Closed");
1098             
1099             if (super._generator.getContentWritten() > 0) 
1100                 throw new IllegalStateException("!empty");
1101 
1102             if (content instanceof HttpContent)
1103             {
1104                 HttpContent c = (HttpContent) content;
1105                 Buffer contentType = c.getContentType();
1106                 if (contentType != null && !_responseFields.containsKey(HttpHeaders.CONTENT_TYPE_BUFFER))
1107                 {
1108                     String enc = _response.getSetCharacterEncoding();
1109                     if(enc==null)
1110                         _responseFields.add(HttpHeaders.CONTENT_TYPE_BUFFER, contentType);
1111                     else
1112                     {
1113                         if(contentType instanceof CachedBuffer)
1114                         {
1115                             CachedBuffer content_type = ((CachedBuffer)contentType).getAssociate(enc);
1116                             if(content_type!=null)
1117                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, content_type);
1118                             else
1119                             {
1120                                 _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, 
1121                                         contentType+";charset="+QuotedStringTokenizer.quote(enc,";= "));
1122                             }
1123                         }
1124                         else
1125                         {
1126                             _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, 
1127                                     contentType+";charset="+QuotedStringTokenizer.quote(enc,";= "));
1128                         }
1129                     }
1130                 }
1131                 if (c.getContentLength() > 0) 
1132                     _responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER, c.getContentLength());
1133                 Buffer lm = c.getLastModified();
1134                 long lml=c.getResource().lastModified();
1135                 if (lm != null) 
1136                     _responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER, lm,lml);
1137                 else if (c.getResource()!=null)
1138                 {
1139                     if (lml!=-1)
1140                         _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, lml);
1141                 }
1142                     
1143                 content = c.getBuffer();
1144                 if (content==null)
1145                     content=c.getInputStream();
1146             }
1147             else if (content instanceof Resource)
1148             {
1149                 resource=(Resource)content;
1150                 _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, resource.lastModified());
1151                 content=resource.getInputStream();
1152             }
1153             
1154             
1155             if (content instanceof Buffer)
1156             {
1157                 super._generator.addContent((Buffer) content, HttpGenerator.LAST);
1158                 commitResponse(HttpGenerator.LAST);
1159             }
1160             else if (content instanceof InputStream)
1161             {
1162                 InputStream in = (InputStream)content;
1163                 
1164                 try
1165                 {
1166                     int max = super._generator.prepareUncheckedAddContent();
1167                     Buffer buffer = super._generator.getUncheckedBuffer();
1168 
1169                     int len=buffer.readFrom(in,max);
1170 
1171                     while (len>=0)
1172                     {
1173                         super._generator.completeUncheckedAddContent();
1174                         _out.flush();
1175 
1176                         max = super._generator.prepareUncheckedAddContent();
1177                         buffer = super._generator.getUncheckedBuffer();
1178                         len=buffer.readFrom(in,max);
1179                     }
1180                     super._generator.completeUncheckedAddContent();
1181                     _out.flush();   
1182                 }
1183                 finally
1184                 {
1185                     if (resource!=null)
1186                         resource.release();
1187                     else
1188                         in.close();
1189                       
1190                 }
1191             }
1192             else
1193                 throw new IllegalArgumentException("unknown content type?");
1194             
1195             
1196         }     
1197     }
1198 
1199     /* ------------------------------------------------------------ */
1200     /* ------------------------------------------------------------ */
1201     /* ------------------------------------------------------------ */
1202     public class OutputWriter extends HttpWriter
1203     {
1204         OutputWriter()
1205         {
1206             super(HttpConnection.this._out);
1207         }
1208     }
1209 
1210 }