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