]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/server/HttpConnection.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / server / HttpConnection.java
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
19 package org.eclipse.jetty.server;
20
21 import java.io.IOException;
22 import java.nio.ByteBuffer;
23 import java.util.concurrent.RejectedExecutionException;
24
25 import org.eclipse.jetty.http.HttpGenerator;
26 import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
27 import org.eclipse.jetty.http.HttpHeader;
28 import org.eclipse.jetty.http.HttpHeaderValue;
29 import org.eclipse.jetty.http.HttpMethod;
30 import org.eclipse.jetty.http.HttpParser;
31 import org.eclipse.jetty.http.HttpStatus;
32 import org.eclipse.jetty.http.HttpVersion;
33 import org.eclipse.jetty.io.AbstractConnection;
34 import org.eclipse.jetty.io.ByteBufferPool;
35 import org.eclipse.jetty.io.Connection;
36 import org.eclipse.jetty.io.EndPoint;
37 import org.eclipse.jetty.io.EofException;
38 import org.eclipse.jetty.util.BufferUtil;
39 import org.eclipse.jetty.util.Callback;
40 import org.eclipse.jetty.util.IteratingCallback;
41 import org.eclipse.jetty.util.log.Log;
42 import org.eclipse.jetty.util.log.Logger;
43
44 /**
45  * <p>A {@link Connection} that handles the HTTP protocol.</p>
46  */
47 public class HttpConnection extends AbstractConnection implements Runnable, HttpTransport
48 {
49     public static final String UPGRADE_CONNECTION_ATTRIBUTE = "org.eclipse.jetty.server.HttpConnection.UPGRADE";
50     private static final boolean REQUEST_BUFFER_DIRECT=false;
51     private static final boolean HEADER_BUFFER_DIRECT=false;
52     private static final boolean CHUNK_BUFFER_DIRECT=false;
53     private static final Logger LOG = Log.getLogger(HttpConnection.class);
54     private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<>();
55
56     private final HttpConfiguration _config;
57     private final Connector _connector;
58     private final ByteBufferPool _bufferPool;
59     private final HttpGenerator _generator;
60     private final HttpChannelOverHttp _channel;
61     private final HttpParser _parser;
62     private volatile ByteBuffer _requestBuffer = null;
63     private volatile ByteBuffer _chunk = null;
64
65
66     /* ------------------------------------------------------------ */
67     /** Get the current connection that this thread is dispatched to.
68      * Note that a thread may be processing a request asynchronously and 
69      * thus not be dispatched to the connection.  
70      * @see Request#getAttribute(String) for a more general way to access the HttpConnection
71      * @return the current HttpConnection or null
72      */
73     public static HttpConnection getCurrentConnection()
74     {
75         return __currentConnection.get();
76     }
77
78     protected static HttpConnection setCurrentConnection(HttpConnection connection)
79     {
80         HttpConnection last=__currentConnection.get();
81         if (connection==null)
82             __currentConnection.remove();
83         else 
84             __currentConnection.set(connection);
85         return last;
86     }
87
88     public HttpConfiguration getHttpConfiguration()
89     {
90         return _config;
91     }
92
93     public HttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint)
94     {
95         // Tell AbstractConnector executeOnFillable==true because we want the same thread that
96         // does the HTTP parsing to handle the request so its cache is hot
97         super(endPoint, connector.getExecutor(),true);
98
99         _config = config;
100         _connector = connector;
101         _bufferPool = _connector.getByteBufferPool();
102         _generator = newHttpGenerator();
103         HttpInput<ByteBuffer> input = newHttpInput();
104         _channel = newHttpChannel(input);
105         _parser = newHttpParser();
106         LOG.debug("New HTTP Connection {}", this);
107     }
108
109     protected HttpGenerator newHttpGenerator()
110     {
111         return new HttpGenerator(_config.getSendServerVersion(),_config.getSendXPoweredBy());
112     }
113     
114     protected HttpInput<ByteBuffer> newHttpInput()
115     {
116         return new HttpInputOverHTTP(this);
117     }
118     
119     protected HttpChannelOverHttp newHttpChannel(HttpInput<ByteBuffer> httpInput)
120     {
121         return new HttpChannelOverHttp(_connector, _config, getEndPoint(), this, httpInput);
122     }
123     
124     protected HttpParser newHttpParser()
125     {
126         return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize());
127     }
128
129     protected HttpParser.RequestHandler<ByteBuffer> newRequestHandler()
130     {
131         return _channel;
132     }
133
134     public Server getServer()
135     {
136         return _connector.getServer();
137     }
138
139     public Connector getConnector()
140     {
141         return _connector;
142     }
143
144     public HttpChannel<?> getHttpChannel()
145     {
146         return _channel;
147     }
148
149     public HttpParser getParser()
150     {
151         return _parser;
152     }
153
154     @Override
155     public int getMessagesIn()
156     {
157         return getHttpChannel().getRequests();
158     }
159
160     @Override
161     public int getMessagesOut()
162     {
163         return getHttpChannel().getRequests();
164     }
165
166     void releaseRequestBuffer()
167     {
168         if (_requestBuffer != null && !_requestBuffer.hasRemaining())
169         {
170             ByteBuffer buffer=_requestBuffer;
171             _requestBuffer=null;
172             _bufferPool.release(buffer);
173         }
174     }
175     
176     public ByteBuffer getRequestBuffer()
177     {
178         if (_requestBuffer == null)
179             _requestBuffer = _bufferPool.acquire(getInputBufferSize(), REQUEST_BUFFER_DIRECT);
180         return _requestBuffer;
181     }
182
183     /**
184      * <p>Parses and handles HTTP messages.</p>
185      * <p>This method is called when this {@link Connection} is ready to read bytes from the {@link EndPoint}.
186      * However, it can also be called if there is unconsumed data in the _requestBuffer, as a result of
187      * resuming a suspended request when there is a pipelined request already read into the buffer.</p>
188      * <p>This method fills bytes and parses them until either: EOF is filled; 0 bytes are filled;
189      * the HttpChannel finishes handling; or the connection has changed.</p>
190      */
191     @Override
192     public void onFillable()
193     {
194         LOG.debug("{} onFillable {}", this, _channel.getState());
195
196         final HttpConnection last=setCurrentConnection(this);
197         int filled=Integer.MAX_VALUE;
198         boolean suspended=false;
199         try
200         {
201             // while not suspended and not upgraded
202             while (!suspended && getEndPoint().getConnection()==this)
203             {
204                 // Do we need some data to parse
205                 if (BufferUtil.isEmpty(_requestBuffer))
206                 {
207                     // If the previous iteration filled 0 bytes or saw a close, then break here 
208                     if (filled<=0)
209                         break;
210                         
211                     // Can we fill?
212                     if(getEndPoint().isInputShutdown())
213                     {
214                         // No pretend we read -1
215                         filled=-1;
216                         _parser.atEOF();
217                     }
218                     else
219                     {
220                         // Get a buffer
221                         // We are not in a race here for the request buffer as we have not yet received a request,
222                         // so there are not an possible legal threads calling #parseContent or #completed.
223                         _requestBuffer = getRequestBuffer();
224
225                         // fill
226                         filled = getEndPoint().fill(_requestBuffer);
227                         if (filled==0) // Do a retry on fill 0 (optimization for SSL connections)
228                             filled = getEndPoint().fill(_requestBuffer);
229                         
230                         // tell parser
231                         if (filled < 0)
232                             _parser.atEOF();
233                     }
234                 }
235                 
236                 // Parse the buffer
237                 if (_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer))
238                 {
239                     // The parser returned true, which indicates the channel is ready to handle a request.
240                     // Call the channel and this will either handle the request/response to completion OR,
241                     // if the request suspends, the request/response will be incomplete so the outer loop will exit.
242                     // Not that onFillable no longer manipulates the request buffer from this point and that is
243                     // left to threads calling #completed or #parseContent (which may be this thread inside handle())
244                     suspended = !_channel.handle();
245                 }
246                 else
247                 {
248                     // We parsed what we could, recycle the request buffer
249                     // We are not in a race here for the request buffer as we have not yet received a request,
250                     // so there are not an possible legal threads calling #parseContent or #completed.
251                     releaseRequestBuffer();
252                 }
253             }
254         }
255         catch (EofException e)
256         {
257             LOG.debug(e);
258         }
259         catch (Exception e)
260         {
261             if (_parser.isIdle())
262                 LOG.debug(e);
263             else
264                 LOG.warn(this.toString(), e);
265             close();
266         }
267         finally
268         {                        
269             setCurrentConnection(last);
270             if (!suspended && getEndPoint().isOpen() && getEndPoint().getConnection()==this)
271             {
272                 fillInterested();
273             }
274         }
275     }
276
277     /* ------------------------------------------------------------ */
278     /** Fill and parse data looking for content
279      * @throws IOException
280      */
281     protected void parseContent() throws IOException
282     {
283         // Not in a race here for the request buffer with #onFillable because an async consumer of
284         // content would only be started after onFillable has given up control.
285         // In a little bit of a race with #completed, but then not sure if it is legal to be doing 
286         // async calls to IO and have a completed call at the same time.
287         ByteBuffer requestBuffer = getRequestBuffer();
288
289         while (_parser.inContentState())
290         {
291             // Can the parser progress (even with an empty buffer)
292             boolean parsed = _parser.parseNext(requestBuffer==null?BufferUtil.EMPTY_BUFFER:requestBuffer);
293
294             // No, we can we try reading some content?
295             if (BufferUtil.isEmpty(requestBuffer) && getEndPoint().isInputShutdown())
296             {
297                 _parser.atEOF();
298                 if (parsed)
299                     break;
300                 continue;
301             }
302
303             if (parsed)
304                 break;
305             
306             // OK lets read some data
307             int filled=getEndPoint().fill(requestBuffer);
308             if (LOG.isDebugEnabled()) // Avoid boxing of variable 'filled'
309                 LOG.debug("{} filled {}",this,filled);
310             if (filled<=0)
311             {
312                 if (filled<0)
313                 {
314                     _parser.atEOF();
315                     continue;
316                 }
317                 break;
318             }
319         }
320     }
321     
322     @Override
323     public void completed()
324     {
325         // Handle connection upgrades
326         if (_channel.getResponse().getStatus() == HttpStatus.SWITCHING_PROTOCOLS_101)
327         {
328             Connection connection = (Connection)_channel.getRequest().getAttribute(UPGRADE_CONNECTION_ATTRIBUTE);
329             if (connection != null)
330             {
331                 LOG.debug("Upgrade from {} to {}", this, connection);
332                 onClose();
333                 getEndPoint().setConnection(connection);
334                 connection.onOpen();
335                 _channel.reset();
336                 _parser.reset();
337                 _generator.reset();
338                 releaseRequestBuffer();
339                 return;
340             }
341         }
342         
343         // Finish consuming the request
344         // If we are still expecting
345         if (_channel.isExpecting100Continue())
346             // close to seek EOF
347             _parser.close();
348         else if (_parser.inContentState() && _generator.isPersistent())
349             // Complete reading the request
350             _channel.getRequest().getHttpInput().consumeAll();
351
352         // Reset the channel, parsers and generator
353         _channel.reset();
354         if (_generator.isPersistent() && !_parser.isClosed())
355             _parser.reset();
356         else
357             _parser.close();
358         
359         // Not in a race here with onFillable, because it has given up control before calling handle.
360         // in a slight race with #completed, but not sure what to do with that anyway.
361         releaseRequestBuffer();
362         if (_chunk!=null)
363             _bufferPool.release(_chunk);
364         _chunk=null;
365         _generator.reset();
366
367         // if we are not called from the onfillable thread, schedule completion
368         if (getCurrentConnection()!=this)
369         {
370             // If we are looking for the next request
371             if (_parser.isStart())
372             {
373                 // if the buffer is empty
374                 if (BufferUtil.isEmpty(_requestBuffer))
375                 {
376                     // look for more data
377                     fillInterested();
378                 }
379                 // else if we are still running
380                 else if (getConnector().isRunning())
381                 {
382                     // Dispatched to handle a pipelined request
383                     try
384                     {
385                         getExecutor().execute(this);
386                     }
387                     catch (RejectedExecutionException e)
388                     {
389                         if (getConnector().isRunning())
390                             LOG.warn(e);
391                         else
392                             LOG.ignore(e);
393                         getEndPoint().close();
394                     }
395                 }
396                 else
397                 {
398                     getEndPoint().close();
399                 }
400             }
401             // else the parser must be closed, so seek the EOF if we are still open 
402             else if (getEndPoint().isOpen())
403                 fillInterested();
404         }
405     }
406
407     @Override
408     protected void onFillInterestedFailed(Throwable cause)
409     {
410         _parser.close();
411         super.onFillInterestedFailed(cause);
412     }
413
414     @Override
415     public void onOpen()
416     {
417         super.onOpen();
418         fillInterested();
419     }
420
421     @Override
422     public void run()
423     {
424         onFillable();
425     }
426
427
428     @Override
429     public void send(ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
430     {
431         if (info==null)
432             new ContentCallback(content,lastContent,callback).iterate();
433         else
434         {
435             // If we are still expecting a 100 continues
436             if (_channel.isExpecting100Continue())
437                 // then we can't be persistent
438                 _generator.setPersistent(false);
439             new CommitCallback(info,content,lastContent,callback).iterate();
440         }
441     }
442
443     @Override
444     public void send(ByteBuffer content, boolean lastContent, Callback callback)
445     {
446         new ContentCallback(content,lastContent,callback).iterate();
447     }
448
449     
450     protected class HttpChannelOverHttp extends HttpChannel<ByteBuffer>
451     {
452         public HttpChannelOverHttp(Connector connector, HttpConfiguration config, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input)
453         {
454             super(connector,config,endPoint,transport,input);
455         }
456         
457         @Override
458         public void earlyEOF()
459         {
460             // If we have no request yet, just close
461             if (getRequest().getMethod()==null)
462                 close();
463             else
464                 super.earlyEOF();
465         }
466
467         @Override
468         public boolean content(ByteBuffer item)
469         {
470             super.content(item);
471             return true;
472         }
473
474         @Override
475         public void badMessage(int status, String reason)
476         {
477             _generator.setPersistent(false);
478             super.badMessage(status,reason);
479         }
480
481         @Override
482         public boolean headerComplete()
483         {
484             boolean persistent;
485             HttpVersion version = getHttpVersion();
486
487             switch (version)
488             {
489                 case HTTP_0_9:
490                 {
491                     persistent = false;
492                     break;
493                 }
494                 case HTTP_1_0:
495                 {
496                     persistent = getRequest().getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString());
497                     if (!persistent)
498                         persistent = HttpMethod.CONNECT.is(getRequest().getMethod());
499                     if (persistent)
500                         getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE);
501                     break;
502                 }
503                 case HTTP_1_1:
504                 {
505                     persistent = !getRequest().getHttpFields().contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString());
506                     if (!persistent)
507                         persistent = HttpMethod.CONNECT.is(getRequest().getMethod());
508                     if (!persistent)
509                         getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE);
510                     break;
511                 }
512                 default:
513                 {
514                     throw new IllegalStateException();
515                 }
516             }
517
518             if (!persistent)
519                 _generator.setPersistent(false);
520
521             return super.headerComplete();
522         }
523
524         @Override
525         protected void handleException(Throwable x)
526         {
527             _generator.setPersistent(false);
528             super.handleException(x);
529         }
530
531         @Override
532         public void failed()
533         {
534             getEndPoint().shutdownOutput();
535         }
536         
537
538         @Override
539         public boolean messageComplete()
540         {
541             super.messageComplete();
542             return false;
543         }
544     }
545
546     private class CommitCallback extends IteratingCallback
547     {
548         final ByteBuffer _content;
549         final boolean _lastContent;
550         final ResponseInfo _info;
551         final Callback _callback;
552         ByteBuffer _header;
553
554         CommitCallback(ResponseInfo info, ByteBuffer content, boolean last, Callback callback)
555         {
556             _info=info;
557             _content=content;
558             _lastContent=last;
559             _callback=callback;
560         }
561
562         @Override
563         public Action process() throws Exception
564         {
565             ByteBuffer chunk = _chunk;
566             while (true)
567             {
568                 HttpGenerator.Result result = _generator.generateResponse(_info, _header, chunk, _content, _lastContent);
569                 if (LOG.isDebugEnabled())
570                     LOG.debug("{} generate: {} ({},{},{})@{}",
571                         this,
572                         result,
573                         BufferUtil.toSummaryString(_header),
574                         BufferUtil.toSummaryString(_content),
575                         _lastContent,
576                         _generator.getState());
577
578                 switch (result)
579                 {
580                     case NEED_HEADER:
581                     {
582                         // Look for optimisation to avoid allocating a _header buffer
583                         /*
584                          Cannot use this optimisation unless we work out how not to overwrite data in user passed arrays.
585                         if (_lastContent && _content!=null && !_content.isReadOnly() && _content.hasArray() && BufferUtil.space(_content)>_config.getResponseHeaderSize() )
586                         {
587                             // use spare space in content buffer for header buffer
588                             int p=_content.position();
589                             int l=_content.limit();
590                             _content.position(l);
591                             _content.limit(l+_config.getResponseHeaderSize());
592                             _header=_content.slice();
593                             _header.limit(0);
594                             _content.position(p);
595                             _content.limit(l);
596                         }
597                         else
598                         */
599                             _header = _bufferPool.acquire(_config.getResponseHeaderSize(), HEADER_BUFFER_DIRECT);
600                             
601                         continue;
602                     }
603                     case NEED_CHUNK:
604                     {
605                         chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, CHUNK_BUFFER_DIRECT);
606                         continue;
607                     }
608                     case FLUSH:
609                     {
610                         // Don't write the chunk or the content if this is a HEAD response
611                         if (_channel.getRequest().isHead())
612                         {
613                             BufferUtil.clear(chunk);
614                             BufferUtil.clear(_content);
615                         }
616
617                         // If we have a header
618                         if (BufferUtil.hasContent(_header))
619                         {
620                             if (BufferUtil.hasContent(_content))
621                             {
622                                 if (BufferUtil.hasContent(chunk))
623                                     getEndPoint().write(this, _header, chunk, _content);
624                                 else
625                                     getEndPoint().write(this, _header, _content);
626                             }
627                             else
628                                 getEndPoint().write(this, _header);
629                         }
630                         else if (BufferUtil.hasContent(chunk))
631                         {
632                             if (BufferUtil.hasContent(_content))
633                                 getEndPoint().write(this, chunk, _content);
634                             else
635                                 getEndPoint().write(this, chunk);
636                         }
637                         else if (BufferUtil.hasContent(_content))
638                         {
639                             getEndPoint().write(this, _content);
640                         }
641                         else
642                             continue;
643                         return Action.SCHEDULED;
644                     }
645                     case SHUTDOWN_OUT:
646                     {
647                         getEndPoint().shutdownOutput();
648                         continue;
649                     }
650                     case DONE:
651                     {
652                         if (_header!=null)
653                         {
654                             // don't release header in spare content buffer
655                             if (!_lastContent || _content==null || !_content.hasArray() || !_header.hasArray() ||  _content.array()!=_header.array())
656                                 _bufferPool.release(_header);
657                         }
658                         return Action.SUCCEEDED;
659                     }
660                     case CONTINUE:
661                     {
662                         break;
663                     }
664                     default:
665                     {
666                         throw new IllegalStateException("generateResponse="+result);
667                     }
668                 }
669             }
670         }
671
672         @Override
673         protected void completed()
674         {
675             _callback.succeeded();
676         }
677
678         @Override
679         public void failed(final Throwable x)
680         {
681             super.failed(x);
682             failedCallback(_callback,x);
683         }
684     }
685
686     private class ContentCallback extends IteratingCallback
687     {
688         final ByteBuffer _content;
689         final boolean _lastContent;
690         final Callback _callback;
691
692         ContentCallback(ByteBuffer content, boolean last, Callback callback)
693         {
694             _content=content;
695             _lastContent=last;
696             _callback=callback;
697         }
698
699         @Override
700         public Action process() throws Exception
701         {
702             ByteBuffer chunk = _chunk;
703             while (true)
704             {
705                 HttpGenerator.Result result = _generator.generateResponse(null, null, chunk, _content, _lastContent);
706                 if (LOG.isDebugEnabled())
707                     LOG.debug("{} generate: {} ({},{})@{}",
708                         this,
709                         result,
710                         BufferUtil.toSummaryString(_content),
711                         _lastContent,
712                         _generator.getState());
713
714                 switch (result)
715                 {
716                     case NEED_HEADER:
717                         throw new IllegalStateException();
718                     case NEED_CHUNK:
719                     {
720                         chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, CHUNK_BUFFER_DIRECT);
721                         continue;
722                     }
723                     case FLUSH:
724                     {
725                         // Don't write the chunk or the content if this is a HEAD response
726                         if (_channel.getRequest().isHead())
727                         {
728                             BufferUtil.clear(chunk);
729                             BufferUtil.clear(_content);
730                             continue;
731                         }
732                         else if (BufferUtil.hasContent(chunk))
733                         {
734                             if (BufferUtil.hasContent(_content))
735                                 getEndPoint().write(this, chunk, _content);
736                             else
737                                 getEndPoint().write(this, chunk);
738                         }
739                         else if (BufferUtil.hasContent(_content))
740                         {
741                             getEndPoint().write(this, _content);
742                         }
743                         else
744                             continue;
745                         return Action.SCHEDULED;
746                     }
747                     case SHUTDOWN_OUT:
748                     {
749                         getEndPoint().shutdownOutput();
750                         continue;
751                     }
752                     case DONE:
753                     {
754                         return Action.SUCCEEDED;
755                     }
756                     case CONTINUE:
757                     {
758                         break;
759                     }
760                     default:
761                     {
762                         throw new IllegalStateException("generateResponse="+result);
763                     }
764                 }
765             }
766         }
767
768         @Override
769         protected void completed()
770         {
771             _callback.succeeded();
772         }
773
774         @Override
775         public void failed(final Throwable x)
776         {
777             super.failed(x);
778             failedCallback(_callback,x);
779         }
780     }
781
782     @Override
783     public void abort()
784     {
785         // Do a direct close of the output, as this may indicate to a client that the 
786         // response is bad either with RST or by abnormal completion of chunked response.
787         getEndPoint().close();
788     }
789
790 }