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