View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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.net.InetSocketAddress;
23  import java.nio.ByteBuffer;
24  import java.nio.channels.ClosedChannelException;
25  import java.util.List;
26  import java.util.concurrent.TimeoutException;
27  import java.util.concurrent.atomic.AtomicBoolean;
28  import java.util.concurrent.atomic.AtomicInteger;
29  
30  import javax.servlet.DispatcherType;
31  import javax.servlet.RequestDispatcher;
32  import javax.servlet.UnavailableException;
33  import javax.servlet.http.HttpServletRequest;
34  
35  import org.eclipse.jetty.http.BadMessageException;
36  import org.eclipse.jetty.http.HttpFields;
37  import org.eclipse.jetty.http.HttpGenerator;
38  import org.eclipse.jetty.http.HttpHeader;
39  import org.eclipse.jetty.http.HttpHeaderValue;
40  import org.eclipse.jetty.http.HttpStatus;
41  import org.eclipse.jetty.http.HttpVersion;
42  import org.eclipse.jetty.http.MetaData;
43  import org.eclipse.jetty.io.ByteBufferPool;
44  import org.eclipse.jetty.io.ChannelEndPoint;
45  import org.eclipse.jetty.io.EndPoint;
46  import org.eclipse.jetty.io.EofException;
47  import org.eclipse.jetty.server.HttpChannelState.Action;
48  import org.eclipse.jetty.server.handler.ContextHandler;
49  import org.eclipse.jetty.server.handler.ErrorHandler;
50  import org.eclipse.jetty.util.BufferUtil;
51  import org.eclipse.jetty.util.Callback;
52  import org.eclipse.jetty.util.SharedBlockingCallback.Blocker;
53  import org.eclipse.jetty.util.log.Log;
54  import org.eclipse.jetty.util.log.Logger;
55  import org.eclipse.jetty.util.thread.Scheduler;
56  
57  
58  /**
59   * HttpChannel represents a single endpoint for HTTP semantic processing.
60   * The HttpChannel is both a HttpParser.RequestHandler, where it passively receives events from
61   * an incoming HTTP request, and a Runnable, where it actively takes control of the request/response
62   * life cycle and calls the application (perhaps suspending and resuming with multiple calls to run).
63   * The HttpChannel signals the switch from passive mode to active mode by returning true to one of the
64   * HttpParser.RequestHandler callbacks.   The completion of the active phase is signalled by a call to
65   * HttpTransport.completed().
66   *
67   */
68  public class HttpChannel implements Runnable, HttpOutput.Interceptor
69  {
70      private static final Logger LOG = Log.getLogger(HttpChannel.class);
71      private final AtomicBoolean _committed = new AtomicBoolean();
72      private final AtomicInteger _requests = new AtomicInteger();
73      private final Connector _connector;
74      private final HttpConfiguration _configuration;
75      private final EndPoint _endPoint;
76      private final HttpTransport _transport;
77      private final HttpChannelState _state;
78      private final Request _request;
79      private final Response _response;
80      private MetaData.Response _committedMetaData;
81      private RequestLog _requestLog;
82  
83      /** Bytes written after interception (eg after compression) */
84      private long _written;
85  
86      public HttpChannel(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport)
87      {
88          _connector = connector;
89          _configuration = configuration;
90          _endPoint = endPoint;
91          _transport = transport;
92  
93          _state = new HttpChannelState(this);
94          _request = new Request(this, newHttpInput(_state));
95          _response = new Response(this, newHttpOutput());
96          _requestLog=_connector==null?null:_connector.getServer().getRequestLog();
97          if (LOG.isDebugEnabled())
98              LOG.debug("new {} -> {},{},{}",this,_endPoint,_endPoint.getConnection(),_state);
99      }
100 
101     protected HttpInput newHttpInput(HttpChannelState state)
102     {
103         return new HttpInput(state);
104     }
105 
106     protected HttpOutput newHttpOutput()
107     {
108         return new HttpOutput(this);
109     }
110 
111     public HttpChannelState getState()
112     {
113         return _state;
114     }
115 
116     public long getBytesWritten()
117     {
118         return _written;
119     }
120 
121     /**
122      * @return the number of requests handled by this connection
123      */
124     public int getRequests()
125     {
126         return _requests.get();
127     }
128 
129     public Connector getConnector()
130     {
131         return _connector;
132     }
133 
134     public HttpTransport getHttpTransport()
135     {
136         return _transport;
137     }
138 
139     public RequestLog getRequestLog()
140     {
141         return _requestLog;
142     }
143 
144     public void setRequestLog(RequestLog requestLog)
145     {
146         _requestLog = requestLog;
147     }
148 
149     public void addRequestLog(RequestLog requestLog)
150     {
151         if (_requestLog==null)
152             _requestLog = requestLog;
153         else if (_requestLog instanceof RequestLogCollection)
154             ((RequestLogCollection) _requestLog).add(requestLog);
155         else
156             _requestLog = new RequestLogCollection(_requestLog, requestLog);
157     }
158 
159     public MetaData.Response getCommittedMetaData()
160     {
161         return _committedMetaData;
162     }
163 
164     /**
165      * Get the idle timeout.
166      * <p>This is implemented as a call to {@link EndPoint#getIdleTimeout()}, but may be
167      * overridden by channels that have timeouts different from their connections.
168      * @return the idle timeout (in milliseconds)
169      */
170     public long getIdleTimeout()
171     {
172         return _endPoint.getIdleTimeout();
173     }
174 
175     /**
176      * Set the idle timeout.
177      * <p>This is implemented as a call to {@link EndPoint#setIdleTimeout(long)}, but may be
178      * overridden by channels that have timeouts different from their connections.
179      * @param timeoutMs the idle timeout in milliseconds
180      */
181     public void setIdleTimeout(long timeoutMs)
182     {
183         _endPoint.setIdleTimeout(timeoutMs);
184     }
185 
186     public ByteBufferPool getByteBufferPool()
187     {
188         return _connector.getByteBufferPool();
189     }
190 
191     public HttpConfiguration getHttpConfiguration()
192     {
193         return _configuration;
194     }
195 
196     @Override
197     public boolean isOptimizedForDirectBuffers()
198     {
199         return getHttpTransport().isOptimizedForDirectBuffers();
200     }
201 
202     public Server getServer()
203     {
204         return _connector.getServer();
205     }
206 
207     public Request getRequest()
208     {
209         return _request;
210     }
211 
212     public Response getResponse()
213     {
214         return _response;
215     }
216 
217     public EndPoint getEndPoint()
218     {
219         return _endPoint;
220     }
221 
222     public InetSocketAddress getLocalAddress()
223     {
224         return _endPoint.getLocalAddress();
225     }
226 
227     public InetSocketAddress getRemoteAddress()
228     {
229         return _endPoint.getRemoteAddress();
230     }
231 
232     /**
233      * If the associated response has the Expect header set to 100 Continue,
234      * then accessing the input stream indicates that the handler/servlet
235      * is ready for the request body and thus a 100 Continue response is sent.
236      *
237      * @param available estimate of the number of bytes that are available
238      * @throws IOException if the InputStream cannot be created
239      */
240     public void continue100(int available) throws IOException
241     {
242         throw new UnsupportedOperationException();
243     }
244 
245     public void recycle()
246     {
247         _committed.set(false);
248         _request.recycle();
249         _response.recycle();
250         _committedMetaData=null;
251         _requestLog=_connector==null?null:_connector.getServer().getRequestLog();
252         _written=0;
253     }
254 
255     public void asyncReadFillInterested()
256     {
257     }
258 
259     @Override
260     public void run()
261     {
262         handle();
263     }
264 
265     /**
266      * @return True if the channel is ready to continue handling (ie it is not suspended)
267      */
268     public boolean handle()
269     {
270         if (LOG.isDebugEnabled())
271             LOG.debug("{} handle {} ", this,_request.getHttpURI());
272 
273         HttpChannelState.Action action = _state.handling();
274 
275         // Loop here to handle async request redispatches.
276         // The loop is controlled by the call to async.unhandle in the
277         // finally block below.  Unhandle will return false only if an async dispatch has
278         // already happened when unhandle is called.
279         loop: while (!getServer().isStopped())
280         {
281             try
282             {
283                 if (LOG.isDebugEnabled())
284                     LOG.debug("{} action {}",this,action);
285 
286                 switch(action)
287                 {
288                     case TERMINATED:
289                     case WAIT:
290                         break loop;
291 
292                     case DISPATCH:
293                     {
294                         if (!_request.hasMetaData())
295                             throw new IllegalStateException("state=" + _state);
296                         _request.setHandled(false);
297                         _response.getHttpOutput().reopen();
298 
299                         List<HttpConfiguration.Customizer> customizers = _configuration.getCustomizers();
300                         if (!customizers.isEmpty())
301                         {
302                             for (HttpConfiguration.Customizer customizer : customizers)
303                                 customizer.customize(getConnector(), _configuration, _request);
304                         }
305                         try
306                         {
307                             _request.setDispatcherType(DispatcherType.REQUEST);
308                             getServer().handle(this);
309                         }
310                         finally
311                         {
312                             _request.setDispatcherType(null);
313                         }
314                         break;
315                     }
316 
317                     case ASYNC_DISPATCH:
318                     {
319                         _request.setHandled(false);
320                         _response.getHttpOutput().reopen();
321                         
322                         try
323                         {
324                             _request.setDispatcherType(DispatcherType.ASYNC);
325                             getServer().handleAsync(this);
326                         }
327                         finally
328                         {
329                             _request.setDispatcherType(null);
330                         }
331                         break;
332                     }
333 
334                     case ERROR_DISPATCH:
335                     {
336                         Throwable ex = _state.getAsyncContextEvent().getThrowable();
337 
338                         // Check for error dispatch loops
339                         Integer loop_detect = (Integer)_request.getAttribute("org.eclipse.jetty.server.ERROR_DISPATCH");
340                         if (loop_detect==null)
341                             loop_detect=1;
342                         else
343                             loop_detect=loop_detect+1;
344                         _request.setAttribute("org.eclipse.jetty.server.ERROR_DISPATCH",loop_detect);
345                         if (loop_detect > getHttpConfiguration().getMaxErrorDispatches())
346                         {
347                             LOG.warn("ERROR_DISPATCH loop detected on {} {}",_request,ex);
348                             try
349                             {
350                                 _response.sendError(HttpStatus.INTERNAL_SERVER_ERROR_500);
351                             }
352                             finally
353                             {
354                                 _state.errorComplete();
355                             }
356                             break loop;
357                         }
358 
359                         _request.setHandled(false);
360                         _response.resetBuffer();
361                         _response.getHttpOutput().reopen();
362 
363 
364                         String reason;
365                         if (ex == null || ex instanceof TimeoutException)
366                         {
367                             reason = "Async Timeout";
368                         }
369                         else
370                         {
371                             reason = HttpStatus.Code.INTERNAL_SERVER_ERROR.getMessage();
372                             _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ex);
373                         }
374 
375                         _request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, 500);
376                         _request.setAttribute(RequestDispatcher.ERROR_MESSAGE, reason);
377                         _request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, _request.getRequestURI());
378 
379                         _response.setStatusWithReason(HttpStatus.INTERNAL_SERVER_ERROR_500, reason);
380 
381                         ErrorHandler eh = ErrorHandler.getErrorHandler(getServer(), _state.getContextHandler());
382                         if (eh instanceof ErrorHandler.ErrorPageMapper)
383                         {
384                             String error_page = ((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_state.getAsyncContextEvent().getSuppliedRequest());
385                             if (error_page != null)
386                                 _state.getAsyncContextEvent().setDispatchPath(error_page);
387                         }
388 
389                         
390                         try
391                         {
392                             _request.setDispatcherType(DispatcherType.ERROR);
393                             getServer().handleAsync(this);
394                         }
395                         finally
396                         {
397                             _request.setDispatcherType(null);
398                         }
399                         break;
400                     }
401 
402                     case READ_CALLBACK:
403                     {
404                         ContextHandler handler=_state.getContextHandler();
405                         if (handler!=null)
406                             handler.handle(_request,_request.getHttpInput());
407                         else
408                             _request.getHttpInput().run();
409                         break;
410                     }
411 
412                     case WRITE_CALLBACK:
413                     {
414                         ContextHandler handler=_state.getContextHandler();
415                         if (handler!=null)
416                             handler.handle(_request,_response.getHttpOutput());
417                         else
418                             _response.getHttpOutput().run();
419                         break;
420                     }
421 
422                     case ASYNC_ERROR:
423                     {
424                         _state.onError();
425                         break;
426                     }
427 
428                     case COMPLETE:
429                     {
430                         // TODO do onComplete here for continuations to work
431 //                        _state.onComplete();
432 
433                         if (!_response.isCommitted() && !_request.isHandled())
434                             _response.sendError(404);
435                         else
436                             _response.closeOutput();
437                         _request.setHandled(true);
438 
439                         // TODO do onComplete here to detect errors in final flush
440                          _state.onComplete();
441 
442                         onCompleted();
443 
444                         break loop;
445                     }
446 
447                     default:
448                     {
449                         throw new IllegalStateException("state="+_state);
450                     }
451                 }
452             }
453             catch (EofException|QuietServletException|BadMessageException e)
454             {
455                 if (LOG.isDebugEnabled())
456                     LOG.debug(e);
457                 handleException(e);
458             }
459             catch (Throwable e)
460             {
461                 if ("ContinuationThrowable".equals(e.getClass().getSimpleName()))
462                 {
463                     LOG.ignore(e);
464                 }
465                 else
466                 {
467                     if (_connector.isStarted())
468                         LOG.warn(String.valueOf(_request.getHttpURI()), e);
469                     else
470                         LOG.debug(String.valueOf(_request.getHttpURI()), e);
471                     handleException(e);
472                 }
473             }
474 
475             action = _state.unhandle();
476         }
477 
478         if (LOG.isDebugEnabled())
479             LOG.debug("{} handle exit, result {}", this, action);
480 
481         boolean suspended=action==Action.WAIT;
482         return !suspended;
483     }
484 
485     /**
486      * <p>Sends an error 500, performing a special logic to detect whether the request is suspended,
487      * to avoid concurrent writes from the application.</p>
488      * <p>It may happen that the application suspends, and then throws an exception, while an application
489      * spawned thread writes the response content; in such case, we attempt to commit the error directly
490      * bypassing the {@link ErrorHandler} mechanisms and the response OutputStream.</p>
491      *
492      * @param x the Throwable that caused the problem
493      */
494     protected void handleException(Throwable x)
495     {
496         if (_state.isAsyncStarted())
497         {
498             // Handle exception via AsyncListener onError
499             Throwable root = _state.getAsyncContextEvent().getThrowable();
500             if (root==null)
501             {
502                 _state.error(x);
503             }
504             else
505             {
506                 // TODO Can this happen?  Should this just be ISE???
507                 // We've already processed an error before!
508                 root.addSuppressed(x);
509                 LOG.warn("Error while handling async error: ", root);
510                 abort(x);
511                 _state.errorComplete();
512             }
513         }
514         else
515         {
516             try
517             {
518                 // Handle error normally
519                 _request.setHandled(true);
520                 _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, x);
521                 _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, x.getClass());
522 
523                 if (isCommitted())
524                 {
525                     abort(x);
526                     if (LOG.isDebugEnabled())
527                         LOG.debug("Could not send response error 500, already committed", x);
528                 }
529                 else
530                 {
531                     _response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
532 
533                     if (x instanceof BadMessageException)
534                     {
535                         BadMessageException bme = (BadMessageException)x;
536                         _response.sendError(bme.getCode(), bme.getReason());
537                     }
538                     else if (x instanceof UnavailableException)
539                     {
540                         if (((UnavailableException)x).isPermanent())
541                             _response.sendError(HttpStatus.NOT_FOUND_404);
542                         else
543                             _response.sendError(HttpStatus.SERVICE_UNAVAILABLE_503);
544                     }
545                     else
546                         _response.sendError(HttpStatus.INTERNAL_SERVER_ERROR_500);
547                 }
548             }
549             catch (Throwable e)
550             {
551                 abort(e);
552                 if (LOG.isDebugEnabled())
553                     LOG.debug("Could not commit response error 500", e);
554             }
555         }
556     }
557 
558     public boolean isExpecting100Continue()
559     {
560         return false;
561     }
562 
563     public boolean isExpecting102Processing()
564     {
565         return false;
566     }
567 
568     @Override
569     public String toString()
570     {
571         return String.format("%s@%x{r=%s,c=%b,a=%s,uri=%s}",
572                 getClass().getSimpleName(),
573                 hashCode(),
574                 _requests,
575                 _committed.get(),
576                 _state.getState(),
577                 _request.getHttpURI());
578     }
579 
580     public void onRequest(MetaData.Request request)
581     {
582         _requests.incrementAndGet();
583         _request.setTimeStamp(System.currentTimeMillis());
584         HttpFields fields = _response.getHttpFields();
585         if (_configuration.getSendDateHeader() && !fields.contains(HttpHeader.DATE))
586             fields.put(_connector.getServer().getDateField());
587 
588         _request.setMetaData(request);
589     }
590 
591     public boolean onContent(HttpInput.Content content)
592     {
593         if (LOG.isDebugEnabled())
594             LOG.debug("{} content {}", this, content);
595 
596         return _request.getHttpInput().addContent(content);
597     }
598 
599     public boolean onRequestComplete()
600     {
601         if (LOG.isDebugEnabled())
602             LOG.debug("{} onRequestComplete", this);
603         return _request.getHttpInput().eof();
604     }
605 
606     public void onCompleted()
607     {
608         if (_requestLog!=null )
609             _requestLog.log(_request, _response);
610 
611         _transport.onCompleted();
612     }
613 
614     public boolean onEarlyEOF()
615     {
616         return _request.getHttpInput().earlyEOF();
617     }
618 
619     public void onBadMessage(int status, String reason)
620     {
621         if (status < 400 || status > 599)
622             status = HttpStatus.BAD_REQUEST_400;
623 
624         try
625         {
626             if (_state.handling()==Action.DISPATCH)
627             {
628                 ByteBuffer content=null;
629                 HttpFields fields=new HttpFields();
630 
631                 ErrorHandler handler=getServer().getBean(ErrorHandler.class);
632                 if (handler!=null)
633                     content=handler.badMessageError(status,reason,fields);
634 
635                 sendResponse(new MetaData.Response(HttpVersion.HTTP_1_1,status,reason,fields,BufferUtil.length(content)),content ,true);
636             }
637         }
638         catch (IOException e)
639         {
640             LOG.debug(e);
641         }
642         finally
643         {
644             // TODO: review whether it's the right state to check.
645             if (_state.unhandle()==Action.COMPLETE)
646                 _state.onComplete();
647             else
648                 throw new IllegalStateException(); // TODO: don't throw from finally blocks !
649             onCompleted();
650         }
651     }
652 
653     protected boolean sendResponse(MetaData.Response info, ByteBuffer content, boolean complete, final Callback callback)
654     {
655         boolean committing = _committed.compareAndSet(false, true);
656         if (committing)
657         {
658             // We need an info to commit
659             if (info==null)
660                 info = _response.newResponseMetaData();
661             commit(info);
662 
663             // wrap callback to process 100 responses
664             final int status=info.getStatus();
665             final Callback committed = (status<200&&status>=100)?new Commit100Callback(callback):new CommitCallback(callback);
666 
667             // committing write
668             _transport.send(info, _request.isHead(), content, complete, committed);
669         }
670         else if (info==null)
671         {
672             // This is a normal write
673             _transport.send(null,_request.isHead(), content, complete, callback);
674         }
675         else
676         {
677             callback.failed(new IllegalStateException("committed"));
678         }
679         return committing;
680     }
681 
682     protected boolean sendResponse(MetaData.Response info, ByteBuffer content, boolean complete) throws IOException
683     {
684         try(Blocker blocker = _response.getHttpOutput().acquireWriteBlockingCallback())
685         {
686             boolean committing = sendResponse(info,content,complete,blocker);
687             blocker.block();
688             return committing;
689         }
690         catch (Throwable failure)
691         {
692             if (LOG.isDebugEnabled())
693                 LOG.debug(failure);
694             abort(failure);
695             throw failure;
696         }
697     }
698 
699     protected void commit (MetaData.Response info)
700     {
701         _committedMetaData=info;
702         if (LOG.isDebugEnabled())
703             LOG.debug("Commit {} to {}",info,this);
704     }
705 
706     public boolean isCommitted()
707     {
708         return _committed.get();
709     }
710 
711     /**
712      * <p>Non-Blocking write, committing the response if needed.</p>
713      * Called as last link in HttpOutput.Filter chain
714      * @param content  the content buffer to write
715      * @param complete whether the content is complete for the response
716      * @param callback Callback when complete or failed
717      */
718     @Override
719     public void write(ByteBuffer content, boolean complete, Callback callback)
720     {
721         _written+=BufferUtil.length(content);
722         sendResponse(null,content,complete,callback);
723     }
724 
725     public HttpOutput.Interceptor getNextInterceptor()
726     {
727         return null;
728     }
729 
730     protected void execute(Runnable task)
731     {
732         _connector.getExecutor().execute(task);
733     }
734 
735     public Scheduler getScheduler()
736     {
737         return _connector.getScheduler();
738     }
739 
740     /**
741      * @return true if the HttpChannel can efficiently use direct buffer (typically this means it is not over SSL or a multiplexed protocol)
742      */
743     public boolean useDirectBuffers()
744     {
745         return getEndPoint() instanceof ChannelEndPoint;
746     }
747 
748     /**
749      * If a write or similar operation to this channel fails,
750      * then this method should be called.
751      * <p>
752      * The standard implementation calls {@link HttpTransport#abort(Throwable)}.
753      *
754      * @param failure the failure that caused the abort.
755      */
756     public void abort(Throwable failure)
757     {
758         _transport.abort(failure);
759     }
760 
761     private class CommitCallback implements Callback
762     {
763         private final Callback _callback;
764 
765         private CommitCallback(Callback callback)
766         {
767             _callback = callback;
768         }
769 
770         @Override
771         public boolean isNonBlocking()
772         {
773             return _callback.isNonBlocking();
774         }
775 
776         @Override
777         public void succeeded()
778         {
779             _callback.succeeded();
780         }
781 
782         @Override
783         public void failed(final Throwable x)
784         {
785             if (LOG.isDebugEnabled())
786                 LOG.debug("Commit failed", x);
787 
788             if (x instanceof EofException || x instanceof ClosedChannelException)
789             {
790                 _callback.failed(x);
791                 _response.getHttpOutput().closed();
792             }
793             else
794             {
795                 _transport.send(HttpGenerator.RESPONSE_500_INFO, false, null, true, new Callback()
796                 {
797                     @Override
798                     public void succeeded()
799                     {
800                         _callback.failed(x);
801                         _response.getHttpOutput().closed();
802                     }
803 
804                     @Override
805                     public void failed(Throwable th)
806                     {
807                         _callback.failed(x);
808                         _response.getHttpOutput().closed();
809                     }
810                 });
811             }
812         }
813     }
814 
815     private class Commit100Callback extends CommitCallback
816     {
817         private Commit100Callback(Callback callback)
818         {
819             super(callback);
820         }
821 
822         @Override
823         public void succeeded()
824         {
825             if (_committed.compareAndSet(true, false))
826                 super.succeeded();
827             else
828                 super.failed(new IllegalStateException());
829         }
830 
831     }
832 
833 
834 }