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