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